BetaSplinePoints:
PUBLIC
PROCEDURE [seg: PETypes.SegmentList, handleSeg: PETypes.SegmentList, closed:
BOOLEAN]
RETURNS [splineSegList: PETypes.SegmentList] = {
This routine computes the points of a Beta spline given a list of control points with corresponding beta values (bias and tension). seg should point to the first control point of the active trajectory.
beta1, beta2: REAL; -- the shape parameters of bias and tension, respectively
b0, b1, b2, b3:
REAL;
-- the cubic Beta-spline basis functions
epsilon: REAL = .01; -- necessary factor to add to max of parameter u to adjust for
-- precision errors
increment: REAL = .1; -- step size through parametric space
invDelta: REAL; -- common factor in the basis functions, cf. article cited above
lessThanFour: BOOLEAN ← FALSE; -- don't compute Beta-spline with < 4 control points
max: REAL = 1.0; -- max value of parameter u
p: PETypes.Point; -- points on the Beta-spline curve
splineSeg: PETypes.Segment ← NIL; -- the spline segment to be stored into the new trajectory
splineSegNode: PETypes.SegmentNode ← NIL;
splineVertex: PETypes.Vertex; -- the spline point to be stored into the trajectory
splineVertexList: PETypes.VertexList ← NIL;
splineVertexNode: PETypes.VertexNode ← NIL;
usq, ucu, u4, u5: REAL; -- various powers of the parameter u for computation purposes
SegmentsOnCurve:
PROCEDURE = {
-- the vertices used in calculating the current segment
current, preceding, next, prepreceding: PETypes.Vertex;
currentNode, precedingNode, preprecedingNode, nextNode: PETypes.VertexNode;
-- firstVertex is true iff current is the 1st vertex of the control polygon
firstVertex: BOOLEAN ← TRUE;
firstSegment, lastWorkingSeg, secondSegment: PETypes.Segment;
splineSegList ← NIL;
IF closed THEN seg ← seg.rest;
firstSegment ← seg.preceding.first;
secondSegment ← seg.preceding.preceding.first;
prepreceding ← seg.preceding.first.vertices.first;
preceding ← seg.preceding.preceding.first.vertices.first;
current ← seg.preceding.preceding.preceding.first.vertices.first;
next ← seg.preceding.preceding.preceding.preceding.first.vertices.first;
WHILE handleSeg.preceding.first.vertices.first^ # prepreceding^
DO
handleSeg ← handleSeg.preceding;
ENDLOOP;
preprecedingNode ← handleSeg.preceding.first.vertices;
precedingNode ← handleSeg.preceding.preceding.first.vertices;
currentNode ← handleSeg.preceding.preceding.preceding.first.vertices;
nextNode ← handleSeg.preceding.preceding.preceding.preceding.first.vertices;
lastWorkingSeg ← seg.preceding.preceding.preceding.preceding.first;
IF (current = prepreceding) OR (current = preceding) OR (current = next) OR (prepreceding = next) THEN lessThanFour ← TRUE;
The 1st clause indicates whether or not we are just starting the calculations, the 2nd clause sets the condition for exiting the loop for an open control polygon, and the 3rd clause sets the exit condition for a closed control polygon.
WHILE
(firstVertex
OR
(lastWorkingSeg #
firstSegment
AND ~closed)
OR (lastWorkingSeg # secondSegment
AND closed))
AND ~lessThanFour
DO
FOR u: REAL ← 0, u+increment UNTIL u>(max+epsilon) DO -- step thru parameter space
usq ← u*u;
ucu ← u*usq;
u4 ← u*ucu;
u5 ← u*u4;
beta1 ← preceding.bias + (current.bias-preceding.bias)*(10*ucu - 15*u4 + 6*u5);
beta2 ← preceding.tension + (current.tension-preceding.tension)* (10*ucu - 15*u4 + 6*u5);
beta1sq ← beta1 * beta1;
beta1cu ← beta1 * beta1sq;
invDelta ← 1.0/(beta2 + 2*beta1cu + 4*beta1sq + 4*beta1 + 2);
b0 ← invDelta*2*ucu;
b1 ← invDelta*(2 + 6*beta1*u + (3*beta2 + 6*beta1sq)*usq - (2*beta2 + 2*beta1sq + 2*beta1 + 2)*ucu);
b2 ← invDelta*( (beta2 + 4*beta1sq + 4*beta1) + (6*beta1cu - 6*beta1)*u - (3*beta2 + 6*beta1cu + 6*beta1sq)*usq + (2*beta2 + 2*beta1cu + 2*beta1sq + 2*beta1)*ucu );
b3 ← invDelta*( 2*beta1cu - 6*beta1cu*u + 6*beta1cu*usq - 2*beta1cu*ucu);
p.x ← prepreceding.point.x*b3 + preceding.point.x*b2 + current.point.x*b1 + next.point.x*b0;
p.y ← prepreceding.point.y*b3 + preceding.point.y*b2 + current.point.y*b1 + next.point.y*b0;
-- building up the new trajectory whose segments consist of the points of the spline
splineVertex ← NEW[PETypes.VertexRec ← [point: p]];
splineVertexList ← NEW[PETypes.VertexNodeRec];
splineVertexList ← NIL;
[splineVertexList, splineVertexNode] ← PETrajectoryOps.InsertVertex[splineVertexList, splineVertex, NIL];
splineSeg ← NEW[PETypes.SegmentRec ← [type: curveTo, fp: splineVertexList.first, vertices: splineVertexList, controlPtPP: preprecedingNode, controlPtP: precedingNode, controlPtC: currentNode, controlPtN: nextNode]];
[splineSegList, splineSegNode] ← PETrajectoryOps.InsertSegment[splineSegList, splineSeg, NIL];
IF firstVertex THEN firstVertex ← FALSE;
-- finding the vertices used in calculating the next curve segment
seg ← seg.preceding;
handleSeg ← handleSeg.preceding;
prepreceding ← seg.preceding.first.vertices.first;
preceding ← seg.preceding.preceding.first.vertices.first;
current ← seg.preceding.preceding.preceding.first.vertices.first;
next ← seg.preceding.preceding.preceding.preceding.first.vertices.first;
preprecedingNode ← handleSeg.preceding.first.vertices;
precedingNode ← handleSeg.preceding.preceding.first.vertices;
currentNode ← handleSeg.preceding.preceding.preceding.first.vertices;
nextNode ← handleSeg.preceding.preceding.preceding.preceding.first.vertices;
lastWorkingSeg ← seg.preceding.preceding.preceding.preceding.first;
ENDLOOP;
};
SegmentsOnCurve[];
};