Click a CurveShape on a canvas

So, I have a canvas with several CurveShape lines. Each line connects two ovals. I want to be able to click such a line and detects the right CurveShape.

But, I have no idea where to start. I have a view values I know:

  • the start-end positions of each line
  • the control points of each CurveShape
  • the X and Y position I clicked on the Canvas

I guess I have to iterate through all the CurveShapes and apply some sort of formula to see if my mouse position is on one of the given positions.
But since I am not a mathematical genius, I have no choice to come here.

		Dim xx As Integer
		Dim yy As Integer
		Dim t As Single
		Dim t2 As Single
		Dim tm As Single
		Dim tm2 As Single
		Dim t3 As Single
		Dim tt As Integer
		Dim flag As Boolean
		flag=False
		' points 0/1 are end points , #2 is control point
		For tt=0 To 500
				t=tt/500
				t2=t*t
				tm=(1-t)
				tm2=tm*tm
				t3=tm*t*2
				xx = tm2 * xpos(0) + t3 * xpos(2) + t2 * xpos(1)
				yy = tm2 * ypos(0) + t3 * ypos(2) + t2 * ypos(1)
				// stop if point on curve is within tolerance of mouse point
				If Abs(xx-x1)<=tolerance And Abs(yy-y1)<=tolerance Then 
						flag=True
						Exit For
				End If
		Next tt
		Return flag

[quote=334764:@Dave S] Dim xx As Integer Dim yy As Integer Dim t As Single Dim t2 As Single Dim tm As Single Dim tm2 As Single Dim t3 As Single Dim tt As Integer Dim flag As Boolean flag=False ' points 0/1 are end points , #2 is control point For tt=0 To 500 t=tt/500 t2=t*t tm=(1-t) tm2=tm*tm t3=tm*t*2 xx = tm2 * xpos(0) + t3 * xpos(2) + t2 * xpos(1) yy = tm2 * ypos(0) + t3 * ypos(2) + t2 * ypos(1) // stop if point on curve is within tolerance of mouse point If Abs(xx-x1)<=tolerance And Abs(yy-y1)<=tolerance Then flag=True Exit For End If Next tt Return flag [/quote]

seems like an expensive process to me. I mean, if I have only one curve it might be ok. But what if I have 50 curved lines…

I bet there is a formula where I enter the start-, end-, control- and mouse points… resulting in a boolean value that tells me whether the mouse point is on a line. And yes, a tolerance level is useful, since it is rather impossible to hit the exact point on the curve

Is that xplatform or for a certain OS?

That code came from my SnapDragon Project, with is a clone of Microsofts VISIO… and it works quite well… I offer it as a possible solution… but since curves are a “time” dependent equation, there are not many ways to determine if a point meets the result or not… It might be possible to enchance this to a “binary-search” method, but I leave that as an exercise for future geneations

Preferably x-platform. But usually I make my apps for OS-X.
But this app might be x-platform, eventually

Too bad! I almost finished a macOS Graphics extension which gives graphics a PathContainsPoint method (and a load of other things). No xplatform sadly.
If you want to try anyway: I am currently looking for testers.

Haha… yep. I was afraid to hear you say that. There are so many applications with node-editors (as this app, I am making will have).
They work and respond quickly. So, that SnapDragon code might need some tinkering to make

That would be awesome. How did you accomplish that? The way Dave pointed out? 'cause that would not speed things up, I think…

I actually base my nodes on Alex Restrepo’s RBCompose project. He is no longer into RB (read Xojo) stuff. He said it would be tricky.

Smile… and I’ll bet there isn’t… I spent days researching this… and all I could find were variations of what I posted, and most of those were parts of White Papers on the subject

  1. you would need to make sure you store z-order (whats in front of what)
  2. then see if the click is in or on whatever is front most - if not then is it the next most front most etc

the IDE layout editor does almost exactly this

the trick is that while we might use object-2d to DRAW the item onto th canvas thats NOT the only representation or data we have about it
IF that is all you have then yes it will be a LOT more work

[quote=334783:@Norman Palardy]the trick is that while we might use object-2d to DRAW the item onto the canvas that’s NOT the only representation or data we have about it
IF that is all you have then yes it will be a LOT more work[/quote]

Of course, everything we see on the canvas is merely a representation of numbers we put into the CurveShape object. The shape is just a result of some math, to draw that shape. And a point on the canvas is just that.

It might just be easier to draw a straight line between two points and check if a given point is on (or near) that line. But a curved line between two nodes looks a lot nicer, doesn’t it?

In these days… did you come across this site?
Right now I have no time to read in depth what is going on. But it seems doable, at first glance. I might be wrong though. I’ll check tomorrow… yawn (Time to hit the sack… long day ahead, tomorrow…)

Oh that’s nice! I am working on a node using project myself and can probably use a bit from RBCompose. Thx for the hint!

I am afraid my project won’t help you. It tests for a click in a path shape but it does not do any hit tests on paths themselves. There is a workaround (duplicating the path as a stroked path with a somewhat bigger line strength), but that’s utterly complicated. Even more as you have to do the test during the paint event. I can still send you the project if you want to, but it won’t solve your problem.

[quote=334793:@Edwin van den Akker]In these days… did you come across this site?
Right now I have no time to read in depth what is going on. But it seems doable, at first glance. I might be wrong though. I’ll check tomorrow… yawn (Time to hit the sack… long day ahead, tomorrow…)[/quote]
As a matter of fact… that IS the site… once you do read it, you will find out how what I posted works

[quote=334794:@Ulrich Bogun]Oh that’s nice! I am working on a node using project myself and can probably use a bit from RBCompose. Thx for the hint!

I am afraid my project won’t help you. It tests for a click in a path shape but it does not do any hit tests on paths themselves. There is a workaround (duplicating the path as a stroked path with a somewhat bigger line strength), but that’s utterly complicated. Even more, as you have to do the test during the paint event. I can still send you the project if you want to, but it won’t solve your problem.[/quote]

I was thinking to draw a separate image of the path, of course with a thicker line to have some “hit tolerance”. If that line is white, on a black background, I can easily detect the pixel’s color. That would be my work around… I think.
Alex pointed out that exact same workaround. Too bad it can’t be done with one simple formula.

[quote=334784:@Edwin van den Akker]Of course, everything we see on the canvas is merely a representation of numbers we put into the CurveShape object. The shape is just a result of some math, to draw that shape. And a point on the canvas is just that.

It might just be easier to draw a straight line between two points and check if a given point is on (or near) that line. But a curved line between two nodes looks a lot nicer, doesn’t it?[/quote]
Thats not quite what I meant

If your application is going to draw “objects” on the canvas that you want to interact with then you should have some classes that represent these objects in your code
Then its easier to test whats frontmost (moveit forward / backward in the “array” of objects and redraw)
Test what its bounds are
Test if a click is on or in the shape that defines this
etc

Once its drawn onto a canvas its no longer usable in the same way

There’s a sample Examples > Graphics and Multimedia > CanvasDrawDrag that does this (with a single shape)
But its along the lines of what I’m talking about

The fastest way (but not the most memory efficient) would be to turn off antialiasing, draw your curves (use a unique colour per curve) into a secondary (unseen) image keeping track of colour to curve id. Colour pick off the cursor position and lookup the colour to id that you stored earlier and you know the curve id of the clicked curve.

Failing that, use maths :slight_smile:

Was just about to post the same thing.
However, you need to watch out: on a Mac, you can draw a line in a color and not get back the color you used.
This infuriates me, but is due to color profiles, which there is (apparently) no way to avoid.

eg you draw a line using &cFF0000 and when you read the value back, you find that it actually used &cFe1000 or something odd.

As Norman point out, you can also consider the shapes as being bounded by a rectangle.
When you click , you can very quickly test whether the point is inside the bounding rectangle.

If you have two close together, it is possible that the point lies inside the bounds of 2 or 3 items.
When that it the case, you can either stop at the first one you find, or take account of the order in which they were drawn, selecting the last one drawn because it is ‘on top’

Isn’t that due to anti-aliasing?

Another workaround is to just create a picture with the size of the rectangle the curved line is in. Doing that with every single curve. Then check if the pixel value of the mouse point is bright or black. That picture will have a certain offset, according to the top-left position of the curved line. That offset will have to be subtracted from the mouse position.

(dang, it’s late here… I should really put my phone on “night mode”, haha)