DIRECTORY Commander USING [CommandProc, Register], 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, FlushImage, GetTrajectoryFile, HitTestCurves, HitTestVertices, MoveActiveVertex, NewTrajectory, OpenActiveTrajectory, PathData, Point, Redraw, SetActiveTrajectory, SetActiveVertex, SetContinuity, SplitSegment, StoreTrajectoryFile, TrajectoryIsEmpty, VertexType], Real USING [RoundI], Rope USING [ROPE], ViewerTools USING [GetSelectionContents]; PathBuilder: CEDAR PROGRAM IMPORTS Commander, 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]; $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], collinear: FALSE, closestVertex: NIL, closestSegment: NIL]; $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]; SetContinuity[pathData: handle.pathData, knot: point, continuity: off]; }; DeleteTrajectory: ButtonProc = { [] _ SetActiveTrajectory[pathData: handle.pathData, where: point]; DeleteActiveTrajectory[pathData: handle.pathData]; }; Start: Commander.CommandProc = BEGIN [handle.pathData] _ CreatePathViewer[ name: "Beta-Spliner", menuLabels: LIST[ [$NewImage, TRUE], [$Redraw, FALSE], [$GridOn, FALSE], [$GridOff, FALSE], [$Get, TRUE], [$Store, TRUE], [$NewTraj, FALSE], [$CloseTraj, FALSE], [$OpenTraj, FALSE] ], buttonProc: ButtonHit, redrawProc: DrawGrid ]; END; Commander.Register[key: "BetaSpliner", proc: Start, doc: "A Beta-spline tinkerer"]; END. ,PathBuilder.mesa Copyright (C) 1984 by Xerox Corporation. All rights reserved. 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: Tso, August 9, 1984 12:46:20 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™>K– "Cedar" stylešœ5™5J™J™-J™1J™3šΟk ˜ Jšœ œ˜(Jšœ œA˜OJšœ œό˜ŒJšœœ ˜Jšœœœ˜Jšœ œ˜)—J˜šœ œ˜Jšœ6˜=—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˜JšœS˜SJšœV˜VJ˜Jšœ2˜2J˜Jšœ[˜[Jšœ[˜[J˜Jšœ#˜#Jšœ œœœ#˜PJšœœœœ!˜OJ˜Jšœ`˜`JšœV˜VJ˜Jšœ/˜/JšœV˜VJ˜JšœYœœœ˜‰J˜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šœœ˜$šœ!œ˜<šœ!œ˜