コード例 #1
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));
                }
            }
        }