<> <> <<>> <> 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.