Let’s explore the division of responsibilities in the L_Bezier class. I think we’ll learn something useful, and I expect that we’ll improve the code. Here are the two methods we’re considering:
function L_Bezier:find_distance(d)
local remaining_distance = d
for i, len in self._lengths do
if remaining_distance < len then
return i, remaining_distance
end
remaining_distance -= len
end
return nil
end
function L_Bezier:point_at_distance(d, debug)
local idx, remaining_distance = self:find_distance(d)
local p0 = self._points[idx-1]
local p1 = self._points[idx]
local len = self._lengths[idx]
local frac = remaining_distance/self._lengths[idx]
result = p0*(1 - frac) + p1*frac
return result
end
Recall that we have two arrays in the L_Bezier, one containing the length of each segment of the polyline path, and the other containing the points that make it up.
The concern that I’m here to explore is this: while the find_distance
method only concerns itself with the lengths, the point_at_distance
method considers both the points array and the lengths array.
As a principle of design, it’s “better” when a method only deals with one kind of thing. So when I realized that the point_at_distance
function I came up this morning was dealing with two kinds of things, I thought maybe it could be better.
Over-simplifying perhaps a bit, it seems clear that if, instead of returning the remaining distance, find_distance
returned the fraction that point_at_distance
wants, the latter would be much simpler, and the former probably not much more complicated.
That turns out to be the case. Here are the new versions:
function L_Bezier:find_distance(d)
local remaining_distance = d
for i, len in self._lengths do
if remaining_distance < len then
return i, remaining_distance/len
end
remaining_distance -= len
end
return nil
end
function L_Bezier:point_at_distance(d, debug)
local idx, frac = self:find_distance(d)
local p0 = self._points[idx-1]
local p1 = self._points[idx]
return p0*(1 - frac) + p1*frac
end
All 859 tests are still reporting correct. The change seems correct and acts correctly.
We changed the responsibility of the find_distance
method just a tiny bit: instead of returning the remaining distance, return the fraction of the segment length that the move requires. That was trivially easy: divide the remainder by the length of the segment.
And that value is exactly what the point_at_distance
needs to do its interpolation, so two lines got removed, one of which was Yet Another Access into the array. Win-win!
This makes me happy. Safe paths!