private static YOrientedRectangle GetEdgeLabelLocation(LayoutGraph graph, Edge e, IEdgeLabelLayout ell) { YOrientedRectangle ellp = ell.LabelModel.GetLabelPlacement( ell.BoundingBox, graph.GetLayout(e), graph.GetLayout(e.Source), graph.GetLayout(e.Target), ell.ModelParameter); return(ellp); }
/// <summary> /// Returns the calculated location of the edge label. Note that the labeling /// machinery returns the edge labels positions as a parameter /// of the model that belongs to the label. This model parameter can be used /// to retrieve the actual location of the label as shown in this method. /// </summary> private static YPoint GetEdgeLabelLocation(LayoutGraph graph, Edge e, IEdgeLabelLayout ell) { var placement = ell.LabelModel.GetLabelPlacement( ell.BoundingBox, graph.GetLayout(e), graph.GetLayout(e.Source), graph.GetLayout(e.Target), ell.ModelParameter); YPoint ellp = new YPoint(placement.Anchor.X, placement.Anchor.Y - placement.Height); return(ellp); }
/// <summary> /// Called by the various node creation callbacks to create a node in the resulting graph view /// that corresponds to the provided <paramref name="layoutNode"/>. /// </summary> /// <remarks> /// If a model node is provided, the ports of the original node will be copied to the created view node. /// Also, a clone of the original node style will be used as the style of the created node. /// </remarks> /// <param name="pageLayoutGraph">The layout graph representing the current page</param> /// <param name="pageView">The <see cref="IGraph"/> that is built to show the multi-page layout in a graph canvas</param> /// <param name="layoutNode">The node of the layout graph that should be copied</param> /// <param name="modelNode">The node of the original input graph that corresponds to the <paramref name="layoutNode"/> (may be <see langword="null"/>)</param> /// <param name="isReferenceNode"></param> /// <param name="nodeDefaults"></param> /// <returns>the created node</returns> /// <seealso cref="CreateConnectorNode"/> /// <seealso cref="CreateNormalNode"/> /// <seealso cref="CreateGroupNode"/> /// <seealso cref="CreateProxyNode"/> /// <seealso cref="CreateProxyReferenceNode"/> protected INode CreateNodeCore(LayoutGraph pageLayoutGraph, IGraph pageView, Node layoutNode, INode modelNode, bool isReferenceNode, INodeDefaults nodeDefaults) { // get the layout from the layout graph INodeLayout nodeLayout = pageLayoutGraph.GetLayout(layoutNode); // get the style from the node defaults or the model node (or the default style if none is provided) INodeStyle style = (INodeStyle)(nodeDefaults.Style != NullNodeStyle ? nodeDefaults.GetStyleInstance() : (modelNode != null ? modelNode.Style.Clone() : pageView.NodeDefaults.Style.Clone())); var tag = modelNode != null ? modelNode.Tag : null; // create the copied node INode viewNode = pageView.CreateNode(new RectD(nodeLayout.X, nodeLayout.Y, nodeLayout.Width, nodeLayout.Height), style, tag); // copy the ports of the model node if (modelNode != null) { CopyPorts(pageView, layoutNode, viewNode, modelNode); } viewToLayoutNode[viewNode] = layoutNode; IMapper <INode, NodeData> referencingMapper = pageView.MapperRegistry.GetMapper <INode, NodeData>(MapperKeyNodeData); NodeData data = new NodeData { IsReferenceNode = isReferenceNode }; referencingMapper[viewNode] = data; return(viewNode); }
private static YOrientedRectangle GetNodeLabelLocation(LayoutGraph graph, Node n, INodeLabelLayout nll) { return(nll.LabelModel.GetLabelPlacement( nll.BoundingBox, graph.GetLayout(n), nll.ModelParameter)); }
protected void PaintEdge(Graphics g, LayoutGraph graph, Edge e) { IEdgeLayout el = graph.GetLayout(e); YPoint sp = graph.GetSourcePointAbs(e); YPoint tp = graph.GetTargetPointAbs(e); PointF[] points = new PointF[el.PointCount() + 2]; points[0] = new PointF((float)sp.X, (float)sp.Y); points[el.PointCount() + 1] = new PointF((float)tp.X, (float)tp.Y); for (int i = 0; i < el.PointCount(); i++) { YPoint p = el.GetPoint(i); points[i + 1] = new PointF((float)p.X, (float)p.Y); } g.DrawLines(edgePen, points); }
/// <summary> /// Fix the ports for <see cref="RoutingMode.ShortestStraightPathToBorder"/> /// by enlarging the adjacent segment to the rotated layout. /// </summary> /// <param name="graph">The layout graph to work on.</param> /// <param name="edge">The edge to fix.</param> /// <param name="path">A <see cref="GeneralPath"/> which represents the rotated layout.</param> /// <param name="atSource">Whether to fix the source or target port of the edge.</param> private static void FixPorts(LayoutGraph graph, Edge edge, GeneralPath path, bool atSource) { var el = graph.GetLayout(edge); var pointCount = el.PointCount(); // find the opposite point of the port at the adjacent segment PointD firstBend = atSource ? (pointCount > 0 ? el.GetPoint(0) : graph.GetTargetPointAbs(edge)).ToPointD() : (pointCount > 0 ? el.GetPoint(pointCount - 1) : graph.GetSourcePointAbs(edge)).ToPointD(); // The port itself PointD port = (atSource ? graph.GetSourcePointAbs(edge) : graph.GetTargetPointAbs(edge)).ToPointD(); // The adjacent segment as vector pointing from the opposite point to the port var direction = port - firstBend; // find the intersection (there is always one) var intersection = path.FindRayIntersection(firstBend.ToPointD(), direction); PointD point = port; if (intersection < Double.PositiveInfinity) { // found an intersection: extend the adjacent segment point = firstBend + (direction * intersection); } else { // no intersection: connect to the original port's nearest point var cursor = path.CreateCursor(); double minDistance = Double.PositiveInfinity; while (cursor.MoveNext()) { var distance = port.DistanceTo(cursor.CurrentEndPoint); if (distance < minDistance) { minDistance = distance; point = cursor.CurrentEndPoint; } } } // set the port position if (atSource) { graph.SetSourcePointAbs(edge, point.ToYPoint()); } else { graph.SetTargetPointAbs(edge, point.ToYPoint()); } }
/// <summary> /// Called by the various edge creation callbacks to create an edge in the resulting graph view /// that corresponds to the provided <paramref name="layoutEdge"/>. /// </summary> /// <remarks> /// If a model edge is provided, the edge will be created between the copies of the corresponding /// source/target ports. /// </remarks> ///<param name="pageLayoutGraph">The layout graph representing the current page.</param> ///<param name="pageView">The <see cref="IGraph"/> that is built to show the multi-page layout in a graph canvas.</param> ///<param name="layoutEdge">The edge of the layout graph that should be copied.</param> ///<param name="modelEdge">The edge of the original input graph that corresponds to the <paramref name="layoutEdge"/> (may be <see langword="null"/>).</param> ///<param name="edgeDefaults"></param> ///<returns>The created edge</returns> /// <seealso cref="CreateConnectorEdge"/> /// <seealso cref="CreateNormalEdge"/> /// <seealso cref="CreateProxyEdge"/> /// <seealso cref="CreateProxyReferenceEdge"/> protected IEdge CreateEdgeCore(LayoutGraph pageLayoutGraph, IGraph pageView, Edge layoutEdge, IEdge modelEdge, IEdgeDefaults edgeDefaults) { IEdge viewEdge; if (modelEdge != null) { // if the edge has a model edge: create the copied edge between // the copies of its source and target ports IPort modelSourcePort = modelEdge.SourcePort; IPort modelTargetPort = modelEdge.TargetPort; IPort viewSourcePort = GetViewPort(modelSourcePort); IPort viewTargetPort = GetViewPort(modelTargetPort); IEdgeStyle style = (IEdgeStyle)(edgeDefaults.Style != NullEdgeStyle ? edgeDefaults.GetStyleInstance() : modelEdge.Style.Clone()); viewEdge = pageView.CreateEdge(viewSourcePort, viewTargetPort, style, modelEdge.Tag); } else { // otherwise create it between the copies of its source and target nodes INode viewSource = GetViewNode(layoutEdge.Source); INode viewTarget = GetViewNode(layoutEdge.Target); viewEdge = pageView.CreateEdge(viewSource, viewTarget); } // adjust the port location YPoint newSourcePortLocation = pageLayoutGraph.GetSourcePointAbs(layoutEdge); YPoint newTargetPortLocation = pageLayoutGraph.GetTargetPointAbs(layoutEdge); pageView.SetPortLocation(viewEdge.SourcePort, newSourcePortLocation.ToPointD()); pageView.SetPortLocation(viewEdge.TargetPort, newTargetPortLocation.ToPointD()); // and copy the bends IEdgeLayout edgeLayout = pageLayoutGraph.GetLayout(layoutEdge); for (int i = 0; i < edgeLayout.PointCount(); i++) { YPoint bendLocation = edgeLayout.GetPoint(i); pageView.AddBend(viewEdge, new PointD(bendLocation.X, bendLocation.Y), i); } return(viewEdge); }
protected void PaintNode(Graphics g, LayoutGraph graph, Node node) { INodeLayout nl = graph.GetLayout(node); RectangleF rect = RectangleF.FromLTRB((float)nl.X, (float)nl.Y, (float)(nl.X + nl.Width), (float)(nl.Y + nl.Height)); Region r = new Region(rect); g.FillRegion(nodeFillBrush, r); points[0] = new PointF(rect.X, rect.Y); points[1] = new PointF(rect.X + rect.Width, rect.Y); points[2] = new PointF(rect.X + rect.Width, rect.Y + rect.Height); points[3] = new PointF(rect.X, rect.Y + rect.Height); g.DrawPolygon(nodeBorderPen, points); string text = node.Index.ToString(); SizeF size = g.MeasureString(text, labelFont); g.DrawString(text, labelFont, labelBrush, rect.X + rect.Width * 0.5f - size.Width * 0.5f, rect.Y + rect.Height * 0.5f - size.Height * 0.5f); }
/// <inheritdoc/> public virtual double GetProfit(LabelCandidate candidate) { if (candidate.Owner is IEdgeLabelLayout) { return(1); } double profit = 0; INodeLabelLayout nl = (INodeLabelLayout)candidate.Owner; var node = graph.GetOwner(nl); var nodeLayout = graph.GetLayout(node); var candidateLayout = candidate.BoundingBox; var isLeft = candidateLayout.X + candidateLayout.Width / 2 < nodeLayout.X; var isRight = candidateLayout.X + candidateLayout.Width / 2 > (nodeLayout.X + nodeLayout.Width); var isTop = candidateLayout.Y + candidateLayout.Height / 2 < nodeLayout.Y; var isBottom = candidateLayout.Y + candidateLayout.Height / 2 > (nodeLayout.Y + nodeLayout.Height); var horizontalCenter = !isLeft && !isRight; var verticalCenter = !isTop && !isBottom; if (horizontalCenter && verticalCenter) { // candidate is in center -> don't use return(0); } else if (horizontalCenter || verticalCenter) { profit = 0.95; } else { // diagonal candidates get a bit less profit profit = 0.9; } foreach (var edge in node.Edges) { var portLocation = edge.Source == node?graph.GetSourcePointRel(edge) : graph.GetTargetPointRel(edge); if (Math.Abs(portLocation.X) > Math.Abs(portLocation.Y)) { // edge at left or right if (portLocation.X < 0 && isLeft || portLocation.X > 0 && isRight) { if (isTop || isBottom) { profit -= 0.03; } else { // edge at same side as candidate profit -= 0.2; } } else if (horizontalCenter) { // candidate is close to the edge but not on the same side profit -= 0.01; } } else { // edge at top or bottom if (portLocation.Y < 0 && isTop || portLocation.Y > 0 && isBottom) { if (isLeft || isRight) { profit -= 0.03; } else { profit -= 0.2; } } else if (verticalCenter) { // candidate is close to the edge but not on the same side profit -= 0.01; } } } return(Math.Max(0, profit)); }
public GraphCanvas(LayoutGraph graph) { this.RenderTransform = new TranslateTransform(_padding, _padding); var grouping = new GroupingSupport(graph); // Add all edges foreach (var edge in graph.Edges) { IEdgeLayout el = graph.GetLayout(edge); var l = new Polyline(); l.Stroke = Brushes.Black; l.Points.Add(new Point(graph.GetSourcePointAbs(edge).X, graph.GetSourcePointAbs(edge).Y)); for (int i = 0; i < el.PointCount(); i++) { Point p = new Point(el.GetPoint(i).X, el.GetPoint(i).Y); l.Points.Add(p); } l.Points.Add(new Point(graph.GetTargetPointAbs(edge).X, graph.GetTargetPointAbs(edge).Y)); this.Children.Add(l); // edge labels var edgeLabelLayout = graph.GetLabelLayout(edge); foreach (var labelLayout in edgeLabelLayout) { var orientedRectangle = labelLayout.LabelModel.GetLabelPlacement( labelLayout.BoundingBox, graph.GetLayout(edge), graph.GetLayout(edge.Source), graph.GetLayout(edge.Target), labelLayout.ModelParameter); this.Children.Add(GetPolygon(orientedRectangle)); } } // add all nodes foreach (var node in graph.Nodes) { INodeLayout nl = graph.GetLayout(node); Color color = grouping.IsGroupNode(node) ? Color.FromArgb(60, 255, 60, 0) : Color.FromArgb(255, 255, 255, 0); var rect = new Rectangle(); this.Children.Add(rect); rect.Stroke = new SolidColorBrush() { Color = Colors.Black }; rect.Fill = new SolidColorBrush() { Color = color }; rect.Width = nl.Width; rect.Height = nl.Height; Canvas.SetTop(rect, nl.Y); Canvas.SetLeft(rect, nl.X); // display the node index var text = new TextBlock() { Text = String.Empty + node.Index, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center }; this.Children.Add(text); text.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity)); Canvas.SetTop(text, nl.Y + nl.Height / 2 - text.DesiredSize.Height / 2); Canvas.SetLeft(text, nl.X + nl.Width / 2 - text.DesiredSize.Width / 2); } }
/// <summary> /// Executes the layout algorithm. /// </summary> /// <remarks> /// <para> /// Enlarges the node layout to fully encompass the rotated layout (the rotated layout's bounding box). /// If the <see cref="EdgeRoutingMode"/> is set to <see cref="RoutingMode.FixedPort"/> /// port constraints are created to keep the ports at their current location. /// Existing port constraints are adjusted to the rotation. /// </para> /// <para> /// Then, the <see cref="LayoutStageBase.CoreLayout"/> is executed. /// </para> /// <para> /// After the core layout the original node sizes are restored. /// If the <see cref="EdgeRoutingMode"/> is set to <see cref="RoutingMode.ShortestStraightPathToBorder"/> /// the last edge segment is extended from the bounding box to the rotated layout. /// </para> /// </remarks> public override void ApplyLayout(LayoutGraph graph) { if (CoreLayout == null) { return; } var boundsProvider = graph.GetDataProvider(RotatedNodeLayoutDpKey); if (boundsProvider == null) { // no provider: this stage adds nothing to the core layout CoreLayout.ApplyLayout(graph); return; } bool addedSourcePortConstraints = false; bool addedTargetPortContstraints = false; IDataMap sourcePortConstraints = (IDataMap)graph.GetDataProvider(PortConstraintKeys.SourcePortConstraintDpKey); IDataMap targetPortConstraints = (IDataMap)graph.GetDataProvider(PortConstraintKeys.TargetPortConstraintDpKey); if (EdgeRoutingMode == RoutingMode.FixedPort) { // Fixed port: create port constraints to keep the ports at position // in this case: create data providers if there are none yet if (sourcePortConstraints == null) { sourcePortConstraints = graph.CreateEdgeMap(); graph.AddDataProvider(PortConstraintKeys.SourcePortConstraintDpKey, sourcePortConstraints); addedSourcePortConstraints = true; } if (targetPortConstraints == null) { targetPortConstraints = graph.CreateEdgeMap(); graph.AddDataProvider(PortConstraintKeys.TargetPortConstraintDpKey, targetPortConstraints); addedTargetPortContstraints = true; } } try { var originalDimensions = new Dictionary <Node, OldDimensions>(); foreach (var node in graph.Nodes) { var nodeShape = (RotatedNodeShape)boundsProvider.Get(node); var orientedLayout = nodeShape != null ? nodeShape.OrientedLayout : null; var outline = nodeShape != null ? nodeShape.Outline : null; if (orientedLayout != null) { // if the current node is rotated: apply fixes // remember old layout and size var oldLayout = graph.GetLayout(node); var newLayout = orientedLayout.GetBounds().ToYRectangle(); var offset = new PointD(newLayout.X - oldLayout.X, newLayout.Y - oldLayout.Y); var originalSize = new SizeD(oldLayout.Width, oldLayout.Height); var oldDimensions = new OldDimensions { offset = offset, size = originalSize, outline = outline }; if (EdgeRoutingMode == RoutingMode.FixedPort) { // EdgeRoutingMode: FixedPort: keep the ports at their current location // The oriented layout's corners to find the best PortSide var tl = new PointD(orientedLayout.AnchorX + orientedLayout.UpX * orientedLayout.Height, orientedLayout.AnchorY + orientedLayout.UpY * orientedLayout.Height); var tr = new PointD(orientedLayout.AnchorX + orientedLayout.UpX * orientedLayout.Height - orientedLayout.UpY * orientedLayout.Width, orientedLayout.AnchorY + orientedLayout.UpY * orientedLayout.Height + orientedLayout.UpX * orientedLayout.Width); var bl = new PointD(orientedLayout.AnchorX, orientedLayout.AnchorY); var br = new PointD(orientedLayout.AnchorX - orientedLayout.UpY * orientedLayout.Width, orientedLayout.AnchorY + orientedLayout.UpX * orientedLayout.Width); // for each out edge foreach (var edge in node.OutEdges) { // create a strong port constraint for the side which is closest to the port location (without rotation) var constraint = sourcePortConstraints.Get(edge); if (constraint == null) { var point = graph.GetSourcePointAbs(edge).ToPointD(); var side = FindBestSide(point, bl, br, tl, tr); sourcePortConstraints.Set(edge, PortConstraint.Create(side, true)); } } foreach (var edge in node.InEdges) { // create a strong port constraint for the side which is closest to the port location (without rotation) var constraint = targetPortConstraints.Get(edge); if (constraint == null) { var point = graph.GetTargetPointAbs(edge).ToPointD(); var side = FindBestSide(point, bl, br, tl, tr); targetPortConstraints.Set(edge, PortConstraint.Create(side, true)); } } } // For source and target port constraints: fix the PortSide according to the rotation var angle = Math.Atan2(orientedLayout.UpY, orientedLayout.UpX); if (sourcePortConstraints != null) { foreach (var edge in node.OutEdges) { FixPortConstraintSide(sourcePortConstraints, edge, angle); } } if (targetPortConstraints != null) { foreach (var edge in node.InEdges) { FixPortConstraintSide(targetPortConstraints, edge, angle); } } // enlarge the node layout var position = new YPoint(newLayout.X, newLayout.Y); oldDimensions.location = position; originalDimensions.Add(node, oldDimensions); graph.SetLocation(node, position); graph.SetSize(node, newLayout); } } // =============================================================== CoreLayout.ApplyLayout(graph); // =============================================================== var groups = graph.GetDataProvider(GroupingKeys.GroupDpKey); foreach (var node in graph.Nodes) { if (groups != null && groups.GetBool(node)) { // groups don't need to be adjusted to their former size and location because their bounds are entirely // calculated by the layout algorithm and they are not rotated continue; } // for each node which has been corrected: undo the correction var oldDimensions = originalDimensions[node]; var offset = oldDimensions.offset; var originalSize = oldDimensions.size; var newLayout = graph.GetLayout(node); // create a general path representing the new roated layout var path = oldDimensions.outline; var transform = new Matrix2D(); transform.Translate(new PointD(newLayout.X - oldDimensions.location.X, newLayout.Y - oldDimensions.location.Y)); path.Transform(transform); // restore the original size graph.SetLocation(node, new YPoint(newLayout.X - offset.X, newLayout.Y - offset.Y)); graph.SetSize(node, originalSize.ToYDimension()); if (EdgeRoutingMode == RoutingMode.NoRouting) { // NoRouting still needs fix for self-loops foreach (var edge in node.Edges) { if (edge.SelfLoop) { FixPorts(graph, edge, path, false); FixPorts(graph, edge, path, true); } } continue; } if (EdgeRoutingMode != RoutingMode.ShortestStraightPathToBorder) { continue; } // enlarge the adjacent segment to the oriented rectangle (represented by the path) // handling in and out edges separately will automatically cause selfloops to be handled correctly foreach (var edge in node.InEdges) { FixPorts(graph, edge, path, false); } foreach (var edge in node.OutEdges) { FixPorts(graph, edge, path, true); } } } finally { // if data provider for the port constraints have been added // remove and dispose them if (addedSourcePortConstraints) { graph.RemoveDataProvider(PortConstraintKeys.SourcePortConstraintDpKey); graph.DisposeEdgeMap((IEdgeMap)sourcePortConstraints); } if (addedTargetPortContstraints) { graph.RemoveDataProvider(PortConstraintKeys.TargetPortConstraintDpKey); graph.DisposeEdgeMap((IEdgeMap)targetPortConstraints); } } }
public override void ApplyLayout(LayoutGraph graph) { ApplyLayoutCore(graph); foreach (Node n in graph.Nodes) { foreach (Edge e in n.OutEdges) { bool lastSegmentOverlap = false; IEdgeLayout er = graph.GetLayout(e); if (er.PointCount() > 0) { // last bend point YPoint bendPoint = er.GetPoint(er.PointCount() - 1); IEnumerator <Edge> ecc = n.OutEdges.GetEnumerator(); loop : while (ecc.MoveNext()) { Edge eccEdge = ecc.Current; if (eccEdge != e) { YPointPath path = graph.GetPath(eccEdge); for (ILineSegmentCursor lc = path.LineSegments(); lc.Ok; lc.Next()) { LineSegment seg = lc.LineSegment; if (seg.Contains(bendPoint)) { lastSegmentOverlap = true; goto loop; } } } } } YList points = graph.GetPointList(e); for (ListCell c = points.FirstCell; c != null; c = c.Succ()) { YPoint p = (YPoint)c.Info; if (c.Succ() == null && !lastSegmentOverlap) { break; } YPoint p0 = (YPoint)(c.Pred() == null ? graph.GetSourcePointAbs(e) : c.Pred().Info); YPoint p2; if (Math.Abs(p0.X - p.X) < 0.01) { p2 = new YPoint(p.X, p.Y - 0.001); } else { p2 = new YPoint(p.X - 0.001, p.Y); } points.InsertBefore(p2, c); } graph.SetPoints(e, points); } } }