javagl / Geom

Utility classes for geometry computations
MIT License
9 stars 3 forks source link

[FR] Option to have CatmullRomSpline be continuous #1

Closed markmandel closed 8 years ago

markmandel commented 8 years ago

I love that your implementation of CatmullRomSpline allows for it to be chordal, but it would be extra useful (at least for me), if it had the option to be continuous - and that way, it would be relatively easy to build smooth loops from a series of points.

javagl commented 8 years ago

I'm not entirely sure what you mean. The different parametrizations (uniform, chordal and centripetal) are covered by the setInterpolation method. Apart from that, I only know the term "continuous" as the usual property of splines, and Catmull-Rom-Splines should exhibit a C1 continuity in general.

Can you point me to some reference explaining what "continuous" refers to here?

Or does this only refer to the last point, in the sense that it should be possible to have a closed spline? This might be possible, by replacing the "artificial", additional points that are created for the first and last segment with real points, but I'd have to take a closer look at this...

markmandel commented 8 years ago

Ah! Sorry, wrong terminology - I was used to "continuous' from the libgdx implementation.

But yes, I mean closed.

To give a bit more context, I'm using your code as part of my generation of my game race track, and you can see in the screenshot that the it hits a hard point, rather than just being smooth all the way around. (red in the chordal spline with your code, blue is a uniform catrom spline from the libgdx code)

selection_003

javagl commented 8 years ago

@markmandel The above commit ad3385a3471b8b86c3d1a3b018d01b8cae4e5d18 introduces a method to create a Catmull-Rom-Spline that receives an additional parameter, saying whether the spline should be closed (by connecting the last and the first point).

I'm not perfectly satisfied with the solution, as it was a bit fiddly and I would have preferred a more elegant one, but I think that declaring the last and the first control point as being "identical" (and updating the additional control points to be equal to existing ones) is reasonable.

The CatmullRomSplineTest has been updated to include a check box that allows switching between closed and open splines. (It is not possible to open or close an existing spline arbitrarily, but I don't think that this is necessary).

markmandel commented 8 years ago

I will give this a shot later this evening, see how it looks and let you know.

Was discussing this topic on https://gamedevelopment.slack.com. One solution proposed was:

xeleh 
[12:17] well, then you just need to compute the previous to last and last points using points 1 and 2 as control points for the spline
[12:20] you also need to calculate first section using (pc - 1, 0, 1, 2) (pc = point count) as control points
[12:22] prev-to-last: (pc - 3, pc - 2, pc - 1, 0)
[12:22] last: (pc - 2, pc - 1, 0, 1)

I don't know if that helps at all.

Either way, I'll let you know how it works with my game.

javagl commented 8 years ago

(I don't have an account there, but) The description sounds similar to what I suggested in the fast answer (and then implemented). The fiddly part was not so much to compute the control points, but to "virtually extend" the path so that it actually interpolates between the last and the first point. Currently, I'm inserting the first point as a "new point after the last one". It could probably also be solved with some modulo- and index magic, but I'm not sure whether it would be much easier or more elegant...

markmandel commented 8 years ago

Excellent! This works nicely!

One (relatively minor?) issue I found, is that the libGDX ConvexHull implementation will repeat the first point, in the last position

For example:

[#object[java.awt.geom.Point2D$Float 0x6eec74a1 "Point2D.Float[-240.0, -234.0]"] 
#object[java.awt.geom.Point2D$Float 0x1f07f348 "Point2D.Float[-66.0, -245.0]"] 
#object[java.awt.geom.Point2D$Float 0x37868b69 "Point2D.Float[147.0, -229.0]"] 
#object[java.awt.geom.Point2D$Float 0x736bedf8 "Point2D.Float[243.0, -206.0]"] 
#object[java.awt.geom.Point2D$Float 0x7e2fc648 "Point2D.Float[246.0, -156.0]"] 
#object[java.awt.geom.Point2D$Float 0x5e12f776 "Point2D.Float[249.0, 22.0]"] 
#object[java.awt.geom.Point2D$Float 0x5d4ce21e "Point2D.Float[236.0, 105.0]"] 
#object[java.awt.geom.Point2D$Float 0x71e4d062 "Point2D.Float[159.0, 239.0]"] 
#object[java.awt.geom.Point2D$Float 0x71794590 "Point2D.Float[-189.0, 241.0]"] 
#object[java.awt.geom.Point2D$Float 0x3819b58e "Point2D.Float[-217.0, 173.0]"] 
#object[java.awt.geom.Point2D$Float 0x2fffc81 "Point2D.Float[-229.0, 102.0]"] 
#object[java.awt.geom.Point2D$Float 0x55cf4b7c "Point2D.Float[-240.0, -234.0]"]

If passed to this implementation and you specify that you want the spline closed, it actually just gives you a big gap in the spline. It's very odd.

However, if I just drop the last point, it works fine.

Not sure if that is something you want to fix or not - I'll leave that up to you.

javagl commented 8 years ago

Well, letting the implementation detect whether the first and the last point are "equal" raises a bunch of issues (most obviously: The always-somewhat-arbitrary epsilon for the equality check).

The intention is that "closing" the path means: Connecting the last point to the first one. (Similar to something like Path2D#closePath())

(EDIT: So for your case, removing the last (duplicate) point should be the best solution - especially when the duplicated start- and end point are part of the specified behavior of the convex hull implemenation. I didn't see such a note in the libGDX documentation, but .... maybe one just has to make this assumption here).

However, if the path is closed, then equal start- and end points should not cause a gap. Instead, they will cause a small "loop", due to the smooth interpolation.

I just pasted the coordinates that you gave into the CatmullRomSplineTest, and it seemed to work properly. If you really observed a gap (and not a loop) when the spline is closed: Can you give an example (maybe based on the CatmullRomSplineTest) where this behavior may be observed?

javagl commented 8 years ago

Unless there is a dedicated case where the appearance of a "gap" can be observed, I'll close this (if there is a bug in the current implementation, it could/should probably be opened as a dedicated issue)