How to produce a clickable curved line

45 views
Skip to first unread message

jles...@gmx.de

unread,
Nov 24, 2014, 3:31:38 PM11/24/14
to piccolo...@googlegroups.com
Hello Piccolos
I have a question concerning curves. I'm about to write an editor which should support curved connections between the elements as you can see in the attached image. I build the connection from a PPath and its curveTo() method. This looks just as expected, but the displayed curved line should also react on mouse events. So I added an input event handler which now reacts on mouse movements all over the (invisible) shape which is made up from the curve.

Any idea how I could limit the mouse events to the curved line itself as if it where just a straight line? Of course I could just "curve back" in the PPath to the starting point. This actually works from a technical point of view but the two curves one over the other look too ugly.

Kind regards,
Jan

curve.png

Michael Heuer

unread,
Nov 24, 2014, 3:51:58 PM11/24/14
to piccolo...@googlegroups.com
Hello Jan,

There is support in Piccolo2D for "picking" nodes, e.g. from a mouse
handler, you can query the PInputEvent for the picked node on
mousePressed

http://piccolo2d.mro.name/doc/piccolo2d.java/release-3.0/core/org/piccolo2d/event/PInputEvent.html#getPickedNode%28%29

If you don't like how picking is implemented for PPath nodes you can
override it here

http://piccolo2d.mro.name/doc/piccolo2d.java/release-3.0/core/org/piccolo2d/PNode.html#pick%28org.piccolo2d.util.PPickPath%29

I hope this helps,

michael
> --
> You received this message because you are subscribed to the Google Groups
> "Piccolo2D Users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to piccolo2d-use...@googlegroups.com.
> To post to this group, send email to piccolo...@googlegroups.com.
> Visit this group at http://groups.google.com/group/piccolo2d-users.
> For more options, visit https://groups.google.com/d/optout.

jles...@gmx.de

unread,
Nov 25, 2014, 2:40:28 PM11/25/14
to piccolo...@googlegroups.com
Hi Michael
Thanks for your reply. Well, the node picking is not actually the problem I'm struggling with. See the attached file which makes it a bit clearer. The user only sees the curved arrows between the boxes but what he actual got are the red area shapes, which are made up from the curves. The areas are transparent in the application - I just made them red here for demonstrating the effect. Adding input listeners to the PPaths containing the curves raises two complications.

One is the fact that I get mouse events when ever the user moves the mouse around in the read areas. Not only when the mouse pointer touches the curves. This is not so serious - I can find out if the mouse pointer is located near the curve and run my listener logic only in this case.

The more serious point is that one of the curves can not be clicked any more where it is covered by another curve's shape area. In the attached image e.g. most of the line which runs from "Schritt 1" to "Schritt 2". The curves are of course not in a parent-child relationship, so the upper shape consumes the mouse event and the lower one doesn't receive any event.

Any idea how to keep both curves clickable over their full length?

Regards,
Jan
curve2.png

tanveerakl

unread,
Nov 26, 2014, 6:45:51 AM11/26/14
to piccolo...@googlegroups.com
Hello Jan,

I was also facing the same problem. I have sub-classed PSelectionEventHandler (C#) as below:


Part of the C# code is as below. If you want full code for this sub-classed class, please write to me: ma...@tansharp.com

The basic strategy is that we can have two methods of selection.
1. Normal Click -- It will select the "minimum area" node of the PickedPath stack
2. Normal Click with Shift Pressed --- It will select the curve on which pointer is positioned. "Intersection Selection" Method

and this works!

Regards
-Tanveer
www.TanSharp.com

Partial Code is:
---------------------------------------
        #region Override Selection Logic

        public override bool IsMarqueeSelection(PInputEventArgs e)
        {
            if (Convert.ToString(camera.Canvas.Tag).ToLower() == "design")
            {
                return base.IsMarqueeSelection(e);
            }
            else
            {
                return (pressNode == null);
            }
        }

        protected override void InitializeSelection(PInputEventArgs e)
        {
            if (Convert.ToString(camera.Canvas.Tag).ToLower() == "design")
            {
                base.InitializeSelection(e);
            }
            else
            {
                if (IsAltKeyPressed(e)) intersectionSelection(e);
                else minAreaSelection(e);
            }
        }

        #region private additions

        private bool IsAltKeyPressed(PInputEventArgs e)
        {
            return (e.Modifiers & Keys.Alt) == Keys.Alt;
        }

        private void minAreaSelection(PInputEventArgs e)
        {
            try
            {
                float area = float.MaxValue;
                PNode nd = e.Path.PickedNode;
                PNode orig = nd;

                while (nd != null)
                {
                    float a1 = nd.Bounds.Width * nd.Bounds.Height;
                    if (a1 < area)
                    {
                        pressNode = nd;
                        area = a1;
                    }
                    nd = e.Path.NextPickedNode;
                }

                if (pressNode is PCamera) { pressNode = null; }

                if (pressNode == null)
                {
                    if (orig != null) pressNode = orig;
                }
            }
            catch { }
        }

        private void intersectionSelection(PInputEventArgs e)
        {
            try
            {
                // compare after scaling to camera's scale
                PointF pf = camera.ViewToLocal(e.Position);

                PNode origPicked = e.Path.PickedNode;
                pressNode = origPicked;

                while (pressNode != null)
                {
                    if (pressNode.IsVisible(camera, pf)) break;
                    pressNode = e.Path.NextPickedNode;
                }

                if (pressNode is PCamera) { pressNode = null; }

                if (pressNode == null)
                {
                    if (origPicked != null) pressNode = origPicked;
                }
            }
            catch { }
        }

        #endregion

        protected override void StartStandardSelection(PInputEventArgs e)
        {
            if (Convert.ToString(camera.Canvas.Tag).ToLower() == "design")
            {
                base.StartStandardSelection(e);
            }
            else
            {
                // Option indicator not down - clear selection, and start fresh
                if (!IsSelected(pressNode))
                {
                    UnselectAll();

                    if (IsSelectable(pressNode))
                    {
                        Select(pressNode);
                    }
                }
            }
        }

        protected override void StartStandardOptionSelection(PInputEventArgs e)
        {
            if (Convert.ToString(camera.Canvas.Tag).ToLower() == "design")
            {
                base.StartStandardOptionSelection(e);
            }
            else
            {
                // Option indicator is down, toggle selection
                if (IsSelectable(pressNode))
                {
                    if (IsSelected(pressNode))
                    {
                        Unselect(pressNode);
                    }
                    else
                    {
                        Select(pressNode);
                    }
                }
            }
        }

        protected override void EndStandardSelection(PInputEventArgs e)
        {
            if (Convert.ToString(camera.Canvas.Tag).ToLower() == "design")
            {
                base.EndStandardSelection(e);
            }
            else
            {
                pressNode = null;
            }
        }

        #endregion

---------------------------------------


Michael Heuer

unread,
Nov 26, 2014, 10:26:23 AM11/26/14
to piccolo...@googlegroups.com
Hello Tanveer,

Thank you for the example.  That is similar to what I was suggesting above (although I may not have been specific enough).

When determining whether a mouse click is selecting a node, query the PickPath.  If you don't like how nodes are picked, then you can override the pick method in your nodes.

   michael


jles...@gmx.de

unread,
Nov 26, 2014, 2:36:34 PM11/26/14
to piccolo...@googlegroups.com
Aah, ok, I got it!
I simply have to override the corresponding curve nodes' pickAfterChildren() method and tell the node only being picked, if the pick bounds (i.e. the tiny area around the mouse cursor position) are near the visible curved line. And if it's not picked, Piccolo askes the nodes beneath if any of these wants to be picked.

That's awesome simple - great!

Thanks a million :-)
Jan
Reply all
Reply to author
Forward
0 new messages