PEConstraintsImpl.mesa
Written by Darlene Plebon on August 22, 1983 10:38 am
Routines for imposing continuity constaints on the bezier spline segments. First order continuity is achieved by imposing colinearity on the tangent vectors at the junction of the two bezier spline segments.
DIRECTORY
PEAlgebra USING [Intersection, Projection],
PEConstraints,
PEDisplay USING [DrawVertex, DrawSegment],
PERefresh USING [DisableSegmentRefresh, EnableSegmentRefresh],
PETrajectoryOps USING [FollowingVertex, IsAKnot, PrecedingVertex],
PETypes,
ViewerClasses USING [Viewer];
PEConstraintsImpl: CEDAR PROGRAM
IMPORTS PEAlgebra, PEDisplay, PERefresh, PETrajectoryOps
EXPORTS PEConstraints =
BEGIN OPEN PEAlgebra, PEConstraints, PEDisplay, PERefresh, PETrajectoryOps, PETypes;
Constrain: PUBLIC PROCEDURE [pathViewer: ViewerClasses.Viewer, vertex: VertexNode, segment: SegmentNode, newPosition: Point] = {
This updates the position of the specified vertex in the specified segment according to the continuity constraints specified for it with respect to adjacent vertices. The application of the constraints may result in other vertices being moved too. This routine also updates the display accordingly.
followingVertex, precedingVertex: VertexNode;
followingSegment, precedingSegment: SegmentNode;
[followingVertex, followingSegment] ← FollowingVertex[vertex, segment];
[precedingVertex, precedingSegment] ← PrecedingVertex[vertex, segment];
SELECT TRUE FROM
ContinuousAt[vertex, segment] => ConstrainedBySelf[pathViewer, vertex, segment, newPosition];
ContinuousAt[precedingVertex, precedingSegment] AND ContinuousAt[followingVertex, followingSegment] => ConstrainedByBoth[pathViewer, vertex, segment, newPosition];
ContinuousAt[precedingVertex, precedingSegment] => ConstrainedByPreceding[pathViewer, vertex, segment, newPosition];
ContinuousAt[followingVertex, followingSegment] => ConstrainedByFollowing[pathViewer, vertex, segment, newPosition];
ENDCASE => UnconstrainedUpdate[pathViewer, vertex, segment, newPosition];
};
ConstrainedBySelf: PROCEDURE [pathViewer: ViewerClasses.Viewer, vertex: VertexNode, segment: SegmentNode, newPosition: Point] = {
This routine updates the position of the specified vertex in the case where its movement is constrained by continuity requirements at itself. That is, the vertex being updated is a knot at which continuity is required.
diff: Point;
intersecting: BOOLEAN;
intersectionPoint: Point;
pVertex, ppVertex, pppVertex, fVertex, ffVertex, fffVertex: VertexNode;
pSegment, ppSegment, pppSegment, fSegment, ffSegment, fffSegment: SegmentNode;
[fVertex, fSegment] ← FollowingVertex[vertex, segment];
[pVertex, pSegment] ← PrecedingVertex[vertex, segment];
DrawVertexAndSegments[pathViewer: pathViewer, vertex: vertex, segment: segment, undo: TRUE];
vertex.first.point ← newPosition;
SELECT TRUE FROM
IsFixed[pVertex] AND IsFixed[fVertex] => {
vertex.first.point ← Projection[newPosition, fVertex.first.point, pVertex.first.point];
};
IsFixed[pVertex] => {
IF ContinuousAt[pVertex, pSegment] THEN {
[ppVertex, ppSegment] ← PrecedingVertex[pVertex, pSegment];
vertex.first.point ← Projection[newPosition, pVertex.first.point, ppVertex.first.point];
};
[ffVertex, ffSegment] ← FollowingVertex[fVertex, fSegment];
DrawVertexAndSegments[pathViewer: pathViewer, vertex: fVertex, segment: fSegment, undo: TRUE];
IF ContinuousAt[ffVertex, ffSegment] THEN {
[fffVertex, fffSegment] ← FollowingVertex[ffVertex, ffSegment];
[intersecting, intersectionPoint] ← Intersection[ffVertex.first.point, fffVertex.first.point, vertex.first.point, pVertex.first.point];
IF intersecting THEN fVertex.first.point ← intersectionPoint;
}
ELSE fVertex.first.point ← Projection[fVertex.first.point, vertex.first.point, pVertex.first.point];
DrawVertexAndSegments[pathViewer: pathViewer, vertex: fVertex, segment: fSegment];
};
IsFixed[fVertex] => {
IF ContinuousAt[fVertex, fSegment] THEN {
[ffVertex, ffSegment] ← FollowingVertex[fVertex, fSegment];
vertex.first.point ← Projection[newPosition, fVertex.first.point, ffVertex.first.point];
};
[ppVertex, ppSegment] ← PrecedingVertex[pVertex, pSegment];
DrawVertexAndSegments[pathViewer: pathViewer, vertex: pVertex, segment: pSegment, undo: TRUE];
IF ContinuousAt[ppVertex, ppSegment] THEN {
[pppVertex, pppSegment] ← PrecedingVertex[ppVertex, ppSegment];
[intersecting, intersectionPoint] ← Intersection[ppVertex.first.point, pppVertex.first.point, vertex.first.point, fVertex.first.point];
IF intersecting THEN pVertex.first.point ← intersectionPoint;
}
ELSE pVertex.first.point ← Projection[pVertex.first.point, vertex.first.point, fVertex.first.point];
DrawVertexAndSegments[pathViewer: pathViewer, vertex: pVertex, segment: pSegment];
};
ENDCASE => {
DrawVertexAndSegments[pathViewer: pathViewer, vertex: pVertex, segment: pSegment, undo: TRUE];
DrawVertexAndSegments[pathViewer: pathViewer, vertex: fVertex, segment: fSegment, undo: TRUE];
diff ← Projection[newPosition, pVertex.first.point, fVertex.first.point];
diff.x ← newPosition.x - diff.x;
diff.y ← newPosition.y - diff.y;
pVertex.first.point.x ← pVertex.first.point.x + diff.x;
pVertex.first.point.y ← pVertex.first.point.y + diff.y;
fVertex.first.point.x ← fVertex.first.point.x + diff.x;
fVertex.first.point.y ← fVertex.first.point.y + diff.y;
DrawVertexAndSegments[pathViewer: pathViewer, vertex: pVertex, segment: pSegment];
DrawVertexAndSegments[pathViewer: pathViewer, vertex: fVertex, segment: fSegment];
};
DrawVertexAndSegments[pathViewer: pathViewer, vertex: vertex, segment: segment];
};
ConstrainedByBoth: PROCEDURE [pathViewer: ViewerClasses.Viewer, vertex: VertexNode, segment: SegmentNode, newPosition: Point] = {
This routine updates the position of the specified vertex in the case where its movement is constrained by continuity requirements at the vertex following it and the vertex preceding it in the path. These vertices are knots at which continuity is required.
intersecting: BOOLEAN;
intersectionPoint: Point;
pVertex, ppVertex, fVertex, ffVertex: VertexNode;
pSegment, ppSegment, fSegment, ffSegment: SegmentNode;
[fVertex, fSegment] ← FollowingVertex[vertex, segment];
[ffVertex, ffSegment] ← FollowingVertex[fVertex, fSegment];
[pVertex, pSegment] ← PrecedingVertex[vertex, segment];
[ppVertex, ppSegment] ← PrecedingVertex[pVertex, pSegment];
SELECT TRUE FROM
IsFixed[ppVertex] AND IsFixed[ffVertex] => {
DrawVertexAndSegments[pathViewer: pathViewer, vertex: vertex, segment: segment, undo: TRUE];
[intersecting, intersectionPoint] ← Intersection[pVertex.first.point, ppVertex.first.point, fVertex.first.point, ffVertex.first.point];
IF intersecting THEN vertex.first.point ← intersectionPoint;
DrawVertexAndSegments[pathViewer: pathViewer, vertex: vertex, segment: segment];
};
IsFixed[ffVertex] => {
newPosition ← Projection[newPosition, fVertex.first.point, ffVertex.first.point];
ConstrainedByPreceding[pathViewer, vertex, segment, newPosition];
};
IsFixed[ppVertex] => {
newPosition ← Projection[newPosition, pVertex.first.point, ppVertex.first.point];
ConstrainedByFollowing[pathViewer, vertex, segment, newPosition];
};
ENDCASE => {
ConstrainedByPreceding[pathViewer, vertex, segment, newPosition];
ConstrainedByFollowing[pathViewer, vertex, segment, newPosition];
};
};
ConstrainedByPreceding: PROCEDURE [pathViewer: ViewerClasses.Viewer, vertex: VertexNode, segment: SegmentNode, newPosition: Point] = {
This routine updates the position of the specified vertex in the case where its movement is constrained by continuity requirements at the vertex preceding it in the path. That vertex is a knot at which continuity is required.
intersecting: BOOLEAN;
intersectionPoint: Point;
pVertex, ppVertex, pppVertex, ppppVertex: VertexNode;
pSegment, ppSegment, pppSegment, ppppSegment: SegmentNode;
[pVertex, pSegment] ← PrecedingVertex[vertex, segment];
[ppVertex, ppSegment] ← PrecedingVertex[pVertex, pSegment];
[pppVertex, pppSegment] ← PrecedingVertex[ppVertex, ppSegment];
DrawVertexAndSegments[pathViewer: pathViewer, vertex: vertex, segment: segment, undo: TRUE];
vertex.first.point ← newPosition;
SELECT TRUE FROM
IsFixed[ppVertex] => {
vertex.first.point ← Projection[newPosition, pVertex.first.point, ppVertex.first.point];
};
ContinuousAt[pppVertex, pppSegment] => {
[ppppVertex, ppppSegment] ← PrecedingVertex[pppVertex, pppSegment];
DrawVertexAndSegments[pathViewer: pathViewer, vertex: ppVertex, segment: ppSegment, undo: TRUE];
[intersecting, intersectionPoint] ← Intersection[pppVertex.first.point, ppppVertex.first.point, vertex.first.point, pVertex.first.point];
IF intersecting THEN ppVertex.first.point ← intersectionPoint;
DrawVertexAndSegments[pathViewer: pathViewer, vertex: ppVertex, segment: ppSegment];
};
ENDCASE => {
DrawVertexAndSegments[pathViewer: pathViewer, vertex: ppVertex, segment: ppSegment, undo: TRUE];
ppVertex.first.point ← Projection[ppVertex.first.point, vertex.first.point, pVertex.first.point];
DrawVertexAndSegments[pathViewer: pathViewer, vertex: ppVertex, segment: ppSegment];
};
DrawVertexAndSegments[pathViewer: pathViewer, vertex: vertex, segment: segment];
};
ConstrainedByFollowing: PROCEDURE [pathViewer: ViewerClasses.Viewer, vertex: VertexNode, segment: SegmentNode, newPosition: Point] = {
This routine updates the position of the specified vertex in the case where its movement is constrained by continuity requirements at the vertex following it in the path. That vertex is a knot at which continuity is required.
intersecting: BOOLEAN;
intersectionPoint: Point;
fVertex, ffVertex, fffVertex, ffffVertex: VertexNode;
fSegment, ffSegment, fffSegment, ffffSegment: SegmentNode;
[fVertex, fSegment] ← FollowingVertex[vertex, segment];
[ffVertex, ffSegment] ← FollowingVertex[fVertex, fSegment];
[fffVertex, fffSegment] ← FollowingVertex[ffVertex, ffSegment];
DrawVertexAndSegments[pathViewer: pathViewer, vertex: vertex, segment: segment, undo: TRUE];
vertex.first.point ← newPosition;
SELECT TRUE FROM
IsFixed[ffVertex] => {
vertex.first.point ← Projection[newPosition, fVertex.first.point, ffVertex.first.point];
};
ContinuousAt[fffVertex, fffSegment] => {
[ffffVertex, ffffSegment] ← FollowingVertex[fffVertex, fffSegment];
DrawVertexAndSegments[pathViewer: pathViewer, vertex: ffVertex, segment: ffSegment, undo: TRUE];
[intersecting, intersectionPoint] ← Intersection[fffVertex.first.point, ffffVertex.first.point, vertex.first.point, fVertex.first.point];
IF intersecting THEN ffVertex.first.point ← intersectionPoint;
DrawVertexAndSegments[pathViewer: pathViewer, vertex: ffVertex, segment: ffSegment];
};
ENDCASE => {
DrawVertexAndSegments[pathViewer: pathViewer, vertex: ffVertex, segment: ffSegment, undo: TRUE];
ffVertex.first.point ← Projection[ffVertex.first.point, vertex.first.point, fVertex.first.point];
DrawVertexAndSegments[pathViewer: pathViewer, vertex: ffVertex, segment: ffSegment];
};
DrawVertexAndSegments[pathViewer: pathViewer, vertex: vertex, segment: segment];
};
UnconstrainedUpdate: PROCEDURE [pathViewer: ViewerClasses.Viewer, vertex: VertexNode, segment: SegmentNode, newPosition: Point] = {
This routine updates the position of the specified vertex without applying any continuity constraints.
DrawVertexAndSegments[pathViewer: pathViewer, vertex: vertex, segment: segment, undo: TRUE];
vertex.first.point ← newPosition;
DrawVertexAndSegments[pathViewer: pathViewer, vertex: vertex, segment: segment];
};
ContinuousAt: PUBLIC PROCEDURE [vertex: VertexNode, segment: SegmentNode] RETURNS [continuous: BOOLEAN] = {
This routine determines whether continuity is required at the specified vertex. Continuity can be a requirement only at knots.
pVertex, fVertex: VertexNode;
pSegment, fSegment: SegmentNode;
[fVertex, fSegment] ← FollowingVertex[vertex, segment];
[pVertex, pSegment] ← PrecedingVertex[vertex, segment];
RETURN [IsAKnot[vertex] AND vertex.first.fixed AND fVertex # NIL AND pVertex # NIL];
};
IsFixed: PUBLIC PROCEDURE [vertex: VertexNode] RETURNS [isFixed: BOOLEAN] = {
This routine determines whether the position of the specified vertex is fixed. A vertex is fixed if it is a knot, or if it is a control point whose position is fixed.
RETURN [IsAKnot[vertex] OR (vertex # NIL AND vertex.first.fixed)]
};
DrawVertexAndSegments: PRIVATE PROCEDURE [pathViewer: ViewerClasses.Viewer, vertex: VertexNode, segment: SegmentNode, undo: BOOLEANFALSE] = {
This routine draws a vertex and all the segments containing that vertex. This routine disables refresh on the segments drawn if undo is true, and enables refresh if undo is false.
IF undo THEN {
DisableSegmentRefresh[segment.first];
IF vertex.rest = NIL AND segment.rest # NIL THEN DisableSegmentRefresh[segment.rest.first];
}
ELSE {
EnableSegmentRefresh[segment.first];
IF vertex.rest = NIL AND segment.rest # NIL THEN EnableSegmentRefresh[segment.rest.first];
};
DrawVertex[pathViewer: pathViewer, vertex: vertex.first, undo: undo];
DrawSegment[pathViewer: pathViewer, segment: segment.first, undo: undo];
IF vertex.rest = NIL AND segment.rest # NIL THEN DrawSegment[pathViewer: pathViewer, segment: segment.rest.first, undo: undo];
};
END.