Widgets - Point is within Path? Non-Rect shapes?

LiveCode Builder is a language for extending LiveCode's capabilities, creating new object types as Widgets, and libraries that access lower-level APIs in OSes, applications, and DLLs.

Moderators: LCMark, LCfraser

Post Reply
PaulDaMacMan
Posts: 326
Joined: Wed Apr 24, 2013 4:53 pm
Contact:

Widgets - Point is within Path? Non-Rect shapes?

Post by PaulDaMacMan » Thu Aug 27, 2020 6:16 pm

Growing frustrated with LCB FFI, I've decided to try to work on some widget GUI stuff in LCB... Is there a "Point is within Path" or has someone already written such a thing in LCB? I see there is a "Point is within Rect" but what if you're not dealing with a rectangular shape?
Last edited by PaulDaMacMan on Fri Aug 28, 2020 9:17 pm, edited 1 time in total.
My LCB Repos: https://github.com/PaulMcClernan/

LCMark
Livecode Staff Member
Livecode Staff Member
Posts: 1095
Joined: Thu Apr 11, 2013 11:27 am

Re: Widgets - Point is within Path? Non-Rect shapes?

Post by LCMark » Fri Aug 28, 2020 9:39 am

@PaulDaMacMan: There isn't a 'point within a path' syntax, but you can do this with a relatively simple handler in LCB (this was written by Monte a while back):

Code: Select all

public handler hitTestPath(in pPoint as Point, in pPath as Path, in pStrokeWidth as Number, in pWithin as Boolean) \
	returns Boolean

	-- create a 1 x 1 canvas
	variable tCanvas as Canvas
	put a new canvas with size [1,1] into tCanvas
	-- ensure we either get pixels drawn with our color or not drawn
	set the antialias of tCanvas to false

	-- translate the path by the point
	translate pPath by [-(the x of pPoint), -(the y of pPoint)]

	-- draw the path to the canvas
	set the paint of tCanvas to solid paint with color [0.0, 0.0, 0.0, 1.0]
	if pStrokeWidth is 0 and not pWithin then
		put 1 into pStrokeWidth
	end if

	if pWithin then
		fill pPath on tCanvas
	end if

	if pStrokeWidth > 0 then
		set the stroke width of tCanvas to pStrokeWidth
		stroke pPath on tCanvas
	end if

	-- get the pixel data of the canvas
	variable tData as Data
	put the pixel data of tCanvas into tData

	-- return whether pixel is opaque
	return the first byte of tData is the byte with code 255
end handler
Here set pWithin true and pStrokeWidth to 0 if you want to hit-test the filled path, pWithin false and pStrokeWidth != 0 if you want hit-test the stroked path (i.e. just the outline) and pWithin true and pStrokeWidth != 0 if you want to hit-test the stroked and filled path.

The approach might seem a little 'crazy' in that it uses rasterization of the path to do the hit-testing - but in reality it isn't (notice that we only use a 1x1 pixel canvas!).

Testing for inside-ness in a general vector path using geometry calculations is actually a great deal harder, more complex and much more error-prone. This approach means you will only get true if there is actually a pixel rendered - i.e. if you could actually see it.

Note: This could be made a little more efficient by translating the canvas and not the path (translating a path makes a copy internally which it would be better to avoid).

PaulDaMacMan
Posts: 326
Joined: Wed Apr 24, 2013 4:53 pm
Contact:

Re: Widgets - Point is within Path? Non-Rect shapes?

Post by PaulDaMacMan » Fri Aug 28, 2020 9:16 pm

Awesome! Thank you so much @LCMark and very clever Monte!
I had started to go another route, making multiple rect 'zones', which I may actually stick with for my current experiment to assigned different values to different areas of the shapes, but I'll be sure to give this handler a try as well, particularly if it's speed is faster then what I come up with (optimization hint much appreciated ).
My LCB Repos: https://github.com/PaulMcClernan/

PaulDaMacMan
Posts: 326
Joined: Wed Apr 24, 2013 4:53 pm
Contact:

Re: Widgets - Point is within Path? Non-Rect shapes?

Post by PaulDaMacMan » Mon Aug 31, 2020 11:43 pm

That worked out very well, and plenty speedy, at least for the amount of vector paths I needed to hit test for (5 and then 7 more).

Functional mouse-Piano that emits noteOn / noteOff messages back to the engine:
https://github.com/PaulMcClernan/LCB_Pi ... /piano.lcb

It's only 1-Octave's worth right now (with a property for setting which Octave), but I'm going to try to make a "Composed Widget" for more.
I couldn't figure out a way to pass control from 1 Widget to another in LCB nor the engine / card level with LCS while the mouse was still down (there's no engine accessible global variables in LCB, though I did figure out a workaround for that, and no polling of mouse button's current state in LCB like the mouse() function in LCS) so I figure it's got to be either one large widget or a composed widget with child widgets for each octave (preferably the latter).
My LCB Repos: https://github.com/PaulMcClernan/

Post Reply

Return to “LiveCode Builder”