static void AddEdgesBetweenColumns(string[] col0, string[] col1, GeometryGraph graph)
 {
     foreach (var id in col0)
     {
         Edge edge = new Edge(graph.FindNodeByUserData(id), graph.FindNodeByUserData(col1[r.Next(col1.Length)]));
         edge.EdgeGeometry.TargetArrowhead = null;
         graph.Edges.Add(edge);
         edge = new Edge(graph.FindNodeByUserData(id), graph.FindNodeByUserData(col1[r.Next(col1.Length)]));
         graph.Edges.Add(edge);
     }
 }
示例#2
0
 private static void CreateDiagramLinks(List <BinaryLinkShape> linkShapes, GeometryGraph graph)
 {
     foreach (BinaryLinkShape linkShape in linkShapes)
     {
         graph.Edges.Add(new Edge(graph.FindNodeByUserData(linkShape.Nodes[0]),
                                  graph.FindNodeByUserData(linkShape.Nodes[1]))
         {
             UserData = linkShape
         });
     }
 }
        private static void AddEdge(GeometryGraph g, string u, string v)
        {
            var e = new Edge(g.FindNodeByUserData(u), g.FindNodeByUserData(v));

            e.EdgeGeometry.SourceArrowhead = new Arrowhead {
                Length = 5, Width = 5
            };
            e.EdgeGeometry.TargetArrowhead = new Arrowhead {
                Length = 5, Width = 5
            };
            g.Edges.Add(e);
        }
 //helper function for ToMSALGraph
 private static void RecursiveCompleteMSALGraphEdges(GeometryGraph target, Node current)
 {
     target.Edges.Add(
         new Edge(
             // Set source and target by finding MSAGL node based on SfDiagram node.
             target.FindNodeByUserData(current.getParent()),
             target.FindNodeByUserData(current))
     {
         Weight = 1,
     });
     foreach (Node child in current.getChildren())
     {
         RecursiveCompleteMSALGraphEdges(target, child);
     }
 }
        internal static GeometryGraph CreateAndLayoutGraph()
        {
            double        w     = 40;
            double        h     = 10;
            GeometryGraph graph = new GeometryGraph();
            // columns
            var col0 = new[] { "a", "b", "c" };
            var col1 = new[] { "d", "e", "f", "g" };
            var col2 = new[] { "k", "l", "m", "n" };
            var col3 = new[] { "w", "y", "z" };

            var settings = new SugiyamaLayoutSettings();

            foreach (var id in col0)
            {
                DrawingUtilsForSamples.AddNode(id, graph, w, h);
            }
            foreach (var id in col1)
            {
                DrawingUtilsForSamples.AddNode(id, graph, w, h);
            }
            foreach (var id in col2)
            {
                DrawingUtilsForSamples.AddNode(id, graph, w, h);
            }
            foreach (var id in col3)
            {
                DrawingUtilsForSamples.AddNode(id, graph, w, h);
            }

            //pinning columns
            settings.PinNodesToSameLayer(col0.Select(s => graph.FindNodeByUserData(s)).ToArray());
            settings.PinNodesToSameLayer(col1.Select(s => graph.FindNodeByUserData(s)).ToArray());
            settings.PinNodesToSameLayer(col2.Select(s => graph.FindNodeByUserData(s)).ToArray());
            settings.PinNodesToSameLayer(col3.Select(s => graph.FindNodeByUserData(s)).ToArray());

            AddEdgesBetweenColumns(col0, col1, graph);
            AddEdgesBetweenColumns(col1, col2, graph);
            AddEdgesBetweenColumns(col2, col3, graph);
            // rotate layer to columns
            settings.Transformation  = PlaneTransformation.Rotation(Math.PI / 2);
            settings.NodeSeparation  = 5;
            settings.LayerSeparation = 100;
            var ll = new LayeredLayout(graph, settings);

            ll.Run();
            return(graph);
        }
        static Node SetNode(
            GeometryGraph g,
            string id,
            double xRad,
            double yRad)
        {
            var geomNode = g.FindNodeByUserData(id);

            if (geomNode == null)
            {
                g.Nodes.Add(geomNode = new Node()
                {
                    UserData = id
                });
            }
            var size = MeasureTextSize(id);

            geomNode.BoundaryCurve = CurveFactory.CreateRectangleWithRoundedCorners(
                size.Width,
                size.Height,
                xRad,
                yRad,
                new Point(0, 0)
                );
            return(geomNode);
        }
        private static Cluster AddRootCluster(GeometryGraph g, params string[] vs)
        {
            var c = new Cluster(from v in vs select g.FindNodeByUserData(v));

            g.RootCluster = c;
            return(c);
        }
        private string GetUniqueId(IComparable p)
        {
            string id;

            do
            {
                id = p.ToString() + idCounter++;
            }while (graph.FindNodeByUserData(id) != null);
            return(id);
        }
示例#9
0
        public static Node AddNode(GeometryGraph geometryGraph, IDiagramItem item)
        {
            Node msaglNode = geometryGraph.FindNodeByUserData(item);

            if (msaglNode == null)
            {
                msaglNode = new Node(CreateCurve(item), item);
                geometryGraph.Nodes.Add(msaglNode);
            }
            return(msaglNode);
        }
示例#10
0
        /// <summary>
        /// Finds the GeometryNode for the drawing node with the given id.
        /// </summary>
        /// <returns></returns>
        public Microsoft.Msagl.Core.Layout.Node FindGeometryNode(string nodeId)
        {
            if (GeometryGraph != null)
            {
                Node node = nodeMap[nodeId] as Node;
                if (node != null)
                {
                    return(GeometryGraph.FindNodeByUserData(node));
                }
            }

            return(null);
        }
示例#11
0
        private static void AddDesignConstraints(List <BinaryLinkShape> linkShapes, ModelRoot modelRoot, GeometryGraph graph)
        {
            // Sugiyama allows for layout constraints, so we can make sure that base classes are above derived classes,
            // and put classes derived from the same base in the same vertical layer. Unfortunately, other layout strategies
            // don't have that ability.
            if (modelRoot.LayoutAlgorithmSettings is SugiyamaLayoutSettings sugiyamaSettings)
            {
                // ensure generalizations are vertically over each other
                foreach (GeneralizationConnector linkShape in linkShapes.OfType <GeneralizationConnector>())
                {
                    if (modelRoot.LayoutAlgorithm == LayoutAlgorithm.Sugiyama)
                    {
                        int upperNodeIndex = linkShape.Nodes[1].ModelElement.GetBaseElement() == linkShape.Nodes[0].ModelElement
                                          ? 0
                                          : 1;

                        int lowerNodeIndex = upperNodeIndex == 0
                                          ? 1
                                          : 0;

                        sugiyamaSettings.AddUpDownConstraint(graph.FindNodeByUserData(linkShape.Nodes[upperNodeIndex]),
                                                             graph.FindNodeByUserData(linkShape.Nodes[lowerNodeIndex]));
                    }
                }

                // add constraints ensuring descendents of a base class are on the same level
                Dictionary <string, List <NodeShape> > derivedClasses = linkShapes.OfType <GeneralizationConnector>()
                                                                        .SelectMany(ls => ls.Nodes)
                                                                        .Where(n => n.ModelElement is ModelClass mc && mc.BaseClass != null)
                                                                        .GroupBy(n => ((ModelClass)n.ModelElement).BaseClass)
                                                                        .ToDictionary(n => n.Key, n => n.ToList());

                foreach (KeyValuePair <string, List <NodeShape> > derivedClassData in derivedClasses)
                {
                    Node[] siblingNodes = derivedClassData.Value.Select(graph.FindNodeByUserData).ToArray();
                    sugiyamaSettings.AddSameLayerNeighbors(siblingNodes);
                }
            }
        }
        private static Cluster AddCluster(double padding, GeometryGraph g, Cluster parent, params string[] vs)
        {
            var c = new Cluster(from v in vs select g.FindNodeByUserData(v))
            {
                UserData            = string.Concat(vs),
                RectangularBoundary =
                    new RectangularClusterBoundary {
                    LeftMargin = padding, RightMargin = padding, BottomMargin = padding, TopMargin = padding
                },
                BoundaryCurve = CurveFactory.CreateRectangle(30, 30, new Point(15, 15))
            };

            parent.AddChild(c);
            return(c);
        }
        /// <summary>
        /// Creates a small, non-trivial clustered and disconnected graph for tests
        ///   ( A B-)-C
        ///   D-(-(-E F)-G)
        ///   H-I
        ///   J
        ///   (K L-)-(-M N)
        /// </summary>
        /// <returns>returns a disconnected clustered graph</returns>
        public static GeometryGraph CreateClusteredGraph(double padding)
        {
            var graph = new GeometryGraph();

            for (int i = 0; i < 14; ++i)
            {
                graph.Nodes.Add(new Node(CurveFactory.CreateRectangle(10, 10, new Point()), GetCharacter(i)));
            }
            AddEdge(graph, "B", "C");
            AddEdge(graph, "D", "E");
            AddEdge(graph, "H", "I");
            var root = AddRootCluster(graph, "C", "D", "H", "I", "J");

            AddCluster(padding, graph, root, "A", "B");
            var parent = AddCluster(padding, graph, root, "G");
            var child  = AddCluster(padding, graph, parent, "E", "F");

            graph.Edges.Add(new Edge(graph.FindNodeByUserData("G"), child));
            graph.Edges.Add(new Edge(AddCluster(padding, graph, root, "K", "L"), AddCluster(padding, graph, root, "M", "N")));

            return(graph);
        }
        static internal GeometryGraph CreateAndLayoutGraph()
        {
            GeometryGraph graph = new GeometryGraph();

            double width  = 40;
            double height = 10;

            foreach (string id in "0 1 2 3 4 5 6 A B C D E F G a b c d e".Split(' '))
            {
                DrawingUtilsForSamples.AddNode(id, graph, width, height);
            }

            graph.Edges.Add(new Edge(graph.FindNodeByUserData("A"), graph.FindNodeByUserData("B")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("A"), graph.FindNodeByUserData("C")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("A"), graph.FindNodeByUserData("D")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("D"), graph.FindNodeByUserData("E")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("B"), graph.FindNodeByUserData("E")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("D"), graph.FindNodeByUserData("F")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("0"), graph.FindNodeByUserData("F")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("1"), graph.FindNodeByUserData("F")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("2"), graph.FindNodeByUserData("F")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("3"), graph.FindNodeByUserData("F")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("4"), graph.FindNodeByUserData("F")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("5"), graph.FindNodeByUserData("F")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("6"), graph.FindNodeByUserData("F")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("a"), graph.FindNodeByUserData("b")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("b"), graph.FindNodeByUserData("c")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("c"), graph.FindNodeByUserData("d")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("d"), graph.FindNodeByUserData("e")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("A"), graph.FindNodeByUserData("a")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("B"), graph.FindNodeByUserData("a")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("C"), graph.FindNodeByUserData("a")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("D"), graph.FindNodeByUserData("a")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("E"), graph.FindNodeByUserData("a")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("F"), graph.FindNodeByUserData("a")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("G"), graph.FindNodeByUserData("a")));

            var settings = new SugiyamaLayoutSettings {
                Transformation      = PlaneTransformation.Rotation(Math.PI / 2),
                EdgeRoutingSettings = { EdgeRoutingMode = EdgeRoutingMode.Spline }
            };
            var layout = new LayeredLayout(graph, settings);

            layout.Run();
            return(graph);
        }
        static internal GeometryGraph CreateAndLayoutGraph()
        {
            GeometryGraph graph = new GeometryGraph();

            double width  = 40;
            double height = 10;

            foreach (string id in "0 1 2 3 4 5 6 A B C D E F G a b c d e".Split(' '))
            {
                AddNode(id, graph, width, height);
            }

            graph.Edges.Add(new Edge(graph.FindNodeByUserData("A"), graph.FindNodeByUserData("B")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("A"), graph.FindNodeByUserData("C")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("A"), graph.FindNodeByUserData("D")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("D"), graph.FindNodeByUserData("E")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("B"), graph.FindNodeByUserData("E")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("D"), graph.FindNodeByUserData("F")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("0"), graph.FindNodeByUserData("F")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("1"), graph.FindNodeByUserData("F")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("2"), graph.FindNodeByUserData("F")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("3"), graph.FindNodeByUserData("F")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("4"), graph.FindNodeByUserData("F")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("5"), graph.FindNodeByUserData("F")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("6"), graph.FindNodeByUserData("F")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("a"), graph.FindNodeByUserData("b")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("b"), graph.FindNodeByUserData("c")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("c"), graph.FindNodeByUserData("d")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("d"), graph.FindNodeByUserData("e")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("A"), graph.FindNodeByUserData("a")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("B"), graph.FindNodeByUserData("a")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("C"), graph.FindNodeByUserData("a")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("D"), graph.FindNodeByUserData("a")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("E"), graph.FindNodeByUserData("a")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("F"), graph.FindNodeByUserData("a")));
            graph.Edges.Add(new Edge(graph.FindNodeByUserData("G"), graph.FindNodeByUserData("a")));

            var settings = new SugiyamaLayoutSettings();

            settings.Transformation = PlaneTransformation.Rotation(Math.PI / 2);
            settings.EdgeRoutingSettings.EdgeRoutingMode = EdgeRoutingMode.Spline;
            var layout = new LayeredLayout(graph, settings);

            layout.Run();
            return(graph);
//            double w = 40;
//            double h = 10;
//            GeometryGraph graph = new GeometryGraph();
            //columns
//            var col0 = new[] { "a", "b", "c" };
//            var col1 = new[] { "d", "e", "f", "g" };
//            var col2 = new[] { "k", "l", "m", "n" };
//            var col3 = new[] { "w", "y", "z" };
//
//            var settings = new SugiyamaLayoutSettings();
//
//            foreach (var id in col0)
//                AddNode(id, graph, w, h);
//            foreach (var id in col1)
//                AddNode(id, graph, w, h);
//            foreach (var id in col2)
//                AddNode(id, graph, w, h);
//            foreach (var id in col3)
//                AddNode(id, graph, w, h);
//
            //pinning columns
//            settings.PinNodesToSameLayer(col0.Select(s=>graph.FindNodeByUserData(s)).ToArray());
//            settings.PinNodesToSameLayer(col1.Select(s => graph.FindNodeByUserData(s)).ToArray());
//            settings.PinNodesToSameLayer(col2.Select(s => graph.FindNodeByUserData(s)).ToArray());
//            settings.PinNodesToSameLayer(col3.Select(s => graph.FindNodeByUserData(s)).ToArray());
//
//            AddEdgesBetweenColumns(col0, col1, graph);
//            AddEdgesBetweenColumns(col1, col2, graph);
//            AddEdgesBetweenColumns(col2, col3, graph);
            //rotate layer to columns
            // graph.Transformation = PlaneTransformation.Rotation(Math.PI / 2);
//            settings.NodeSeparation = 5;
//            settings.LayerSeparation = 100;
//            var ll = new LayeredLayout(graph, settings);
//            ll.Run();
//            return graph;
        }
        public void GetClusteredConnectedComponentsTest()
        {
            List <GeometryGraph> components = graph.GetClusteredConnectedComponents().ToList();

            List <Cluster> expectedTopLevelClusters = graph.RootCluster.Clusters.ToList();

            Assert.AreEqual(5, components.Count, "Expected 5 connected components");

            Node nodeA = graph.FindNodeByUserData("A");
            Node nodeB = graph.FindNodeByUserData("B");
            Node nodeC = graph.FindNodeByUserData("C");

            // go through each of the components and make sure that it matches the original
            // since the graph is traversed in order of Nodes we know what order the components will be appear in Actual
            GeometryGraph c = components[0];

            Assert.AreEqual(3, c.Nodes.Count, "Component has incorrect node count");
            Assert.IsTrue(c.FindNodeByUserData(nodeA) != null, "Component should contain node A");
            Assert.IsTrue(c.FindNodeByUserData(nodeB) != null, "Component should contain node B");
            Assert.IsTrue(c.FindNodeByUserData(nodeC) != null, "Component should contain node C");
            Assert.AreSame(c.FindNodeByUserData(nodeC).InEdges.First().UserData, nodeC.InEdges.First(), "Edge doesn't match original");

            Assert.AreEqual(1, c.Edges.Count, "Component should contain 1 edge");
            var cluster = c.RootCluster.Clusters.First();

            Assert.AreEqual(2, cluster.Nodes.Count(), "Component should contain 2 nodes");
            Assert.AreSame(expectedTopLevelClusters[0], cluster.UserData, "Component invalid");

            c = components[1];
            Assert.AreEqual(4, c.Nodes.Count, "Component invalid");
            Assert.AreEqual(2, c.Edges.Count, "Component invalid");
            cluster = c.RootCluster.Clusters.First();
            Assert.AreSame(expectedTopLevelClusters[1], cluster.UserData, "Component invalid");
            Assert.AreEqual(1, cluster.Nodes.Count(), "Component invalid");
            Assert.AreEqual("G", ((Node)cluster.Nodes.First().UserData).UserData, "Component invalid"); // double user data lookup since the first user data is the components source node
            var nested = cluster.Clusters.First();

            Assert.AreEqual("E", ((Node)nested.Nodes.First().UserData).UserData, "Component invalid");
            Assert.AreEqual("F", ((Node)nested.Nodes.Last().UserData).UserData, "Component invalid");
            Assert.AreSame(expectedTopLevelClusters[1].Clusters.First(), nested.UserData, "Component invalid");
            Assert.AreEqual(2, nested.Nodes.Count(), "Component invalid");

            c = components[2];
            Assert.AreEqual(2, c.Nodes.Count, "Component invalid");
            Assert.AreEqual(1, c.Edges.Count, "Component invalid");
            Assert.IsFalse(c.RootCluster.Clusters.Any(), "Component invalid");

            c = components[3];
            Assert.AreEqual(1, c.Nodes.Count, "Component invalid");
            Assert.AreEqual(0, c.Edges.Count, "Component invalid");
            Assert.IsFalse(c.RootCluster.Clusters.Any(), "Component invalid");

            c       = components[4];
            cluster = c.RootCluster.Clusters.First();
            Assert.AreEqual("K", ((Node)cluster.Nodes.First().UserData).UserData, "Component invalid");
            Assert.AreEqual("L", ((Node)cluster.Nodes.Last().UserData).UserData, "Component invalid");
            cluster = c.RootCluster.Clusters.Last();
            Assert.AreEqual("M", ((Node)cluster.Nodes.First().UserData).UserData, "Component invalid");
            Assert.AreEqual("N", ((Node)cluster.Nodes.Last().UserData).UserData, "Component invalid");
            Assert.AreEqual(c.Edges.Count, 1, "Component invalid");
        }
示例#17
0
    internal void CreateGraph()
    {
        graph = new GeometryGraph();

        double width  = 100;
        double height = 30;

        foreach (string id in "0 1 2 3 4 5 6 A B C D E F G a b c d e".Split(' '))
        {
            AddNode(id, graph, width, height);
        }

        graph.Edges.Add(new Edge(graph.FindNodeByUserData("A"), graph.FindNodeByUserData("B")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("A"), graph.FindNodeByUserData("C")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("A"), graph.FindNodeByUserData("D")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("D"), graph.FindNodeByUserData("E")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("B"), graph.FindNodeByUserData("E")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("D"), graph.FindNodeByUserData("F")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("0"), graph.FindNodeByUserData("F")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("1"), graph.FindNodeByUserData("F")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("2"), graph.FindNodeByUserData("F")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("3"), graph.FindNodeByUserData("F")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("4"), graph.FindNodeByUserData("F")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("5"), graph.FindNodeByUserData("F")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("6"), graph.FindNodeByUserData("F")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("a"), graph.FindNodeByUserData("b")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("b"), graph.FindNodeByUserData("c")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("c"), graph.FindNodeByUserData("d")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("d"), graph.FindNodeByUserData("e")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("A"), graph.FindNodeByUserData("a")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("B"), graph.FindNodeByUserData("a")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("C"), graph.FindNodeByUserData("a")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("D"), graph.FindNodeByUserData("a")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("E"), graph.FindNodeByUserData("a")));
        graph.Edges.Add(new Edge(graph.FindNodeByUserData("F"), graph.FindNodeByUserData("a")));
        Edge edge = new Edge(graph.FindNodeByUserData("G"), graph.FindNodeByUserData("a"));

        graph.Edges.Add(edge);
    }