Long and largely pointless musing, retained for some thoughts on what Bezier should return. Includes timing result on creation vs reuse of small tables.
I need to think a bit about the sources for our courses, including of course the source of the courses for our horses. In particular, a careful reading of NightShade’s masterful and well-organized code for SLRR has made me aware of a kind of access I’ve not really thought deeply about, though I have referred to the concern somewhere in these screeds.
Most of our courses have a beginning and an end. A train goes from here to there; a coaster runs in a loop but actually has a start and finish. The Uber Horses calculate a series of paths from where you are to where you ask to go. But the SLRR is different. It has no defined start or end, or even any defined path at all. It consists of guide objects laid down where the tracks go, and an SLRR train looks for the guides and follows them, one after another, inch by inch, step by step.
Therefore, most of our vehicles move by asking to move to a particular distance along their defined curve, incrementing distance by their speed. The code deep down converts that distance to a point and rotation by means we’ve written about in previous articles. But an SLRR vehicle works differently.
Recall that a Bezier curve is indexed by a variable conventionally called t
. Setting aside how things get started, each time the code ass the path logic for a new position/rotation, those values are returned, but in addition, the t_value
is also returned, presumably the value of t
that was used to compute that location.
The SLRR mover then asks to be moved a small distance on each call passing back in the t_value
from the last call. ANd it gets back the new location information and the new t_value
, to be used next time.
You might wonder why the t_value
isn’t just remembered somewhere down low. The reason is that a vehicle typically has two or more points that need to be positioned on the track, its front bogies and its real bogies, for example, so each such location needs to remember its own particular t_value
.
What does this mean for us? I don’t know yet. That’s why I’m here, thinking.
It seems on the face of it that, for SLRR, we’ll return three things from the call to the path finder: a position, a rotation, and a t_value
. In what I’d call a “good” design, these three things would be enclosed in a small object, perhaps an SlrrLocation. The only concern one might have with this is that an object like that should typically be immutable, and if it were, then the path-finding code would be creating at least a couple of those ten times a second, and that’s a lot of garbage to be creating and destroying, and that might be bad.
Some aging white male once urged me to “trust your garbage collector”, and that could be good advice, but I am a bit concerned. If my concerns are well-founded, well, we can surely make our packet mutable and reuse the same ones over and over.
In Lua, and therefore SLua, one would often “just” return three things from the call to the path finder and call it a day. That might well be faster, since we wouldn’t have to put the items into the containing SlrrLocation, we wouldn’t pay to fetch them back out, and we’d save the overhead of a table with a few symbols in it for each one of those packets.
It is a trade-off of course, and a judgment call. My experience and training both lead me to build the object. Another aging white male used to tell us “Make it work, make it right, make it fast”, suggesting that using the simplest and best design we can think of should be our preferred choice, and we’d only go to an more complex scheme if we needed to.
I’ll take that all under advisement, and since my own experience has led me to prefer to encapsulate when I can, I’ll probably go with the return packet idea, unless some indication arises that says it’s not going to serve.
Bezier:at(t)
returns the point at t
. tanget_at(t)
returns r1
and r2
, two points defining the tangent line at t. The at
function uses tangent_at
, because those two points are the last step in the de Casteljau algorithm for the point at t
.It is likely that at
should have two or three returns, not just one, perhaps packaged in a little object, perhaps not. It could at least return r1
and r2
, avoiding an extra call to get that information. Perhaps better would be to return the tangent vector, r1-r2
, or a suitable(?) rotation, whatever that means.
However, with the existence of linearize
, we may never actually use Bezier’s public methods at all, instead just using linearize and following the resulting simpler path.
Remains to be seen.
linearized
, split
, partition
, _split_array
, compute_length
and estimate_length
methods in addition to the at
and tangettn_at
mentioned above. Some of those should be removed and others made private.Final decisions on this await better understanding of the objects that will use Bezier.
local t = {x=1234, y=2345}
Compared to just assigning to an existing table:
t.x = 1234
t.y = 2345
Creating a new table on every iteration in a long loop showed creating a new table to b between 2.5 and 3 times slower than reusing the existing one. That suggests that our logic for providing position and rotation to a mover should probably pass a variable back and forth and fill it in.
Timing three cases: returning two values explicitly, returning a fixed table and fetching the two values, and returning a new table containing the values and fetching them, typical result:
two items 0.0213 1.00
fixed table 0.0480 2.25
new table 0.0598 2.80
Seems that returning two items separately is the clear winner.