public void CreateCustomBridge(IRenderContext context, GeneralPath path, PointD start, PointD end, double gapLength) { // first finish the last segment path.LineTo(start); // then calculate the gap var vectorLength = gapLength; if (vectorLength > 1) { // some helper vectors first var delta = end - start; var rightVector = delta / vectorLength; var upVector = new PointD(rightVector.Y, -rightVector.X); // get the height from the context double height = GetBridgeHeight(context); // determine bending for our arc double arc = 3; // now draw two arcs at the end and the start of the segment path.MoveTo(start + upVector * height - rightVector * arc); path.QuadTo(start + rightVector * arc, start + upVector * -height - rightVector * arc); path.MoveTo(end + rightVector * arc + upVector * height); path.QuadTo(end - rightVector * arc, end + upVector * -height + rightVector * arc); // finally make sure that the edge continues at the right location path.MoveTo(end); } else { // for very short gaps, we use a trivial rendering path.LineTo(start); path.MoveTo(end); } }
/// <summary> /// Creates the rendering visual. /// </summary> /// <remarks> /// An ellipse with a + or - symbol. /// </remarks> /// <param name="context">The render context.</param> /// <param name="node">The node.to render.</param> /// <returns>A visual representation.</returns> protected override VisualGroup CreateVisual(IRenderContext context, INode node) { var vg = new AggregationVisual(); var info = (AggregationNodeInfo)node.Tag; vg.HasNode = info.Aggregate.Node != null; vg.IsAggregated = info.IsAggregated; // draw a grey ellipse // if the aggregate represents a node draw a solid border. Draw a dashed border otherwise. var pen = info.Aggregate.Node == null ? new Pen(Brushes.LightGray) { DashStyle = DashStyle.Dash, DashPattern = new[] { 2f, 2f } } : Pens.LightGray; vg.Add(new EllipseVisual(new RectD(PointD.Origin, node.Layout.GetSize())) { Pen = pen, Brush = new SolidBrush(info.IsAggregated ? Color.FromArgb(0x11, 0x6c, 0x91, 0xbf) : Color.FromArgb(0x09, 0x6c, 0x91, 0xbf)) }); // draw a + if aggregated, - otherwise GeneralPath gp = new GeneralPath(); if (info.IsAggregated) { gp.MoveTo(0, -4); gp.LineTo(0, 4); gp.MoveTo(-4, 0); gp.LineTo(4, 0); } else { gp.MoveTo(-4, 0); gp.LineTo(4, 0); } gp.Transform(new Matrix2D(1, 0, 0, 1, node.Layout.Width / 2, node.Layout.Height / 2)); GeneralPathVisual path = new GeneralPathVisual(gp); path.Pen = new Pen(Brushes.DimGray) { Width = 1.5f }; vg.Add(path); vg.Transform = new Matrix(1, 0, 0, 1, (float)node.Layout.X, (float)node.Layout.Y); return(vg); }
public override Visual CreateVisual(IRenderContext context) { VisualGroup container = new VisualGroup(); var bounds = Bounds; var width = bounds.Width; var height = bounds.Height; var cornerSize = Math.Min(width, height) * 0.4; var path = new GeneralPath(); path.MoveTo(0, 0); path.LineTo(width - cornerSize, 0); path.LineTo(width, cornerSize); path.LineTo(width, height); path.LineTo(0, height); path.Close(); container.Add(path.CreatePath(Brush, Pen, new Matrix2D(), FillMode.Always)); path = new GeneralPath(); path.MoveTo(width - cornerSize, 0); path.LineTo(width - cornerSize, cornerSize); path.LineTo(width, cornerSize); container.Add(path.CreatePath(null, Pen, new Matrix2D(), FillMode.Never)); container.SetRenderDataCache(new PathIconState(width, height, Pen, Brush)); container.SetCanvasArrangeRect(bounds.ToRectD()); return(container); }
public override Visual CreateVisual(IRenderContext context) { VisualGroup container = new VisualGroup(); var bounds = Bounds; var width = bounds.Width; var height = bounds.Height; var path = new GeneralPath(); path.MoveTo(0, TopLeftRadius); path.QuadTo(0, 0, TopLeftRadius, 0); path.LineTo(width - TopRightRadius, 0); path.QuadTo(width, 0, width, TopRightRadius); path.LineTo(width, height - BottomRightRadius); path.QuadTo(width, height, width - BottomRightRadius, height); path.LineTo(BottomLeftRadius, height); path.QuadTo(0, height, 0, height - BottomRightRadius); path.Close(); var pathVisual = path.CreatePath(Brush, Pen, new Matrix2D(), FillMode.Always); container.Add(pathVisual); container.SetRenderDataCache(new PathIconState(width, height, Pen, Brush)); container.SetCanvasArrangeRect(bounds.ToRectD()); return(container); }
public override IVisual CreateVisual(IRenderContext context) { VisualGroup container = new VisualGroup(); var bounds = Bounds; var width = bounds.Width; var height = bounds.Height; var cornerSize = Math.Min(width, height) * 0.4; var path = new GeneralPath(); path.MoveTo(0, 0); path.LineTo(width - cornerSize, 0); path.LineTo(width, cornerSize); path.LineTo(width, height); path.LineTo(0, height); path.Close(); container.Add(new GeneralPathVisual(path) { Brush = Brush, Pen = Pen }); path = new GeneralPath(); path.MoveTo(width - cornerSize, 0); path.LineTo(width - cornerSize, cornerSize); path.LineTo(width, cornerSize); container.Add(new GeneralPathVisual(path) { Brush = null, Pen = Pen }); container.Transform = new Matrix(1, 0, 0, 1, (float)Bounds.X, (float)Bounds.Y); return(container); }
protected override GeneralPath CreatePath(IStripe stripe, IRectangle layout) { var column = (IColumn)stripe; GeneralPath outline = new GeneralPath(); //Left border: outline.MoveTo(0, 0); outline.LineTo(layout.Width, 0); outline.LineTo(layout.Width + WedgeWidth, WedgeHeight); outline.LineTo(layout.Width, 2 * WedgeHeight); outline.LineTo(layout.Width, layout.Height); outline.LineTo(0, layout.Height); if (IsFirst(column)) { outline.Close(); } else { outline.LineTo(0, layout.Height); outline.LineTo(0, 2 * WedgeHeight); outline.LineTo(WedgeWidth, WedgeHeight); outline.Close(); } return(outline); }
/// <summary> /// Creates an <see cref="IVisual"/> which renders the label. /// </summary> /// <remarks> /// Adds separate visuals for background and text in a VisualGroup. /// Delegates to method <see cref="LabelStyleBase{TVisual}.GetTransform"/> /// to get a valid transform for the label. /// </remarks> /// <param name="context">The Render Context.</param> /// <param name="label">The label to render.</param> /// <returns>A visual which renders the label.</returns> protected override VisualGroup CreateVisual(IRenderContext context, ILabel label) { var layout = label.GetLayout(); // don't render invisible labels if (layout.Width < 0 || layout.Height < 0) { return(null); } var group = new VisualGroup(); // convert to convenient coordinate space group.Transform = GetTransform(context, layout, true); // Draw the label background as a rounded rectangle GeneralPath gp = new GeneralPath(10); double xRad = layout.Width / 4; double yRad = layout.Height / 10; gp.MoveTo(0, yRad); gp.QuadTo(0, 0, xRad, 0); gp.LineTo(layout.Width - xRad, 0); gp.QuadTo(layout.Width, 0, layout.Width, yRad); gp.LineTo(layout.Width, layout.Height - yRad); gp.QuadTo(layout.Width, layout.Height, layout.Width - xRad, layout.Height); gp.LineTo(xRad, layout.Height); gp.QuadTo(0, layout.Height, 0, layout.Height - yRad); gp.Close(); var pathVisual = new GeneralPathVisual(gp) { Brush = new SolidBrush(Color.FromArgb(255, 240, 248, 255)), Pen = Pens.SkyBlue }; group.Add(pathVisual); // Draw the label's text if (context.Zoom >= buttonZoomThreshold) { // draw the action button group.Add(new ImageVisual { Image = Resources.edit_label, Rectangle = new RectD(layout.Width - (inset + buttonWidth), inset, buttonWidth, layout.Height - inset * 2) }); group.Add(new TextVisual { Text = label.Text, Font = Font, Brush = Brushes.Black, Location = PointD.Origin }); } else { group.Add(new TextVisual { Text = label.Text, Font = Font, Brush = Brushes.Black, Location = new PointD((buttonWidth + 2 * inset) * 0.5, 0) }); } return(group); }
protected override GeneralPath GetPath(IEdge edge) { // Create a general path from the locations of the ports and the bends of the edge. GeneralPath path = new GeneralPath(); path.MoveTo(GetLocation(edge.SourcePort)); foreach (var bend in edge.Bends) { path.LineTo(bend.Location); } path.LineTo(GetLocation(edge.TargetPort)); // shorten the path in order to provide room for drawing the arrows. return base.CropPath(edge, Arrows, Arrows, path); }
//////////////// New in this sample //////////////// // Path creation has been extracted into method CreatePath /// <summary> /// Creates a general path for the locations of the ports and the bends of the edge. /// </summary> /// <param name="edge">The edge.</param> /// <returns>A general path for the locations of the ports and the bends of the edge.</returns> private static GeneralPath CreatePath(IEdge edge) { GeneralPath path = new GeneralPath(); path.MoveTo(edge.SourcePort.GetLocation()); foreach (var bend in edge.Bends) { path.LineTo(bend.Location); } path.LineTo(edge.TargetPort.GetLocation()); return(path); }
protected override GeneralPath CreatePath() { var pathPoints = Edge.GetPathPoints(); var gp = new GeneralPath(pathPoints.Count + 1); gp.MoveTo(pathPoints[0]); for (int i = 1; i < pathPoints.Count; ++i) { if (i % 3 == 2) { //Skip to the next triple gp.MoveTo(pathPoints[i]); } else { //Draw a line to the next control pint in the triple gp.LineTo(pathPoints[i]); } } return(gp); }
protected override GeneralPath GetOutline(INode node) { double size = Math.Min(node.Layout.Width, node.Layout.Height); RectD bounds = new RectD(node.Layout.X + node.Layout.Width / 2 - size / 2, node.Layout.Y + node.Layout.Height / 2 - size / 2, size, size); var path = new GeneralPath(); path.MoveTo(bounds.X, bounds.CenterY); // < path.LineTo(bounds.CenterX, bounds.Y); // ^ path.LineTo(bounds.MaxX, bounds.CenterY); // > path.LineTo(bounds.CenterX, bounds.MaxY); // v path.Close(); return(path); }
protected override GeneralPath GetPath(IEdge edge) { // Create a general path from the locations of the ports and the bends of the edge. GeneralPath path = new GeneralPath(edge.Bends.Count + 2); path.MoveTo(edge.SourcePort.GetLocation()); foreach (var bend in edge.Bends) { path.LineTo(bend.Location); } path.LineTo(edge.TargetPort.GetLocation()); return(path); }
static LogicGateNodeStyle() { // path for AND nodes AndOutlinePath = new GeneralPath(); AndOutlinePath.MoveTo(0.6, 0); AndOutlinePath.LineTo(0.1, 0); AndOutlinePath.LineTo(0.1, 1); AndOutlinePath.LineTo(0.6, 1); AndOutlinePath.QuadTo(0.8, 1.0, 0.8, 0.5); AndOutlinePath.QuadTo(0.8, 0.0, 0.6, 0); // path for OR nodes OrOutlinePath = new GeneralPath(); OrOutlinePath.MoveTo(0.6, 0); OrOutlinePath.LineTo(0.1, 0); OrOutlinePath.QuadTo(0.3, 0.5, 0.1, 1); OrOutlinePath.LineTo(0.6, 1); OrOutlinePath.QuadTo(0.8, 1.0, 0.8, 0.5); OrOutlinePath.QuadTo(0.8, 0.0, 0.6, 0); // path for NAND nodes NandOutlinePath = new GeneralPath(); NandOutlinePath.MoveTo(0.6, 0); NandOutlinePath.LineTo(0.1, 0); NandOutlinePath.LineTo(0.1, 1); NandOutlinePath.LineTo(0.6, 1); NandOutlinePath.QuadTo(0.8, 1.0, 0.8, 0.5); NandOutlinePath.QuadTo(0.8, 0.0, 0.6, 0); NandOutlinePath.AppendEllipse(new RectD(0.8, 0.4, 0.1, 0.2), false); // path for NOR nodes NorOutlinePath = new GeneralPath(); NorOutlinePath.MoveTo(0.6, 0); NorOutlinePath.LineTo(0.1, 0); NorOutlinePath.QuadTo(0.3, 0.5, 0.1, 1); NorOutlinePath.LineTo(0.6, 1); NorOutlinePath.QuadTo(0.8, 1.0, 0.8, 0.5); NorOutlinePath.QuadTo(0.8, 0.0, 0.6, 0); NorOutlinePath.AppendEllipse(new RectD(0.8, 0.4, 0.1, 0.2), false); // path for NOT nodes NotOutlinePath = new GeneralPath(); NotOutlinePath.MoveTo(0.8, 0.5); NotOutlinePath.LineTo(0.1, 0); NotOutlinePath.LineTo(0.1, 1); NotOutlinePath.LineTo(0.8, 0.5); NotOutlinePath.AppendEllipse(new RectD(0.8, 0.4, 0.1, 0.2), false); }
private static GeneralPath CreatePath(IOrientedRectangle layout) { GeneralPath gp = new GeneralPath(10); double xRad = layout.Width / 4; double yRad = layout.Height / 10; gp.MoveTo(0, yRad); gp.QuadTo(0, 0, xRad, 0); gp.LineTo(layout.Width - xRad, 0); gp.QuadTo(layout.Width, 0, layout.Width, yRad); gp.LineTo(layout.Width, layout.Height - yRad); gp.QuadTo(layout.Width, layout.Height, layout.Width - xRad, layout.Height); gp.LineTo(xRad, layout.Height); gp.QuadTo(0, layout.Height, 0, layout.Height - yRad); gp.Close(); return(gp); }
/// <summary> /// Creates an <see cref="IVisual"/> which renders the label. /// </summary> /// <remarks> /// Adds separate visuals for background and text in a VisualGroup. /// Delegates to method <see cref="LabelStyleBase{TVisual}.GetTransform"/> /// to get a valid transform for the label. /// </remarks> /// <param name="context">The Render Context.</param> /// <param name="label">The label to render.</param> /// <returns>A visual which renders the label.</returns> protected override VisualGroup CreateVisual(IRenderContext context, ILabel label) { var layout = label.GetLayout(); // don't render invisible labels if (layout.Width < 0 || layout.Height < 0) { return(null); } var group = new VisualGroup(); // convert to convenient coordinate space group.Transform = GetTransform(context, layout, true); // Draw the label background as a rounded rectangle GeneralPath gp = new GeneralPath(10); double xRad = layout.Width / 4; double yRad = layout.Height / 10; gp.MoveTo(0, yRad); gp.QuadTo(0, 0, xRad, 0); gp.LineTo(layout.Width - xRad, 0); gp.QuadTo(layout.Width, 0, layout.Width, yRad); gp.LineTo(layout.Width, layout.Height - yRad); gp.QuadTo(layout.Width, layout.Height, layout.Width - xRad, layout.Height); gp.LineTo(xRad, layout.Height); gp.QuadTo(0, layout.Height, 0, layout.Height - yRad); gp.Close(); var pathVisual = new GeneralPathVisual(gp) { Brush = new SolidBrush(Color.FromArgb(255, 155, 226, 255)), Pen = Pens.SkyBlue }; group.Add(pathVisual); // Draw the label's text group.Add(new TextVisual { Text = label.Text, Font = Font, Brush = Brushes.Black, Location = PointD.Origin }); return(group); }
protected override GeneralPath GetOutline(INode node) { var cornerSize = Math.Min(node.Layout.Width, node.Layout.Height) * 0.4; var path = new GeneralPath(); path.MoveTo(0, 0); path.LineTo(node.Layout.Width - cornerSize, 0); path.LineTo(node.Layout.Width, cornerSize); path.LineTo(node.Layout.Width, node.Layout.Height); path.LineTo(0, node.Layout.Height); path.Close(); var transform = new Matrix2D(); transform.Translate(node.Layout.GetTopLeft()); path.Transform(transform); return(path); }
protected override GeneralPath GetOutline(INode node) { const double halfEllipseHeight = 0.125; GeneralPath path = new GeneralPath(); path.MoveTo(0, halfEllipseHeight); path.LineTo(0, 1 - halfEllipseHeight); path.CubicTo(0, 1, 1, 1, 1, 1 - halfEllipseHeight); path.LineTo(1, halfEllipseHeight); path.CubicTo(1, 0, 0, 0, 0, halfEllipseHeight); path.Close(); var transform = new Matrix2D(); transform.Translate(node.Layout.GetTopLeft()); transform.Scale(node.Layout.Width, node.Layout.Height); path.Transform(transform); return(path); }
protected override GeneralPath CreatePath(IStripe stripe, IRectangle layout) { var row = (IRow)stripe; GeneralPath outline = new GeneralPath(); outline.MoveTo(0, 0); outline.LineTo(0, layout.Height); outline.LineTo(layout.Width, layout.Height); if (IsFirst(row)) { outline.LineTo(layout.Width, 2 * WedgeHeight); outline.LineTo(layout.Width + WedgeWidth, WedgeHeight); outline.LineTo(layout.Width, 0); outline.Close(); } else { outline.LineTo(layout.Width, 0); } return(outline); }
protected override GeneralPath GetPath(IEdge edge) { // Create a general path from the locations of the ports and the bends of the edge. GeneralPath path = new GeneralPath(); path.MoveTo(edge.SourcePort.GetLocation()); foreach (var bend in edge.Bends) { path.LineTo(bend.Location); } path.LineTo(edge.TargetPort.GetLocation()); //////////////////////////////////////////////////// //////////////// New in this sample //////////////// //////////////////////////////////////////////////// // The following lines shorten the path in order to provide room for drawing the arrows. path = CropPath(edge, Arrows, Arrows, path); //////////////////////////////////////////////////// return(path); }
/// <summary> /// Creates a <see cref="GeneralPath"/> that describes the face on the front side of the block. /// </summary> /// <param name="corners">The coordinates of the corners of the block.</param> /// <returns>A <see cref="GeneralPath"/> that describes the face on the front side of the block.</returns> static GeneralPath GetFrontFacePath(double[] corners) { var path = new GeneralPath(); path.MoveTo( corners[LowBottomLeftX], corners[LowBottomLeftY] ); path.LineTo( corners[UpBottomLeftX], corners[UpBottomLeftY] ); path.LineTo( corners[UpBottomRightX], corners[UpBottomRightY] ); path.LineTo( corners[LowBottomRightX], corners[LowBottomRightY] ); path.Close(); return(path); }
protected override GeneralPath GetOutline(INode node) { double width = Math.Min(node.Layout.Width, node.Layout.Height / BpmnConstants.ConversationWidthHeightRatio); double height = width * BpmnConstants.ConversationWidthHeightRatio; RectD bounds = new RectD(node.Layout.GetCenter().X - width / 2, node.Layout.GetCenter().Y - height / 2, width, height); var path = new GeneralPath(); path.MoveTo(0, 0.5); path.LineTo(0.25, 0); path.LineTo(0.75, 0); path.LineTo(1, 0.5); path.LineTo(0.75, 1); path.LineTo(0.25, 1); path.Close(); var transform = new Matrix2D(); transform.Translate(bounds.GetTopLeft()); transform.Scale(bounds.Width, bounds.Height); path.Transform(transform); return(path); }
/// <summary> /// Creates a <see cref="GeneralPath"/> that describes the face on the back side of the block. /// </summary> /// <param name="corners">The coordinates of the corners of the block.</param> /// <returns>A <see cref="GeneralPath"/> that describes the face on the back side of the block.</returns> static GeneralPath GetBackFacePath(double[] corners) { var path = new GeneralPath(); path.MoveTo( corners[LowTopLeftX], corners[LowTopLeftY] ); path.LineTo( corners[UpTopLeftX], corners[UpTopLeftY] ); path.LineTo( corners[UpTopRightX], corners[UpTopRightY] ); path.LineTo( corners[LowTopRightX], corners[LowTopRightY] ); path.Close(); return(path); }
protected override GeneralPath GetOutline(INode node) { // Create a rounded rectangle path var layout = node.Layout.ToRectD(); var path = new GeneralPath(12); var x = layout.X; var y = layout.Y; var w = layout.Width; var h = layout.Height; var arcX = Math.Min(w * 0.5, 5); var arcY = Math.Min(h * 0.5, 5); path.MoveTo(x, y + arcY); path.QuadTo(x, y, x + arcX, y); path.LineTo(x + w - arcX, y); path.QuadTo(x + w, y, x + w, y + arcY); path.LineTo(x + w, y + h - arcY); path.QuadTo(x + w, y + h, x + w - arcX, y + h); path.LineTo(x + arcX, y + h); path.QuadTo(x, y + h, x, y + h - arcY); path.Close(); return(path); }
public override IVisual CreateVisual(IRenderContext context) { var bounds = Bounds; var width = bounds.Width; var height = bounds.Height; var path = new GeneralPath(); path.MoveTo(0, TopLeftRadius); path.QuadTo(0, 0, TopLeftRadius, 0); path.LineTo(width - TopRightRadius, 0); path.QuadTo(width, 0, width, TopRightRadius); path.LineTo(width, height - BottomRightRadius); path.QuadTo(width, height, width - BottomRightRadius, height); path.LineTo(BottomLeftRadius, height); path.QuadTo(0, height, 0, height - BottomRightRadius); path.Close(); return(new GeneralPathVisual(path) { Brush = Brush, Pen = Pen, Transform = new Matrix(1, 0, 0, 1, (float)Bounds.X, (float)Bounds.Y) }); }
/// <summary> /// Generates a path for the callout decoration, which is an isosceles triangle from the label's center pointing /// to the node. /// </summary> /// <param name="label">The label for which to create the path.</param> /// <returns>A path for the callout triangle.</returns> private static GeneralPath GenerateTrianglePath(ILabel label) { var node = (INode)label.Owner; var layout = label.GetLayout(); // Find the point where the triangle attaches to the outline shape var outlineShape = node.Style.Renderer.GetShapeGeometry(node, node.Style).GetOutline(); var nodeCenter = node.Layout.GetCenter(); var labelCenter = layout.GetCenter(); if (outlineShape == null) { outlineShape = new GeneralPath(4); outlineShape.AppendOrientedRectangle(layout, false); } var intersection = outlineShape.FindLineIntersection(nodeCenter, labelCenter); var intersectionPoint = nodeCenter + intersection * (labelCenter - nodeCenter); // Find the vector that is orthogonal to the line connecting the label and node centers var triangleVector = labelCenter - intersectionPoint; var orthoVector = OrthoNormal(triangleVector); // Construct the other points of the triangle var point2 = labelCenter + orthoVector * 20; var point3 = labelCenter - orthoVector * 20; // Create the path var p = new GeneralPath(); p.MoveTo(intersectionPoint); p.LineTo(point2); p.LineTo(point3); p.Close(); return(p); }
/// <summary> /// If the existing number of bends is 2 mod 3 (i.e. the bends are consistent with /// what the bezier style expects), /// this implementation creates a triple of collinear bends and adjust the neighboring bends /// in a way that the shape of the curve is not changed initially and returns the middle bend. /// If there are no bends at all, it creates a triple plus two initial and final control bends, all of them collinear. /// Otherwise, the fallback bend creator is used to create a bend with its default strategy. /// </summary> /// <returns>The index of middle bend of a control point triple if such a triple was created, /// or the index of the newly created single bend.</returns> public int CreateBend(IInputModeContext context, IGraph graph, IEdge edge, PointD location) { switch (edge.Bends.Count) { case 0: var spl = edge.SourcePort.GetLocation(); var tpl = edge.TargetPort.GetLocation(); //a single linear segment... we just insert 5 collinear bends adjusted to the angle of the linear segment, //approximately evenly spaced graph.AddBend(edge, (location - spl) / 4 + spl, 0); graph.AddBend(edge, -(location - spl) / 4 + location, 1); graph.AddBend(edge, location, 2); graph.AddBend(edge, (location - spl) / 4 + location, 3); graph.AddBend(edge, location + (tpl - location) * 3 / 4, 4); return(2); case 1: //Use the default strategy to insert a single bend at the correct index return(fallBackCreator.CreateBend(context, graph, edge, location)); default: var pathPoints = edge.GetPathPoints(); if (pathPoints.Count % 3 == 1) { //Consistent number of existing points //Try to insert a smooth bend //I.e. a triple of three collinear bends and adjust the neighbor bends //Various quality measures and counters var segmentIndex = 0; var pathCounter = 0; var bestDistanceSqr = Double.PositiveInfinity; double bestRatio = Double.NaN; //The index of the segment where we want to create the bend in the end int bestIndex = -1; //Find the best segment while (pathCounter + 3 < pathPoints.Count) { //Get the control points defining the current segment var cp0 = pathPoints[pathCounter++]; var cp1 = pathPoints[pathCounter++]; var cp2 = pathPoints[pathCounter++]; //Consecutive segments share the last/first control point! So we may not advance the counter here var cp3 = pathPoints[pathCounter]; //Shift a cubic segment // //Here we assume that the path is actually composed of cubic segments, only. //Alternatively, we could inspect the actual path created by the edge renderer - this would also //allow to deal with intermediate non cubic segments, but we'd have to associate those //path segments somehow with the correct bends again, so again this would be tied to the actual //renderer implementation. var fragment = new GeneralPath(2); fragment.MoveTo(cp0); fragment.CubicTo(cp1, cp2, cp3); //Try to find the projection onto the fragment var ratio = fragment.GetProjection(location, 0); if (ratio.HasValue) { //Actually found a projection ratio //Determine the point on the curve - the tangent provides this var tangent = fragment.GetTangent(0, ratio.Value); if (tangent.HasValue) { //There actually is a tangent var d = (location - tangent.Value.Point).SquaredVectorLength; //Is this the best distance? if (d < bestDistanceSqr) { bestDistanceSqr = d; //Remember ratio (needed to split the curve) bestRatio = ratio.Value; //and the index, of course bestIndex = segmentIndex; } } } ++segmentIndex; } if (bestIndex != -1) { //Actually found a segment //For the drag, we want to move the middle bend return(CreateBends(graph, edge, bestIndex, bestRatio, pathPoints).GetIndex()); } //No best segment found (for whatever reason) - we don't want to create a bend so that we don't mess up anything return(-1); } else { //No consistent number of bends - just insert a single bend //We could also see whether we actually would have a cubic segment on the path, and treat that differently //However, why bother - just create the edge with a correct number of points instead return(fallBackCreator.CreateBend(context, graph, edge, location)); } } }
private static PortConstraint CreatePortConstraintFromSketch(IEdge e, bool source, bool strong) { //Get connection port and owner IPort port = source ? e.SourcePort : e.TargetPort; INode portOwner = port.Owner as INode; if (portOwner != null) { //Einfachste Loesung: //Erzeugt einen strong PortConstraint genau an der port location //anschluesse in alle richtungen moeglich // return PortConstraint.create(PortConstraint.ANY_SIDE, strong); //alternativ: z.B. Kantenpfad bestimmen und einen PortConstraint //erzeugen, dessen Richtung durch den Schnittpunkt zwischen Pfad und Knotenrand gegeben ist. //hier nur geradlinige Verbindung zwischen Bends PointD portLocation = port.GetLocation(); PointD seg = new PointD(); var bends = e.Bends; if (bends.Count == 0) { // no bends, instead take the endpoint seg = source ? e.TargetPort.GetLocation() : e.SourcePort.GetLocation(); } else { IPoint p1 = bends[0].Location; IPoint p2 = bends[bends.Count - 1].Location; seg = source ? new PointD(p1) : new PointD(p2); } // Some offset for ports, which lie exactly on the border RectD enlarged = portOwner.Layout.ToRectD().GetEnlarged(5); var generalPath = new GeneralPath(2); generalPath.MoveTo(portLocation); generalPath.LineTo(seg); if (generalPath.FindLineIntersection(enlarged.TopLeft, enlarged.TopRight) < 1) { //Erstes Segment verlaesst den Knoten auf der Nordseite //Die tatsaechliche Position des Constraints ergibt sich aus dem Startpunkt der Kante, muss //hier also nicht noch mal angegeben werden, dafuer aber, dass es sich wirklich um einen STRONG constraint //handelt. return(PortConstraint.Create(PortSide.North, strong)); } if (generalPath.FindLineIntersection(enlarged.TopLeft, enlarged.BottomLeft) < 1) { //first segment leaves at west... return(PortConstraint.Create(PortSide.West, strong)); } if (generalPath.FindLineIntersection(enlarged.TopRight, enlarged.BottomRight) < 1) { //first segment leaves at east... return(PortConstraint.Create(PortSide.East, strong)); } if (generalPath.FindLineIntersection(enlarged.BottomLeft, enlarged.BottomRight) < 1) { //first segment leaves at south... return(PortConstraint.Create(PortSide.South, strong)); } //keine intersection mit dem ersten segment, hier waehlen wir den einfachen Weg... return(PortConstraint.Create(PortSide.Any, strong)); } return(null); }
/// <summary> /// Creates a visual representation of the constraint indicator. /// </summary> public GeneralPath CreateConstraintIndicator() { IPort port = sourceEnd ? bend.Owner.SourcePort : bend.Owner.TargetPort; var nodeLayout = ((INode)port.Owner).Layout.ToRectD(); PointD portLocation = nodeLayout.Center; double plX = portLocation.X, plY = portLocation.Y; PointD bendLocation = bend.Location.ToPointD(); PointD delta = bendLocation - portLocation; if (delta.VectorLength > MinDistance && !nodeLayout.Contains(bendLocation)) { PointD direction = delta.Normalized; GeneralPath path = new GeneralPath(20); path.MoveTo(-15, 0); path.LineTo(-5, 10); path.LineTo(-2, 7); path.LineTo(-5, 4); path.LineTo(8, 4); path.LineTo(8, -4); path.LineTo(-5, -4); path.LineTo(-2, -7); path.LineTo(-5, -10); path.Close(); // mirror at target end if (!sourceEnd) { path.Transform(new Matrix2D(-1, 0, 0, 1, 0, 0)); } // rotate and translate arrow const int ArrowOffset = 11; if (direction.IsHorizontalVector) { plY = nodeLayout.CenterY; if (direction.X > 0) { plX = nodeLayout.MaxX + ArrowOffset; path.Transform(new Matrix2D(-1, 0, 0, 1, plX, plY)); } else { plX = nodeLayout.X - ArrowOffset; path.Transform(new Matrix2D(1, 0, 0, 1, plX, plY)); } } else { plX = nodeLayout.CenterX; if (direction.Y < 0) { plY = nodeLayout.Y - ArrowOffset; path.Transform(new Matrix2D(0, 1, 1, 0, plX, plY)); } else { plY = nodeLayout.MaxY + ArrowOffset; path.Transform(new Matrix2D(0, 1, -1, 0, plX, plY)); } } return(path); } return(null); }
protected override VisualGroup CreateVisual(IRenderContext context, ILabel label) { var group = new VisualGroup(); bool enabled = false; if (Button != null) { enabled = Button.CanExecute((INode)label.Owner, context.CanvasControl); } var labelLayout = label.GetLayout(); var layout = new RectD(labelLayout.AnchorX, labelLayout.AnchorY - label.PreferredSize.Height, label.PreferredSize.Width, label.PreferredSize.Height); Brush backgroundBrush; Brush foregroundBrush; Pen foregroundPen; if (enabled) { // enabled style if (Icon != ButtonIcon.Increase) { backgroundBrush = new LinearGradientBrush(layout.TopLeft, layout.BottomLeft, BackgroundColor, Mix(Color.White, BackgroundColor, 0.5d)); } else { backgroundBrush = new LinearGradientBrush(layout.TopLeft, layout.BottomLeft, Mix(Color.White, BackgroundColor, 0.5d), BackgroundColor); } foregroundPen = new Pen(ForegroundColor); foregroundBrush = new SolidBrush(ForegroundColor); } else { // disabled style backgroundBrush = new LinearGradientBrush(layout.TopLeft, layout.BottomLeft, Mix(Color.White, BackgroundColor, 0.7), Mix(Color.White, BackgroundColor, 0.7)); foregroundPen = new Pen(Mix(Color.White, ForegroundColor, 0.7)); foregroundBrush = new SolidBrush(Mix(Color.White, ForegroundColor, 0.7)); } var backgroundRect = new RectangleVisual(layout) { Brush = backgroundBrush, Pen = BorderPen }; group.Add(backgroundRect); GeneralPath path; ShapeVisual pathPaintable; switch (Icon) { case ButtonIcon.Increase: // paint "up"-arrow path = new GeneralPath(); path.MoveTo(layout.TopLeft + new PointD(layout.Width * 0.3, layout.Height * 0.7)); path.LineTo(layout.TopLeft + new PointD(layout.Width * 0.7, layout.Height * 0.7)); path.LineTo(layout.TopLeft + new PointD(layout.Width * 0.5, layout.Height * 0.3)); path.Close(); pathPaintable = new GeneralPathVisual(path) { Brush = foregroundBrush }; group.Add(pathPaintable); break; case ButtonIcon.Decrease: // paint "down"-arrow path = new GeneralPath(); path.MoveTo(layout.TopLeft + new PointD(layout.Width * 0.3, layout.Height * 0.3)); path.LineTo(layout.TopLeft + new PointD(layout.Width * 0.7, layout.Height * 0.3)); path.LineTo(layout.TopLeft + new PointD(layout.Width * 0.5, layout.Height * 0.7)); path.Close(); pathPaintable = new GeneralPathVisual(path) { Brush = foregroundBrush }; group.Add(pathPaintable); break; case ButtonIcon.Toggle: // paint "check" path = new GeneralPath(); path.MoveTo(layout.TopLeft + new PointD(layout.Width * 0.3, layout.Height * 0.5)); path.LineTo(layout.TopLeft + new PointD(layout.Width * 0.5, layout.Height * 0.7)); path.LineTo(layout.TopLeft + new PointD(layout.Width * 0.7, layout.Height * 0.3));; pathPaintable = new GeneralPathVisual(path) { Pen = foregroundPen }; group.Add(pathPaintable); break; case ButtonIcon.None: // paint nothing break; default: // can't happen throw new ArgumentOutOfRangeException(); } return(group); }
protected override VisualGroup CreateVisual(IRenderContext context, INode node) { Color borderColor, backgroundColor1, backgroundColor2; if (((GraphControl)context.CanvasControl).CurrentItem == node) { borderColor = Color.Orange; backgroundColor1 = Color.White; backgroundColor2 = Color.Orange; } else { borderColor = Color.FromArgb(255, 24, 154, 231); backgroundColor1 = Color.FromArgb(255, 204, 255, 255); backgroundColor2 = Color.FromArgb(255, 24, 154, 231); } var layout = node.Layout; var employee = (Employee)node.Tag; var text = new TextVisual { Text = GetShortName(employee.Name), Font = new Font("Arial", 37, FontStyle.Regular, GraphicsUnit.Pixel), Brush = Brushes.Black, Location = new PointD(5, 5) }; double locationX = layout.Width / 2 - text.GetBounds(context).Width / 2; double locationY = layout.Height / 2 - text.GetBounds(context).Height / 2; string s1 = GetShortName(employee.Name); Font font1 = new Font("Arial", 37, FontStyle.Regular, GraphicsUnit.Pixel); Brush brush1 = Brushes.Black; IPoint location1 = new PointD(locationX, locationY); text = new TextVisual { Text = s1, Font = font1, Brush = brush1, Location = location1 }; var ribbonColor = Color.Green; if (employee.Status == EmployeeStatus.Travel) { ribbonColor = Color.Purple; } if (employee.Status == EmployeeStatus.Unavailable) { ribbonColor = Color.Red; } var border = new RectangleVisual(0, 0, layout.Width, layout.Height) { Pen = new Pen(borderColor) }; var background = new RectangleVisual(0, 0, layout.Width, layout.Height) { Brush = new LinearGradientBrush(new PointF(0, 0), new PointF((float)layout.Width, (float)layout.Height), backgroundColor1, backgroundColor2) }; var ribbonPath = new GeneralPath(); ribbonPath.MoveTo(0, 20); ribbonPath.LineTo(25, 0); ribbonPath.LineTo(40, 0); ribbonPath.LineTo(0, 35); ribbonPath.Close(); var ribbon = new GeneralPathVisual(ribbonPath) { Brush = new SolidBrush(ribbonColor) }; // Set a transform on the group, matching the node's location. // That way only the transform has to be updated instead of every single child visual. var transform = new Matrix(); transform.Translate((float)layout.X, (float)layout.Y); var group = new VisualGroup { Transform = transform, Children = { border, background, text, ribbon } }; return(group); }