Example #1
0
        void EditPath(Shape shape)
        {
            // get the existing verts
            PathSegment[]      oldSegments = shape.GetPathWorldSegments();
            List <PathSegment> segments    = new List <PathSegment>(oldSegments);
            bool hasMaxSegments            = segments.Count == Shape.MaxPathSegments;
            // are we in delete mode?  what color should handles be?
            Color pink       = new Color(1, 0, 0.75f);
            bool  deleteMode = false;

            if ((Event.current.control || Event.current.command) && segments.Count > 1)
            {
                Handles.color = Color.red;
                deleteMode    = true;
            }
            else
            {
                Handles.color = pink;
            }
            bool    splitMode      = Event.current.shift;
            Vector3 mouseWorldPos  = GetMouseWorldPos();
            bool    isWithinBounds = shape.PointIsWithinShapeBounds(mouseWorldPos);

            // drag handle result for getting info from our handles
            CustomHandles.DragHandleResult dhResult;
            // draw handles for each existing point and check if they've been moved or clicked
            bool changed = false;

            for (int i = segments.Count * 3 - 1; i >= 0; i--)
            {
                PathSegment segment          = segments[i / 3];
                Vector3     p                = segment.p0;
                bool        isInfluencePoint = false;
                if (i % 3 == 1)
                {
                    p = segment.p1;
                    isInfluencePoint = true;
                }
                else if (i % 3 == 2)
                {
                    p = segment.p2;
                }
                if (deleteMode && isInfluencePoint)
                {
                    continue;
                }
                float size = 0.04f * HandleUtility.GetHandleSize(p);
                #if UNITY_5_6_OR_NEWER
                Handles.CapFunction cap = Handles.RectangleHandleCap;
                #else
                Handles.DrawCapFunction cap = Handles.RectangleCap;
                #endif
                if (isInfluencePoint)
                {
                    #if UNITY_5_6_OR_NEWER
                    cap = Handles.CircleHandleCap;
                    #else
                    cap = Handles.CircleCap;
                    #endif
                    size = 0.05f * HandleUtility.GetHandleSize(p);
                }
                if (isInfluencePoint)
                {
                    Color oldColor = Handles.color;
                    Handles.color = Color.grey;
                    Handles.DrawDottedLine(p, segment.p0, HandleUtility.GetHandleSize(p));
                    Handles.DrawDottedLine(p, segment.p2, HandleUtility.GetHandleSize(p));
                    Handles.color = oldColor;
                }
                Vector3 newPos = CustomHandles.DragHandle(p, size, cap, pink, out dhResult);
                if (deleteMode && !isInfluencePoint && dhResult == CustomHandles.DragHandleResult.LMBPress)
                {
                    // the user clicked on the handle while in delete mode, so delete the segment
                    segments.RemoveAt(i / 3);
                    changed = true;
                    break;
                }
                else if (!deleteMode && isInfluencePoint && dhResult == CustomHandles.DragHandleResult.LMBDoubleClick)
                {
                    segment.MakeLinear();
                    segments[i / 3] = segment;
                    changed         = true;
                }
                else if (!deleteMode && newPos != p)
                {
                    // the handle has been dragged, so move the point to the new position
                    if (isInfluencePoint || isWithinBounds)
                    {
                        MovePathPoint(segments, i, newPos, moveConnected: !splitMode);
                        changed = true;
                    }
                }
                else if (!splitMode && !deleteMode && !isInfluencePoint &&
                         dhResult == CustomHandles.DragHandleResult.LMBRelease)
                {
                    // the handle has been released.  snap it to any nearby points.
                    for (int c = 0; c < segments.Count; c++)
                    {
                        PathSegment seg2 = segments[c];
                        if (seg2.p0 != newPos && Vector2.Distance(seg2.p0, newPos) < HandleUtility.GetHandleSize(newPos) * 0.25f)
                        {
                            newPos = seg2.p0;
                            break;
                        }
                        if (seg2.p2 != newPos && Vector2.Distance(seg2.p2, newPos) < HandleUtility.GetHandleSize(newPos) * 0.25f)
                        {
                            newPos = seg2.p2;
                            break;
                        }
                    }
                    MovePathPoint(segments, i, newPos, moveConnected: true);
                    changed = true;
                }
            }
            // check if the mouse is hovering over a space where we could add a new point,
            // and draw it if so
            bool closeToExistingPoint = false;
            if (!changed && !hasMaxSegments && !deleteMode)
            {
                foreach (PathSegment s in segments)
                {
                    // if close to an existing point, we don't want to add a new one
                    if (Vector2.Distance(HandleUtility.WorldToGUIPoint(mouseWorldPos),
                                         HandleUtility.WorldToGUIPoint(s.p0)) < 15)
                    {
                        closeToExistingPoint = true;
                        break;
                    }
                    if (Vector2.Distance(HandleUtility.WorldToGUIPoint(mouseWorldPos),
                                         HandleUtility.WorldToGUIPoint(s.p1)) < 15)
                    {
                        closeToExistingPoint = true;
                        break;
                    }
                    if (Vector2.Distance(HandleUtility.WorldToGUIPoint(mouseWorldPos),
                                         HandleUtility.WorldToGUIPoint(s.p2)) < 15)
                    {
                        closeToExistingPoint = true;
                        break;
                    }
                }
                if (!closeToExistingPoint && isWithinBounds)
                {
                    // not too close to an existing vert, so draw a new one
                    // find the closest point
                    float   closestDistance = 99999;
                    Vector2 closestPoint    = Vector2.zero;
                    for (int i = 0; i < segments.Count; i++)
                    {
                        float dist = Vector2.Distance(segments[i].p0, (Vector2)mouseWorldPos);
                        if (dist < closestDistance)
                        {
                            closestPoint    = segments[i].p0;
                            closestDistance = dist;
                        }
                        dist = Vector2.Distance(segments[i].p2, (Vector2)mouseWorldPos);
                        if (dist < closestDistance)
                        {
                            closestPoint    = segments[i].p2;
                            closestDistance = dist;
                        }
                    }
                    // don't use an actual handle cause we want to intercept nearby clicks
                    // and not just clicks directly on the handle.
                    Rect  rect = new Rect();
                    float dim  = 0.05f * HandleUtility.GetHandleSize(mouseWorldPos);
                    rect.center   = mouseWorldPos - new Vector3(dim, dim, 0);
                    rect.size     = new Vector2(dim * 2, dim * 2);
                    Handles.color = Color.white; // remove the weird tint it does
                    Handles.DrawSolidRectangleWithOutline(rect, Color.green, Color.clear);
                    Color oldColor = Handles.color;
                    Handles.color = Color.grey;
                    Handles.DrawDottedLine(rect.center, closestPoint, HandleUtility.GetHandleSize(closestPoint));
                    Handles.color = oldColor;
                    if (Event.current.type == EventType.MouseDown && !Event.current.alt &&
                        !Event.current.shift && !Event.current.command && !Event.current.control)
                    {
                        // the user has clicked to add a new segment, so add it for real
                        segments.Add(new PathSegment(closestPoint, mouseWorldPos));
                        changed = true;
                    }
                }
            }
            // something has been changed, so apply the new points back to the shape
            if (changed)
            {
                Undo.RecordObject(shape, "Edit Shapes2D Path Points");
                shape.SetPathWorldSegments(segments.GetRange(0, segments.Count).ToArray());
                EditorUtility.SetDirty(target);
            }
            else
            {
                HandleUtility.Repaint(); // to draw the new point placeholder handle
                if (Event.current.type == EventType.MouseDown && !isWithinBounds)
                {
                    StopEditingShape(true);
                }
            }
        }