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] = { 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] = { 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] = { 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] = { 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] = { 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] = { 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] = { 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] = { RETURN [IsAKnot[vertex] OR (vertex # NIL AND vertex.first.fixed)] }; DrawVertexAndSegments: PRIVATE PROCEDURE [pathViewer: ViewerClasses.Viewer, vertex: VertexNode, segment: SegmentNode, undo: BOOLEAN _ 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. :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. 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. 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. 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. 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. 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. This routine updates the position of the specified vertex without applying any continuity constraints. This routine determines whether continuity is required at the specified vertex. Continuity can be a requirement only at knots. 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. 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. Κπ˜Iproc– "Cedar" stylešœ™K– "Cedar" stylešœ5™5J™J™ΠunitšΟk ˜ Jšœ œ˜+J˜Jšœ œ˜*Jšœ œ/˜>Jšœœ-˜BJšœ˜Jšœœ ˜—šœœ˜ Jšœ1˜8Jšœ˜—Lš œH˜TJ˜šΟn œœe˜€J™¬J˜-J˜0J˜GJ˜Gšœœ˜Jšœ]˜]Jšœ0œp˜£Jšœt˜tJšœt˜tJšœB˜I—J˜—J˜šžœ œe˜J™ΫJ˜ Jšœœ˜J˜J˜GJ˜NJ˜7J˜7JšœVœ˜\Jšœ!˜!šœ˜šœœ˜*JšœW˜WJ˜—šœ˜šœ!œ˜)J˜;JšœX˜XJ˜—J˜;JšœXœ˜^šœ#œ˜+J˜?Jšœ‡˜‡Jšœ œ)˜=Jšœ˜—Jšœ`˜dJšœR˜RJšœ˜—šœ˜šœ!œ˜)Jšœ;˜;JšœX˜XJ˜—J˜;JšœXœ˜^šœ#œ˜+J˜?Jšœ‡˜‡Jšœœ)˜=Jšœ˜—Jšœ`˜dJšœR˜RJšœ˜—šœ˜ JšœXœ˜^JšœXœ˜^JšœI˜IJšœ ˜ Jšœ ˜ Jšœ7˜7Jšœ7˜7Jšœ7˜7Jšœ7˜7JšœR˜RJšœR˜RJšœ˜——JšœP˜PJ˜—J˜šžœ œe˜J™Jšœœ˜J˜J˜1J˜6J˜7J˜;J˜7J˜;šœ˜šœœ˜,JšœVœ˜\Jšœ‡˜‡Jšœœ(˜JšœT˜TJšœ˜—šœ˜ JšœZœ˜`Jšœa˜aJšœT˜TJšœ˜——JšœP˜PJ˜—J˜šžœ œe˜†J™βJšœœ˜J˜J˜5J˜:J˜7J˜;J˜?JšœVœ˜\Jšœ!˜!šœœ˜šœ˜JšœX˜XJšœ˜—šœ(˜(J˜CJšœZœ˜`Jšœ‰˜‰Jšœœ*˜>JšœT˜TJšœ˜—šœ˜ JšœZœ˜`Jšœa˜aJšœT˜TJšœ˜——JšœP˜PJ˜—J˜šžœ œe˜ƒJ™fJšœVœ˜\Jšœ!˜!JšœP˜PJ˜—J˜šž œœ,œœ˜kJ™J˜J˜ J˜7J˜7Jš œœœ œœ œ˜TJ˜—J˜šžœœœ œ˜MJ™§Jšœœ œœ˜AJ˜—J˜šžœœTœœ˜J™΄šœœ˜Jšœ%˜%Jš œœœœœ+˜[Jšœ˜—šœ˜Jšœ$˜$Jš œœœœœ*˜ZJ˜—JšœE˜EJšœH˜HJš œœœœœN˜~J˜—J˜I modheaderšœ˜J˜—…—*ή: