Skip to content
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

filter: Add FilterParaboloid #19

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

ctbur
Copy link
Contributor

@ctbur ctbur commented Jan 8, 2022

Changes

Add the FilterParaboloid filter than can filter points for an axis-aligned paraboloid given a vertex and a scale factor. Useful for range-checks on projectile with a fixed launch speed and arbitrary launch direction.

Verification

Added a test case.

@ctbur ctbur changed the title Add FilterParaboloid filter: Add FilterParaboloid Jan 8, 2022
@improbable-til improbable-til self-assigned this Jan 10, 2022
@improbable-til
Copy link
Contributor

I am not really sure about this PR.
Unlike the sphere filter (#16), this seems like very specialised use case that adds quite a bit of code.
Do I understand correctly that this represents a paraboloid (like a roundish cone) rather than a parabolic trajectory?
Some questions:

  • I am not sure that projectiles really look like this, right?
  • I am not sure how it could be used in collision detection, I assume you would have check against a trajectory, i.e. a cylinder, possibly a parabolic bent cylinder.
  • Do you have any benchmarks that demonstrate that it actually improves performance for collision detection?
  • I don't quite understand the constraint of "fixed launch speed", where does the speed play a role and why does it need to fixed, and fixed to what value?

@ctbur
Copy link
Contributor Author

ctbur commented Jan 17, 2022

My use case for it is to check whether a given entity can hit a target with a projectile launched at a fixed speed in any direction. For this I need the volume that represents all points where it is possible to launch the projectile to, i.e., the envelope over all possible projectile trajectories. In 3D this is a paraboloid. There is a better explanation in the Wikipedia article.

I don't quite understand the constraint of "fixed launch speed", where does the speed play a role and why does it need to fixed, and fixed to what value?

It's fixed at the maximum speed the entity can launch the projectile at. If you want to maximize range and minimize time then the launch speed is always the maximum speed.

Do you have any benchmarks that demonstrate that it actually improves performance for collision detection?

No there is no performance benchmark. This is to make the query possible in the first place. The alternative would be to approximate the paraboloid with either a cylinder, which would also have to be implemented, or an AABB, and then removing the points not in the paraboloid from the query results.

@improbable-til
Copy link
Contributor

Ah, I had misunderstood the purpose, I thought you were simulating the shape of a projectile, rather than the boundary shape of it's potential flight paths.

Performance: The naive way to use the tree is to use a query bounding box (or a sphere, after your other contribution) and then filter all results with a is-inside-paraboloid check. Your PR adds a filter that moves the is-inside-paraboloid check into the PH-tree. The problem is that doing this check inside the PH-tree comes at a cost.

If you filter externally (without this PR), you have to filter every entry that is inside a given box.
If you filter internally, you may have to filter fewer entries (it could still be a similar amount of entries). However, you also have to filter every node. If filtering the additional nodes internally is more expensive than what we avoid by a smaller query shape, then the internal filter does actually slow down query performance.

It basically depends on two factors:

  • How much smaller is the volume of the paraboloid compared to it's bounding box? This ratio determines how many entity-check you can expect to avoid with an internal filter (= the dedicated filter function in this PR)
  • How expensive is the filter function for nodes? If it is much more expensive than the bounding-box check then it becomes more likely that the additional node filter outweighs the reduced filter calls on entries.

Maybe to understand why I am a bit reluctant (performance aside): if we accept such an PR then we have to maintain the code for basically as long as this project exists, which may be (hopefully) many years. Obviously maintaining code (fixing bugs, adapting to C++20/23/..., fix warnings with new compilers, improve documentation, ...) is not for free, so any additional code should provide a worthwhile benefit for users. So, despite having worked for ~10 years with spatial indexes, this is probably the first time I heard of this paraboloid problem being used in this context. It sounds like a sensible problem + solution, but I want to make sure that it really has a strong benefit for users. Unfortunately I am not convinced yet. I also haven't seen such a paraboloid i any other spatial index yet.
I am thinking about leaving it open, maybe wait whether we get other users to upvote it?

phtree/common/filter.h Show resolved Hide resolved
/*
* @param axis The axis along which the paraboloid is aligned.
* @param vertex The vertex of the paraboloid.
* @param scale_factor Scales the paraboloid along the axis. A positive scale factor
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you maybe give an example or explain what the scale factor does? The sign obviously affects the direction. But why is it a 'double'? Does it have any other effect or could it be a 'bool'?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the scale factor in the quadratic term. This image shows two parabolas with scale factors 1 and 2. A negative value flips the parabola.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So basically for vertex=origin we get y=scale_factor * x^2? Maybe you could add a sentence to the doc?

}

KeyInternal closest_to_axis;
for (size_t i = 0; i < prefix.size(); ++i) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this could use 3 or maybe the DIM provided by the converter, instead of .size()

Copy link
Contributor Author

@ctbur ctbur Jan 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Note that FilterAABB and FilterSphere both use .size(). Changing it for those does not make sense in this PR as it's not clear if it will be merged.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. I think FilterAABB and FilterSphere had inconsistent use of DIM vs size(), I fixed that.


// radius of paraboloid at axis offset of closest_to_axis
ScalarExternal diff_axis = closest_point[axis_] - vertex_external_[axis_];
ScalarExternal radius = sqrt(diff_axis / scale_factor_);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure it makes a difference, but it may:
The sqrt could be avoided by comparing the squared radius. Division could be avoided by storing the inverse scale_factor_

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, is updated.

@ctbur
Copy link
Contributor Author

ctbur commented Jan 29, 2022

Hi Til, sorry for the delay.

I didn't really consider performance to that level, I mostly added the filter because it's generally applicable and thus makes the library easy to use. Without a parabola you need to choose a finitely sized box appropriate for the specific application and then filter the points with the code in IsEntryValid.

How much smaller is the volume of the paraboloid compared to it's bounding box? This ratio determines how many entity-check you can expect to avoid with an internal filter (= the dedicated filter function in this PR)

Given the circular shape of the parabola it can be quite a difference. Using the formula from this Quora question, the volume of a parabola is (π/2)hr², while the enclosing box has a volume of 4hr², so the box' volume is 8/π ≈ 2.5 times larger. Of course, how relevant this is depends heavily on how the points are distributed.

How expensive is the filter function for nodes? If it is much more expensive than the bounding-box check then it becomes more likely that the additional node filter outweighs the reduced filter calls on entries.

Comparing IsEntryValid with IsNodeValid I wouldn't expect a huge difference in runtime on a modern architecture (2x or 3x, but not 10x). But yes, a benchmark would be needed.

if we accept such an PR then we have to maintain the code for basically as long as this project exists
...
I am thinking about leaving it open, maybe wait whether we get other users to upvote it?

Yes, I get your point. My assumption was that projectile range checks would be a relatively common use-case. But if Improbable does not have a need for it then maybe it's not as common as I expected. We can wait and see if someone else could make use of it.


// radius of paraboloid at axis offset of closest_to_axis
ScalarExternal diff_axis = closest_point[axis_] - vertex_external_[axis_];
ScalarExternal radius_sqr = sqrt(diff_axis / scale_factor_);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that sqrt should also be removed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't the tests report that?

@improbable-til
Copy link
Contributor

My assumption was that projectile range checks would be a relatively common use-case.

I think there are two points here:

  • I am not sure if it is that common in the sense that it gives you everything in a given range but only for ballistic trajectories without considering line-of-sight. I.e. you would need separate code path for non-ballistic trajectories and also need to double check whether line of sight is given. I think it may be more common to just do a direct range check for any interesting entity that is in focus or that you are aiming at.
  • If it is common, a spatial index library may still be the wrong place to put this logic unless this repository would become a dedicated gaming/physics library. At the moment it is a generic spatial index.

Anyway, thanks again for putting your time and effort into this.

We can wait and see if someone else could make use of it.

@ALL Please like this if you want this to become part of the library.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants