-
Notifications
You must be signed in to change notification settings - Fork 200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix bug with concave hull algorithm #541
Conversation
5704ef1
to
ba30680
Compare
634c8f5
to
f09ae51
Compare
f09ae51
to
4d1aa4b
Compare
geo/src/algorithm/concave_hull.rs
Outdated
let two = T::add(T::one(), T::one()); | ||
let search_dist = T::div(T::sqrt(T::powi(w, 2) + T::powi(h, 2)), two); | ||
let four = T::add(two, two); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
little readability nit: I think generally we should prefer the infix notation where available:
let four = T::add(two, two); | |
let four = two + two; | |
let search_distance = diagonal_squared / four; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm having a little trouble following this - does this method correspond to concaveman's findCandidate
method? Or has our implementation diverged sufficiently that there's not really a corollary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
concaveman uses a rectangular envelope where the borders are a distance of max_dist from the line to look for points. Unfortunately, rstar uses a circular envelope
That's interesting - is that something inherent in the rstar implementation? Can you link to any doc or code example that highlights this difference?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The infix notation usage sounds reasonable. I'll set that up.
With regard to findCandidate
, yeah, this is reasonably equivalent. The main difference is that findCandidate
implements the traversal of the queue within itself while I offload that onto rstar.
With regard to where concaveman uses a rectangular envelope, I'm embarrassed to admit that it's not entirely clear from looking at the code again where that is. With regard to rstar, I use locate_within_distance
which references a radius r which I took to mean that it looks for points within a circle of radius r. I'll have to dive into the concaveman code again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@michaelkirk From checking again, it seems I was wrong to think that concaveman used a rectangular envelope to search within. It seems to be using a circle just like rstar, I'm not entirely sure where I got the impression that it used a rectangular envelope. I've adjusted the calculation to be simpler.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for double checking the envelop shape stuff @restitutororbis!
Since we don't have many tests to debug edge cases, anything we can do to make our code look more like the reference implementation (assuming that's correct) would make review easier.
Since our implementation has diverged from concaveman it was hard to see, but it looks like we compute max distance as the (line_length / concavity)^2
whereas concaveman has max distance as (line_length^2 / concavity^2)
. Is that divergence intentional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That divergence was intentional on the basis that those are equivalent statements anyways (ex. (x/y)^2=x^2/y^2
) and it would be easier to understand the function call if it was handing over dist
instead of a stranger notion of squared_dist
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤣 apparently I wasn't ready to do 4th grade math yesterday AM.
I thought concaveman searched within a rectangular box around the edge to find close points. It actually searches within a circle.
@michaelkirk Hey! I finally found the time to get around to this. So, after checking against A/B Street, I found that those edges which visually look like they should flex don't because they are not the closest edge to their closest interior point. For example, in There are a few ways to resolve this:
I'm inclined to say 3 because there is no real definition of what a concave hull should be and the current behaviour strikes me as reasonable. |
Hi @restitutororbis - thanks for continuing to look at this! Here's what I'm seeing with this commit: Do you think concaveman would give the same results? Or do you think it's an artifact in our implementation? |
Thanks for doing the work to compare results. Do you understand where and why we are diverging from concaveman's implementation? To my eye it looks like we want something more like concaveman's results - if that means making our implementation more fully align with theirs, I think we should. Is there a reason we intentionally diverged? |
This one example looks pretty close to my eye, but it's hard for me to say if it's a reasonable solution in general. Since this code is based on a reference implementation, rather than any rigorous definition of "what concavity is", I'd prefer matching the reference implementation unless there's some reason we intentionally diverged. |
Unfortunately, I don't think I can do that. From my perspective, I was using concaveman more for inspiration than as a reference. If we want to use concaveman as a reference, then it might be reasonable to open an issue to rewrite the algo to align it with concaveman. |
Fair enough! Thanks for looking into it as much as you have. I'll try to take a pass at it in the next couple weeks unless someone else gets to it first. |
As noted in a-b-street/abstreet#198, there seems to be a bug where the concave hull algorithm doesn't always "bend" edges inwards when it should. So, to fix this I've noticed that a parameter which I thought was the radius of the circle to search was actually the
radius squared
of the circle to search. So, I corrected this. I've also removed the Norway test since it's too brittle IMO.