The key to understand how a Moho curve transformed into a regular cubic Bezier curve, is the way Moho calculates these internal points:
On the curve the point P1 has two neighbour points, P0 and P2.
P1 has two internal control points on each side, one for the P0-P1 curve and one for the P1-P2. To calculate the position of these points, get the vector between the P0 and P2 neighbours, normalize it (rescale its length to 1) then multiply it with the P1 point's Curvature value.
N= Norm(P2-P0) * Curvature(P1)
For the P0-P1 curve, calculate the length of the P1-P0 vector, and multiply the normalized "direction" vector with it:
CP01= P1 - N * Mag(P1 - P0)
And for the P1-P2 curve, calculate the length of the P2-P1 vector, and multiply the
normalized "direction" vector with it:
CP12= P1 + N * Mag(P2 - P1)
At the ends of an open curve, where only one neighbour exists for the endpoint, use the endpoint itself instead of the missing neighbour, ie. P0= P1 or P2= P1.
The sample below is a tool's DrawMe() callback function. You can copy it into any of the existing tools to see the internal control point handles and the calculated curves. It calculates the things in a bit "optimized" way, but the calculation basically the same as above.
Code: Select all
function FA_TryMe:DrawMe(moho, view)
	local mesh = moho:Mesh()
	if (mesh == nil) then return end
	local gfx= view:Graphics()
	local matrix= LM.Matrix:new_local()
	moho.layer:GetFullTransform(moho.frame, matrix, moho.document)
	gfx:Push()
	gfx:ApplyMatrix(matrix)
	gfx:SetColor(255, 0, 0)
	local cveN= mesh:CountCurves()
	for i= 0, cveN - 1 do
		local cve= mesh:Curve(i)
		local ptN= cve:CountPoints()
		if (ptN > 1) then
			local oldPt= cve:Point(0)
			local oldCurvature= cve:GetCurvature(0, moho.frame)
		--cp0..cp3 are the cubic Bezier curve points
			local cp0x= oldPt.fPos.x
			local cp0y= oldPt.fPos.y
			local cp1x,cp1y,cp2x,cp2y,cp3x,cp3y, len
			local newPt= cve:Point(1)
			local newCurvature= cve:GetCurvature(1, moho.frame)
		--setting the neighbours of the first point
			local prevPt= oldPt
			local handle= LM.Vector2:new_local()
			local vec= LM.Vector2:new_local()
			if (cve.fClosed) then
				prevPt= cve:Point(ptN - 1)
			end
		--first point's parameters
			handle:Set(newPt.fPos.x - prevPt.fPos.x, newPt.fPos.y - prevPt.fPos.y)
			handle:NormMe()
			vec:Set(newPt.fPos.x - oldPt.fPos.x, newPt.fPos.y - oldPt.fPos.y)
			local oldLen= vec:Mag()
			gfx:DrawFatMarker(cp0x,cp0y, 4)
		--CountSegments() is ptN-1 for open curves...
			for j= 2, cve:CountSegments() + 1 do
		--calculating the inner control points
				cp3x= newPt.fPos.x
				cp3y= newPt.fPos.y
		--distance to the previous neighbour: control point #1
				len= oldLen * oldCurvature
				cp1x= cp0x + handle.x * len
				cp1y= cp0y + handle.y * len
				len= oldLen * newCurvature
		--administration and getting the next point
				prevPt= oldPt
				oldPt= newPt
				oldCurvature= newCurvature
				if (j < ptN) then
					newPt= cve:Point(j)
					newCurvature= cve:GetCurvature(j, moho.frame)
				--the next neighbour vector
					vec:Set(newPt.fPos.x - oldPt.fPos.x, newPt.fPos.y - oldPt.fPos.y)
					oldLen= vec:Mag()
				elseif (cve.fClosed) then
			--closed curve, last two segments
						newPt= cve:Point(j - ptN)
						newCurvature= cve:GetCurvature(j - ptN, moho.frame)
				end
			--open curve, last segment: keep the original newPt for the handle vector
			--handle of the endpoint: normalized vector between the neighbours
				handle:Set(newPt.fPos.x - prevPt.fPos.x, newPt.fPos.y - prevPt.fPos.y)
				handle:NormMe()
		--distance to the next neigbour: control point #2
				cp2x= cp3x - handle.x * len
				cp2y= cp3y - handle.y * len
		--drawing the control handles
				gfx:DrawLine(cp0x,cp0y,cp1x,cp1y)
				gfx:DrawLine(cp2x,cp2y,cp3x,cp3y)
		--drawing the Bezier curve, based on http://en.wikipedia.org/wiki/B%C3%A9zier_curve
				local cx = 3.0 * (cp1x - cp0x)
				local bx = 3.0 * (cp2x - cp1x) - cx
				local ax = cp3x - cp0x - cx - bx
				local cy = 3.0 * (cp1y - cp0y)
				local by = 3.0 * (cp2y - cp1y) - cy
				local ay = cp3y - cp0y - cy - by
				local oldX = nil
				local oldY
				for t= 0, 1, 0.05 do
					local tSquared = t * t
					local tCubed = tSquared * t
					local resultX = (ax * tCubed) + (bx * tSquared) + (cx * t) + cp0x
					local resultY = (ay * tCubed) + (by * tSquared) + (cy * t) + cp0y
					if (oldX) then
						gfx:DrawLine(oldX,oldY,resultX,resultY)
					end
					oldX= resultX
					oldY= resultY
				end
				cp0x= cp3x
				cp0y= cp3y
			end
			gfx:DrawFatMarker(cp0x,cp0y, 4)
		end
	end
        gfx:Pop()
end