points = nil function getPoints() return points end function init(points_) points = points_ pointDistances = {} local distAd2Sum = 0 local lastP = nil for pointIdx, p in ipairs(points) do if lastP ~= nil then local distAd2 = lastP:distAd2(p) pointDistances[pointIdx-1] = distAd2 distAd2Sum = distAd2Sum + distAd2 end lastP = p end for pointIdx, distAd2 in ipairs(pointDistances) do pointDistances[pointIdx] = distAd2 / distAd2Sum end end function eval(t) if points == nil or #points == 0 then return nil end -- find the closest points for the given t parameter local pointIdx = 1 while pointIdx <= #pointDistances and t - pointDistances[pointIdx] >= 0 do t = t - pointDistances[pointIdx] pointIdx = pointIdx + 1 end if pointIdx == #pointDistances + 1 then return points[pointIdx] end -- normalize the t parameter to only mean the distance between the two closest points t = t / pointDistances[pointIdx] return circleInterpolate(points[pointIdx], points[pointIdx+1], t) end function circleInterpolate(p1, p2, t) local dxa, dya = p2:getX()-p1:getX(), p2:getY()-p1:getY() local x, y = (p1:getX()+p2:getX())/2, (p1:getY()+p2:getY())/2 local r = p1:dist(p2) / 2 local alpha = math.atan(dya/dxa) + ((p1:getX() < p2:getX()) and (1-t) or t)*math.pi return point.new(x + r*math.cos(alpha), y + r*math.sin(alpha)) end