JanetRossini.github.io

Lua, LSL, Blender, Python in Second Life


Project maintained by JanetRossini

Responsibility

Jun 17, 2025 • [designluamoverstesting]


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.

Lesson?

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!