/// <summary> /// Initializes the layout algorithm and its layout data. /// </summary> private void InitializeLayout() { orthogonalEdgeRouter = new EdgeRouter { ConsiderNodeLabels = true }; hl = new HierarchicLayout { OrthogonalRouting = true, LayoutOrientation = LayoutOrientation.LeftToRight, ConsiderNodeLabels = true }; // outgoing edges must be routed to the right of the node // we use the same value for all edges, which is a strong port constraint that forces // the edge to leave at the east (right) side var east = PortConstraint.Create(PortSide.East, true); // incoming edges must be routed to the left of the node // we use the same value for all edges, which is a strong port constraint that forces // the edge to enter at the west (left) side var west = PortConstraint.Create(PortSide.West, true); MapperDelegate <IEdge, PortConstraint> sourceDelegate = edge => ((PortDescriptor)edge.SourcePort.Tag).X == 0 ? west : east; MapperDelegate <IEdge, PortConstraint> targetDelegate = edge => ((PortDescriptor)edge.TargetPort.Tag).X == 0 ? west : east; oerData = new PolylineEdgeRouterData { SourcePortConstraints = { Delegate = sourceDelegate }, TargetPortConstraints = { Delegate = targetDelegate } }; hlData = new HierarchicLayoutData { SourcePortConstraints = { Delegate = sourceDelegate }, TargetPortConstraints = { Delegate = targetDelegate } }; }
public PortConstraint CreatePortConstraint(IEdge key, PortConstraintType type, bool atSource, bool strong) { if (type == PortConstraintType.FromSketch) { return(CreatePortConstraintFromSketch(key, atSource, strong)); } return(PortConstraint.Create((PortSide)type, strong)); }
protected override void OnFinished(IInputModeContext context, PointD originalLocation, PointD newLocation) { base.OnFinished(context, originalLocation, newLocation); // remove the indicator canvasObject.Remove(); // calculate the direction IPort port = sourceEnd ? bend.Owner.SourcePort : bend.Owner.TargetPort; var nodeLayout = ((INode)port.Owner).Layout.ToRectD(); PointD portLocation = nodeLayout.Center; PointD bendLocation = bend.Location.ToPointD(); PointD delta = bendLocation - portLocation; PortConstraint pc = null; if (delta.VectorLength > MinDistance && !nodeLayout.Contains(bendLocation)) { PointD direction = delta.Normalized; if (direction.IsHorizontalVector) { if (direction.X > 0) { pc = PortConstraint.Create(PortSide.East); } else { pc = PortConstraint.Create(PortSide.West); } } else { if (direction.Y > 0) { pc = PortConstraint.Create(PortSide.South); } else { pc = PortConstraint.Create(PortSide.North); } } } // and set the port constraint if (pc == null) { portConstraints.RemoveValue(bend.Owner); } else { portConstraints[bend.Owner] = pc; } }
public LayoutData CreateHierarchicLayoutData() { // create the layout data return(new HierarchicLayoutData { // maps each edge with its thickness so that the layout algorithm takes the edge thickness under consideration EdgeThickness = { Delegate = edge => ((CustomTag)edge.Tag).thickness }, // since orientation is LEFT_TO_RIGHT, we add port constraints so that the edges leave the source node at its // right side and enter the target node at its left side SourcePortConstraints = { Delegate = edge => PortConstraint.Create(PortSide.East, false) }, TargetPortConstraints = { Delegate = edge => PortConstraint.Create(PortSide.West, false) }, EdgeLabelPreferredPlacement = { Constant = new PreferredPlacementDescriptor { PlaceAlongEdge = LabelPlacements.AtSource } } }); }
public static void Main() { DefaultLayoutGraph graph = new DefaultLayoutGraph(); //construct graph. assign sizes to nodes Node v1 = graph.CreateNode(); graph.SetSize(v1, 30, 30); Node v2 = graph.CreateNode(); graph.SetSize(v2, 30, 30); Node v3 = graph.CreateNode(); graph.SetSize(v3, 30, 30); // add a label to one node var nodeLabelLayoutModel = new DiscreteNodeLabelLayoutModel(DiscreteNodeLabelPositions.InternalMask, 4); var labelLayoutFactory = LayoutGraphUtilities.GetLabelFactory(graph); labelLayoutFactory.AddLabelLayout(v1, labelLayoutFactory.CreateLabelLayout(v1, new YOrientedRectangle(0, 0, 80, 20), nodeLabelLayoutModel)); Edge e1 = graph.CreateEdge(v1, v2); Edge e2 = graph.CreateEdge(v1, v3); // add a label to an edge var edgeLabelLayoutModel = new SliderEdgeLabelLayoutModel(SliderMode.Side); labelLayoutFactory.AddLabelLayout(e1, labelLayoutFactory.CreateLabelLayout(e1, new YOrientedRectangle(0, 0, 80, 20), edgeLabelLayoutModel, PreferredPlacementDescriptor.NewSharedInstance(LabelPlacements.LeftOfEdge))); //optionally setup some port constraints for HierarchicLayout IEdgeMap spc = graph.CreateEdgeMap(); IEdgeMap tpc = graph.CreateEdgeMap(); //e1 shall leave and enter the node on the right side spc.Set(e1, PortConstraint.Create(PortSide.East, false)); //additionally set a strong port constraint on the target side. tpc.Set(e1, PortConstraint.Create(PortSide.East, true)); //ports with strong port constraints will not be reset by the //layout algorithm. So we specify the target port right now to connect //to the upper left corner of the node graph.SetTargetPointRel(e1, new YPoint(15, -15)); //e2 shall leave and enter the node on the top side spc.Set(e2, PortConstraint.Create(PortSide.North, false)); tpc.Set(e2, PortConstraint.Create(PortSide.North, false)); graph.AddDataProvider(PortConstraintKeys.SourcePortConstraintDpKey, spc); graph.AddDataProvider(PortConstraintKeys.TargetPortConstraintDpKey, tpc); HierarchicLayout layout = new HierarchicLayout(); layout.IntegratedEdgeLabeling = true; layout.ConsiderNodeLabels = true; layout.LayoutMode = LayoutMode.FromScratch; new BufferedLayout(layout).ApplyLayout(graph); Console.WriteLine("\n\nGRAPH LAID OUT HIERARCHICALLY FROM SCRATCH"); Console.WriteLine("v1 center position = " + graph.GetCenter(v1)); Console.WriteLine("v2 center position = " + graph.GetCenter(v2)); Console.WriteLine("v3 center position = " + graph.GetCenter(v3)); Console.WriteLine("e1 path = " + graph.GetPath(e1)); Console.WriteLine("e2 path = " + graph.GetPath(e2)); //display the graph in a simple viewer GraphViewer gv = new GraphViewer(); gv.AddLayoutGraph(new CopiedLayoutGraph(graph), "Before Addition"); // now add a node and two edges incrementally... Node v4 = graph.CreateNode(); graph.SetSize(v4, 30, 30); Edge e4 = graph.CreateEdge(v4, v2); Edge e3 = graph.CreateEdge(v1, v4); //mark elements as newly added so that the layout algorithm can place //them nicely. IIncrementalHintsFactory ihf = layout.CreateIncrementalHintsFactory(); IDataMap map = Maps.CreateHashedDataMap(); map.Set(v4, ihf.CreateLayerIncrementallyHint(v4)); map.Set(e3, ihf.CreateSequenceIncrementallyHint(e3)); map.Set(e4, ihf.CreateSequenceIncrementallyHint(e4)); graph.AddDataProvider(HierarchicLayout.IncrementalHintsDpKey, map); layout.LayoutMode = LayoutMode.Incremental; new BufferedLayout(layout).ApplyLayout(graph); Console.WriteLine("\n\nGRAPH AFTER ELEMENTS HAVE BEEN ADDED INCREMENTALLY"); Console.WriteLine("v1 center position = " + graph.GetCenter(v1)); Console.WriteLine("v2 center position = " + graph.GetCenter(v2)); Console.WriteLine("v3 center position = " + graph.GetCenter(v3)); Console.WriteLine("v4 center position = " + graph.GetCenter(v4)); Console.WriteLine("e1 path = " + graph.GetPath(e1)); Console.WriteLine("e2 path = " + graph.GetPath(e2)); Console.WriteLine("e3 path = " + graph.GetPath(e3)); Console.WriteLine("e4 path = " + graph.GetPath(e4)); //clean up data maps graph.RemoveDataProvider(HierarchicLayout.IncrementalHintsDpKey); //display the graph in a simple viewer gv.AddLayoutGraph(new CopiedLayoutGraph(graph), "After Addition"); Application.Run(gv); }
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); }
public static void Main() { DefaultLayoutGraph graph = new DefaultLayoutGraph(); //construct graph. assign sizes to nodes Node v1 = graph.CreateNode(); graph.SetSize(v1, 30, 30); Node v2 = graph.CreateNode(); graph.SetSize(v2, 30, 30); Node v3 = graph.CreateNode(); graph.SetSize(v3, 30, 30); Edge e1 = graph.CreateEdge(v1, v2); Edge e2 = graph.CreateEdge(v2, v3); Edge e3 = graph.CreateEdge(v1, v3); //optionally setup some port constraints for HierarchicLayout IEdgeMap spc = graph.CreateEdgeMap(); IEdgeMap tpc = graph.CreateEdgeMap(); //e1 shall leave and enter the node on the right side spc.Set(e1, PortConstraint.Create(PortSide.East)); //additionally set a strong port constraint on the target side. tpc.Set(e1, PortConstraint.Create(PortSide.East, true)); //ports with strong port constraints will not be reset by the //layout algorithm. So we specify the target port right now to connect //to the upper left corner of the node graph.SetTargetPointRel(e1, new YPoint(15, -15)); //e2 shall leave and enter the node on the top side spc.Set(e2, PortConstraint.Create(PortSide.North)); tpc.Set(e2, PortConstraint.Create(PortSide.North)); //e3 uses no port constraints, i.e. layout will choose best side graph.AddDataProvider(PortConstraintKeys.SourcePortConstraintDpKey, spc); graph.AddDataProvider(PortConstraintKeys.TargetPortConstraintDpKey, tpc); //setup two edge labels for edge e1. The size of the edge labels will be set to //80x20. Usually the size of the labels will be determined by //calculaing the bounding box of a piece text that is displayed //with a specific font. var labelFactory = LayoutGraphUtilities.GetLabelFactory(graph); graph.SetLabelLayout(e1, new[] { CreateEdgeLabelLayout(labelFactory, e1, new SliderEdgeLabelLayoutModel(SliderMode.Center), PreferredPlacementDescriptor.NewSharedInstance(LabelPlacements.AtCenter)), CreateEdgeLabelLayout(labelFactory, e1, new SliderEdgeLabelLayoutModel(SliderMode.Side), PreferredPlacementDescriptor.NewSharedInstance(LabelPlacements.LeftOfEdge)) }); var layout = new HierarchicLayout(); layout.LabelingEnabled = true; layout.Labeling = new GenericLabeling(); new BufferedLayout(layout).ApplyLayout(graph); Console.WriteLine("\n\nGRAPH LAID OUT USING GENERIC EDGE LABELING"); Console.WriteLine("v1 center position = " + graph.GetCenter(v1)); Console.WriteLine("v2 center position = " + graph.GetCenter(v2)); Console.WriteLine("v3 center position = " + graph.GetCenter(v3)); Console.WriteLine("e1 path = " + graph.GetPath(e1)); Console.WriteLine("e2 path = " + graph.GetPath(e2)); Console.WriteLine("e3 path = " + graph.GetPath(e3)); Console.WriteLine("ell1 upper left location = " + GetEdgeLabelLocation(graph, e1, CreateEdgeLabelLayout(labelFactory, e1, new SliderEdgeLabelLayoutModel(SliderMode.Center), PreferredPlacementDescriptor.NewSharedInstance(LabelPlacements.AtCenter)))); Console.WriteLine("ell2 upper left location = " + GetEdgeLabelLocation(graph, e1, graph.GetLabelLayout(e1)[1])); GraphViewer gv = new GraphViewer(); gv.AddLayoutGraph(new CopiedLayoutGraph(graph), "Layout with Generic Labeling"); var freeModel = new FreeEdgeLabelLayoutModel(); graph.SetLabelLayout(e1, new[] { CreateEdgeLabelLayout(labelFactory, e1, freeModel, PreferredPlacementDescriptor.NewSharedInstance(LabelPlacements.AtCenter)), CreateEdgeLabelLayout(labelFactory, e1, freeModel, PreferredPlacementDescriptor.NewSharedInstance(LabelPlacements.LeftOfEdge)) }); layout.LabelingEnabled = true; layout.Labeling = new LabelLayoutTranslator(); new BufferedLayout(layout).ApplyLayout(graph); Console.WriteLine("\n\nGRAPH LAID OUT USING HIERACHIC LAYOUT SPECIFIC EDGE LABELING"); Console.WriteLine("v1 center position = " + graph.GetCenter(v1)); Console.WriteLine("v2 center position = " + graph.GetCenter(v2)); Console.WriteLine("v3 center position = " + graph.GetCenter(v3)); Console.WriteLine("e1 path = " + graph.GetPath(e1)); Console.WriteLine("e2 path = " + graph.GetPath(e2)); Console.WriteLine("e3 path = " + graph.GetPath(e3)); Console.WriteLine("ell1 upper left location = " + GetEdgeLabelLocation(graph, e1, CreateEdgeLabelLayout(labelFactory, e1, new SliderEdgeLabelLayoutModel(SliderMode.Center), PreferredPlacementDescriptor.NewSharedInstance(LabelPlacements.AtCenter)))); Console.WriteLine("ell2 upper left location = " + GetEdgeLabelLocation(graph, e1, graph.GetLabelLayout(e1)[1])); //display the result in a simple viewer gv.AddLayoutGraph(graph, "Layout with Integrated Labeling"); var application = new System.Windows.Application(); application.Run(gv); }
private void MarkFixedAndAffectedItems(LayoutGraphAdapter adapter, bool layoutOnlySelection) { if (layoutOnlySelection) { var affectedEdges = Mappers.FromDelegate((IEdge edge) => adapter.SelectionModel.IsSelected(edge) || adapter.SelectionModel.IsSelected(edge.GetSourceNode()) || adapter.SelectionModel.IsSelected(edge.GetTargetNode())); adapter.AddDataProvider(LayoutKeys.AffectedEdgesDpKey, affectedEdges); // fix ports of unselected edges and edges at event ports adapter.AddDataProvider(PortConstraintKeys.SourcePortConstraintDpKey, Mappers.FromDelegate <IEdge, PortConstraint>( edge => (!affectedEdges[edge] || edge.SourcePort.Style is EventPortStyle) ? PortConstraint.Create(GetSide(edge, true)) : null)); adapter.AddDataProvider(PortConstraintKeys.TargetPortConstraintDpKey, Mappers.FromDelegate <IEdge, PortConstraint>( edge => !affectedEdges[edge] ? PortConstraint.Create(GetSide(edge, false)) : null)); // give core layout hints that selected nodes and edges should be incremental IncrementalHints.ContextDelegate = (item, factory) => { if (item is INode && adapter.SelectionModel.IsSelected(item)) { return(factory.CreateLayerIncrementallyHint(item)); } else if (item is IEdge && affectedEdges[(IEdge)item]) { return(factory.CreateSequenceIncrementallyHint(item)); } return(null); }; adapter.AddDataProvider(BpmnLayout.AffectedLabelsDpKey, Mappers.FromDelegate <ILabel, bool>(label => { var edge = label.Owner as IEdge; if (edge != null) { return(affectedEdges[edge]); } var node = label.Owner as INode; if (node != null) { var isInnerLabel = node.Layout.Contains(label.GetLayout().GetCenter()); bool isPool = node.Style is PoolNodeStyle; bool isChoreography = node.Style is ChoreographyNodeStyle; return(!isInnerLabel && !isPool && !isChoreography && adapter.SelectionModel.IsSelected(node)); } return(false); })); } else { // fix source port of edges at event ports adapter.AddDataProvider(PortConstraintKeys.SourcePortConstraintDpKey, Mappers.FromDelegate <IEdge, PortConstraint>( edge => edge.SourcePort.Style is EventPortStyle ? PortConstraint.Create(GetSide(edge, true)) : null)); adapter.AddDataProvider(BpmnLayout.AffectedLabelsDpKey, Mappers.FromDelegate <ILabel, bool>(label => { if (label.Owner is IEdge) { return(true); } var node = label.Owner as INode; if (node != null) { var isInnerLabel = node.Layout.Contains(label.GetLayout().GetCenter()); bool isPool = node.Style is PoolNodeStyle; bool isChoreography = node.Style is ChoreographyNodeStyle; return(!isInnerLabel && !isPool && !isChoreography); } return(false); })); } }
/// <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); } } }
/// <summary> /// Fix the <see cref="PortSide"/> of the given edge's port constraints /// for the oriented rectangles rotation. /// </summary> /// <remarks> /// If the oriented rectangle is rotated 180° the port sides will be flipped, e.g. /// The port constraints will be replaced. /// </remarks> /// <param name="portConstraints">The data provider for source or target constraints.</param> /// <param name="edge">The edge to fix the port constraints for.</param> /// <param name="angle">The angle as obtained by applying <see cref="Math.Atan2"/> /// to the oriented rectangle's upX and upY vectors.</param> private static void FixPortConstraintSide(IDataMap portConstraints, Edge edge, double angle) { var constraint = (PortConstraint)portConstraints.Get(edge); if (constraint != null && !constraint.AtAnySide) { var side = constraint.Side; if (angle < Math.PI / 4 && angle > -Math.PI / 4) { // top is rotated 90 deg left switch (side) { case PortSide.West: side = PortSide.North; break; case PortSide.South: side = PortSide.West; break; case PortSide.East: side = PortSide.South; break; case PortSide.North: side = PortSide.East; break; } } else if (angle > Math.PI / 4 && angle < Math.PI * 0.75 && angle > 0) { // 180 deg switch (side) { case PortSide.West: side = PortSide.East; break; case PortSide.South: side = PortSide.North; break; case PortSide.East: side = PortSide.West; break; case PortSide.North: side = PortSide.South; break; } } else if (angle > Math.PI * 0.75 || angle < -Math.PI * 0.75) { // top is rotated 90 deg right switch (side) { case PortSide.West: side = PortSide.South; break; case PortSide.South: side = PortSide.East; break; case PortSide.East: side = PortSide.North; break; case PortSide.North: side = PortSide.West; break; } } else { // no rotation return; } // Side is not writable, so set new constraint portConstraints.Set(edge, PortConstraint.Create(side, constraint.Strong)); } }
private static void MarkFixedAndAffectedItems(GenericLayoutData data, HierarchicLayoutData hierarchicLayoutData, ISelectionModel <IModelItem> graphSelection, bool layoutOnlySelection) { if (layoutOnlySelection) { var affectedEdges = Mappers.FromDelegate((IEdge edge) => graphSelection.IsSelected(edge) || graphSelection.IsSelected(edge.GetSourceNode()) || graphSelection.IsSelected(edge.GetTargetNode())); data.AddItemCollection(LayoutKeys.AffectedEdgesDpKey).Mapper = affectedEdges; // fix ports of unselected edges and edges at event ports data.AddItemMapping(PortConstraintKeys.SourcePortConstraintDpKey).Delegate = edge => (!affectedEdges[edge] || edge.SourcePort.Style is EventPortStyle) ? PortConstraint.Create(GetSide(edge, true)) : null; data.AddItemMapping(PortConstraintKeys.TargetPortConstraintDpKey).Delegate = edge => !affectedEdges[edge] ? PortConstraint.Create(GetSide(edge, false)) : null; // give core layout hints that selected nodes and edges should be incremental hierarchicLayoutData.IncrementalHints.ContextDelegate = (item, factory) => { if (item is INode && graphSelection.IsSelected(item)) { return(factory.CreateLayerIncrementallyHint(item)); } else if (item is IEdge && affectedEdges[(IEdge)item]) { return(factory.CreateSequenceIncrementallyHint(item)); } return(null); }; data.AddItemCollection(BpmnLayout.AffectedLabelsDpKey).Delegate = label => { var edge = label.Owner as IEdge; if (edge != null) { return(affectedEdges[edge]); } var node = label.Owner as INode; if (node != null) { var isInnerLabel = node.Layout.Contains(label.GetLayout().GetCenter()); bool isPool = node.Style is PoolNodeStyle; bool isChoreography = node.Style is ChoreographyNodeStyle; return(!isInnerLabel && !isPool && !isChoreography && graphSelection.IsSelected(node)); } return(false); }; } else { // fix source port of edges at event ports data.AddItemMapping(PortConstraintKeys.SourcePortConstraintDpKey).Delegate = edge => edge.SourcePort.Style is EventPortStyle?PortConstraint.Create(GetSide(edge, true)) : null; data.AddItemCollection(BpmnLayout.AffectedLabelsDpKey).Delegate = label => { if (label.Owner is IEdge) { return(true); } var node = label.Owner as INode; if (node != null) { var isInnerLabel = node.Layout.Contains(label.GetLayout().GetCenter()); bool isPool = node.Style is PoolNodeStyle; bool isChoreography = node.Style is ChoreographyNodeStyle; return(!isInnerLabel && !isPool && !isChoreography); } return(false); }; } }