DIRECTORY CedarProcess USING [CheckAbort, SetPriority], Feedback USING [Append, PutF], FeedbackOps USING [GetLabel], FitBasic USING [Handle], FitState USING [AddSample, Create, CurrentJoints, CurrentSamples, ResetData], FitStateUtils USING [ForAllSamples, SampleProc], GeometricLogicOps USING [ANDs, MaskFill, MINUSs, NOTs, ORs, PathActionProc, Scale, ScaleRec, Set, SetToFlatPath, XORs], GGBasicTypes USING [Point], GGFromImager USING [Capture], GGHistory USING [NewCapture, PushCurrent], GGInterfaceTypes USING [GGData], GGModelTypes USING [BoundBox, Scene, Slice, SliceDescriptor, Traj, TrajData], GGOutline USING [CreateOutline, HasHoles], GGParent USING [FirstChild], GGParseIn USING [ReadWNAT, ReadWReal, ReadWRope, SyntaxError], GGRefresh USING [InterpressEntireScene], GGRefreshTypes, GGScene USING [AddSlice, BoundBoxOfSelected, DeleteAllSelected, MergeScenes, WalkSelectedSlices], GGSegment USING [MakeBezier, MakeLine], GGSegmentTypes USING [Segment], GGSelect USING [DeselectAll, SelectAll], GGSliceOps USING [BuildPath, GetBoundBox, GetFillColor, GetType, IsCompleteParts, WalkSegments], GGState USING [GetDefaultFillColor], GGTraj USING [AddSegment, CloseByDistorting, CreateTraj], GGUserInput USING [ArgumentType, RegisterAction, UserInputProc], GGWindow USING [RestoreScreenAndInvariants], Imager USING [ClipRectangle, Color, Context, MaskFill, PathProc, SetColor, SetStrokeWidth, TranslateT], ImagerBox USING [Rectangle, RectangleFromBox], ImagerPixel USING [MakePixelMap], ImagerSample USING [Fill, Get, ObtainScratchMap, ReleaseScratchMap, SampleMap], ImagerSmoothContext USING [Create, SetOutputBuffer], ImagerTransformation USING [Transformation], IO USING [PutFR, RIS, STREAM], LSPiece USING [Metrics, MetricsRec], Outline USING [GetOutline, Handle, OutlineEdge, Rec], PBasics USING [IsBound], PiecewiseFit USING [Cubic2Proc, Data, DataRec, GrowSpline], PotentialKnots USING [CircleTangents, DynPoly], Real USING [Ceiling, Round], Rope USING [ROPE], SF USING [Vec]; GGEventImplG: CEDAR PROGRAM IMPORTS CedarProcess, Feedback, FeedbackOps, FitState, FitStateUtils, GeometricLogicOps, GGFromImager, GGHistory, GGOutline, GGParent, GGParseIn, GGRefresh, GGScene, GGSegment, GGSelect, GGSliceOps, GGState, GGTraj, GGUserInput, GGWindow, Imager, ImagerBox, ImagerPixel, ImagerSample, ImagerSmoothContext, IO, Outline, PBasics, PiecewiseFit, PotentialKnots, Real EXPORTS GGInterfaceTypes = BEGIN BoundBox: TYPE = GGModelTypes.BoundBox; GGData: TYPE = GGInterfaceTypes.GGData; Point: TYPE = GGBasicTypes.Point; RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj; ROPE: TYPE = Rope.ROPE; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; Traj: TYPE = GGModelTypes.Traj; TrajData: TYPE = GGModelTypes.TrajData; Transformation: TYPE = ImagerTransformation.Transformation; UserInputProc: TYPE = GGUserInput.UserInputProc; EdgeData: TYPE = REF EdgeDataObj; EdgeDataObj: TYPE = RECORD [ sampleMap: ImagerSample.SampleMap, trans: Point, edgeCount: CARD, height: INTEGER, ggData: GGData ]; BadThreshold: SIGNAL = CODE; CheckThreshold: PROC [r: REAL] RETURNS [REAL] = { IF r NOT IN (0.0 .. 1.0) THEN SIGNAL BadThreshold; RETURN[r]; }; GetSample: PROC [client: REF, x,y: INT] RETURNS [INT] = { edgeData: EdgeData _ NARROW[client]; sample: WORD _ ImagerSample.Get[edgeData.sampleMap, [f: x, s: edgeData.height-y]]; RETURN[sample]; }; FeedBackEdge: PROC [client: REF, x0,y0,x1,y1: REAL] RETURNS [abort: BOOL _ FALSE] = { edgeData: EdgeData _ NARROW[client]; ggData: GGData _ edgeData.ggData; ggData.refresh.spotPoint _ [x0+edgeData.trans.x, y0+edgeData.trans.y]; --back to ggworld coordinates ggData.refresh.hitPoint _ [x1+edgeData.trans.x, y1+edgeData.trans.y]; edgeData.edgeCount _ edgeData.edgeCount+1; CedarProcess.CheckAbort[]; IF edgeData.edgeCount MOD 20 = 0 THEN Feedback.PutF[ggData.router, middle, $Feedback, "."]; GGWindow.RestoreScreenAndInvariants[paintAction: $FitFeedback, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; DoFit: PROC[ggData: GGData, makeCurves: BOOL _ TRUE] = { thisTraj: Traj; fit: FitBasic.Handle; lastPt: Point _ [0,0]; h: Outline.Handle; edgeData: EdgeData; contourCount: NAT _ 0; NewPoint: PROC [x,y: REAL] = { pt: Point _ [x+edgeData.trans.x, y+edgeData.trans.y]; FitState.AddSample[fit, pt.x, pt.y]; }; NewCubic: PiecewiseFit.Cubic2Proc = { IF thisTraj = NIL THEN thisTraj _ GGTraj.CreateTraj[bezier.b0]; CedarProcess.CheckAbort[]; Feedback.PutF[ggData.router, middle, $Feedback, "."]; [] _ GGTraj.AddSegment[thisTraj, hi, GGSegment.MakeBezier[bezier.b0, bezier.b1, bezier.b2, bezier.b3, NIL], lo]; }; ContourFromSelection: PROC = { selectedBox: BoundBox _ GGScene.BoundBoxOfSelected[ggData.scene, normal]; rect: ImagerBox.Rectangle _ ImagerBox.RectangleFromBox[[selectedBox.loX, selectedBox.loY, selectedBox.hiX, selectedBox.hiY] ]; --gg type to Imager type size: SF.Vec ~ [s: Real.Ceiling[rect.h], f: Real.Ceiling[rect.w]]; context: Imager.Context _ ImagerSmoothContext.Create[size: size]; sampleMap: ImagerSample.SampleMap _ ImagerSample.ObtainScratchMap[box: [max: size], bitsPerSample: 8]; ImagerSample.Fill[sampleMap, [max: size], 255]; ImagerSmoothContext.SetOutputBuffer[context: context, pixelMap: ImagerPixel.MakePixelMap[s0: sampleMap], components: LIST[$Intensity]]; Imager.TranslateT[context, [-rect.x, -rect.y]]; --move lower left corner to origin Imager.ClipRectangle[context,rect]; --clip in Gargoyle world coordinates Feedback.PutF[ggData.router, oneLiner, $Feedback, "Fit: rendering selected region for thresholding"]; GGRefresh.InterpressEntireScene[dc: context, ggData: ggData]; -- not really Interpress. Means just draw the objects. edgeData _ NEW[EdgeDataObj _ [sampleMap, [rect.x, rect.y], 0, 0, ggData]]; h _ NEW[Outline.Rec]; h.tValue _ ggData.fitData.threshold*255.0; IF h.tValue=Real.Round[h.tValue] THEN h.tValue _ h.tValue+0.0001; -- can't use integer thresholds h.border _ 255; h.xStart _ 0; h.yStart _ 0; h.xPixels _ size.f; h.yPixels _ size.s; edgeData.height _ h.yPixels-1; h.get _ GetSample; h.newEdge _ FeedBackEdge; h.client _ edgeData; Feedback.PutF[ggData.router, oneLiner, $Feedback, "Fit: finding edges in selected region"]; contourCount _ Outline.OutlineEdge[h]; ImagerSample.ReleaseScratchMap[map: sampleMap]; sampleMap _ NIL; context _ NIL; }; CedarProcess.SetPriority[background]; GGHistory.NewCapture["Fit", ggData]; -- capture scene BEFORE UPDATE ContourFromSelection[]; --local proc rather than inline for readability Feedback.PutF[ggData.router, begin, $Feedback, "Fit: %g contours found. Begin fitting", [integer[contourCount]]]; BEGIN lineLength: REAL _ 10; --minimum line length to trigger corner check in DynPoly thisOutline: Slice; data: PiecewiseFit.Data; metrics: LSPiece.Metrics _ NEW[LSPiece.MetricsRec _ [ maxItr: ggData.fitData.maxIterations, maxDev: ggData.fitData.tolerance, sumErr: 0, deltaT: 0]]; checkSamples: PROC RETURNS[ok: BOOLEAN] = { count: NAT _ 0; proc: FitStateUtils.SampleProc = {count _ count+1}; FitStateUtils.ForAllSamples[fit.slist, proc]; IF count > 3 THEN RETURN[TRUE] ELSE RETURN[FALSE]; }; checkJoints: PROC RETURNS[ok: BOOLEAN] = { count: NAT _ 0; proc: FitStateUtils.SampleProc = { IF s.jointType#none THEN count _ count+1}; FitStateUtils.ForAllSamples[fit.slist, proc]; IF count > 2 THEN RETURN[TRUE] ELSE RETURN[FALSE]; }; fit _ FitState.Create[]; fit.closed _ TRUE; fit.minDist _ ggData.fitData.minDistance; FOR i: CARD IN [0..contourCount) DO thisTraj _ NIL; Outline.GetOutline[h.data, NewPoint, i]; -- make a FitState.Handle IF NOT checkSamples[] THEN { -- do this here as minDistance filter affects total Feedback.PutF[ggData.router, oneLiner, $Feedback, "Fit: less than 3 samples in outline. Skipping outline"]; LOOP; }; PotentialKnots.DynPoly[fit, ggData.fitData.polyPenalty, lineLength, ggData.fitData.angle]; -- set potential knots IF makeCurves THEN { IF NOT checkJoints[] THEN { Feedback.PutF[ggData.router, oneLiner, $Feedback, "Fit: less than 2 potential knots in outline. Skipping outline"]; LOOP; }; Feedback.PutF[ggData.router, oneLiner, $Feedback, "Fit: potential knots found. Start curve finding"]; PotentialKnots.CircleTangents[fit, ggData.fitData.angle]; --set the tangents at the potential knots data _ NEW[PiecewiseFit.DataRec _ [ samples: FitState.CurrentSamples[fit], closed: TRUE, joints: NIL, tangents: NIL]]; [data.joints, data.tangents] _ FitState.CurrentJoints[fit]; PiecewiseFit.GrowSpline[data, metrics, NewCubic]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "Fit: new curve outline found"]; } ELSE { lastPoint: Point _ [0,0]; sampleProc: FitStateUtils.SampleProc = { IF s.jointType#none THEN { p: Point _ [s.xy.x, s.xy.y]; IF thisTraj=NIL THEN thisTraj _ GGTraj.CreateTraj[p] ELSE { newLine: Segment _ GGSegment.MakeLine[lastPoint, p, NIL]; [] _ GGTraj.AddSegment[thisTraj, hi, newLine, lo]; }; lastPoint _ p; }; }; IF NOT checkJoints[] THEN { Feedback.PutF[ggData.router, oneLiner, $Feedback, "Fit: less than 2 vertices in outline. Skipping outline"]; LOOP; }; FitStateUtils.ForAllSamples[fit.slist, sampleProc]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "Fit: new Polygon found"]; }; GGTraj.CloseByDistorting[thisTraj, lo]; --no distortion should be necessary thisOutline _ GGOutline.CreateOutline[thisTraj, NIL]; GGScene.AddSlice[ggData.scene, thisOutline, -1]; ggData.refresh.addedObject _ thisOutline; ggData.refresh.startBoundBox^ _ GGSliceOps.GetBoundBox[thisOutline]^; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; FitState.ResetData[fit, samples]; --cleanup after fit FitState.ResetData[fit, links]; ENDLOOP; END; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list CedarProcess.SetPriority[normal]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "Fit: completed. %g outlines added to scene", [integer[contourCount]]]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; FitIsLoaded: PROC [ggData: GGData] RETURNS [BOOL _ FALSE] = TRUSTED { IF PBasics.IsBound[LOOPHOLE[Outline.GetOutline]] AND PBasics.IsBound[LOOPHOLE[ImagerSmoothContext.Create]] THEN RETURN[TRUE]; Feedback.Append[ggData.router, oneLiner, $Complaint, "Fit failed: please install Fit and ImagerSmooth, then retry this operation"]; }; FitLines: UserInputProc = { IF FitIsLoaded[ggData] THEN DoFit[ggData, FALSE]; }; FitCurves: UserInputProc = { IF FitIsLoaded[ggData] THEN DoFit[ggData, TRUE]; }; SetFitParameters: UserInputProc = { ENABLE GGParseIn.SyntaxError=> GOTO SyntaxError; tolerance, minDistance, threshold, angle, polyPenalty: REAL; maxIterations: NAT; s: IO.STREAM _ IO.RIS[NARROW[event.rest.first]]; -- same form as ShowFitParameters GGParseIn.ReadWRope[s, "Fit: threshold:"]; threshold _ GGParseIn.ReadWReal[s]; GGParseIn.ReadWRope[s, "minDistance:"]; minDistance _ GGParseIn.ReadWReal[s]; GGParseIn.ReadWRope[s, "polyPenalty:"]; polyPenalty _ GGParseIn.ReadWReal[s]; GGParseIn.ReadWRope[s, "tolerance:"]; tolerance _ GGParseIn.ReadWReal[s]; GGParseIn.ReadWRope[s, "angle:"]; angle _ GGParseIn.ReadWReal[s]; GGParseIn.ReadWRope[s, "iterations:"]; maxIterations _ GGParseIn.ReadWNAT[s]; ggData.fitData.threshold _ CheckThreshold[threshold ! BadThreshold => GOTO Abort]; ggData.fitData.minDistance _ minDistance; ggData.fitData.polyPenalty _ polyPenalty; ggData.fitData.tolerance _ tolerance; ggData.fitData.angle _ angle; ggData.fitData.maxIterations _ maxIterations; ShowFitParameters[ggData, NIL]; EXITS Abort => { Feedback.Append[ggData.router, oneLiner, $Complaint, "Fit failed: fit threshold must be greater than 0.0 and less than 1.0"]; }; SyntaxError => { Feedback.Append[ggData.router, oneLiner, $Complaint, "Fit failed: syntax error while parsing Fit parameters: use exact format from ShowFitParameters"]; }; }; ShowFitParameters: UserInputProc = { rope: ROPE _ IO.PutFR["Fit: threshold: %g minDistance: %g polyPenalty: %g", [real[ggData.fitData.threshold]], [real[ggData.fitData.minDistance]], [real[ggData.fitData.polyPenalty]] ]; Feedback.PutF[ggData.router, oneLiner, $Show, "%g tolerance: %g angle: %g iterations: %g", [rope[rope]], [real[ggData.fitData.tolerance]], [real[ggData.fitData.angle]], [integer[ggData.fitData.maxIterations]]]; }; reallyBigReal: REAL = 1.0e37; SetFitTolerance: UserInputProc = { r: REAL _ NARROW[event.rest.first, REF REAL]^; IF r>reallyBigReal THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFit failed: select a reasonable value for fit tolerance"]; RETURN; }; ggData.fitData.tolerance _ r; ShowFitParameters[ggData, NIL]; }; SetFitMinDistance: UserInputProc = { r: REAL _ NARROW[event.rest.first, REF REAL]^; IF r>reallyBigReal THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFit failed: select a reasonable value for fit min distance"]; RETURN; }; ggData.fitData.minDistance _ r; ShowFitParameters[ggData, NIL]; }; SetFitIterations: UserInputProc = { i: INT _ NARROW[event.rest.first, REF INT]^; IF i=LAST[INT] THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFit failed: select a reasonable value for fit iterations"]; RETURN; }; ggData.fitData.maxIterations _ i; ShowFitParameters[ggData, NIL]; }; SetFitThreshold: UserInputProc = { threshold: REAL _ NARROW[event.rest.first, REF REAL]^; ggData.fitData.threshold _ CheckThreshold[threshold ! BadThreshold => GOTO Abort]; ShowFitParameters[ggData, NIL]; EXITS Abort => { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFit failed: fit threshold must be greater than 0.0 and less than 1.0"]; }; }; SetFitAngle: UserInputProc = { r: REAL _ NARROW[event.rest.first, REF REAL]^; IF r>reallyBigReal THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFit failed: select a reasonable value for fit angle"]; RETURN; }; ggData.fitData.angle _ r; ShowFitParameters[ggData, NIL]; }; SetFitPolyPenalty: UserInputProc = { r: REAL _ NARROW[event.rest.first, REF REAL]^; IF r>reallyBigReal THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFit failed: select a reasonable value for fit poly penalty"]; RETURN; }; ggData.fitData.polyPenalty _ r; ShowFitParameters[ggData, NIL]; }; SetCombine: UserInputProc ~ { IF PBasics.IsBound[LOOPHOLE[GeometricLogicOps.SetToFlatPath]] THEN { DoCombine: PROC [nextD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IsClosed: PROC [slice: Slice] RETURNS [BOOL] = { firstChild: Slice; RETURN[GGSliceOps.GetType[slice]#$Outline OR GGOutline.HasHoles[slice] OR (GGSliceOps.GetType[(firstChild _ GGParent.FirstChild[slice, first])]=$Traj AND NARROW[firstChild.data, TrajData].role#open)]; }; IsLineSegment: PROC [seg: Segment, transform: Transformation] RETURNS [keep: BOOL] = { keep _ seg.class.type=$Line; }; linesD: SliceDescriptor _ GGSliceOps.WalkSegments[nextD.slice, IsLineSegment]; IF GGSliceOps.IsCompleteParts[nextD] AND GGSliceOps.IsCompleteParts[linesD] AND IsClosed[nextD.slice] THEN { SlicePath: Imager.PathProc = { GGSliceOps.BuildPath[nextD.slice, NIL, NIL, moveTo, lineTo, curveTo, conicTo, arcTo]; }; nextSet _ GeometricLogicOps.MaskFill[SlicePath, FALSE, compositeScale]; opSet _ IF opSet=NIL THEN nextSet ELSE SELECT opAtom FROM $NOTs => GeometricLogicOps.NOTs[nextSet], $ANDs => GeometricLogicOps.ANDs[opSet, nextSet], $ORs => GeometricLogicOps.ORs[opSet, nextSet], $MINUSs => GeometricLogicOps.MINUSs[opSet, nextSet], $XORs => GeometricLogicOps.XORs[opSet, nextSet], ENDCASE => ERROR; lastD _ nextD; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "Combine failed: only complete closed objects composed solely of straight line segments are combined"]; }; lastD: SliceDescriptor; -- describes the last legitimate slice participating in Combine compositeScale: GeometricLogicOps.Scale _ NEW[GeometricLogicOps.ScaleRec]; nextSet, opSet: GeometricLogicOps.Set; opAtom: ATOM _ NARROW[event.rest.first]; -- what GeometricLogic operation to perform deleteAtom: ATOM _ NARROW[event.rest.rest.first]; -- optional delete of selected objects bBox: BoundBox; GGHistory.NewCapture["Geometric combine", ggData]; -- capture scene BEFORE UPDATE bBox _ GGScene.BoundBoxOfSelected[scene: ggData.scene, selectClass: normal, sliceLevel: TRUE]; compositeScale^ _ [minX: bBox.loX, minY: bBox.loY, maxX: bBox.hiX, maxY: bBox.hiY]; ggData.refresh.startBoundBox^ _ bBox^; [] _ GGScene.WalkSelectedSlices[ggData.scene, first, DoCombine, normal, $Outline]; [] _ GGScene.WalkSelectedSlices[ggData.scene, first, DoCombine, normal, $Box]; IF opSet#NIL THEN { SceneAction: PROC [context: Imager.Context] = { MaskSetProc: GeometricLogicOps.PathActionProc = { -- PROC [path: Imager.PathProc] Imager.MaskFill[context: context, path: path, oddWrap: FALSE]; }; Imager.SetStrokeWidth[context, 0.0]; Imager.SetColor[context, IF fillColor#NIL THEN fillColor ELSE GGState.GetDefaultFillColor[ggData] ]; GeometricLogicOps.SetToFlatPath[opSet, MaskSetProc]; }; fillColor: Imager.Color _ GGSliceOps.GetFillColor[lastD.slice, NIL].color; scene: Scene _ GGFromImager.Capture[action: SceneAction, camera: ggData.camera]; -- get a scene containing only the combined object IF deleteAtom=$DeleteSelected THEN [] _ GGScene.DeleteAllSelected[ggData.scene] ELSE GGSelect.DeselectAll[ggData.scene, normal]; GGSelect.SelectAll[scene, normal]; ggData.scene _ GGScene.MergeScenes[ggData.scene, scene]; -- is this all it takes ?? GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.PutF[ggData.router, oneLiner, $Feedback, "Combine: combined object added to scene"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "Combine failed: no legitimate selections for Combine"]; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "Combine failed: please install GeometricLogicOps, then retry this operation"]; }; RegisterAction: PROC [atom: ATOM, eventProc: GGUserInput.UserInputProc, argType: GGUserInput.ArgumentType, causeMouseEventsToComplete: BOOL _ TRUE, ensureUnique: BOOL _ TRUE] = GGUserInput.RegisterAction; RegisterEventProcs: PROC = { RegisterAction[$FitCurves, FitCurves, none]; RegisterAction[$ShowFitParameters, ShowFitParameters, none]; RegisterAction[$FitLines, FitLines, none]; RegisterAction[$FitTolerance, SetFitTolerance, refReal]; RegisterAction[$SetFitParameters, SetFitParameters, rope]; RegisterAction[$SetFitPolyPenalty, SetFitPolyPenalty, refReal]; RegisterAction[$SetFitAngle, SetFitAngle, refReal]; RegisterAction[$SetFitThreshold, SetFitThreshold, refReal]; RegisterAction[$SetFitIterations, SetFitIterations, refInt]; RegisterAction[$SetFitMinDistance, SetFitMinDistance, refReal]; RegisterAction[$SetCombine, SetCombine, none]; }; RegisterEventProcs[]; END. μGGEventImplG.mesa Copyright Σ 1986, 1987, 1988, 1989 by Xerox Corporation. All rights reserved. Bier, November 20, 1991 10:13 am PST Kurlander, July 21, 1987 10:52:27 am PDT Eisenman, August 11, 1987 5:36:57 pm PDT Pier, November 30, 1990 1:22 pm PST Maureen Stone, October 5, 1987 11:15:23 am PDT Doug Wyatt, December 7, 1989 3:14:41 pm PST Contents: stuff implemented in DCedar but not PCedar (yet) EdgeFit operations extracted from GGEventImplC.mesa SetCombine extracted from GGEventImplA.mesa EdgeFit (formerly in GGEventImplC) EdgeFit Menu Paint the object for feedback Here if you didn't get a syntax error SetCombine (formerly in GGEventImplA) SliceDescriptorWalkProc. Called for each selected slice. nextD should be a complete descriptor for closed objects containing only $Line segments. We verify: linesD is a descriptor for all the straight line segments in nextD.slice i.e. every part was selected and every segment is a line segment of a closed shape Registration EdgeFit Menu Κ–•NewlineDelimiter – "cedar" style˜codešœ™KšœN™NKšœ$™$Kšœ%Οk™(Kšœ%™(Kšœ#™#Kšœ.™.K™+—K™šΟnœ2™:Kšœ3™3Kšœ+™+K™—š ˜ Kšœ œ˜-Kšœ œ˜Kšœ œ ˜Kšœ œ ˜Kšœ œ?˜MKšœœ˜0Kšœœ`˜wKšœ œ ˜Kšœ œ ˜Kšœ œ˜*Kšœœ ˜ Kšœ œ;˜MKšœ œ˜*Kšœ œ˜Kšœ œ/˜>Kšœ œ˜(Kšœ˜KšœœT˜aKšœ œ˜'Kšœœ ˜Kšœ œ˜(Kšœ œP˜`Kšœœ˜$Kšœœ-˜9Kšœ œ/˜@Kšœ œ˜,Kšœœ[˜gKšœ œ˜.Kšœ œ˜!Kšœ œ=˜OKšœœ˜4Kšœœ˜,Kšœœ œœ˜Kšœœ˜$Kšœœ(˜5Kšœœ ˜Kšœ œ)˜;Kšœœ˜/Kšœœ˜Kšœœœ˜Kšœœ˜—K˜šž œœ˜Jšœ«œ6˜κKšœ˜ K˜Kšœ œ˜'Kšœœ˜'Kšœœ˜!Kšœœœ!˜ 7˜uKšœ œ<˜JKšœœ˜Kšœ*˜*Kšœœ ˜aKšœ˜Kšœ ˜ Kšœ ˜ Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜K–Q[screen: Imager.Context, ggData: GGInterfaceTypes.GGData, showColors: BOOL]˜[Kšœ&˜&K–[map: ImagerSample.SampleMap]šœ<œ œ˜OK˜—K˜%Kšœ ž œ ˜CKšœ /˜GKšœq˜qš˜Kšœ œ 1Οi˜OKšœ˜K˜šœœ˜5Kšœ_˜_—šœœœœ˜+Kšœœ˜K˜3K˜-Kšœ œœœœœœ˜2K˜—šœ œœœ˜*Kšœœ˜˜"Kšœœ˜*—K˜-Kšœ œœœœœœ˜2K˜—K˜Kšœ œ˜Kšœ)˜)šœœœ˜#Kšœ œ˜Kšœ* ˜Dšœœœ 3˜PK˜lKšœ˜K˜—Kšœ\ ˜ršœ œ˜šœœœ˜K˜tKšœ˜K˜—K–Q[screen: Imager.Context, ggData: GGInterfaceTypes.GGData, showColors: BOOL]˜gKšœ< )˜ešœœ˜#K˜&Kšœœ˜ Kšœœ œ˜—K˜;Kšœ1˜1K–Q[screen: Imager.Context, ggData: GGInterfaceTypes.GGData, showColors: BOOL]˜RK˜—šœ˜K˜šœ(˜(šœœ˜Kšœ˜Kšœ œœ ˜4šœ˜Kšœ4œ˜9Kšœ2˜2K˜—K˜K˜—K˜—šœœœ˜K˜mKšœ˜K˜—Kšœ3˜3K–Q[screen: Imager.Context, ggData: GGInterfaceTypes.GGData, showColors: BOOL]˜LK˜—Kšœ) #˜LKšœ0œ˜5Kšœ0˜0K™Kšœ)˜)KšœE˜EKšœeœœ˜ƒKšœ" ˜5K˜Kšœ˜—Kšœ˜—Kš‘œ  0˜PKšœ!˜!Kšœz˜zKšœpœœ˜ŽKšœ˜K˜—š ž œœœœœœ˜EKšœœœœœœœ˜}Kšœƒ˜ƒK˜K˜—šžœ˜Kšœœœ˜1Kšœ˜K˜—šž œ˜Kšœœœ˜0Kšœ˜K˜—šžœ˜$Kšœœ ˜0Kšœ7œ˜J˜—J–0[context: Imager.Context, strokeWidth: REAL]šœ$˜$K–7[context: Imager.Context, color: ImagerColor.Color]š œœ œœ œ'˜dJšœ ‘œ˜4J˜—J˜Jšœ?œ˜JJšœQ 2˜ƒK–K[scene: GGModelTypes.Scene, selectClass: GGSegmentTypes.SelectionClass]šœœ.œ,˜€Jšœ ž œ˜"Jšœ9 ˜SKšœ ‘ œ  0˜PJ–έ[feedback: Feedback.FeedbackData, msgType: Feedback.MsgType, format: ROPE _ NIL, v1: IO.Value _ [null[]], v2: IO.Value _ [null[]], v3: IO.Value _ [null[]], v4: IO.Value _ [null[]], v5: IO.Value _ [null[]]]šœ]˜]Jšœ}œœ˜›Kšœ˜—Kšœn˜rKšœ˜—Kšœ…˜‰K˜—K˜L™ Kšžœœœgœœœœ˜ΜK˜šžœœ˜K™ Kšœ,˜,Kšœ<˜