/// <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 }
            };
        }
Example #2
0
 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;
            }
        }
        private void AdjustNodeSize(Node node, LayoutGraph graph)
        {
            double width  = 60;
            double height = 40;

            var leftEdgeSpace  = CalcRequiredSpace(node.InEdges, graph);
            var rightEdgeSpace = CalcRequiredSpace(node.OutEdges, graph);

            if (LayoutOrientation == LayoutOrientation.TopToBottom || LayoutOrientation == LayoutOrientation.BottomToTop)
            {
                // we have to enlarge the width such that the in-/out-edges can be placed side by side without overlaps
                width = Math.Max(width, leftEdgeSpace);
                width = Math.Max(width, rightEdgeSpace);
            }
            else
            {
                // we have to enlarge the height such that the in-/out-edges can be placed side by side without overlaps
                height = Math.Max(height, leftEdgeSpace);
                height = Math.Max(height, rightEdgeSpace);
            }

            // adjust size for edges with strong port constraints
            var edgeThicknessDP = graph.GetDataProvider(HierarchicLayout.EdgeThicknessDpKey);

            if (edgeThicknessDP != null)
            {
                foreach (var edge in node.Edges)
                {
                    var thickness = edgeThicknessDP.GetDouble(edge);

                    var spc = PortConstraint.GetSPC(graph, edge);
                    if (edge.Source == node && spc != null && spc.Strong)
                    {
                        var sourcePoint = graph.GetSourcePointRel(edge);
                        width  = Math.Max(width, Math.Abs(sourcePoint.X) * 2 + thickness);
                        height = Math.Max(height, Math.Abs(sourcePoint.Y) * 2 + thickness);
                    }

                    var tpc = PortConstraint.GetTPC(graph, edge);
                    if (edge.Target == node && tpc != null && tpc.Strong)
                    {
                        var targetPoint = graph.GetTargetPointRel(edge);
                        width  = Math.Max(width, Math.Abs(targetPoint.X) * 2 + thickness);
                        height = Math.Max(height, Math.Abs(targetPoint.Y) * 2 + thickness);
                    }
                }
            }
            graph.SetSize(node, width, height);
        }
 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
             }
         }
     });
 }
Example #6
0
        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);
        }
Example #7
0
        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);
        }
Example #8
0
        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);
                }));
            }
        }
Example #10
0
        /// <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);
                }
            }
        }
Example #11
0
        /// <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);
                };
            }
        }