/// <summary>
        /// Creates the initial sample graph.
        /// </summary>
        private void CreateSampleGraph()
        {
            IGraph graph = graphControl.Graph;
            INode  node0 = graph.CreateNode(new RectD(180, 40, 30, 30));
            INode  node1 = graph.CreateNode(new RectD(260, 50, 30, 30));
            INode  node2 = graph.CreateNode(new RectD(284, 200, 30, 30));
            INode  node3 = graph.CreateNode(new RectD(350, 40, 30, 30));
            IEdge  edge0 = graph.CreateEdge(node1, node2);

            // Add some bends
            graph.AddBend(edge0, new PointD(350, 130));
            graph.AddBend(edge0, new PointD(230, 170));
            graph.CreateEdge(node1, node0);
            graph.CreateEdge(node1, node3);
            ILabel label0 = graph.AddLabel(edge0, "Edge Label");
            ILabel label1 = graph.AddLabel(node1, "Node Label");

            ////////////////////////////////////////////////////
            //////////////// New in this sample ////////////////
            ////////////////////////////////////////////////////

            CreateLabelTags(label0, label1);

            ////////////////////////////////////////////////////
        }
Пример #2
0
        /// <summary>
        /// Creates the initial sample graph.
        /// </summary>
        private void CreateSampleGraph()
        {
            IGraph graph = graphControl.Graph;

            ////////////////////////////////////////////////////
            //////////////// New in this sample ////////////////
            ////////////////////////////////////////////////////
            INode node0 = graph.CreateNode(new RectD(180, 40, 30, 30));

            node0.Tag = Colors.Green;
            INode node1 = graph.CreateNode(new RectD(260, 50, 30, 30));
            INode node2 = graph.CreateNode(new RectD(284, 200, 30, 30));
            INode node3 = graph.CreateNode(new RectD(350, 40, 30, 30), new MySimpleNodeStyle()
            {
                NodeColor = Colors.Yellow
            });
            ////////////////////////////////////////////////////
            IEdge edge0 = graph.CreateEdge(node1, node2);

            // Add some bends
            graph.AddBend(edge0, new PointD(350, 130));
            graph.AddBend(edge0, new PointD(230, 170));
            graph.CreateEdge(node1, node0);
            graph.CreateEdge(node1, node3);
            ILabel label0 = graph.AddLabel(edge0, "Edge Label");
            ILabel label1 = graph.AddLabel(node1, "Node Label");
        }
Пример #3
0
        public EdgeStylePanel()
        {
            IGraph graph = graphControl.Graph;

            graph.NodeDefaults.Style = VoidNodeStyle.Instance;
            this.dummyEdge           = graph.CreateEdge(
                graph.CreateNode(new RectD(10, 10, 0, 0)),
                graph.CreateNode(new RectD(50, 30, 0, 0)));
            graph.AddBend(dummyEdge, new PointD(30, 10), 0);
            graph.AddBend(dummyEdge, new PointD(30, 30), 1);

            graphControl.ContentRect = new RectD(5, 5, 50, 30);
        }
Пример #4
0
        /// <summary>
        /// Creates the sample graph of this demo.
        /// </summary>
        private void CreateSampleGraph(IGraph graph)
        {
            CreateSubgraph(graph, Colors.Firebrick, 0, false);
            CreateSubgraph(graph, Colors.Green, 110, false);
            CreateSubgraph(graph, Colors.Purple, 220, true);
            CreateSubgraph(graph, Colors.Orange, 330, false);

            // The blue edge has more bends than the other edges
            var blueEdge  = CreateSubgraph(graph, Colors.RoyalBlue, 440, false);
            var blueBends = blueEdge.Bends.ToArray();

            graph.Remove(blueBends[1]);
            graph.Remove(blueBends[0]);
            var sourcePortLocation = blueEdge.SourcePort.GetLocation();
            var targetPortLocation = blueEdge.TargetPort.GetLocation();

            graph.AddBend(blueEdge, new PointD(220, sourcePortLocation.Y - 30));
            graph.AddBend(blueEdge, new PointD(300, sourcePortLocation.Y - 30));
            graph.AddBend(blueEdge, new PointD(300, targetPortLocation.Y + 30));
            graph.AddBend(blueEdge, new PointD(380, targetPortLocation.Y + 30));
        }
Пример #5
0
        /// <summary>
        /// Creates a new bend at the given location. If this bend is on the first or last segment,
        /// a second bend is created and placed at a location that ensures that the newly create
        /// inner segment is orthogonal.
        /// </summary>
        public int CreateBend(IInputModeContext context, IGraph graph, IEdge edge, PointD location)
        {
            var edgePoints     = GetEdgePoints(edge);
            var closestSegment = DetermineBendSegmentIndex(edgePoints, location);

            int firstSegment = 0;
            int lastSegment  = edge.Bends.Count;

            // if bend wasn't created in first or last segment, call default action
            if (closestSegment != firstSegment && closestSegment != lastSegment)
            {
                return((new DefaultBendCreator()).CreateBend(context, graph, edge, location));
            }

            // add created bend and another one to make the edge stay orthogonal
            if (closestSegment == -1 || context == null || !(context.ParentInputMode is CreateBendInputMode))
            {
                return(-1);
            }
            var editingContext = context.Lookup <OrthogonalEdgeEditingContext>();

            if (editingContext == null)
            {
                return(-1);
            }
            if (closestSegment == firstSegment)
            {
                IPoint nextPoint = edgePoints[1];
                // get orientation of next edge segment to determine second bend location
                SegmentOrientation orientation = editingContext.GetSegmentOrientation(edge, 1);
                graph.AddBend(edge, location, 0);
                if (orientation == SegmentOrientation.Horizontal)
                {
                    graph.AddBend(edge, new PointD(nextPoint.X, location.Y), 1);
                }
                else if (orientation == SegmentOrientation.Vertical)
                {
                    graph.AddBend(edge, new PointD(location.X, nextPoint.Y), 1);
                }
                return(0);
            }
            if (closestSegment == lastSegment)
            {
                IPoint prevPoint = edgePoints[edge.Bends.Count];
                // get orientation of next edge segment to determine second bend location
                SegmentOrientation orientation = editingContext.GetSegmentOrientation(edge, edge.Bends.Count - 1);
                graph.AddBend(edge, location, edge.Bends.Count);
                if (orientation == SegmentOrientation.Horizontal)
                {
                    graph.AddBend(edge, new PointD(prevPoint.X, location.Y), edge.Bends.Count - 1);
                }
                else if (orientation == SegmentOrientation.Vertical)
                {
                    graph.AddBend(edge, new PointD(location.X, prevPoint.Y), edge.Bends.Count - 1);
                }
                return(edge.Bends.Count - 1);
            }
            return(-1);
        }
Пример #6
0
        /// <summary>
        /// Creates the sample graph of the given color with two nodes and a single edge.
        /// </summary>
        private static IEdge CreateSubgraph(IGraph graph, Color color, double yOffset, bool createPorts)
        {
            var brush = new SolidColorBrush(color);
            // Create two nodes
            var nodeStyle = new ShinyPlateNodeStyle {
                Brush = brush
            };
            var n1 = graph.CreateNode(new RectD(110, 100 + yOffset, 40, 40), nodeStyle, color);
            var n2 = graph.CreateNode(new RectD(450, 130 + yOffset, 40, 40), nodeStyle, color);

            // Create an edge, either between the two nodes or between the nodes's ports
            IEdge edge;

            if (!createPorts)
            {
                edge = graph.CreateEdge(n1, n2, new PolylineEdgeStyle {
                    Pen = new Pen(brush, 1)
                }, color);
            }
            else
            {
                var p1 = CreateSamplePorts(graph, n1, true);
                var p2 = CreateSamplePorts(graph, n2, false);
                edge = graph.CreateEdge(p1[1], p2[2], new PolylineEdgeStyle {
                    Pen = new Pen(brush, 1)
                }, color);
            }

            // Add bends that create a veredge.SourcePort.Locationtical segment in the middle of the edge
            var sourcePortLocation = edge.SourcePort.GetLocation();
            var targetPortLocation = edge.TargetPort.GetLocation();
            var x = (sourcePortLocation.X + targetPortLocation.X) / 2;

            graph.AddBend(edge, new PointD(x, sourcePortLocation.Y));
            graph.AddBend(edge, new PointD(x, targetPortLocation.Y));

            return(edge);
        }
        /// <summary>
        /// Called by the various edge creation callbacks to create an edge in the resulting graph view
        /// that corresponds to the provided <paramref name="layoutEdge"/>.
        /// </summary>
        /// <remarks>
        /// If a model edge is provided, the edge will be created between the copies of the corresponding
        /// source/target ports.
        /// </remarks>
        ///<param name="pageLayoutGraph">The layout graph representing the current page.</param>
        ///<param name="pageView">The <see cref="IGraph"/> that is built to show the multi-page layout in a graph canvas.</param>
        ///<param name="layoutEdge">The edge of the layout graph that should be copied.</param>
        ///<param name="modelEdge">The edge of the original input graph that corresponds to the <paramref name="layoutEdge"/> (may be <see langword="null"/>).</param>
        ///<param name="edgeDefaults"></param>
        ///<returns>The created edge</returns>
        /// <seealso cref="CreateConnectorEdge"/>
        /// <seealso cref="CreateNormalEdge"/>
        /// <seealso cref="CreateProxyEdge"/>
        /// <seealso cref="CreateProxyReferenceEdge"/>
        protected IEdge CreateEdgeCore(LayoutGraph pageLayoutGraph, IGraph pageView, Edge layoutEdge, IEdge modelEdge, IEdgeDefaults edgeDefaults)
        {
            IEdge viewEdge;

            if (modelEdge != null)
            {
                // if the edge has a model edge: create the copied edge between
                // the copies of its source and target ports
                IPort      modelSourcePort = modelEdge.SourcePort;
                IPort      modelTargetPort = modelEdge.TargetPort;
                IPort      viewSourcePort  = GetViewPort(modelSourcePort);
                IPort      viewTargetPort  = GetViewPort(modelTargetPort);
                IEdgeStyle style           = (IEdgeStyle)(edgeDefaults.Style != NullEdgeStyle ?
                                                          edgeDefaults.GetStyleInstance() :
                                                          modelEdge.Style.Clone());
                viewEdge = pageView.CreateEdge(viewSourcePort, viewTargetPort, style, modelEdge.Tag);
            }
            else
            {
                // otherwise create it between the copies of its source and target nodes
                INode viewSource = GetViewNode(layoutEdge.Source);
                INode viewTarget = GetViewNode(layoutEdge.Target);
                viewEdge = pageView.CreateEdge(viewSource, viewTarget);
            }

            // adjust the port location
            YPoint newSourcePortLocation = pageLayoutGraph.GetSourcePointAbs(layoutEdge);
            YPoint newTargetPortLocation = pageLayoutGraph.GetTargetPointAbs(layoutEdge);

            pageView.SetPortLocation(viewEdge.SourcePort, newSourcePortLocation.ToPointD());
            pageView.SetPortLocation(viewEdge.TargetPort, newTargetPortLocation.ToPointD());

            // and copy the bends
            IEdgeLayout edgeLayout = pageLayoutGraph.GetLayout(layoutEdge);

            for (int i = 0; i < edgeLayout.PointCount(); i++)
            {
                YPoint bendLocation = edgeLayout.GetPoint(i);
                pageView.AddBend(viewEdge, new PointD(bendLocation.X, bendLocation.Y), i);
            }

            return(viewEdge);
        }
Пример #8
0
        /// <summary>
        /// If the existing number of bends is 2 mod 3 (i.e. the bends are consistent with
        /// what the bezier style expects),
        /// this implementation creates a triple of collinear bends and adjust the neighboring bends
        /// in a way that the shape of the curve is not changed initially and returns the middle bend.
        /// If there are no bends at all, it creates a triple plus two initial and final control bends, all of them collinear.
        /// Otherwise, the fallback bend creator is used to create a bend with its default strategy.
        /// </summary>
        /// <returns>The index of middle bend of a control point triple if such a triple was created,
        /// or the index of the newly created single bend.</returns>
        public int CreateBend(IInputModeContext context, IGraph graph, IEdge edge, PointD location)
        {
            switch (edge.Bends.Count)
            {
            case 0:
                var spl = edge.SourcePort.GetLocation();
                var tpl = edge.TargetPort.GetLocation();

                //a single linear segment... we just insert 5 collinear bends adjusted to the angle of the linear segment,
                //approximately evenly spaced
                graph.AddBend(edge, (location - spl) / 4 + spl, 0);
                graph.AddBend(edge, -(location - spl) / 4 + location, 1);
                graph.AddBend(edge, location, 2);
                graph.AddBend(edge, (location - spl) / 4 + location, 3);
                graph.AddBend(edge, location + (tpl - location) * 3 / 4, 4);
                return(2);

            case 1:
                //Use the default strategy to insert a single bend at the correct index
                return(fallBackCreator.CreateBend(context, graph, edge, location));

            default:
                var pathPoints = edge.GetPathPoints();
                if (pathPoints.Count % 3 == 1)
                {
                    //Consistent number of existing points
                    //Try to insert a smooth bend
                    //I.e. a triple of three collinear bends and adjust the neighbor bends

                    //Various quality measures and counters
                    var    segmentIndex    = 0;
                    var    pathCounter     = 0;
                    var    bestDistanceSqr = Double.PositiveInfinity;
                    double bestRatio       = Double.NaN;

                    //The index of the segment where we want to create the bend in the end
                    int bestIndex = -1;

                    //Find the best segment
                    while (pathCounter + 3 < pathPoints.Count)
                    {
                        //Get the control points defining the current segment
                        var cp0 = pathPoints[pathCounter++];
                        var cp1 = pathPoints[pathCounter++];
                        var cp2 = pathPoints[pathCounter++];
                        //Consecutive segments share the last/first control point! So we may not advance the counter here
                        var cp3 = pathPoints[pathCounter];
                        //Shift a cubic segment
                        //
                        //Here we assume that the path is actually composed of cubic segments, only.
                        //Alternatively, we could inspect the actual path created by the edge renderer - this would also
                        //allow to deal with intermediate non cubic segments, but we'd have to associate those
                        //path segments somehow with the correct bends again, so again this would be tied to the actual
                        //renderer implementation.
                        var fragment = new GeneralPath(2);
                        fragment.MoveTo(cp0);
                        fragment.CubicTo(cp1, cp2, cp3);

                        //Try to find the projection onto the fragment
                        var ratio = fragment.GetProjection(location, 0);
                        if (ratio.HasValue)
                        {
                            //Actually found a projection ratio
                            //Determine the point on the curve - the tangent provides this
                            var tangent = fragment.GetTangent(0, ratio.Value);
                            if (tangent.HasValue)
                            {
                                //There actually is a tangent
                                var d = (location - tangent.Value.Point).SquaredVectorLength;
                                //Is this the best distance?
                                if (d < bestDistanceSqr)
                                {
                                    bestDistanceSqr = d;
                                    //Remember ratio (needed to split the curve)
                                    bestRatio = ratio.Value;
                                    //and the index, of course
                                    bestIndex = segmentIndex;
                                }
                            }
                        }
                        ++segmentIndex;
                    }
                    if (bestIndex != -1)
                    {
                        //Actually found a segment
                        //For the drag, we want to move the middle bend
                        return(CreateBends(graph, edge, bestIndex, bestRatio, pathPoints).GetIndex());
                    }
                    //No best segment found (for whatever reason) - we don't want to create a bend so that we don't mess up anything
                    return(-1);
                }
                else
                {
                    //No consistent number of bends - just insert a single bend
                    //We could also see whether we actually would have a cubic segment on the path, and treat that differently
                    //However, why bother - just create the edge with a correct number of points instead
                    return(fallBackCreator.CreateBend(context, graph, edge, location));
                }
            }
        }
Пример #9
0
        private static IBend CreateBends([NotNull] IGraph graph, [NotNull] IEdge edge, int segmentIndex, double ratio,
                                         [NotNull] IListEnumerable <IPoint> pathPoints)
        {
            //Create 3 bends and adjust the neighbors
            //The first bend we need to touch is at startIndex
            var startIndex = segmentIndex * 3;

            //This holds the new coordinates left and right of the split point
            //We don't actually need all of them, but this keeps the algorithm more straightforward.
            var left  = new PointD[4];
            var right = new PointD[4];

            //Determine the new control points to cleanly split the curve

            GetCubicSplitPoints(ratio,
                                new[] {
                pathPoints[startIndex].ToPointD(), pathPoints[startIndex + 1].ToPointD(),
                pathPoints[startIndex + 2].ToPointD(), pathPoints[startIndex + 3].ToPointD()
            }, left, right);

            //Previous control point - does always exist as a bend, given our precondition
            var previousBend = edge.Bends[startIndex];
            //Next control point - also always exists given the precondition for bend counts (i.e. there have to be at least two)
            var nextBend = edge.Bends[startIndex + 1];

            //We create the three new bends between previous bend and next bend and adjust these two.
            //We don't have to adjust more bends, since we just have a cubic curve.
            IBend bendToMove;
            var   engine = graph.GetUndoEngine();

            //Wrap everything into a single compound edit, so that everything can be undone in a single unit
            using (var edit = graph.BeginEdit("Create Bezier Bend", "Create Bezier Bend")) {
                try {
                    //Adjust the previous bend - given the split algorithm, its coordinate is in left[1]
                    //(left[0] is actually kept unchanged from the initial value)
                    var oldPrevLocation = previousBend.Location.ToPointD();
                    var newPrevLocation = left[1];
                    graph.SetBendLocation(previousBend, newPrevLocation);
                    // Add unit to engine
                    graph.AddUndoUnit("Set bend location", "Set bend location",
                                      () => graph.SetBendLocation(previousBend, oldPrevLocation),
                                      () => graph.SetBendLocation(previousBend, newPrevLocation));

                    //Insert the new triple, using the values from left and right in order
                    graph.AddBend(edge, left[2], startIndex + 1);
                    bendToMove = graph.AddBend(edge, left[3], startIndex + 2);
                    //right[0] == left[3], so right[1] is the next new control point
                    graph.AddBend(edge, right[1], startIndex + 3);

                    //Adjust the next bend
                    var oldNextLocation = nextBend.Location.ToPointD();
                    var newNextLocation = right[2];
                    graph.SetBendLocation(nextBend, newNextLocation);
                    // Add unit to engine
                    graph.AddUndoUnit("Set bend location", "Set bend location",
                                      () => graph.SetBendLocation(nextBend, oldNextLocation),
                                      () => graph.SetBendLocation(nextBend, newNextLocation));
                } catch {
                    //Cancel the edit in case anything goes wrong.
                    edit.Cancel();
                    throw;
                }
            }

            return(bendToMove);
        }
Пример #10
0
 public static IBend AddBend(this IGraph graph, IEdge edge, int index, PointD location)
 {
     return(graph.AddBend(edge, location, index));
 }
Пример #11
0
 public static IBend AppendBend(this IGraph graph, IEdge edge, PointD location)
 {
     return(graph.AddBend(edge, location));
 }