Page 1 of 1

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

Posted: Thu Aug 27, 2020 6:16 pm
by PaulDaMacMan
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?

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

Posted: Fri Aug 28, 2020 9:39 am
by LCMark
@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).

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

Posted: Fri Aug 28, 2020 9:16 pm
by PaulDaMacMan
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 ).

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

Posted: Mon Aug 31, 2020 11:43 pm
by PaulDaMacMan
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).