nanovg
nanovg copied to clipboard
Added picking/hit regions
As discussed in issue #197.
(Also added the missing files from the original check in :-) )
Working nvgInFill and nvgInStroke have been added, although I may tweak them a bit in the future.
Happy to make any other changes etc...
New functions:
// Marks the fill of the current path as pickable with the specified id.
void nvgFillHitRegion(NVGcontext* ctx, int id);
// Marks the stroke of the current path as pickable with the specified id.
void nvgStrokeHitRegion(NVGcontext* ctx, int id);
// ...
enum NVGPickFlags {
NVG_TEST_FILL = 1,
NVG_TEST_STROKE = 2,
NVG_TEST_ALL = 3,
};
// Returns the id of the topmost pickable hit region containing x,y or -1 if not shape is found.
int nvgHitTest(NVGcontext* ctx, float x, float y, int flags);
// Fills ids with a list of the top most maxids ids under the specified position.
// Returns the number of ids filled.
int nvgHitTestAll(NVGcontext* ctx, float x, float y, int flags, int* ids, int maxids);
// Returns 1 if the given point is within the fill of the currently defined path.
// Returns 0 otherwise.
int nvgInFill(NVGcontext* ctx, float x, float y);
// Returns 1 if the given point is within the stroke of the currently defined path.
// Returns 0 otherwise.
int nvgInStroke(NVGcontext* ctx, float x, float y);
A lot of nitpicking about style, sorry about that. I've tried to favor quite compact and simple style, please follow the (loose) conventions from nanovg.c.
No problem, didn't take long to fix. Just have a few remaining niggles and then I will update later when I have more time.
The style issue fixes are in (think I got everything) as is the transformed scissor rect issue (didn't notice it could be transformed initially!). How do you want to handle the dependence on demo.h?
I was treating demo.h as defining an interface that was then implemented by demo.c and pickdemo.c (so that minimal changes to example_gl*.c were needed). If you want to break sharing of demo.h how do you want to handle the example files? (Duping them seems wastefull)
To keep things simple, I think the best way for the demo is to put it in one separate file, just like the fbo demo.
I'll try to find time over the weekend to take a proper look at the changes.
Sounds good - I had somehow missed the fbo example. I'll try to get the new demo code in before the weekend and double check I didn't miss anything.
On Thu, 9 Jul 2015, Mikko Mononen wrote:
To keep things simple, I think the best way for the demo is to put it in one separate file, just like the fbo demo.
I'll try to find time over the weekend to take a proper look at the changes.
— Reply to this email directly or view it on GitHub.[ABglbRqiE-2dEZspb9tn9y7ktCQzhuWMks5obmwTgaJpZM4FSRae.gif]
thanks to the excellent implementation, I'll try as soon as possible
Nearly all fixed locally, will complete and commit on Sunday. (I don't use git locally - everything is in fossil here - so commiting to github is more like publishing rather than a normal commit for me, hence not seeing much WIP on my repo)
OK, I'll await any further comments :-) I had a bit more time today that before so hopefully I did a better job than last time...
I just looked for this functionality, and I'm glad someone already worked out a solution. What's the status of the pull request?
Many thanks for a great library.
@MikeWW, there seems to be a problem with the nvg__intersectLine
function in this pull request. On polygons build from straight lines the code sometimes fails to detect an intersection. It seems like there was an attempt to optimize maths by removing redundant variables from formulas, but probably there was some error. If you replace this function with the general one without optimizations, everything seems to work as expected, but I haven't done thorough testing. Probably I am missing something. It would be great if you could correctly fix the problem, I can provide more information/samples if you need.
BTW, are there any plans to merge this functionality into NanoVG? It is quite useful to have not only painting, but also hit testing in the core library.
@zapolnov If you could provide the vertices of a shape that shows this problem I'll sort it out.
I have no idea on plans to merge this into NanoVG, ask @memononen :-)
I ended up moving away from NanoVG for the project I was working on (although I may re-evaluate since more work has gone on since I stopped using it), but when I have time I'm happy to fix bugs etc... in this branch.
All the picking code was written pretty quickly and since I no longer use it myself day to day I am sure there are things that need some love so any comments are appreciated :-)
I'll look at merging the latest nanovg changes into this branch at least.
( @lathen Sorry I missed your msg, I've been super busy until now. Anyway, answer above )
@MikeWW, generally, current implementation works ok when edges of polygon are parallel to the axes, but when they are not, something bad happens. Here is the one, for example:
nvgTranslate(vg, 150, 150);
nvgBeginPath(vg);
nvgMoveTo(vg, 0, 100);
nvgLineTo(vg, 95.105651855469, 30.901697158813);
nvgLineTo(vg, 58.778518676758, -80.901702880859);
nvgLineTo(vg, -58.778537750244, -80.901695251465);
nvgLineTo(vg, -95.105651855469, 30.901712417603);
nvgClosePath(vg);
nvgFillColor(vg, (nvgInFill(vg, mx, my)) ? nvgRGB(255, 0, 0) : nvgRGB(0, 255, 0) );
nvgFill(vg);
nvgResetTransform(vg);
Current implementation works as follows: https://monosnap.com/file/4lHDApjiNOcksTRJ6R7MG6OPyMC58I
And if you replace current implementation with the generic one:
static int intersection(float x1, float y1,float x2, float y2, float x3, float y3, float x4, float y4)
{
float d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (d == 0.0f)
return 0;
float xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
return xi > x3;
}
static int nvg__intersectLine(const float* points, float x, float y)
{
float x1 = points[0];
float y1 = points[1];
float x2 = points[2];
float y2 = points[3];
return intersection(x1, y1, x2, y2, x, y, x + 1, y);
}
then it works as expected: https://monosnap.com/file/I6iltxSksH87ARehiGWHBm8hBh3gfz
I am not sure if this is a correct solution, but it seems to work in all cases I have tried.
I think I just messed up handling divide by zero handling originally. I've committed a quick fix that appears to do the job.
Next time I have have a minute I'll merge in the latest changes from nanovg.
@MikeWW, thank you for a quick fix, it seems to work correctly! But there is also a problem with rotated ellipses. Here is an example:
nvgSave(vg);
nvgBeginPath(vg);
nvgTranslate(vg, 145.40129089355f, 483.73629760742f);
nvgRotate(vg, nvgDegToRad(3.0f));
nvgEllipse(vg, 0.0f, 0.0f, 103.41604614258f, 16.976871490479f);
nvgFillColor(vg, (nvgInFill(vg, mx, my)) ? nvgRGB(255, 0, 0) : nvgRGB(0, 255, 0) );
nvgFill(vg);
nvgRestore(vg);
Other figures with beziers seem to handle rotation correctly, but ellipses are absolutely crazy.
@zapolnov Ignore what this comment used to say, I found and fixed this bug. Test it out and let me know if you find anything else :-)
@MikeWW, your commit seems to fix the problem.
I was making videos while you did the commit :)
Originally the problem only reproduced on Mac. And on Windows in Visual Studio debug checks reported "corrupt stack" after calling nvgFillIn, but in Release builds on Windows it worked correctly.
Here is a link to the video for OSX in case you are interested. https://monosnap.com/file/QoLyo30bqCd2mu9rYtGwL4RvKvCSbW
Thanks for fixing the bug!
Hey guys, are there any plans on still merging this PR? It's imo the missing piece to an otherwise great library.
I think it would be nice as well.
Can we get a bump on this. This would be really helpful if integrated with master and reusing some interfaces inside nanovg instead of duplicating them.
What's the status of this?
It would be great to have this feature.