DIRECTORY Graphics USING [black, Box, DrawTo, GetBounds, SetColor, SetCP, SetFat, white], PathEditor USING [AddControlPoint, AddKnotToFront, AddKnotToRear, AdjustSegmentSplit, BooleanSpecification, ButtonHitProc, CloseActiveTrajectory, ConfirmSegmentSplit, CreatePathViewer, CurveType, DeleteActiveTrajectory, DeleteVertex, DrawInPathViewer, DrawProc, FixControlPoint, FlushImage, GetTrajectoryFile, HitTestCurves, HitTestVertices, MoveActiveVertex, NewTrajectory, OpenActiveTrajectory, PathData, Point, Redraw, SetActiveTrajectory, SetActiveVertex, SetContinuity, SetTrajectoryContinuity, SplitSegment, StoreTrajectoryFile, TrajectoryIsEmpty, VertexType], Real USING [RoundI], Rope USING [ROPE], ViewerTools USING [GetSelectionContents]; PathBuilder: CEDAR PROGRAM IMPORTS Graphics, PathEditor, Real, ViewerTools = BEGIN OPEN PathEditor; handle: Handle _ NEW[SplineToolRec]; Handle: TYPE = REF SplineToolRec; SplineToolRec: TYPE = RECORD [ pathData: PathData, gridConstrain: BOOLEAN _ FALSE, redHeldProc: ButtonProc _ NIL, redUpProc: ButtonProc _ NIL ]; ButtonProc: TYPE = PROCEDURE [point: Point]; ButtonHit: ButtonHitProc = { IF handle.gridConstrain THEN [x, y] _ GridConstrain[x, y]; SELECT event FROM $NewImage => FlushImage[pathData: handle.pathData]; $Redraw => RedrawViewer; $GridOn => SetGridConstraints[TRUE]; $GridOff => SetGridConstraints[FALSE]; $Get => GetTrajectories[]; $Store => StoreTrajectories[]; $NewTraj => NewTrajectory[pathData: handle.pathData]; $CloseTraj => CloseActiveTrajectory[pathData: handle.pathData]; $OpenTraj => OpenActiveTrajectory[pathData: handle.pathData]; $ConstrainTraj => SetTrajectoryContinuity[handle.pathData, toggle]; $SetActiveVertex => [] _ SetActiveVertex[pathData: handle.pathData, where: [x, y]]; $MoveActiveVertex => MoveActiveVertex[pathData: handle.pathData, newPosition: [x, y]]; $SetConstraints => SetConstraints[point: [x, y]]; $SetActiveTrajectory => [] _ SetActiveTrajectory[pathData: handle.pathData, where: [x, y]]; $SetActiveTrajectory => [] _ SetActiveTrajectory[pathData: handle.pathData, where: [x, y]]; $AddKnot => AddKnot[point: [x, y]]; $MoveKnot => IF handle.redHeldProc # NIL THEN handle.redHeldProc[point: [x, y]]; $ConfirmMove => IF handle.redUpProc # NIL THEN handle.redUpProc[point: [x, y]]; $AddControlPoint => AddControlPoint[pathData: handle.pathData, where: [x, y], position: [x, y]]; $MoveActiveVertex => MoveActiveVertex[pathData: handle.pathData, newPosition: [x, y]]; $AddTrajectory => AddTrajectory[point: [x, y]]; $MoveActiveVertex => MoveActiveVertex[pathData: handle.pathData, newPosition: [x, y]]; $DeleteVertex => DeleteVertex[pathData: handle.pathData, vertex: [x, y]]; $DeleteTrajectory => DeleteTrajectory[point: [x, y]]; ENDCASE; }; RedrawViewer: PROCEDURE [] = { DrawInPathViewer[handle.pathData, DrawGrid]; Redraw[pathData: handle.pathData]; }; gridOffsetX: REAL = 0.0; gridOffsetY: REAL = 0.0; gridResolutionX: REAL = 16.0; gridResolutionY: REAL = 16.0; GridConstrain: PROCEDURE [x, y: REAL] RETURNS [newX, newY: REAL] = { newX _ gridOffsetX + Real.RoundI[(x - gridOffsetX) / gridResolutionX] * gridResolutionX; newY _ gridOffsetY + Real.RoundI[(y - gridOffsetY) / gridResolutionY] * gridResolutionY; }; DrawGrid: DrawProc = { bounds: Graphics.Box _ Graphics.GetBounds[context]; startX, startY, x, y: REAL; [startX, startY] _ GridConstrain[bounds.xmin, bounds.ymin]; IF startX < bounds.xmin THEN startX _ startX + gridResolutionX; IF startY < bounds.ymin THEN startY _ startY + gridResolutionY; IF handle.gridConstrain THEN Graphics.SetColor[context, Graphics.black] ELSE Graphics.SetColor[context, Graphics.white]; [] _ Graphics.SetFat[context, TRUE]; FOR y _ startY, y + gridResolutionY UNTIL y > bounds.ymax DO FOR x _ startX, x + gridResolutionX UNTIL x > bounds.xmax DO Graphics.SetCP[context, x, y]; Graphics.DrawTo[context, x, y]; ENDLOOP; ENDLOOP; }; SetGridConstraints: PROCEDURE [on: BOOLEAN] = { handle.gridConstrain _ on; DrawInPathViewer[handle.pathData, DrawGrid]; }; GetTrajectories: PROCEDURE [] = { fileName: Rope.ROPE _ ViewerTools.GetSelectionContents[]; GetTrajectoryFile[pathData: handle.pathData, fileName: fileName]; }; StoreTrajectories: PROCEDURE [] = { fileName: Rope.ROPE _ ViewerTools.GetSelectionContents[]; StoreTrajectoryFile[pathData: handle.pathData, fileName: fileName]; }; AddKnot: ButtonProc = { closeToVertex, closeToCurve: BOOLEAN; vertexType: VertexType; curveType: CurveType; [closeToVertex, vertexType,] _ HitTestVertices[pathData: handle.pathData, where: point]; SELECT TRUE FROM TrajectoryIsEmpty[handle.pathData] OR (closeToVertex AND vertexType = frontKnot) => { handle.redHeldProc _ MoveVertex; handle.redUpProc _ NIL; AddKnotToFront[pathData: handle.pathData, position: point]; }; closeToVertex AND vertexType = rearKnot => { handle.redHeldProc _ MoveVertex; handle.redUpProc _ NIL; AddKnotToRear[pathData: handle.pathData, position: point]; }; ENDCASE => { [closeToCurve, curveType,] _ HitTestCurves[pathData: handle.pathData, where: point]; IF ~closeToCurve THEN { handle.redHeldProc _ NIL; handle.redUpProc _ NIL; } ELSE { handle.redHeldProc _ MoveSplit; handle.redUpProc _ ConfirmSplit; SplitSegment[pathData: handle.pathData, where: point]; }; }; }; AddTrajectory: ButtonProc = { NewTrajectory[pathData: handle.pathData]; AddKnot[point: point]; }; MoveVertex: ButtonProc = { MoveActiveVertex[pathData: handle.pathData, newPosition: point]; }; MoveSplit: ButtonProc = { AdjustSegmentSplit[pathData: handle.pathData, where: point]; }; ConfirmSplit: ButtonProc = { ConfirmSegmentSplit[pathData: handle.pathData]; handle.redHeldProc _ NIL; handle.redUpProc _ NIL; }; SetConstraints: ButtonProc = { closeToVertex: BOOLEAN; vertexType: VertexType; [closeToVertex, vertexType,] _ HitTestVertices[pathData: handle.pathData, where: point]; SELECT TRUE FROM closeToVertex AND vertexType = controlPoint => { FixControlPoint[pathData: handle.pathData, controlPoint: point, fixed: toggle]; }; closeToVertex => { SetContinuity[pathData: handle.pathData, knot: point, continuity: toggle]; }; ENDCASE; }; DeleteTrajectory: ButtonProc = { [] _ SetActiveTrajectory[pathData: handle.pathData, where: point]; DeleteActiveTrajectory[pathData: handle.pathData]; }; [handle.pathData] _ CreatePathViewer[ name: "Path Tool", menuLabels: LIST[ [$NewImage, TRUE], [$Redraw, FALSE], [$GridOn, FALSE], [$GridOff, FALSE], [$Get, TRUE], [$Store, TRUE], [$NewTraj, FALSE], [$CloseTraj, FALSE], [$OpenTraj, FALSE], [$ConstrainTraj, FALSE] ], buttonProc: ButtonHit, redrawProc: DrawGrid ]; END. πPathBuilder.mesa Written by Darlene Plebon on August 31, 1983 11:36 am This program is the driver for the Path Tool. Last Edited by: Beach, September 26, 1983 1:56 pm Last Edited by: Spreitzer, May 29, 1985 12:12:32 pm PDT This routine redraws the grid if it should currently be visible and the image being manipulated. This routine constrains the point (x,y) to lie at the nearest grid point. This routine turns the grid constraints option on/off and set the grid visible/invisible accordingly. This routine gets a path from a file. This routine stores a path on a file. This routine adds a knot to the path. Adding knots results in the creation of new segments. This routine starts a new trajectory. This routine moves a vertex. This routine adjusts a segment split. This routine confirms the splitting of a segment. That is it makes it permanent. This routine sets the continuity constraints at a vertex (for both knots and control points). This routine deletes the trajectory being pointed at. Κ†– "Cedar" style˜Iproc– "Cedar" stylešœ™K– "Cedar" stylešœ5™5J™J™-J™1J™7šΟk ˜ Jšœ œA˜OJšœ œ¦˜ΆJšœœ ˜Jšœœœ˜Jšœ œ˜)—J˜šœ œ˜Jšœ+˜2—J˜Jš œ ˜J˜Jšœœ˜$J˜Jšœœœ˜!šœœœ˜K˜Kšœœœ˜Kšœœ˜Kšœ˜Jšœ˜—J˜JšΟn œœ œ˜,J˜šž œ˜Jšœœ˜:šœ˜J˜3J˜Jšœœ˜$Jšœœ˜&Jšœ˜Jšœ˜J˜5J˜?J˜=JšœC˜CJ˜JšœS˜SJšœV˜VJ˜Jšœ2˜2J˜Jšœ[˜[Jšœ[˜[J˜Jšœ#˜#Jšœ œœœ#˜PJšœœœœ!˜OJ˜Jšœ`˜`JšœV˜VJ˜Jšœ/˜/JšœV˜VJ˜JšœI˜IJ˜Jšœ5˜5Jšœ˜—J˜—J˜šž œ œ˜J™`J˜,Jšœ"˜"J˜—J˜Jšœ œ˜Jšœ œ˜Jšœœ˜Jšœœ˜J˜š ž œ œœœœ˜DJ™IJ˜XJ˜XJ˜—J˜šžœ˜Jšœ3˜3Jšœœ˜Jšœ;˜;Jšœœ#˜?Jšœœ#˜?Jšœœ+˜GJšœ,˜0Jšœœ˜$šœ!œ˜<šœ!œ˜