DIRECTORY Ascii, BasicTime, ColorTool, Feedback, GGCoreOps, Imager, ImagerColor, ImagerColorFns, ImagerColorPrivate, ImagerInterpress, IO, NamedColors, PrincOpsUtils, Rope, SV2d, SV3d, SVArtwork, SVAssembly, SVBasicTypes, SVCaret, SVCastRays, SVCoordSys, SVEditUser, SVEvent, SVInterfaceTypes, SVMasterObject, SVMasterObjectTypes, SVMatrix3d, SVModelTypes, SVPreprocess3d, SVRay, SVRefresh, SVScene, SVSceneToTree, SVSceneTypes, SVSelect, SVSelections, SVState, SVTransforms, SVUserInput, SVUtility, SVVector3d, SVViewersOnScene, SVViewerTools, SVWindow, TIPUser, ViewerClasses, ViewerTools; SVEventImplC: CEDAR PROGRAM IMPORTS Ascii, BasicTime, ColorTool, GGCoreOps, SVCoordSys, SVSceneToTree, Feedback, Imager, ImagerColor, ImagerColorFns, ImagerColorPrivate, ImagerInterpress, IO, SVMatrix3d, NamedColors, PrincOpsUtils, SVPreprocess3d, Rope, SVArtwork, SVAssembly, SVCaret, SVCastRays, SVEditUser, SVEvent, SVRay, SVRefresh, SVScene, SVSelect, SVSelections, SVState, SVTransforms, SVUtility, SVVector3d, SVViewersOnScene, SVViewerTools, SVWindow, ViewerTools EXPORTS SVEvent = BEGIN Artwork: TYPE = SVModelTypes.Artwork; ArtworkToolData: TYPE = SVInterfaceTypes.ArtworkToolData; Slice: TYPE = SVSceneTypes.Slice; SliceList: TYPE = SVSceneTypes.SliceList; BoundBox: TYPE = SVBasicTypes.BoundBox; Camera: TYPE = SVModelTypes.Camera; Classification: TYPE = SVSceneTypes.Classification; Color: TYPE = Imager.Color; CoordSystem: TYPE = SVModelTypes.CoordSystem; CSGTree: TYPE = SVSceneTypes.CSGTree; CylinderRec: TYPE = SVMasterObjectTypes.CylinderRec; EditToolData: TYPE = SVInterfaceTypes.EditToolData; FeedbackData: TYPE = Feedback.FeedbackData; FileCamera: TYPE = SVSceneTypes.FileCamera; FrameBox: TYPE = SVModelTypes.FrameBox; MasterObject: TYPE = SVSceneTypes.MasterObject; Matrix4by4: TYPE = SV3d.Matrix4by4; Point2d: TYPE = SV2d.Point2d; Point3d: TYPE = SV3d.Point3d; Primitive: TYPE = SVSceneTypes.Primitive; Ray: TYPE = SVSceneTypes.Ray; Scene: TYPE = SVSceneTypes.Scene; SearchDepth: TYPE = SVSceneTypes.SearchDepth; Selection: TYPE = SVInterfaceTypes.Selection; SelectionGenerator: TYPE = SVInterfaceTypes.SelectionGenerator; Shape: TYPE = SVSceneTypes.Shape; SliceDescriptor: TYPE = SVSceneTypes.SliceDescriptor; SliceDescriptorGenerator: TYPE = SVSceneTypes.SliceDescriptorGenerator; Skitter: TYPE = SVSceneTypes.Skitter; SkitterMode: TYPE = SVSceneTypes.SkitterMode; Sphere: TYPE = SV3d.Sphere; ToolData: TYPE = SVSceneTypes.ToolData; TrigLine: TYPE = SV2d.TrigLine; Vector3d: TYPE = SV3d.Vector3d; SVData: TYPE = SVInterfaceTypes.SVData; StrokeColorFromColorTool: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { IF ColorToolIsBound[svData] THEN StrokeColorAux[ColorTool.GetColor[], svData]; }; StrokeColorToColorTool: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { IF ColorToolIsBound[svData] THEN { color: Imager.Color; sliceDescGen: SliceDescriptorGenerator _ SVSelect.SelectedSlices[svData.scene, normal]; sliceD: SliceDescriptor _ SVSelect.NextSliceDescriptor[sliceDescGen]; IF sliceD=NIL THEN { Feedback.Append[svData.feedback, "Select exactly one segment for sending color to ColorTool", oneLiner]; } ELSE { color _ SVAssembly.GetStrokeColor[sliceD.slice, sliceD.parts]; IF color#NIL THEN ColorTool.SetColor[color, NIL] ELSE Feedback.Append[svData.feedback, ". . . Object has no stroke color", oneLiner]; }; }; }; PrintStrokeColor: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { FetchStrokeColor: PROC [sliceD: SliceDescriptor] RETURNS [color: Imager.ConstantColor] = { color _ NARROW[SVAssembly.GetStrokeColor[sliceD.slice, sliceD.parts].color]; }; PrintColorAux[svData.scene, svData.feedback, "Stroke", FetchStrokeColor]; }; StrokeColorNone: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { StrokeColorAux[NIL, svData]; }; StrokeColorGray: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { whiteness: REAL _ NARROW[event.rest.first, REF REAL]^; color: Imager.Color _ ImagerColor.ColorFromGray[1.0 - whiteness]; StrokeColorAux[color, svData]; }; StrokeColorFromSelectedName: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { name: Rope.ROPE _ IF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first]; color: Imager.Color _ ParseColorName[name, svData.feedback]; IF color#NIL THEN StrokeColorAux[color, svData]; }; StrokeColorFromSelectedRGB: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { name: Rope.ROPE _ IF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first]; color: Imager.Color _ ImagerColor.ColorFromRGB[RGBFromRope[name ! RGBFromRopeError => GOTO SyntaxError]]; StrokeColorAux[color, svData]; EXITS SyntaxError => {Feedback.Append[svData.feedback, "RGB Syntax is R: [0.0..1.0] G: [0.0..1.0] B: [0.0..1.0]", oneLiner]; Feedback.Blink[svData.feedback];}; }; StrokeColorAux: PROC [color: Imager.Color, svData: SVData] = { sliceDescGen: SliceDescriptorGenerator _ SVSelect.SelectedSlices[svData.scene, normal]; FOR sliceD: SliceDescriptor _ SVSelect.NextSliceDescriptor[sliceDescGen], SVSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO SVAssembly.SetStrokeColor[sliceD.slice, sliceD.parts, color]; ENDLOOP; SVWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, svData: svData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; ColorToolIsBound: PROC [svData: SVData] RETURNS [BOOL _ FALSE] = TRUSTED { IF PrincOpsUtils.IsBound[LOOPHOLE[ColorTool.GetRGBValue]] THEN RETURN[TRUE]; Feedback.Append[svData.feedback, "Please start ColorTool and retry this operation", oneLiner]; }; SetDefaultStrokeColor: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { sliceD: SliceDescriptor; sliceDescGen: SliceDescriptorGenerator _ SVSelect.SelectedSlices[svData.scene, normal]; IF sliceDescGen=NIL OR (sliceD _ SVSelect.NextSliceDescriptor[sliceDescGen])=NIL OR SVSelect.NextSliceDescriptor[sliceDescGen]#NIL THEN Feedback.Append[svData.feedback, "Select exactly one object for setting default line color", oneLiner] ELSE { isProcessBlack: Rope.ROPE; red, green, blue: REAL; color: Imager.ConstantColor _ NARROW[SVAssembly.GetStrokeColor[sliceD.slice, sliceD.parts]]; IF color#NIL THEN { [red,green,blue] _ GGCoreOps.ExtractRGB[color]; IF color = Imager.black THEN isProcessBlack _ " (process black)" ELSE IF ImagerColorPrivate.GrayFromColor[color]=1.0 THEN isProcessBlack _ " (CMY black)" ELSE isProcessBlack _ ""; Feedback.Append[ svData.feedback, IO.PutFR["Default Stroke Color: R: %1.3f G: %1.3f B: %1.3f CNS: %g%g", [real[red]], [real[green]], [real[blue]], [rope[NamedColors.HSLToRope[ImagerColorFns.HSLFromRGB[[red,green,blue]]]]], [rope[isProcessBlack]] ], oneLiner]; } ELSE Feedback.Append[svData.feedback, "Default Stroke Color: None", oneLiner]; svData.defaults.strokeColor _ color; }; }; ShowDefaultStrokeColor: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { isProcessBlack: Rope.ROPE; red, green, blue: REAL; color: Imager.ConstantColor _ NARROW[svData.defaults.strokeColor]; IF color#NIL THEN { [red,green,blue] _ GGCoreOps.ExtractRGB[color]; IF color = Imager.black THEN isProcessBlack _ " (process black)" ELSE IF ImagerColorPrivate.GrayFromColor[color]=1.0 THEN isProcessBlack _ " (CMY black)" ELSE isProcessBlack _ ""; Feedback.Append[ svData.feedback, IO.PutFR["Default Stroke Color: R: %1.3f G: %1.3f B: %1.3f CNS: %g%g", [real[red]], [real[green]], [real[blue]], [rope[NamedColors.HSLToRope[ImagerColorFns.HSLFromRGB[[red,green,blue]]]]], [rope[isProcessBlack]] ], oneLiner]; } ELSE Feedback.Append[svData.feedback, "Default Stroke Color: None", oneLiner]; }; AreaColorFromColorTool: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { IF ColorToolIsBound[svData] THEN AreaColorAux[ColorTool.GetColor[], svData]; }; AreaColorToColorTool: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { FetchFillColor: PROC [sliceD: SliceDescriptor] RETURNS [color: Imager.ConstantColor] = { color _ NARROW[SVAssembly.GetFillColor[sliceD.slice, sliceD.parts].color]; }; IF ColorToolIsBound[svData] THEN { color: Imager.Color; sliceDescGen: SliceDescriptorGenerator _ SVSelect.SelectedSlices[svData.scene, normal]; sliceD: SliceDescriptor _ SVSelect.NextSliceDescriptor[sliceDescGen]; IF sliceD=NIL THEN { Feedback.Append[svData.feedback, "Select exactly one slice for sending color to ColorTool", oneLiner]; } ELSE { color _ FetchFillColor[sliceD]; IF color#NIL THEN ColorTool.SetColor[color, NIL] ELSE Feedback.Append[svData.feedback, ". . . Object has no fill color", oneLiner]; }; }; }; PrintAreaColor: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { FetchFillColor: PROC [sliceD: SliceDescriptor] RETURNS [color: Imager.ConstantColor] = { color _ NARROW[SVAssembly.GetFillColor[sliceD.slice, sliceD.parts].color]; }; PrintColorAux[svData.scene, svData.feedback, "Fill", FetchFillColor]; }; ColorFetchProc: TYPE = PROC [sliceD: SliceDescriptor] RETURNS [color: Imager.ConstantColor]; PrintColorAux: PROC [scene: Scene, feedback: FeedbackData, opName: Rope.ROPE, colorFetch: ColorFetchProc] = { sliceD: SliceDescriptor; isProcessBlack: Rope.ROPE; sliceDescGen: SliceDescriptorGenerator _ SVSelect.SelectedSlices[scene, normal]; IF sliceDescGen=NIL OR (sliceD _ SVSelect.NextSliceDescriptor[sliceDescGen])=NIL OR SVSelect.NextSliceDescriptor[sliceDescGen]#NIL THEN Feedback.PutF[feedback, oneLiner, "Select exactly one object for Print%gColor", [rope[opName]]] ELSE { red, green, blue: REAL; color: Imager.ConstantColor _ colorFetch[sliceD]; IF color#NIL THEN { [red,green,blue] _ GGCoreOps.ExtractRGB[color]; IF color = Imager.black THEN isProcessBlack _ " (process black)" ELSE IF ImagerColorPrivate.GrayFromColor[color]=1.0 THEN isProcessBlack _ " (CMY black)" ELSE isProcessBlack _ ""; Feedback.Append[ feedback, IO.PutFR["R: %1.3f G: %1.3f B: %1.3f CNS: %g%g", [real[red]], [real[green]], [real[blue]], [rope[NamedColors.HSLToRope[ImagerColorFns.HSLFromRGB[[red,green,blue]]]]], [rope[isProcessBlack]] ], oneLiner]; } ELSE Feedback.PutF[feedback, oneLiner, "No %g Color", [rope[opName]]]; }; }; ParseColorName: PROC [name: Rope.ROPE, feedback: FeedbackData] RETURNS [color: Imager.Color _ NIL] ~ { IF Rope.Match["*/*", name] THEN { --try for a registered name color _ ImagerColor.Find[name]; IF color=NIL THEN color _ ImagerColor.Find[Rope.Cat["Xerox/Research/", name]]; IF color=NIL THEN GOTO UnregisteredName; } ELSE color _ ImagerColor.ColorFromRGB[ImagerColorFns.RGBFromHSL[NamedColors.RopeToHSL[name ! NamedColors.UndefinedName => GOTO UndefinedName; NamedColors.BadGrammar => GOTO BadGrammar; ]]]; EXITS UnregisteredName => {Feedback.Append[feedback, "Hierarchical name not registered", oneLiner]; Feedback.Blink[feedback];}; UndefinedName => {Feedback.Append[feedback, "Undefined Color Name", oneLiner]; Feedback.Blink[feedback];}; BadGrammar => {Feedback.Append[feedback, "Bad Color Name", oneLiner]; Feedback.Blink[feedback];}; }; AreaColorAux: PROC [color: Imager.Color, svData: SVData] = { artwork: Artwork _ SVArtwork.CreateColorArtwork[color, chalk]; sliceDescGen: SliceDescriptorGenerator _ SVSelect.SelectedSlices[svData.scene, normal]; FOR sliceD: SliceDescriptor _ SVSelect.NextSliceDescriptor[sliceDescGen], SVSelect.NextSliceDescriptor[sliceDescGen] UNTIL sliceD=NIL DO SVAssembly.SetFillColor[sliceD.slice, sliceD.parts, artwork]; ENDLOOP; SVWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedInPlace, svData: svData, remake: none, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; }; AreaColorNone: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { AreaColorAux[NIL, svData]; }; AreaColorGray: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { whiteness: REAL _ NARROW[event.rest.first, REF REAL]^; color: Imager.Color _ ImagerColor.ColorFromGray[1.0 - whiteness]; AreaColorAux[color, svData]; }; AreaColorFromSelectedName: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { name: Rope.ROPE _ IF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first]; color: Imager.Color _ ParseColorName[name, svData.feedback]; IF color#NIL THEN AreaColorAux[color, svData]; }; AreaColorFromSelectedRGB: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { name: Rope.ROPE _ IF event.rest = NIL THEN ViewerTools.GetSelectionContents[] ELSE NARROW[event.rest.first]; color: Imager.Color _ ImagerColor.ColorFromRGB[RGBFromRope[name ! RGBFromRopeError => GOTO SyntaxError]]; AreaColorAux[color, svData]; EXITS SyntaxError => {Feedback.Append[svData.feedback, "RGB Syntax is R: [0.0..1.0] G: [0.0..1.0] B: [0.0..1.0]", oneLiner]; Feedback.Blink[svData.feedback];}; }; RGBFromRopeError: SIGNAL = CODE; RGBFromRope: PROC [name: Rope.ROPE] RETURNS [rgb: ImagerColor.RGB] = { ENABLE IO.Error, IO.EndOfStream => GOTO RGBError; Check: PROC [x: REAL] = { IF x NOT IN [0.0..1.0] THEN SIGNAL RGBFromRopeError; }; rs: IO.STREAM _ IO.RIS[name]; IF Ascii.Upper[rs.GetChar[]]#'R THEN GOTO RGBError; IF rs.GetChar[]#': THEN GOTO RGBError; rgb.R _ rs.GetReal[]; [] _ rs.SkipWhitespace[]; IF Ascii.Upper[rs.GetChar[]]#'G THEN GOTO RGBError; IF rs.GetChar[]#': THEN GOTO RGBError; rgb.G _ rs.GetReal[]; [] _ rs.SkipWhitespace[]; IF Ascii.Upper[rs.GetChar[]]#'B THEN GOTO RGBError; IF rs.GetChar[]#': THEN GOTO RGBError; rgb.B _ rs.GetReal[]; Check[rgb.R]; Check[rgb.G]; Check[rgb.B]; EXITS RGBError => SIGNAL RGBFromRopeError; }; SetDefaultFillColor: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { sliceD: SliceDescriptor; sliceDescGen: SliceDescriptorGenerator _ SVSelect.SelectedSlices[svData.scene, normal]; IF sliceDescGen=NIL OR (sliceD _ SVSelect.NextSliceDescriptor[sliceDescGen])=NIL OR SVSelect.NextSliceDescriptor[sliceDescGen]#NIL THEN Feedback.Append[svData.feedback, "Select exactly one filled object for setting default fill color", oneLiner] ELSE { isProcessBlack: Rope.ROPE; red, green, blue: REAL; artwork: Artwork; artwork _ SVAssembly.GetFillColor[sliceD.slice, sliceD.parts]; IF artwork#NIL AND artwork.class = justColor THEN { color: Imager.ConstantColor _ NARROW[artwork.color]; [red,green,blue] _ GGCoreOps.ExtractRGB[color]; IF color = Imager.black THEN isProcessBlack _ " (process black)" ELSE IF ImagerColorPrivate.GrayFromColor[color]=1.0 THEN isProcessBlack _ " (CMY black)" ELSE isProcessBlack _ ""; Feedback.Append[ svData.feedback, IO.PutFR["Default Fill Color: R: %1.3f G: %1.3f B: %1.3f CNS: %g%g", [real[red]], [real[green]], [real[blue]], [rope[NamedColors.HSLToRope[ImagerColorFns.HSLFromRGB[[red,green,blue]]]]], [rope[isProcessBlack]] ], oneLiner]; } ELSE Feedback.Append[svData.feedback, "Default Fill Color: None", oneLiner]; svData.defaults.fillColor _ artwork; }; }; ShowDefaultFillColor: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { isProcessBlack: Rope.ROPE; red, green, blue: REAL; color: Imager.ConstantColor _ NARROW[svData.defaults.fillColor.color]; IF color#NIL THEN { [red,green,blue] _ GGCoreOps.ExtractRGB[color]; IF color = Imager.black THEN isProcessBlack _ " (process black)" ELSE IF ImagerColorPrivate.GrayFromColor[color]=1.0 THEN isProcessBlack _ " (CMY black)" ELSE isProcessBlack _ ""; Feedback.Append[ svData.feedback, IO.PutFR["Default Fill Color: R: %1.3f G: %1.3f B: %1.3f CNS: %g%g", [real[red]], [real[green]], [real[blue]], [rope[NamedColors.HSLToRope[ImagerColorFns.HSLFromRGB[[red,green,blue]]]]], [rope[isProcessBlack]] ], oneLiner]; } ELSE Feedback.Append[svData.feedback, "Default Fill Color: None", oneLiner]; }; SelectAll: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { editToolData: EditToolData _ svData.editToolData; scene: Scene _ svData.scene; SVSelect.DeselectAll[svData.scene, normal]; SVSelect.SelectAll[svData.scene, normal]; SVViewersOnScene.PaintSceneAllViewers[paintAction: $SelectionChanged, editToolData: editToolData, scene: scene, edited: FALSE]; }; pointsPerIn: REAL = 72.0; pointsPerCm: REAL = 72.0/2.54; cmPerIn: REAL = 2.54; ScaleUnitFromSegment: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { }; ScaleUnitFromValue: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { }; ScaleUnitFromSelection: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { }; InchScaleUnit: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { ScaleUnitAux[svData, pointsPerIn]; -- one inch in screen dots }; ScaleUnitAux: PROC [svData: SVData, distance: REAL] = { SVState.SetScaleUnit[svData, distance]; -- in screen dots PrintScaleUnit[event: NIL, svData: svData]; SVWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, svData: svData, remake: alignBag, backgndOK: TRUE, edited: FALSE, okToClearFeedback: TRUE]; }; CentimeterScaleUnit: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { ScaleUnitAux[svData, pointsPerCm]; }; PointsScaleUnit: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { ScaleUnitAux[svData, 1]; }; PrintScaleUnit: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { inches: REAL _ SVState.GetScaleUnit[svData]; Feedback.PutF[svData.feedback, oneLiner, "Current scale is %g points = %g inches = %g centimeters", [real[inches*pointsPerIn]], [real[inches]], [real[inches/cmPerIn]] ]; }; IPSnapShot: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { DoMakeInterpress: PROC [dc: Imager.Context] = { ENABLE UNWIND => { svData.camera.displayStyle _ tempStyle; }; DoItInterpress: PROC = { Imager.ScaleT[dc, metersPerPixel]; svData.camera.displayStyle _ print; SVRefresh.SnapShot[dc, svData]; svData.camera.displayStyle _ tempStyle; }; Imager.DoSave[dc, DoItInterpress]; }; ipRef: ImagerInterpress.Ref; fullName: Rope.ROPE; success: BOOL; metersPerPixel: REAL = 0.0254/72.0; startTime: BasicTime.GMT; endTime: BasicTime.GMT; totalTime: INT; msgRope: Rope.ROPE; tempStyle: SVModelTypes.DisplayStyle _ svData.camera.displayStyle; [fullName, success] _ SVUtility.GetInterpressFileName["snapshot.ip", svData.currentWDir, svData.feedback]; IF NOT success THEN RETURN; ipRef _ ImagerInterpress.Create[fullName]; msgRope _ IO.PutFR["Writing to IP file: %g . . . ", [rope[fullName]]]; Feedback.Append[svData.feedback, msgRope, begin]; startTime _ BasicTime.Now[]; ImagerInterpress.DoPage[ipRef, DoMakeInterpress, 1.0]; ImagerInterpress.Close[ipRef]; endTime _ BasicTime.Now[]; totalTime _ BasicTime.Period[startTime, endTime]; msgRope _ IO.PutFR[" Done in time (%r)", [integer[totalTime]]]; Feedback.Append[svData.feedback, msgRope, end]; }; JackPivotX: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { JackPivotAux[event, svData, 1]; }; JackPivotY: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { JackPivotAux[event, svData, 2]; }; JackPivotZ: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { JackPivotAux[event, svData, 3]; }; JackPivotAux: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData, axis: [1..3]] = { editToolData: EditToolData _ svData.editToolData; scene: Scene _ svData.scene; degrees: REAL _ NARROW[event.rest.first, REF REAL]^; targetSel: Selection; coincident: Slice; targetSel _ SVSelections.TopTarget[]; IF targetSel = NIL THEN RETURN; coincident _ targetSel.coincident; SVTransforms.Rotate[coincident, scene, axis, degrees, coincident.coordSys, TRUE]; SVViewersOnScene.PaintSceneAllViewers[paintAction: $SelectionChanged, editToolData: editToolData, scene: scene, edited: TRUE]; }; SourcePivotX: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { SourcePivotAux[event, svData, 1]; }; SourcePivotY: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { SourcePivotAux[event, svData, 2]; }; SourcePivotZ: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { SourcePivotAux[event, svData, 3]; }; SourcePivotAux: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData, axis: [1..3]] = { editToolData: EditToolData _ svData.editToolData; scene: Scene _ svData.scene; degrees: REAL _ NARROW[event.rest.first, REF REAL]^; sourceSel: Selection; jack, indirect: Slice; sourceSel _ SVSelections.TopMovee[]; IF sourceSel = NIL THEN RETURN; SELECT sourceSel.referentType FROM hook => { jack _ sourceSel.coincident; indirect _ sourceSel.indirect; }; coordSys => { jack _ sourceSel.coincident; indirect _ sourceSel.coincident; }; ENDCASE => ERROR; SVViewersOnScene.SceneNewVersion[editToolData.currentSVData]; SVTransforms.Rotate[indirect, scene, axis, degrees, jack.coordSys, TRUE]; svData.refresh.addedObject _ indirect; SVViewersOnScene.PaintSceneAllViewers[$ObjectChangedBoundBoxProvided, editToolData, scene]; }; ArrowShoot: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { source, target: Slice; sourceSel, firstTargetSel: Selection; editToolData: EditToolData; sourceSel _ SVSelections.PopMovee[]; IF sourceSel = NIL THEN RETURN; source _ sourceSel.coincident; svData _ sourceSel.svData; editToolData _ svData.editToolData; firstTargetSel _ SVSelections.PopTarget[]; IF firstTargetSel = NIL THEN RETURN; FOR targetSel: Selection _ firstTargetSel, SVSelections.NextTarget[] UNTIL targetSel = NIL DO IF targetSel.svData # sourceSel.svData THEN { Feedback.Append[svData.feedback, "Can't shoot arrows between viewers.", oneLiner]; Feedback.Blink[svData.feedback]; LOOP; }; target _ targetSel.coincident; ArrowShootAux[source.coordSys, target.coordSys, svData]; ENDLOOP; SVViewersOnScene.SceneNewVersion[editToolData.currentSVData]; SVViewersOnScene.PaintSceneAllViewers[$PaintEntireScene, svData.editToolData, svData.scene]; }; ArrowShootAux: PROC [sourceCS, targetCS: CoordSystem, svData: SVData] = { sOriginWorld, tOriginWorld: Point3d; tree: CSGTree; ray: Ray; class: Classification; t: REAL; worldDirection, primitiveNormal, worldNormal: Vector3d; selectionMat: Matrix4by4; surfacePtInWorld: Point3d; primitive: Primitive; assembly: Slice; scene: Scene; camera: Camera; skitter: Skitter _ svData.editToolData.skitter; scene _ svData.scene; camera _ svData.camera; tree _ SVSceneToTree.AssemblyToTree[scene.assembly, scene, camera]; [] _ SVPreprocess3d.PreprocessForCatScan[tree, camera]; sOriginWorld _ SVMatrix3d.OriginOfMatrix[SVCoordSys.WRTWorld[sourceCS]]; tOriginWorld _ SVMatrix3d.OriginOfMatrix[SVCoordSys.WRTWorld[targetCS]]; ray _ SVRay.GetRayFromPool[]; worldDirection _ SVVector3d.Sub[tOriginWorld, sOriginWorld]; SVRay.StuffWorldRay[ray, sOriginWorld, worldDirection, camera]; class _ SVCastRays.RayCastBoundingSpheres[ray, tree.son]; FOR i: NAT IN [1..class.count] DO t _ class.params[i]; -- the parameter of the ray intersection primitiveNormal _ class.normals[i]; primitive _ class.primitives[i]; worldNormal _ SVMatrix3d.UpdateVectorWithInverse[primitive.worldWRTPrim, primitiveNormal]; surfacePtInWorld[1] _ sOriginWorld[1] + t*worldDirection[1]; surfacePtInWorld[2] _ sOriginWorld[2] + t*worldDirection[2]; surfacePtInWorld[3] _ sOriginWorld[3] + t*worldDirection[3]; assembly _ NARROW[primitive.assembly]; selectionMat _ MakeAlignedMat[worldNormal, surfacePtInWorld, assembly.coordSys]; SVCaret.SetAssemblyAndPrimitive[skitter, assembly, primitive]; SVCaret.PositionFromMatrix[skitter, [0,0], selectionMat]; SVSelections.SetModeSkitter[svData, surface]; SVEvent.SkitterMakes[NIL, svData]; ENDLOOP; SVCastRays.ReturnClassToPool[class]; SVRay.ReturnRayToPool[ray]; }; Sign: PROC [r: REAL] RETURNS [INT] = { IF r = 0.0 THEN RETURN[2]; IF r < 0.0 THEN RETURN[-1] ELSE RETURN[1]; }; AntiParallel: PRIVATE PROC [v1, v2: Vector3d] RETURNS [BOOL] = { RETURN[Sign[v1[1]] = -Sign[v2[1]] OR Sign[v1[2]] = -Sign[v2[2]] OR Sign[v1[3]] = -Sign[v2[3]] ]; }; MakeAlignedMat: PRIVATE PROC [worldNormal: Vector3d, surfacePtInWorld: Point3d, cs: CoordSystem] RETURNS [mat: Matrix4by4] = { yAxisOfCS: Vector3d _ SVMatrix3d.YAxisOfMatrix[SVCoordSys.WRTWorld[cs]]; xAxis: Vector3d; IF SVVector3d.Parallel[yAxisOfCS, worldNormal] THEN { xAxis _ SVMatrix3d.XAxisOfMatrix[SVCoordSys.WRTWorld[cs]]; IF AntiParallel[yAxisOfCS, worldNormal] THEN xAxis _ SVVector3d.Negate[xAxis]; } ELSE xAxis _ SVVector3d.CrossProduct[yAxisOfCS, worldNormal]; mat _ SVMatrix3d.MakeMatFromZandXAxis[worldNormal, xAxis, surfacePtInWorld]; }; MoveUntilTouch: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { source, target: Slice; sourceSel, targetSel: Selection; editToolData: EditToolData; sourceSel _ SVSelections.PopMovee[]; IF sourceSel = NIL THEN RETURN; source _ sourceSel.coincident; svData _ sourceSel.svData; editToolData _ svData.editToolData; targetSel _ SVSelections.PopTarget[]; IF targetSel = NIL THEN RETURN; IF targetSel.svData # sourceSel.svData THEN { Feedback.Append[svData.feedback, "Can't skewer between viewers.", oneLiner]; Feedback.Blink[svData.feedback]; RETURN; }; target _ targetSel.coincident; MoveUntilTouchAux[source, target, svData]; SVViewersOnScene.SceneNewVersion[editToolData.currentSVData]; SVViewersOnScene.PaintSceneAllViewers[$PaintEntireScene, svData.editToolData, svData.scene]; }; MoveUntilTouchAux: PROC [source, target: Slice, svData: SVData] = { sOriginWorld, tOriginWorld: Point3d; tree, sourceTree: CSGTree; worldDirection: Vector3d; scene: Scene; camera: Camera; boundSphere: Sphere; R: REAL; basisMat: Matrix4by4; scene _ svData.scene; camera _ svData.camera; sourceTree _ SVSceneToTree.AssemblyToTree[source, scene, camera]; boundSphere _ SVPreprocess3d.PreprocessForCatScan[sourceTree, camera]; R _ boundSphere.radius; tree _ SVSceneToTree.AssemblyToTree[scene.assembly, scene, camera]; [] _ SVPreprocess3d.PreprocessForCatScan[tree, camera]; sOriginWorld _ SVMatrix3d.OriginOfMatrix[SVCoordSys.WRTWorld[source.coordSys]]; tOriginWorld _ SVMatrix3d.OriginOfMatrix[SVCoordSys.WRTWorld[target.coordSys]]; worldDirection _ SVVector3d.Sub[tOriginWorld, sOriginWorld]; basisMat _ SVMatrix3d.MakeHorizontalMatFromZAxis[worldDirection, sOriginWorld]; MoveUntilTouchAuxAux[source, basisMat, worldDirection, R, scene, camera, tree]; }; -- end of MoveUntilTouchAux MoveUntilTouchAuxAux: PROC [source: Slice, basisMat: Matrix4by4, worldDirection: Vector3d, R: REAL, scene: Scene, camera: Camera, tree: CSGTree] = { ray: Ray; minDeltaT, lastSourceT, firstOtherT: REAL; class: Classification; success, someData: BOOL; basisBasePt, worldBasePt: Point3d; moveVector: Vector3d; raysPerHalfSide: NAT = 20; raysPerHalfSideF: REAL = 20.0; ray _ SVRay.GetRayFromPool[]; someData _ FALSE; FOR i: INT IN [-raysPerHalfSide..raysPerHalfSide-1] DO basisBasePt[1] _ (i+0.5)*R/raysPerHalfSideF; FOR j: INT IN [-raysPerHalfSide..raysPerHalfSide-1] DO basisBasePt[2] _ (j+0.5)*R/raysPerHalfSideF; basisBasePt[3] _ 0.0; worldBasePt _ SVMatrix3d.Update[basisBasePt, basisMat]; SVRay.StuffWorldRay[ray, worldBasePt, worldDirection, camera]; class _ SVCastRays.RayCastBoundingSpheres[ray, tree.son, FALSE]; [lastSourceT, firstOtherT, success] _ SourceAndOtherT[class, source]; IF NOT success THEN { SVCastRays.ReturnClassToPool[class]; LOOP; }; IF firstOtherT < lastSourceT THEN { Feedback.AppendRaw[$Solidviews, "Source is already touching.", oneLiner]; Feedback.BlinkRaw[$Solidviews]; RETURN; }; IF NOT someData THEN { minDeltaT _ firstOtherT-lastSourceT; someData _ TRUE; } ELSE minDeltaT _ MIN[minDeltaT, firstOtherT-lastSourceT]; SVCastRays.ReturnClassToPool[class]; ENDLOOP; -- j ENDLOOP; -- i IF NOT someData THEN { Feedback.AppendRaw[$Solidviews, "No Obstacles Found. Object NOT moved.", oneLiner]; Feedback.BlinkRaw[$Solidviews]; RETURN; }; moveVector _ SVVector3d.Scale[worldDirection, minDeltaT]; SVTransforms.Translate[source, scene, moveVector]; SVRay.ReturnRayToPool[ray]; }; SourceAndOtherT: PROC [class: Classification, source: Slice] RETURNS [lastSourceT, firstOtherT: REAL, success: BOOL _ FALSE] = { sourceFound, otherFound: BOOL; thisAssembly: Slice; sourceFound _ FALSE; otherFound _ FALSE; IF class.count = 0 THEN RETURN [0,0,FALSE]; FOR i: NAT IN [1..class.count] DO thisAssembly _ NARROW[class.primitives[i].assembly]; IF SVSceneToTree.IsSuccessorOf[thisAssembly, source] THEN { lastSourceT _ class.params[i]; sourceFound _ TRUE; } ELSE IF NOT otherFound THEN { firstOtherT _ class.params[i]; otherFound _ TRUE; }; ENDLOOP; success _ sourceFound AND otherFound; }; Skewer: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { source, target: Slice; sourceSel, firstTargetSel: Selection; editToolData: EditToolData; sourceSel _ SVSelections.PopMovee[]; IF sourceSel = NIL THEN RETURN; source _ sourceSel.coincident; svData _ sourceSel.svData; editToolData _ svData.editToolData; firstTargetSel _ SVSelections.PopTarget[]; IF firstTargetSel = NIL THEN RETURN; IF firstTargetSel.svData # sourceSel.svData THEN { Feedback.Append[svData.feedback, "Can't skewer between viewers.", oneLiner]; Feedback.Blink[svData.feedback]; RETURN; }; target _ firstTargetSel.coincident; SkewerAux[source, target, svData]; SVViewersOnScene.SceneNewVersion[editToolData.currentSVData]; SVViewersOnScene.PaintSceneAllViewers[$PaintEntireScene, svData.editToolData, svData.scene]; }; SkewerAux: PROC [source, target: Slice, svData: SVData] = { sOriginWorld, tOriginWorld: Point3d; tree: CSGTree; ray: Ray; class: Classification; t: REAL; directionWorld: Vector3d; surfacePtInWorld, lastSurfacePtInWorld: Point3d; primitive: Primitive; assembly, lastAssembly: Slice; moveVector, totalVector, localDiff: Vector3d; scene: Scene; camera: Camera; sourceCS, targetCS: CoordSystem; sourceCS _ source.coordSys; targetCS _ target.coordSys; scene _ svData.scene; camera _ svData.camera; tree _ SVSceneToTree.AssemblyToTree[scene.assembly, scene, camera]; [] _ SVPreprocess3d.PreprocessForCatScan[tree, camera]; sOriginWorld _ SVMatrix3d.OriginOfMatrix[SVCoordSys.WRTWorld[sourceCS]]; tOriginWorld _ SVMatrix3d.OriginOfMatrix[SVCoordSys.WRTWorld[targetCS]]; ray _ SVRay.GetRayFromPool[]; directionWorld _ SVVector3d.Sub[tOriginWorld, sOriginWorld]; sOriginWorld _ SVVector3d.Sub[sOriginWorld, SVVector3d.Scale[directionWorld, 0.3]]; directionWorld _ SVVector3d.Scale[directionWorld, 1.6]; SVRay.StuffWorldRay[ray, sOriginWorld, directionWorld, camera]; class _ SVCastRays.RayCastBoundingSpheres[ray, tree.son, FALSE]; SVCastRays.SortClassByPrimitive[class]; IF class.count = 0 THEN { SVCastRays.ReturnClassToPool[class]; SVRay.ReturnRayToPool[ray]; RETURN; }; lastAssembly _ NARROW[class.primitives[1].assembly]; t _ class.params[1]; lastSurfacePtInWorld[1] _ sOriginWorld[1] + t*directionWorld[1]; lastSurfacePtInWorld[2] _ sOriginWorld[2] + t*directionWorld[2]; lastSurfacePtInWorld[3] _ sOriginWorld[3] + t*directionWorld[3]; totalVector _ [0,0,0]; FOR i: NAT IN [2..class.count] DO t _ class.params[i]; -- the parameter of the ray intersection IF t > 1.0 THEN EXIT; -- Compression occurs only BETWEEN source and target. primitive _ class.primitives[i]; surfacePtInWorld[1] _ sOriginWorld[1] + t*directionWorld[1]; surfacePtInWorld[2] _ sOriginWorld[2] + t*directionWorld[2]; surfacePtInWorld[3] _ sOriginWorld[3] + t*directionWorld[3]; assembly _ NARROW[primitive.assembly]; IF lastAssembly # assembly THEN { localDiff _ SVVector3d.Sub[lastSurfacePtInWorld, surfacePtInWorld]; moveVector _ SVVector3d.Add[localDiff, totalVector]; totalVector _ SVVector3d.Add[totalVector, localDiff]; SVTransforms.Translate[assembly, scene, moveVector]; }; lastAssembly _ assembly; lastSurfacePtInWorld _ surfacePtInWorld; ENDLOOP; SVCastRays.ReturnClassToPool[class]; SVRay.ReturnRayToPool[ray]; }; AddCylinder: PUBLIC PROC [event: LIST OF REF ANY, svData: SVData] = { sourceSel, firstTargetSel: Selection; sourceCS, targetCS: CoordSystem; editToolData: EditToolData; radius: REAL; scene: Scene; sourceSel _ SVSelections.PopMovee[]; IF sourceSel = NIL THEN RETURN; firstTargetSel _ SVSelections.PopTarget[]; IF firstTargetSel = NIL THEN RETURN; IF sourceSel.svData # firstTargetSel.svData THEN { Feedback.Append[svData.feedback, "Can't add cylinder between viewers.", oneLiner]; Feedback.Blink[svData.feedback]; RETURN; }; svData _ sourceSel.svData; scene _ svData.scene; editToolData _ svData.editToolData; sourceCS _ sourceSel.coincident.coordSys; radius _ SVViewerTools.GetReal[editToolData.cylinderSection.radius, 20]; FOR targetSel: Selection _ firstTargetSel, SVSelections.NextTarget[] UNTIL targetSel = NIL DO targetCS _ targetSel.coincident.coordSys; AddCylinderAux[sourceCS, targetCS, radius, editToolData, scene]; ENDLOOP; SVViewersOnScene.SceneNewVersion[editToolData.currentSVData]; SVViewersOnScene.PaintSceneAllViewers[$PaintEntireScene, editToolData, svData.scene]; }; AddCylinderAux: PROC [sourceCS, targetCS: CoordSystem, radius: REAL, editToolData: EditToolData, scene: Scene] = { cylMO: MasterObject; moFound: BOOL; cylinderRec: CylinderRec; sOriginWorld, tOriginWorld, midPointWorld: Point3d; cylWorld, superWorld, cylSuper: Matrix4by4; cylinderY: Vector3d; height: REAL; newAssembly, superAssembly: Slice; success: BOOL; addSucceeds: BOOL _ TRUE; cylName: Rope.ROPE; [cylMO, moFound] _ SVScene.FindObjectFromName["cylinder", scene]; IF NOT moFound THEN ERROR; cylinderRec _ NARROW[cylMO.mainBody]; sOriginWorld _ SVMatrix3d.OriginOfMatrix[SVCoordSys.WRTWorld[sourceCS]]; tOriginWorld _ SVMatrix3d.OriginOfMatrix[SVCoordSys.WRTWorld[targetCS]]; midPointWorld _ SVVector3d.Add[sOriginWorld, tOriginWorld]; midPointWorld _ SVVector3d.Scale[midPointWorld, 0.5]; cylinderY _ SVVector3d.Sub[tOriginWorld, sOriginWorld]; height _ SVVector3d.Magnitude[cylinderY]; cylWorld _ MakeHorizontalMatFromYAxis[cylinderY, midPointWorld]; radius _ radius/cylinderRec.radius; height _ height/cylinderRec.height; cylName _ ViewerTools.GetContents[editToolData.sceneSection.new]; cylName _ SVCoordSys.UniqueNameFrom[cylName, scene.coordSysRoot]; [superAssembly, success] _ SVEditUser.GetParent[editToolData]; IF NOT success THEN RETURN; superWorld _ SVCoordSys.WRTWorld[superAssembly.coordSys]; cylSuper _ SVMatrix3d.AInTermsOfB[cylWorld, superWorld]; [newAssembly, ----, addSucceeds] _ SVAssembly.CreatePrimitive[cylName, "cylinder", scene]; IF NOT addSucceeds THEN RETURN; addSucceeds _ SVAssembly.AddPrimitive[newAssembly, [radius, height, radius], superAssembly, cylSuper, scene]; }; -- end of AddCylinderAux MakeHorizontalMatFromYAxis: PROC [yAxis: Vector3d, origin: Point3d] RETURNS [mat: Matrix4by4] = { xAxis: Vector3d; IF yAxis[1] = 0 AND yAxis[3] = 0 THEN xAxis _ [1,0,0] ELSE xAxis _ SVVector3d.CrossProduct[[0,1,0], yAxis]; mat _ SVMatrix3d.MakeMatFromYandXAxis[yAxis, xAxis, origin]; }; END. ΘFile: SVEventImplC.mesa Last edited by Bier on September 24, 1987 1:40:32 pm PDT Copyright c 1984 by Xerox Corporation. All rights reserved. Contents: Procedures for responding to button clicks made in a solidviewer. Bloomenthal, June 16, 1987 7:52:46 pm PDT Stroke Color Menu Fill Color Menu Select Menu Units Menu TIP Table Functions This command is executed in the middle of a dragging operation to make an interpress master of the current state of the screen. Luckily, all dragging operations use the same painting commands, namely: SVWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, svData: svData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; We wish to have the same effect as these commands except written to an interpress master. We will create an interpress master named snapshot.ip and use SVRefresh.SnapShot to draw the scene into it. Rotate the distinguished target coincident with respect to itself by degrees. This makes sense for both hook and coordsys targets. Erases the target shape. For a jack, this leaves nothing. For another object, this leaves the object. Rotate the distinguished source indirect with respect to the distinguished source coincident. If the selection is not a hook jack, then indirect and coincident are identical. Cast a ray from the distinguished source to each of the targets. Create a hook at each intersection point, aligned with the surface normal at that point. Cast a ray from source to target. Create a hook at each intersection point, aligned with the surface normal at that point. Create a Matrix4by4 with origin at surfacePtInWorld whose z axis is parallel to worldNormal and whose x zxis is orthogonal to both worldNormal and the y axis of cs in WORLD coordinates. Assume that SVCoordSys.WRTWorld[cs] is accurate Allows positioning code to distinguish between top surfaces and bottom surfaces. Consider a line from the distinguished source to the distinguished target. Using translation along the ray direction (only) move the distinguish source towards the distinguished target until it touches something other than itself. If the distinguished source already touches something else, leave it alone. Cast a set of rays parallel to the line from source to target and starting on a plane normal to that line a bounding sphere's radius behind source. Using translation along the ray direction (only) move the source until it touches something (anything). Shoot many rays in this direction starting from the plane of points x satisfying (x-sOriginWorld).w = 0. First, let's find two basis vectors for this plane. They are the x and y axes of the basis matrix. Shoot a ray from the distinguished source to the distinguished target. Using translation along the ray direction (only) bring all intersected objects together so they are touching at the ray intersection points. Cast a ray from source to target. Using translation along the ray direction (only) bring all intersected objects together so they are touching at the ray intersection points. The skewer extends from the earliest point hit on the source object (by the infinite line) to the last point hit on the target object. For now, a kludge: Cast a ray from 30% behind the source to 30% after the target. Here comes the kludge End of kludge. Add a cylinder whose radius is taken from the editTool and whose height is just large enough so it stretches from the distinguished source to the distinguished target. Add a cylinder whose radius is taken from the editTool and whose height is just large enough so it stretches from sourceCS to targetCS. Uses yAxis as it is. Finds a horizontal x axis orthogonal to zAxis. If yAxis is vertical, then there are infinitely many. Chooses [1,0,0] in this case. Κ#Μ˜Iheadšœ™Jšœ8™8Jšœ Οmœ1™<šœK™KIcode™)—J˜šΟk ˜ Jšœ}žœΖ˜Ε—J˜šœž ˜Jšžœ™žœ˜˜ΊJšžœ ˜—J˜šž˜Jšœ žœ˜%Jšœžœ$˜9Jšœžœ˜!Jšœ žœ˜)Jšœ žœ˜'Jšœžœ˜#Jšœžœ˜3Jšœžœ˜Jšœ žœ˜-Jšœ žœ˜%Jšœ žœ#˜4Jšœžœ!˜3Jšœžœ˜+Jšœ žœ˜+Jšœ žœ˜'Jšœžœ˜/Jšœ žœ˜#Jšœ žœ˜Jšœ žœ˜Jšœ žœ˜)Jšœžœ˜Jšœžœ˜!Jšœ žœ˜-Jšœ žœ˜-Jšœžœ'˜?Jšœžœ˜!Jšœžœ ˜5Jšœžœ)˜GJšœ žœ˜%Jšœ žœ˜-Jšœžœ˜Jšœ žœ˜'Jšœ žœ˜Jšœ žœ˜Jšœžœ˜'J˜—J˜Iprocšœ™šΟnœžœžœ žœžœžœžœ˜RLšžœžœ.˜NMšœ˜—šŸœžœžœ žœžœžœžœ˜Pšžœžœ˜"Lšœ˜L•StartOfExpansion=[rgb: RGB, calibration: ImagerColor.RGBCalibration _ NIL]šœW˜WLšœE˜Ešžœžœžœ˜L˜hL˜—šžœ˜Lšœ>˜>Lš žœžœžœžœžœP˜…L˜—L˜—L˜L˜—šŸœžœžœ žœžœžœžœ˜JšŸœžœžœ"˜ZMšœ žœ>˜MM˜—MšœI˜IM˜M˜—šŸœžœžœ žœžœžœžœ˜IMšœžœ ˜M˜—šŸœžœžœ žœžœžœžœ˜IMš œ žœžœžœžœ˜6MšœA˜AMšœ˜M˜—šŸœžœžœ žœžœžœžœ˜ULš œ žœžœžœžœ$žœžœ˜lLšœ=˜=Lšžœžœžœ˜0M˜—šŸœžœžœ žœžœžœžœ˜TLš œ žœžœžœžœ$žœžœ˜lLšœVžœ˜iLšœ˜šž˜Lšœ™˜™—M˜M˜—šŸœžœ*˜>LšœW˜Wšžœržœžœž˜ˆLšœ=˜=Lšžœ˜—Lšœqžœ žœžœ˜ŸL˜L˜—š Ÿœžœžœžœžœžœ˜JLš žœžœžœžœžœ˜LLšœ^˜^L˜L˜—M™šŸœžœžœ žœžœžœžœ˜OLšœ˜LšœW˜WLšžœžœžœ7žœžœ,žœžœg˜ξšžœ˜Lšœžœ˜Lšœžœ˜Lšœžœ8˜\šžœžœžœ˜Lšœ/˜/Lšžœžœ$˜@Lšžœžœ-žœ ˜XLšžœ˜šœ˜Lšœ˜LšžœΦ˜ΨLšœ ˜ —L˜—LšžœJ˜NLšœ$˜$L˜—L˜—šŸœžœžœ žœžœžœžœ˜PLšœžœ˜Lšœžœ˜Lšœžœ˜Bšžœžœžœ˜Lšœ/˜/Lšžœžœ$˜@Lšžœžœ-žœ ˜XLšžœ˜šœ˜Lšœ˜LšžœΦ˜ΨLšœ ˜ —L˜—LšžœJ˜NL˜L˜L˜—M™Mšœ™šŸœžœžœ žœžœžœžœ˜PLšžœžœ,˜LL˜L˜—šŸœžœžœ žœžœžœžœ˜NšŸœžœžœ"˜XMšœ žœ<˜KM˜—šžœžœ˜"Lšœ˜L–=[rgb: RGB, calibration: ImagerColor.RGBCalibration _ NIL]šœW˜WLšœE˜Ešžœžœžœ˜L˜fL˜—šžœ˜Lšœ˜Lš žœžœžœžœžœN˜ƒL˜—L˜—L˜L˜—šŸœžœžœ žœžœžœžœ˜HšŸœžœžœ"˜XMšœ žœ<˜KM˜—MšœE˜EMšœ˜M˜—Mšœžœžœžœ˜\šŸ œžœ5žœ!˜mLšœ˜Lšœžœ˜LšœP˜PL˜Idefaultšžœžœžœ7žœžœ,žœžœ`˜ηšžœ˜Lšœžœ˜Lšœ1˜1šžœžœžœ˜Lšœ/˜/Lšžœžœ$˜@Lšžœžœ-žœ ˜XLšžœ˜šœ˜Lšœ ˜ Lšžœΐ˜ΒLšœ ˜ —L˜—LšžœB˜FL˜—M˜—š Ÿœžœ žœžœžœ˜fšžœžœΟc˜=Lšœ˜Lšžœžœžœ=˜NLšžœžœžœžœ˜(L˜—šžœX˜\Lšœžœ˜0Lšœžœ ˜*L˜—šž˜Lšœy˜yLšœj˜jLšœa˜a—L˜L˜—šŸ œžœ*˜˜>LšœW˜Wšžœržœžœž˜ˆLšœ=˜=Lšžœ˜—Lšœqžœ žœžœ˜ŸL˜L˜—šŸ œžœžœ žœžœžœžœ˜GLšœ žœ ˜M˜—šŸ œžœžœ žœžœžœžœ˜GMš œ žœžœžœžœ˜6MšœA˜AMšœ˜M˜—šŸœžœžœ žœžœžœžœ˜SLš œ žœžœžœžœ$žœžœ˜lLšœ=˜=Lšžœžœžœ˜.M˜—šŸœžœžœ žœžœžœžœ˜RLš œ žœžœžœžœ$žœžœ˜lLšœVžœ˜iLšœ˜šž˜Lšœ™˜™—M˜—LšŸœžœžœ˜ š Ÿ œžœ žœžœžœ˜FLšžœžœžœžœ ˜1šŸœžœžœ˜Lš žœžœžœ žœžœ˜4L˜—Lš œžœžœžœžœ˜Lšžœžœžœ ˜3Lšžœžœžœ ˜&L˜/Lšžœžœžœ ˜3Lšžœžœžœ ˜&L˜/Lšžœžœžœ ˜3Lšžœžœžœ ˜&L˜L˜)šž˜Lšœ žœ˜$—L˜—M™šŸœžœžœ žœžœžœžœ˜MLšœ˜LšœW˜WLšžœžœžœ7žœžœ,žœžœn˜υšžœ˜Lšœžœ˜Lšœžœ˜Lšœ˜Lšœ>˜>šžœ žœžœžœ˜3Lšœžœ˜4Lšœ/˜/Lšžœžœ$˜@Lšžœžœ-žœ ˜XLšžœ˜šœ˜Lšœ˜LšžœΤ˜ΦLšœ ˜ —L˜—LšžœH˜LLšœ$˜$L˜—L˜L˜—šŸœžœžœ žœžœžœžœ˜NLšœžœ˜Lšœžœ˜Lšœžœ"˜Fšžœžœžœ˜Lšœ/˜/Lšžœžœ$˜@Lšžœžœ-žœ ˜XLšžœ˜šœ˜Lšœ˜LšžœΤ˜ΦLšœ ˜ —L˜—LšžœH˜LL˜L˜—M™M™ šŸ œžœžœ žœžœžœžœ˜CJšœ1˜1Jšœ˜Lšœ+˜+Lšœ)˜)Jšœxžœ˜M˜—J™Lšœ ™ Lšœ žœ˜Lšœ žœ ˜Lšœ žœ˜M˜šŸœžœžœ žœžœžœžœ˜NM˜M˜—šŸœžœžœ žœžœžœžœ˜LM˜M˜—šŸœžœžœ žœžœžœžœ˜PM˜M˜—šŸ œžœžœ žœžœžœžœ˜GLšœ# ˜=M˜—šŸ œžœžœ˜7Lšœ( ˜9Lšœžœ˜+L–§[paintAction: ATOM, gargoyleData: GGInterfaceTypes.GargoyleData, remake: GGWindow.ForegroundParts _ triggerBag, backgndOK: BOOL _ FALSE, edited: BOOL _ TRUE]šœvžœ žœžœ˜€L˜L™—šŸœžœžœ žœžœžœžœ˜MMšœ"˜"M˜—šŸœžœžœ žœžœžœžœ˜IMšœ˜M˜—šŸœžœžœ žœžœžœžœ˜HLšœžœ ˜,Lšœ©˜©L˜—J™J™šŸ œžœžœ žœžœžœžœ˜DL™ΙLšœižœ žœžœ™˜L™ΖšŸœžœ˜/šžœžœ˜Lšœ'˜'L˜—šŸœžœ˜L˜"Lšœ#˜#Lšœ˜Lšœ'˜'L˜—LšœŸœ˜"L˜—Lšœ˜Lšœžœ˜Lšœ žœ˜Lšœžœ˜#Lšœžœ˜Lšœžœ˜Lšœ žœ˜Lšœžœ˜LšœB˜BL˜Lšœj˜jLšžœžœ žœžœ˜Lšœ*˜*Lšœ žœ:˜FLšœ1˜1LšΟb˜Lšœ6˜6Lšœ˜Lš‘˜Lšœ1˜1Lšœ žœ3˜?Lšœ/˜/J˜—M˜šŸ œžœžœ žœžœžœžœ˜DJšœ˜J˜—šŸ œžœžœ žœžœžœžœ˜DJšœ˜J˜—šŸ œžœžœ žœžœžœžœ˜DJšœ˜J˜J˜J˜—šŸ œžœžœ žœžœžœžœ#˜TJ™ƒJšœ1˜1Jšœ˜Jš œ žœžœžœžœ˜4J˜J˜Jšœ%˜%Jšžœ žœžœžœ˜šœ"˜"J™g—JšœKžœ˜QJšœ~˜~M˜M˜—šŸ œžœžœ žœžœžœžœ˜FJšœ!˜!J˜—šŸ œžœžœ žœžœžœžœ˜FJšœ!˜!J˜—šŸ œžœžœ žœžœžœžœ˜FJšœ!˜!J˜J˜—šŸœžœžœ žœžœžœžœ#˜VJ™―Jšœ1˜1Jšœ˜Jš œ žœžœžœžœ˜4J˜J˜Jšœ$˜$Jšžœ žœžœžœ˜Jšžœž˜"šœ ˜ Jšœ˜Jšœ˜J˜—šœ ˜ Jšœ˜Jšœ ˜ J˜—Jšžœžœ˜Jšœ=˜=JšœCžœ˜IJšœ&˜&Jšœ[˜[M˜M˜—š Ÿ œž œ žœžœžœžœ˜DJ™šJ˜Jšœ%˜%J˜J˜Jš‘ œ˜$Jšžœ žœžœžœ˜Jšœ˜Jšœ˜Jšœ#˜#J˜Jš‘œ˜*Jšžœžœžœžœ˜$šžœBžœ žœž˜]šžœ%žœ˜-JšœR˜RJšœ ˜ Jšžœ˜J˜—Jšœ˜Jšœ8˜8—Jšžœ˜Jšœ=˜=Jšœ\˜\J˜J˜—šŸ œžœ6˜IJšœ{™{MšœΟuœ ’œ ˜$Jšœ˜Jšœ ˜ J˜Jšœžœ˜Jšœ7˜7Jšœ˜Jšœ˜Jšœ˜J˜Jšœ ˜ Jšœ˜Jšœ/˜/M˜Jšœ˜Jšœ˜Jšœ‘œ ˜CJšœ‘œ˜7J˜Mšœ’œ<˜HMšœ’œ<˜HM˜Jšœ˜Jšœ'’œ ’œ˜Jšœ9˜9Jšœ ‘œ˜-Jšœ‘ œžœ ˜"Jšžœ˜—Jšœ$˜$Jšœ˜J˜J˜—š Ÿœžœžœžœžœ˜&Jšžœ žœžœ˜Jšžœ žœžœ˜Jšžœžœ˜J˜J˜—š Ÿ œžœžœžœžœ˜@šžœž˜$Jšœž˜Jšœ˜—J˜J˜—šŸœžœžœEžœ˜~Jšœκ™κJšœH˜HJšœ˜šžœ-žœ˜5Jšœ:˜:Jšžœ&žœ"˜NJ™PJ˜—Jšžœ9˜=JšœL˜LJšœ˜J˜—š Ÿœž œ žœžœžœžœ˜HJ™΄J˜Jšœ ˜ J˜J˜Jš‘ œ˜$Jšžœ žœžœžœ˜Jšœ˜Jšœ˜Jšœ#˜#J˜Jš‘ œ˜%Jšžœ žœžœžœ˜šžœ%žœ˜-JšœL˜LJšœ ˜ Jšžœ˜J˜—Jšœ˜Jš‘œ˜*Jšœ=˜=Jšœ\˜\J˜M˜—šŸœžœ,˜CJšœό™όMšœ’œ ’œ ˜$Jšœ˜Jšœ˜Jšœ ˜ Jšœ˜J˜Jšžœžœ˜J˜M˜Jšœ˜Jšœ˜Jšœ‘œ˜AJšœ‘œ˜FJšΠbkœ˜Jš‘œ‘œ ˜CJšœ‘œ˜7J˜Mšœ’œC˜OMšœ’œC˜OM˜Jš‘œ’œ ’œ˜<˜Jšœ[’œm™Ν—Jš‘œ@’œ˜OJ˜Jš‘œ#žœ˜OJšœ ˜J˜—šŸœžœAžœžœ2˜”Jšœ ˜ Jšœ%žœ˜*J˜Jšœžœ˜Jšœ"˜"J˜Jšœžœ˜Jšœžœ˜Jšœžœ˜Jšœ žœ˜šžœžœžœ'ž˜6Jšœžœ˜,šžœžœžœ'ž˜6Jšœžœ˜,Jšœ˜Jšœ7˜7Jšžœ9˜>Jšœ‘œžœ˜@Jšœ&‘œ˜Ešžœžœ žœ˜Jšœ$˜$Jšžœ˜J˜—šžœžœ˜#JšœI˜IJšœ˜Jšžœ˜J˜—šžœžœ žœ˜Jšœ$˜$Jšœ žœ˜J˜—Jšžœ žœ%˜9Jšœ$˜$—Jšžœ ˜ —Jšžœ ˜ šžœžœ žœ˜JšœT˜TJšœ˜Jšžœ˜J˜—Jšœ9˜9Jšœ2˜2Jšžœ˜J˜J˜—š Ÿœžœ(žœžœ žœžœ˜€Jšœžœ˜Jšœ˜Jšœžœ˜Jšœ žœ˜Jšžœžœžœžœ˜+šžœžœžœž˜!Jšœžœ˜4šžœ3žœ˜;Jšœ˜Jšœžœ˜J˜—šžœžœžœ žœ˜Jšœ˜Jšœ žœ˜J˜——Jšžœ˜Jšœžœ ˜%J˜J˜—š Ÿœž œ žœžœžœžœ˜@J™ΤJ˜Jšœ%˜%J˜J˜Jš‘ œ˜$Jšžœ žœžœžœ˜Jšœ˜Jšœ˜Jšœ#˜#J˜Jš‘œ˜*Jšžœžœžœžœ˜$šžœ*žœ˜2JšœL˜LJšœ ˜ Jšžœ˜J˜—Jšœ#˜#JšŸ œ˜"Jšœ=˜=Jšœ\˜\J˜J˜—šŸ œžœ,˜;Jšœ‹™‹Mšœ’œ ’œ ˜$Jšœ˜Jšœ ˜ J˜Jšœžœ˜Jšœ ’œ ˜Jšœ0˜0Jšœ˜Jšœ˜J˜-Jšœ ˜ Jšœ˜Jšœ ˜ M˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ‘œ ˜CJšœ‘œ˜7J˜Mšœ’œ<˜HMšœ’œ<˜HM˜Jšœžœ˜šœ ’œ’œ ’œ˜Mšžœžœ žœžœ˜Mšœ’œ/˜9Mšœ’œ’œ’œ˜8M˜Mšœ œŸœ˜ZMšžœžœ žœžœ˜MšœŸ œ:’œ ˜mMšœ ˜M˜J˜—šŸœžœ$žœ˜aMšœš™šMšœ˜Mšžœžœžœ˜5Mšžœ1˜5Mšœ<˜