DIRECTORY Feedback, FeedbackTypes, FileNames, FS, FunctionCache, GGBasicTypes, GGBoundBox, GGCoreTypes, GGFileOps, GGFromImager, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGMUserProfile, GGParseIn, GGParseOut, GGProps, GGScene, GGSegment, GGSegmentTypes, GGShapes, GGSlice, GGSliceOps, GGTransform, GGUtility, Imager, ImagerBox, ImagerInterpress, ImagerMaskCapture, ImagerMemory, ImagerTransformation, Interpress, IO, NodeStyle, Real, RefTab, Rope, SF, SymTab, Vectors2d; GGSliceImplF: CEDAR PROGRAM IMPORTS Feedback, FileNames, FS, FunctionCache, GGBoundBox, GGFileOps, GGFromImager, GGMUserProfile, GGParseIn, GGParseOut, GGProps, GGScene, GGSegment, GGShapes, GGSlice, GGSliceOps, GGTransform, Imager, ImagerBox, ImagerInterpress, ImagerMaskCapture, ImagerMemory, ImagerTransformation, Interpress, IO, RefTab, Rope, SymTab, Vectors2d EXPORTS GGSlice = BEGIN BoundBox: TYPE = REF BoundBoxObj; BoundBoxObj: TYPE = GGCoreTypes.BoundBoxObj; Camera: TYPE = GGModelTypes.Camera; CameraObj: TYPE = GGModelTypes.CameraObj; Color: TYPE = Imager.Color; DefaultData: TYPE = GGModelTypes.DefaultData; EditConstraints: TYPE = GGModelTypes.EditConstraints; ExtendMode: TYPE = GGModelTypes.ExtendMode; MsgRouter: TYPE = FeedbackTypes.MsgRouter; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; Point: TYPE = GGBasicTypes.Point; PointGenerator: TYPE = GGModelTypes.PointGenerator; PointGeneratorObj: TYPE = GGModelTypes.PointGeneratorObj; PointPairGenerator: TYPE = GGModelTypes.PointPairGenerator; PointPairGeneratorObj: TYPE = GGModelTypes.PointPairGeneratorObj; PointWalkProc: TYPE = GGModelTypes.PointWalkProc; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; SegmentGeneratorObj: TYPE = GGModelTypes.SegmentGeneratorObj; SelectedObjectData: TYPE = GGModelTypes.SelectedObjectData; SelectionClass: TYPE = GGSegmentTypes.SelectionClass; SelectMode: TYPE = GGModelTypes.SelectMode; Slice: TYPE = GGModelTypes.Slice; SliceClass: TYPE = GGModelTypes.SliceClass; SliceClassObj: TYPE = GGModelTypes.SliceClassObj; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorObj: TYPE = GGModelTypes.SliceDescriptorObj; SliceObj: TYPE = GGModelTypes.SliceObj; SliceParts: TYPE = GGModelTypes.SliceParts; Transformation: TYPE = ImagerTransformation.Transformation; Vector: TYPE = GGBasicTypes.Vector; WalkProc: TYPE = GGModelTypes.WalkProc; Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = Feedback.Problem; ipMaxPoints: INTEGER = 4; ipMaxEdges: INTEGER = 4; IPPointArray: TYPE = ARRAY [0..ipMaxPoints) OF BOOL; -- ll, ul, ur, lr IPEdgeArray: TYPE = ARRAY [0..ipMaxEdges) OF BOOL; -- left, top, right, bottom ipCornerRopes: ARRAY [0..ipMaxPoints) OF Rope.ROPE = [ "lower left corner", "upper left corner", "upper right corner", "lower right corner"]; ipEdgeRopes: ARRAY [0..ipMaxEdges) OF Rope.ROPE = [ "left edge", "top edge", "right edge", "bottom edge"]; IPData: TYPE = REF IPDataObj; IPDataObj: TYPE = RECORD [ localTightBox: BoundBox, -- a tight box in local coordinates. Set when slice is created. localBox: BoundBox, -- a loose box in local coordinates. Set when slice is created. includeByValue: BOOL _ FALSE, -- store the actual interpress in the Gargoyle master? file: Rope.ROPE, -- fileName of Interpress Master mem: Imager.Context, -- imager memory context transform: ImagerTransformation.Transformation, -- includes default position inverse: ImagerTransformation.Transformation, inverseScale: Vector, -- cached value of ImagerTransformation.Factor[inverse].s seg: Segment -- a null segment whose entire purpose is to contain properties in seg.props ]; IPParts: TYPE = REF IPPartsObj; IPPartsObj: TYPE = RECORD [ points: IPPointArray, -- which corners of box are selected. edges: IPEdgeArray, -- which edges of box are selected. innards: BOOL _ FALSE -- FALSE => corners and edges only; no innards ]; IPHitData: TYPE = REF IPHitDataObj; IPHitDataObj: TYPE = RECORD [ point: [-1..ipMaxPoints), -- which point of box is hit. edge: INTEGER, -- which edge of box is hit. hitPoint: Point ]; MasterStuff: TYPE = RECORD [ master: Interpress.Master, pixelsPerUnit: REAL ]; ContextStuff: TYPE = RECORD [ memContext: Imager.Context, localTightBox: BoundBox, localBox: BoundBox ]; BuildIPSliceClass: PUBLIC PROC [] RETURNS [class: SliceClass] = { OPEN GGSlice; class _ NEW[SliceClassObj _ [ type: $IP, unlink: GGSlice.UnlinkSlice, -- ipData doesn't need unlinking getBoundBox: IPGetBoundBox, getTransformedBoundBox: GGSlice.GenericTransformedBoundBox, getTightBox: IPGetTightBox, copy: IPCopy, restore: IPRestore, buildPath: NoOpBuildPath, drawBorder: NoOpDrawBorder, drawParts: IPDrawParts, drawTransform: IPDrawTransform, drawSelectionFeedback: IPDrawSelectionFeedback, drawAttractorFeedback: NoOpDrawAttractorFeedback, saveSelections: NoOpSaveSelections, remakeSelections: NoOpRemakeSelections, transform: IPTransform, describe: IPDescribe, describeHit: IPDescribeHit, fileout: IPFileout, filein: IPFilein, isEmptyParts: IPIsEmptyParts, isCompleteParts: IPIsCompleteParts, newParts: IPNewParts, unionParts: IPUnionParts, differenceParts: IPDiffParts, movingParts: IPMovingParts, augmentParts: IPAugmentParts, alterParts: NoOpAlterParts, setSelectedFields: NoOpSetSelectedFields, pointsInDescriptor: IPPointsInDescriptor, walkPointsInDescriptor: IPWalkPointsInDescriptor, pointPairsInDescriptor: IPPointPairsInDescriptor, segmentsInDescriptor: IPSegmentsInDescriptor, walkSegments: IPWalkSegments, nextPoint: IPNextPoint, nextPointPair: IPNextPointPair, nextSegment: IPNextSegment, closestPoint: IPClosestPoint, closestJointToHitData: NoOpClosestJointToHitData, closestPointAndTangent: NoOpClosestPointAndTangent, closestSegment: IPClosestSegment, filledPathsUnderPoint: IPFilledPathsUnderPoint, lineIntersection: NoOpLineIntersection, circleIntersection: NoOpCircleIntersection, hitDataAsSimpleCurve: NoOpHitDataAsSimpleCurve, setDefaults: NoOpSetDefaults, setStrokeWidth: NoOpSetStrokeWidth, getStrokeWidth: NoOpGetStrokeWidth, setStrokeEnd: NoOpSetStrokeEnd, getStrokeEnd: NoOpGetStrokeEnd, setStrokeJoint: NoOpSetStrokeJoint, getStrokeJoint: NoOpGetStrokeJoint, setStrokeColor: NoOpSetStrokeColor, getStrokeColor: NoOpGetStrokeColor, setFillColor: NoOpSetFillColor, getFillColor: NoOpGetFillColor, setArrows: NoOpSetArrows, getArrows: NoOpGetArrows, setDashed: NoOpSetDashed, getDashed: NoOpGetDashed, setOrientation: NoOpSetOrientation, getOrientation: NoOpGetOrientation ]]; }; MakeIPSliceFromFile: PROC [fullName: Rope.ROPE, router: MsgRouter, transform: ImagerTransformation.Transformation _ NIL, localTightBox: BoundBox, localBox: BoundBox, includeByValue: BOOL] RETURNS [slice: Slice] = { ipMaster: Interpress.Master _ NIL; success: BOOL _ FALSE; [ipMaster, success] _ GetMaster[fullName, router]; IF NOT success THEN ipMaster _ NIL; -- and continue to make the IPSlice slice _ MakeIPSliceFromMaster[ipMaster, 2834.646, fullName, router, transform, localTightBox, localBox, includeByValue]; }; MakeIPSliceFromMaster: PUBLIC PROC [ipMaster: Interpress.Master, pixelsPerUnit: REAL _ 2834.646, fullName: Rope.ROPE _ NIL, router: MsgRouter, transform: ImagerTransformation.Transformation _ NIL, localTightBox: BoundBox _ NIL, localBox: BoundBox _ NIL, includeByValue: BOOL] RETURNS [slice: Slice] = { CheckPair: PROC [key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOL _ FALSE] = { mRef: REF MasterStuff _ NARROW[key]; IF mRef.master=ipMaster AND mRef.pixelsPerUnit=pixelsPerUnit THEN { contextStuff _ NARROW[val]; memContext _ contextStuff.memContext; IF localTightBox=NIL THEN localTightBox _ contextStuff.localTightBox; IF localBox=NIL THEN localBox _ contextStuff.localBox; RETURN[TRUE]; }; }; found: BOOL _ FALSE; memContext: Imager.Context; masterStuff: REF MasterStuff; contextStuff: REF ContextStuff; found _ RefTab.Pairs[contextTable, CheckPair]; IF NOT found THEN { -- compute everything ShowWarnings: Interpress.LogProc = { Feedback.Append[router, oneLiner, $Warning, explanation]; }; PlayFakeScene: PROC [context: Imager.Context] = { ImagerMemory.Replay[memContext, context]; }; memContext _ ImagerMemory.NewMemoryContext[]; -- in pixels Imager.ScaleT[memContext, pixelsPerUnit]; -- pointsPerMeter=2834.646 Imager.SetColor[memContext, Imager.black]; Imager.SetAmplifySpace[memContext, 1.0]; Imager.SetStrokeWidth[memContext, 0.0]; Imager.SetStrokeEnd[memContext, square]; Imager.SetStrokeJoint[memContext, miter]; IF ipMaster # NIL THEN Interpress.DoPage[master: ipMaster, page: 1, context: memContext, log: ShowWarnings]; IF localTightBox = NIL THEN { IF ipMaster = NIL THEN { localTightBox _ GGBoundBox.CreateBoundBox[0,0,100,100]; localBox _ GGBoundBox.CopyBoundBox[localTightBox]; } ELSE { IF useMaskCapture THEN { sfBox: SF.Box; imagerBox: Imager.Rectangle; sfBox _ ImagerMaskCapture.CaptureBounds[PlayFakeScene, ImagerTransformation.Scale[2.0] ! ImagerMaskCapture.Cant => RESUME]; imagerBox _ ImagerTransformation.InverseTransformRectangle[ImagerTransformation.Scale[2.0], ImagerBox.RectangleFromBox[[sfBox.min.s, sfBox.min.f, sfBox.max.s, sfBox.max.f]]]; -- Michael Plass magic localTightBox _ GGBoundBox.BoundBoxFromRectangle[rect: imagerBox]; localBox _ GGBoundBox.CopyBoundBox[localTightBox]; } ELSE { fakeCamera: Camera _ NEW[CameraObj]; fakeScene: Scene _ GGFromImager.Capture[action: PlayFakeScene, camera: fakeCamera ! GGFromImager.WarningMessage => { Feedback.Append[router, oneLiner, $Warning, message]; RESUME; }; ]; localTightBox _ GGScene.TightBoxOfScene[fakeScene]; localBox _ GGScene.BoundBoxOfScene[fakeScene]; GGScene.MakeSceneGarbage[fakeScene]; }; }; }; masterStuff _ NEW[MasterStuff _ [ipMaster, pixelsPerUnit]]; contextStuff _ NEW[ContextStuff _ [memContext, localTightBox, localBox]]; [] _ RefTab.Insert[contextTable, masterStuff, contextStuff]; }; slice _ MakeIPSliceAux[fullName, memContext, router, transform, includeByValue, localTightBox, localBox, NIL]; }; useMaskCapture: BOOL _ FALSE; MakeIPSliceFromMaskPixel: PUBLIC PROC [pa: Imager.PixelArray, color: Color, router: MsgRouter, transform: ImagerTransformation.Transformation _ NIL] RETURNS [slice: Slice] = { memContext: Imager.Context; ipData: IPData; inverse: ImagerTransformation.Transformation; inverseScale: Imager.VEC; localTightBox, localBox: BoundBox; memContext _ ImagerMemory.NewMemoryContext[]; Imager.SetColor[memContext, color]; Imager.MaskPixel[memContext, pa]; transform _ IF transform=NIL THEN GGTransform.Identity[] ELSE transform; inverse _ ImagerTransformation.Invert[transform]; inverseScale _ ImagerTransformation.Factor[inverse].s; localTightBox _ GGBoundBox.BoundBoxOfPixelArray[pa]; localBox _ GGBoundBox.CopyBoundBox[localTightBox]; ipData _ NEW[IPDataObj _ [localTightBox: localTightBox, localBox: localBox, includeByValue: TRUE, file: NIL, mem: memContext, transform: transform, inverse: inverse, inverseScale: inverseScale, seg: GGSegment.MakeLine[p0: [-1.0, -1.0], p1: [1.0, 1.0], props: NIL ] ]]; ipData.seg.strokeWidth _ -1.0; -- so no legal stroke width matches slice _ NEW[SliceObj _ [ class: GGSlice.FetchSliceClass[$IP], data: ipData, parent: NIL, selectedInFull: [FALSE, FALSE, FALSE], tightBox: GGBoundBox.NullBoundBox[], -- filled in by call to TextSetBoundBox boundBox: GGBoundBox.NullBoundBox[], -- filled in by call to TextSetBoundBox boxValid: FALSE ]]; IPSetBoundBox[slice]; }; MakeIPSliceAux: PROC [fileName: Rope.ROPE, memContext: Imager.Context, router: MsgRouter, transform: ImagerTransformation.Transformation, includeByValue: BOOL, localTightBox: BoundBox, localBox: BoundBox, seg: Segment] RETURNS [slice: Slice] = { ipData: IPData; inverse: ImagerTransformation.Transformation; inverseScale: Imager.VEC; transform _ IF transform=NIL THEN GGTransform.Identity[] ELSE transform; inverse _ ImagerTransformation.Invert[transform]; inverseScale _ ImagerTransformation.Factor[inverse].s; ipData _ NEW[IPDataObj _ [localTightBox: localTightBox, localBox: localBox, includeByValue: includeByValue, file: fileName, mem: memContext, transform: transform, inverse: inverse, inverseScale: inverseScale, seg: IF seg#NIL THEN seg ELSE GGSegment.MakeLine[p0: [-1.0, -1.0], p1: [1.0, 1.0], props: NIL ] ]]; ipData.seg.strokeWidth _ -1.0; -- so no legal stroke width matches slice _ NEW[SliceObj _ [ class: GGSlice.FetchSliceClass[$IP], data: ipData, parent: NIL, selectedInFull: [FALSE, FALSE, FALSE], tightBox: GGBoundBox.NullBoundBox[], -- filled in by call to TextSetBoundBox boundBox: GGBoundBox.NullBoundBox[], -- filled in by call to TextSetBoundBox boxValid: FALSE ]]; IPSetBoundBox[slice]; }; SetIncludeByValue: PUBLIC PROC [slice: Slice, includeByValue: BOOL, history: HistoryEvent] = { ipData: IPData _ NARROW[slice.data]; ipData.includeByValue _ includeByValue; }; GetIncludeByValue: PUBLIC PROC [slice: Slice] RETURNS [includeByValue: BOOL _ FALSE] = { ipData: IPData _ NARROW[slice.data]; includeByValue _ ipData.includeByValue; }; gUseLatestIPVersion: BOOL _ FALSE; SetDefaultUseLatestIPVersion: PUBLIC PROC [useLatestIPVersion: BOOL] = { gUseLatestIPVersion _ useLatestIPVersion; }; GetDefaultUseLatestIPVersion: PUBLIC PROC [] RETURNS [useLatestIPVersion: BOOL] = { useLatestIPVersion _ gUseLatestIPVersion; }; IPSetBoundBox: PROC [slice: Slice] = { ipData: IPData _ NARROW[slice.data]; slice.tightBox _ GGBoundBox.BoundBoxOfBoundBox[ipData.localTightBox, ipData.transform]; slice.boundBox _ GGBoundBox.BoundBoxOfBoundBox[ipData.localBox, ipData.transform]; }; IPGetBoundBox: PROC [slice: Slice, parts: SliceParts _ NIL] RETURNS [box: BoundBox]= { RETURN[slice.boundBox]; }; IPGetTightBox: PROC [slice: Slice, parts: SliceParts _ NIL] RETURNS [box: BoundBox]= { ipData: IPData _ NARROW[slice.data]; RETURN[slice.tightBox]; }; IPCopy: PROC [slice: Slice, parts: SliceParts _ NIL] RETURNS [copy: LIST OF Slice] = { copySlice: Slice; ipData: IPData _ NARROW[slice.data]; transform: Imager.Transformation _ ImagerTransformation.Copy[ipData.transform]; copySlice _ MakeIPSliceAux[ipData.file, ipData.mem, NIL, transform, ipData.includeByValue, GGBoundBox.CopyBoundBox[ipData.localTightBox], GGBoundBox.CopyBoundBox[ipData.localBox], GGSegment.CopySegment[ipData.seg] ]; GGProps.CopyAll[fromSlice: slice, toSlice: copySlice]; RETURN[LIST[copySlice]]; }; IPRestore: PROC [from: Slice, to: Slice] = { IF to=NIL OR from=NIL THEN ERROR; IF to.class#from.class THEN ERROR; IF to.class.type#$IP THEN ERROR; BEGIN fromData: IPData _ NARROW[from.data]; toData: IPData _ NARROW[to.data]; IF GGSlice.copyRestore THEN { toData.localTightBox^ _ fromData.localTightBox^; toData.localBox^ _ fromData.localBox^; toData.includeByValue _ fromData.includeByValue; toData.file _ fromData.file; toData.mem _ fromData.mem; -- ASSERT: ImagerMemory context immutable toData.transform _ fromData.transform; toData.inverse _ fromData.inverse; toData.inverseScale _ fromData.inverseScale; toData.seg _ fromData.seg; } ELSE to.data _ from.data; to.selectedInFull _ from.selectedInFull; -- RECORD of BOOL to.normalSelectedParts _ NIL; -- caller must reselect to.hotSelectedParts _ NIL; -- caller must reselect to.activeSelectedParts _ NIL; -- caller must reselect to.matchSelectedParts _ NIL; -- caller must reselect to.tightBox^ _ from.tightBox^; to.tightBoxValid _ from.tightBoxValid; to.boundBox^ _ from.boundBox^; to.boxValid _ from.boxValid; to.onOverlay _ from.onOverlay; to.extraPoints _ from.extraPoints; to.priority _ from.priority; to.historyTop _ from.historyTop; END; }; IPDrawParts: PROC [slice: Slice, parts: SliceParts _ NIL, dc: Imager.Context, camera: Camera, quick: BOOL] = { ipData: IPData _ NARROW[slice.data]; ipParts: IPParts _ NARROW[parts]; DoIPDrawParts: PROC = { CachedIPDraw[dc, slice, NIL]; }; IF ipParts = NIL OR ipParts.innards THEN Imager.DoSave[dc, DoIPDrawParts]; }; IPDrawTransform: PROC [slice: Slice, parts: SliceParts _ NIL, dc: Imager.Context, camera: Camera, transform: Transformation, editConstraints: EditConstraints] = { DoIPDrawTransform: PROC = { CachedIPDraw[dc, slice, transform]; }; Imager.DoSave[dc, DoIPDrawTransform]; }; IPDrawSelectionFeedback: PROC [slice: Slice, selectedParts: SliceParts, hotParts: SliceParts, dc: Imager.Context, camera: Camera, dragInProgress, caretIsMoving, hideHot, quick: BOOL] = { slowNormal, slowHot, completeNormal, completeHot: BOOL _ FALSE; firstJoint: Point; normalParts, hotIPParts: IPParts; ipData: IPData _ NARROW[slice.data]; transform: Transformation _ ipData.transform; IF caretIsMoving OR dragInProgress OR camera.quality=quality THEN RETURN; IF selectedParts = NIL AND hotParts = NIL THEN RETURN; normalParts _ NARROW[selectedParts]; hotIPParts _ NARROW[hotParts]; completeNormal _ normalParts#NIL AND IsComplete[normalParts]; completeHot _ hotIPParts#NIL AND IsComplete[hotIPParts]; slowNormal _ normalParts#NIL AND (NOT quick OR (quick AND NOT completeNormal)); slowHot _ hotIPParts#NIL AND (NOT quick OR (quick AND NOT completeHot)); IF slowNormal AND slowHot THEN DrawSelectionFeedbackAux[slice, ipData, normalParts, hotIPParts, dc, transform, camera] ELSE IF slowNormal THEN DrawSelectionFeedbackAux[slice, ipData, normalParts, NIL, dc, transform, camera] ELSE IF slowHot THEN DrawSelectionFeedbackAux[slice, ipData, NIL, hotIPParts, dc, transform, camera]; IF (NOT slowNormal AND completeNormal) OR (NOT slowHot AND completeHot) THEN { fullParts: IPParts _ IF completeNormal THEN normalParts ELSE hotIPParts; [] _ GGSliceOps.GetTightBox[slice]; -- force update of internal data firstJoint _ ImagerTransformation.Transform[transform, [ipData.localTightBox.loX, ipData.localTightBox.loY]]; }; IF NOT slowHot AND completeHot THEN GGShapes.DrawQuickSelectedJoint[dc, firstJoint, hot, camera.cpScale]; IF NOT slowNormal AND completeNormal THEN GGShapes.DrawQuickSelectedJoint[dc, firstJoint, normal, camera.cpScale]; }; Props: TYPE = REF PropsRec; PropsRec: TYPE = RECORD [ slice: Slice, cacheable: BOOL _ TRUE ]; FindImagerObject: PROC [ipData: IPData] RETURNS [object: Imager.Object] = { CompareProps: FunctionCache.CompareProc = { cachedProps: Props _ NARROW[argument]; cachedSlice: Slice _ cachedProps.slice; cachedData: IPData _ NARROW[cachedSlice.data]; RETURN[cachedData.mem=ipData.mem]; }; value: FunctionCache.Range; ok: BOOL; [value, ok] _ FunctionCache.Lookup[x: cache, compare: CompareProps, clientID: $GGIPObject]; RETURN [IF ok THEN NARROW[value] ELSE NIL]; }; CreateProps: PROC [slice: Slice] RETURNS [props: Props] = { props _ NEW[PropsRec]; props.slice _ IPCopy[slice].first; }; CachedIPDraw: PROC [dc: Imager.Context, slice: Slice, viewView: Transformation] = { OPEN ImagerTransformation; -- see Transform and InverseTransform object: Imager.Object; ipProps: Props; ipData: IPData _ NARROW[slice.data]; success: BOOL _ TRUE; IF GGMUserProfile.GetTurboOn[] AND (viewView=NIL OR CloseToTranslation[viewView,identity]) THEN { object _ FindImagerObject[ipData]; IF object = NIL THEN { -- put circle in cache clipR: Imager.Rectangle _ GGBoundBox.RectangleFromBoundBox[ipData.localBox]; props: Props _ CreateProps[slice]; object _ NEW[Imager.ObjectRep _ [draw: IPDrawObject, clip: clipR, data: props]]; FunctionCache.Insert[cache, props, object, 60, $GGIPObject]; }; BEGIN ipProps _ NARROW[object.data]; IF viewView # NIL THEN Imager.ConcatT[dc, viewView]; Imager.ConcatT[dc, ipData.transform]; IF NOT ipProps.cacheable THEN GOTO DrawPlain; Imager.DrawObject[context: dc, object: object, interactive: TRUE, position: [0.0, 0.0] ! ImagerMaskCapture.Cant => { success _ FALSE; CONTINUE; }; ]; IF NOT success THEN { ipProps.cacheable _ FALSE; GOTO DrawPlain; }; EXITS DrawPlain => IPDrawInnards[dc, ipData]; END } ELSE { IF viewView # NIL THEN Imager.ConcatT[dc, viewView]; Imager.ConcatT[dc, ipData.transform]; IPDrawInnards[dc, ipData]; }; }; identity: Transformation _ ImagerTransformation.Scale[1.0]; IPDrawObject: PROC [self: Imager.Object, context: Imager.Context] = { OPEN ImagerTransformation; ipProps: Props _ NARROW[self.data]; ipData: IPData _ NARROW[ipProps.slice.data]; IPDrawInnards[context, ipData]; }; IPDrawInnards: PROC [dc: Imager.Context, ipData: IPData] = { ENABLE Imager.Warning => { Feedback.PutFByName[$Gargoyle, oneLiner, $Warning, "Imager Warning[%g]: %g", [atom[error.code]], [rope[error.explanation]] ]; RESUME; }; Imager.SetColor[dc, Imager.black]; -- otherwise the replay could end up in a random color. ImagerMemory.Replay[ipData.mem, dc]; }; DrawSelectionFeedbackAux: PROC [slice: Slice, ipData: IPData, normalParts: IPParts, hotParts: IPParts, dc: Imager.Context, t: Transformation, camera: Camera] = { DoDrawFeedback: PROC = { box: BoundBox _ ipData.localTightBox; thisCPisHot, thisCPisSelected: BOOL; pts: ARRAY [0..3] OF Point; pts[0] _ ImagerTransformation.Transform[t, [box.loX, box.loY]]; pts[1] _ ImagerTransformation.Transform[t, [box.loX, box.hiY]]; pts[2] _ ImagerTransformation.Transform[t, [box.hiX, box.hiY]]; pts[3] _ ImagerTransformation.Transform[t, [box.hiX, box.loY]]; FOR point: INTEGER IN [0..ipMaxPoints) DO thisCPisHot _ hotParts#NIL AND hotParts.points[point]; thisCPisSelected _ normalParts#NIL AND normalParts.points[point]; IF thisCPisHot THEN GGShapes.DrawSelectedJoint[dc, pts[point], hot, camera.cpScale]; IF thisCPisSelected THEN GGShapes.DrawSelectedJoint[dc, pts[point], normal, camera.cpScale]; IF NOT thisCPisHot AND NOT thisCPisSelected THEN GGShapes.DrawCP[dc, pts[point], camera.cpScale]; ENDLOOP; Imager.SetStrokeJoint[dc, round]; Imager.SetStrokeWidth[dc, 1.0]; Imager.SetColor[dc, Imager.black]; IF normalParts # NIL THEN { FOR edge: INTEGER IN [0..ipMaxEdges) DO IF normalParts.edges[edge] THEN SELECT edge FROM 0 => Imager.MaskVector[dc, pts[0], pts[1]]; 1 => Imager.MaskVector[dc, pts[1], pts[2]]; 2 => Imager.MaskVector[dc, pts[2], pts[3]]; 3 => Imager.MaskVector[dc, pts[3], pts[0]]; ENDCASE => ERROR; ENDLOOP; }; }; Imager.DoSave[dc, DoDrawFeedback]; }; IPTransform: PROC [slice: Slice, parts: SliceParts _ NIL, transform: Transformation, editConstraints: EditConstraints, history: HistoryEvent] = { ipData: IPData _ NARROW[slice.data]; ipData.transform _ ImagerTransformation.Concat[ipData.transform, transform]; ipData.inverse _ ImagerTransformation.Invert[ipData.transform]; ipData.inverseScale _ ImagerTransformation.Factor[ipData.inverse].s; IPSetBoundBox[slice]; }; IPDescribe: PROC [sliceD: SliceDescriptor] RETURNS [rope: Rope.ROPE] = { prefix: Rope.ROPE; ipParts: IPParts; eCount, pCount: NAT _ 0; IF sliceD.parts = NIL THEN RETURN["an IP slice"]; ipParts _ NARROW[sliceD.parts]; FOR i: INTEGER IN [0..ipMaxPoints) DO IF ipParts.points[i] THEN { pCount _ pCount + 1; prefix _ ipCornerRopes[i]; }; ENDLOOP; FOR i: INTEGER IN [0..ipMaxEdges) DO IF ipParts.edges[i] THEN { eCount _eCount + 1; prefix _ ipEdgeRopes[i]; }; ENDLOOP; IF pCount+eCount>1 THEN RETURN["multiple parts of an IP slice"]; IF eCount=0 AND pCount=0 THEN RETURN["NO parts of an IP slice"]; rope _ Rope.Concat[prefix, " of an IP slice"]; }; IPDescribeHit: PROC [slice: Slice, hitData: REF ANY] RETURNS [rope: Rope.ROPE] = { ipHitData: IPHitData _ NARROW[hitData]; prefix: Rope.ROPE; IF ipHitData.point#-1 THEN prefix _ ipCornerRopes[ipHitData.point] ELSE IF ipHitData.edge#-1 THEN prefix _ ipEdgeRopes[ipHitData.edge] ELSE ERROR; rope _ Rope.Concat[prefix, " of an IP slice"]; }; IPFileout: PROC [slice: Slice, f: IO.STREAM] = { FromMemory: PROC [dc: Imager.Context] = { Imager.SetColor[dc, Imager.black]; -- otherwise the replay could end up in a random color. ImagerMemory.Replay[ipData.mem, dc]; }; ipData: IPData _ NARROW[slice.data]; ipRef: ImagerInterpress.Ref; masterStream: IO.STREAM; masterSize: CARD; masterRope: Rope.ROPE; f.PutF["\"%q\" ", [rope[ipData.file]]]; GGParseOut.WriteTransformation[f, ipData.transform]; f.PutChar[IO.SP]; GGParseOut.WriteBox[f, ipData.localTightBox]; f.PutChar[IO.SP]; GGParseOut.WriteBox[f, ipData.localBox]; f.PutRope[" includeByValue: "]; GGParseOut.WriteBool[f, ipData.includeByValue]; f.PutChar[IO.CR]; IF ipData.includeByValue THEN { masterStream _ IO.ROS[]; ipRef _ ImagerInterpress.CreateFromStream[masterStream, "Interpress/Xerox/3.0 "]; ImagerInterpress.DoPage[ipRef, FromMemory]; ImagerInterpress.Finish[ipRef]; masterRope _ masterStream.RopeFromROS[]; masterSize _ Rope.Length[masterRope]; f.PutF["%g\n", [integer[masterSize]]]; f.PutRope[masterRope]; }; f.PutRope[" props: ( "]; GGParseOut.WriteBool[f, ipData.seg.props#NIL]; IF ipData.seg.props#NIL THEN GGParseOut.WriteProps[f, ipData.seg.props] ELSE f.PutChar[IO.SP]; -- list of ROPE f.PutRope[")"]; }; IPFilein: PROC [f: IO.STREAM, version: REAL, router: MsgRouter, camera: Camera] RETURNS [slice: Slice _ NIL] = { ipMaster: Interpress.Master _ NIL; fullName: Rope.ROPE; success: BOOL _ FALSE; transform: ImagerTransformation.Transformation; masterText: Rope.ROPE; masterSize: CARD; includeByValue: BOOL; localTightBox, localBox: BoundBox; props: LIST OF Rope.ROPE _ NIL; fullName _ f.GetRopeLiteral[]; transform _ GGParseIn.ReadTransformation[f]; IF version >= 8701.28 THEN { localTightBox _ GGParseIn.ReadBox[f]; IF version >= 8710.19 THEN { localBox _ GGParseIn.ReadBox[f]; } ELSE { localBox _ GGBoundBox.CopyBoundBox[localTightBox]; GGBoundBox.EnlargeByOffset[localTightBox, 5.0]; -- allow for stroke widths up to 10.0 }; } ELSE {localTightBox _ NIL; localBox _ NIL}; IF Rope.Length[fullName] #0 THEN { IF GetDefaultUseLatestIPVersion[] THEN { fullName _ FileNames.StripVersionNumber[fullName]; localTightBox _ NIL; -- force recalculation of bound box }; [fullName] _ FS.FileInfo[fullName ! FS.Error => CONTINUE;]; -- add the version number or die if no file name (includeByValue better be true!) }; IF version >= 8612.04 THEN { goodValue: BOOL; GGParseIn.ReadWRope[f, "includeByValue:"]; [includeByValue, goodValue] _ GGParseIn.ReadBool[f, version]; IF NOT goodValue THEN ERROR; IF includeByValue THEN { masterSize _ GGParseIn.ReadWCARD[f]; GGParseIn.ReadBlank[f]; masterText _ IO.GetRope[f, masterSize, TRUE]; ipMaster _ Interpress.FromRope[masterText, NIL]; slice _ MakeIPSliceFromMaster[ipMaster, 1.0, fullName, router, transform, localTightBox, localBox, TRUE]; } ELSE { slice _ MakeIPSliceFromFile[fullName, router, transform, localTightBox, localBox, FALSE]; }; } ELSE { slice _ MakeIPSliceFromFile[fullName, router, transform, localTightBox, localBox, FALSE]; }; IF version>=8706.08 THEN { -- read in segment props hasProps: BOOL _ FALSE; GGParseIn.ReadWRope[f, "props: ( "]; hasProps _ GGParseIn.ReadBool[f, version].truth; props _ IF hasProps THEN GGParseIn.ReadListOfRope[f] ELSE NIL; GGParseIn.ReadWRope[f, ")"]; IF props#NIL THEN { seg: Segment _ NARROW[slice.data, IPData].seg; FOR next: LIST OF Rope.ROPE _ props, next.rest UNTIL next=NIL DO seg.props _ CONS[next.first, seg.props]; ENDLOOP; }; }; }; IPIsEmptyParts: PROC [sliceD: SliceDescriptor] RETURNS [BOOL _ FALSE] = { RETURN[IsEmpty[sliceD.parts]]; }; IPIsCompleteParts: PROC [sliceD: SliceDescriptor] RETURNS [BOOL _ FALSE] = { RETURN[IsComplete[sliceD.parts]]; }; IPNewParts: PROC [slice: Slice, hitData: REF ANY, mode: SelectMode] RETURNS [sliceD: SliceDescriptor] = { ipHitData: IPHitData _ NARROW[hitData]; ipParts: IPParts _ NEW[IPPartsObj _ [points: ALL[FALSE], edges: ALL[FALSE] ] ]; SELECT mode FROM literal => { IF ipHitData.point#-1 THEN ipParts.points[ipHitData.point] _ TRUE ELSE IF ipHitData.edge#-1 THEN ipParts.edges[ipHitData.edge] _ TRUE ELSE ERROR; }; joint => { IF ipHitData.point#-1 THEN { ipParts.points[ipHitData.point] _ TRUE; } ELSE { hitData: REF ANY; pointHitData: IPHitData; success: BOOL; wholeD: SliceDescriptor; wholeD _ IPNewParts[slice, NIL, slice]; [----, ----, ----, hitData, success] _ GGSliceOps.ClosestPoint[wholeD, ipHitData.hitPoint, GGUtility.plusInfinity]; IF NOT success THEN ERROR; pointHitData _ NARROW[hitData]; IF pointHitData.point = -1 THEN ERROR; ipParts.points[pointHitData.point] _ TRUE; }; ipParts.innards _ TRUE; }; segment => IF ipHitData.edge#-1 THEN{ ipParts.edges[ipHitData.edge] _ TRUE; ipParts.innards _ TRUE; }; controlPoint => { ipParts.points _ ALL[TRUE]; ipParts.edges _ ALL[TRUE]; }; traj, slice, topLevel => MakeComplete[ipParts]; none => { -- leave ipParts empty }; ENDCASE => ERROR; sliceD _ GGSlice.DescriptorFromParts[slice, ipParts]; }; IPUnionParts: PROC [partsA: SliceDescriptor, partsB: SliceDescriptor] RETURNS [aPlusB: SliceDescriptor] = { ipPartsA: IPParts _ NARROW[partsA.parts]; ipPartsB: IPParts _ NARROW[partsB.parts]; newParts: IPParts; IF partsA = NIL OR partsB = NIL THEN ERROR; IF partsA.parts = NIL THEN RETURN[partsB]; IF partsB.parts = NIL THEN RETURN[partsA]; newParts _ NEW[IPPartsObj _ [points: ALL[FALSE], edges: ALL[FALSE] ] ]; FOR i: INTEGER IN [0..ipMaxPoints) DO newParts.points[i] _ ipPartsA.points[i] OR ipPartsB.points[i]; ENDLOOP; FOR i: INTEGER IN [0..ipMaxEdges) DO newParts.edges[i] _ ipPartsA.edges[i] OR ipPartsB.edges[i]; ENDLOOP; newParts.innards _ ipPartsA.innards OR ipPartsB.innards; aPlusB _ GGSlice.DescriptorFromParts[partsA.slice, newParts]; }; IPDiffParts: PROC [partsA: SliceDescriptor, partsB: SliceDescriptor] RETURNS [aMinusB: SliceDescriptor] = { ipPartsA: IPParts _ NARROW[partsA.parts]; ipPartsB: IPParts _ NARROW[partsB.parts]; newParts: IPParts; IF partsA = NIL OR partsB = NIL THEN ERROR; IF partsA.parts = NIL OR partsB.parts = NIL THEN RETURN[partsA]; newParts _ NEW[IPPartsObj _ [points: ALL[FALSE], edges: ALL[FALSE] ] ]; FOR i: INTEGER IN [0..ipMaxPoints) DO newParts.points[i] _ IF ipPartsB.points[i] THEN FALSE ELSE ipPartsA.points[i]; ENDLOOP; FOR i: INTEGER IN [0..ipMaxEdges) DO newParts.edges[i] _ IF ipPartsB.edges[i] THEN FALSE ELSE ipPartsA.edges[i]; ENDLOOP; newParts.innards _ IF ipPartsB.innards THEN FALSE ELSE ipPartsA.innards; aMinusB _ GGSlice.DescriptorFromParts[partsA.slice, newParts]; }; IPMovingParts: PROC [slice: Slice, selectedParts: SliceParts, editConstraints: EditConstraints, bezierDrag: GGInterfaceTypes.BezierDragRecord] RETURNS [background, overlay, rubber, drag: SliceDescriptor] = { dragParts: IPParts _ NEW[IPPartsObj _ [points: ALL[TRUE], edges: ALL[TRUE], innards: TRUE ] ]; backgroundParts: IPParts _ NEW[IPPartsObj _ [points: ALL[TRUE], edges: ALL[TRUE], innards: TRUE ] ]; IF IsEmpty[selectedParts] THEN { -- all on background dragParts.edges _ ALL[FALSE]; dragParts.points _ ALL[FALSE]; dragParts.innards _ FALSE; } ELSE { -- all dragging backgroundParts.edges _ ALL[FALSE]; backgroundParts.points _ ALL[FALSE]; backgroundParts.innards _ FALSE; }; background _ GGSlice.DescriptorFromParts[slice, backgroundParts]; overlay _ rubber _ GGSlice.DescriptorFromParts[slice, NIL]; drag _ GGSlice.DescriptorFromParts[slice, dragParts]; }; IPAugmentParts: PROC [sliceD: SliceDescriptor, selectClass: SelectionClass] RETURNS [more: SliceDescriptor] = { ipParts: IPParts _ NARROW[sliceD.parts]; newParts: IPParts _ NEW[IPPartsObj _ [points: ipParts.points, edges: ipParts.edges, innards: ipParts.innards] ]; FOR i: INTEGER IN [0..ipMaxEdges) DO IF ipParts.edges[i] THEN SELECT i FROM 0 => {newParts.points[0] _ TRUE; newParts.points[1] _ TRUE;}; 1 => {newParts.points[1] _ TRUE; newParts.points[2] _ TRUE;}; 2 => {newParts.points[2] _ TRUE; newParts.points[3] _ TRUE;}; 3 => {newParts.points[3] _ TRUE; newParts.points[0] _ TRUE;}; ENDCASE => ERROR; ENDLOOP; more _ GGSlice.DescriptorFromParts[sliceD.slice, newParts]; }; IPPointsInDescriptor: PROC [sliceD: SliceDescriptor] RETURNS [pointGen: PointGenerator] = { parts: IPParts _ NARROW[sliceD.parts]; pointGen _ NEW[PointGeneratorObj _ [sliceD, 0, 0, NIL] ]; FOR point: INTEGER IN [0..ipMaxPoints) DO IF parts.points[point] THEN pointGen.toGo _ pointGen.toGo + 1; ENDLOOP; }; IPWalkPointsInDescriptor: PROC [sliceD: SliceDescriptor, walkProc: PointWalkProc] = { parts: IPParts _ NARROW[sliceD.parts]; ipData: IPData _ NARROW[sliceD.slice.data]; done: BOOL _ FALSE; FOR point: INTEGER IN [0..ipMaxPoints) DO IF parts.points[point] THEN { done _ walkProc[GetIPPoint[ipData, point]]; IF done THEN RETURN; }; ENDLOOP; }; IPPointPairsInDescriptor: PROC [sliceD: SliceDescriptor] RETURNS [pointPairGen: PointPairGenerator] = { parts: IPParts _ NARROW[sliceD.parts]; pointPairGen _ NEW[PointPairGeneratorObj _ [sliceD, 0, 0, NIL] ]; -- toGo and index not used FOR edge: INTEGER IN [0..ipMaxEdges) DO IF parts.edges[edge] THEN pointPairGen.toGo _ pointPairGen.toGo + 1; ENDLOOP; }; IPSegmentsInDescriptor: PROC [sliceD: SliceDescriptor] RETURNS [segGen: SegmentGenerator] = { parts: IPParts _ NARROW[sliceD.parts]; segGen _ NEW[SegmentGeneratorObj _ [NIL, 0, 0, 0, NIL, FALSE, sliceD, NIL] ]; -- toGo and index now used FOR edge: INTEGER IN [0..ipMaxEdges) DO IF parts.edges[edge] THEN segGen.toGo _ 1;-- any edge sets segGen.toGo = 1 ENDLOOP; }; IPWalkSegments: PROC [slice: Slice, walkProc: WalkProc] RETURNS [sliceD: SliceDescriptor] = { ipData: IPData _ NARROW[slice.data]; RETURN[IF walkProc[ipData.seg, NIL] THEN IPNewParts[slice, NIL, slice] ELSE IPNewParts[slice, NIL, none]]; }; IPNextSegment: PROC [slice: Slice, segGen: SegmentGenerator] RETURNS [seg: Segment, transform: Transformation] = { IF segGen=NIL OR segGen.toGo = 0 THEN RETURN[NIL, NIL] ELSE { ipData: IPData _ NARROW[segGen.sliceD.slice.data]; seg _ ipData.seg; segGen.toGo _ 0; }; }; IPNextPoint: PROC [slice: Slice, pointGen: PointGenerator] RETURNS [pointAndDone: GGModelTypes.PointAndDone] = { IF pointGen=NIL OR pointGen.toGo = 0 THEN { pointAndDone.done _ TRUE; RETURN; } ELSE { sliceD: SliceDescriptor _ pointGen.sliceD; ipData: IPData _ NARROW[sliceD.slice.data]; ipParts: IPParts _ NARROW[sliceD.parts]; index: INTEGER _ -1; pointAndDone.done _ FALSE; FOR index _ pointGen.index, index+1 UNTIL index >=ipMaxPoints DO IF ipParts.points[index] THEN EXIT; -- index will point to next available point ENDLOOP; pointAndDone.point _ GetIPPoint[ipData, index]; pointGen.toGo _ pointGen.toGo-1; pointGen.index _ IF pointGen.toGo=0 THEN 99 ELSE index+1; -- bump to the next available point in the generator }; }; GetIPPoint: PROC [ipData: IPData, index: INTEGER] RETURNS [point: Point] = { box: BoundBox _ ipData.localTightBox; point _ ImagerTransformation.Transform[ipData.transform, SELECT index FROM 0 => [box.loX, box.loY], 1 => [box.loX, box.hiY], 2 => [box.hiX, box.hiY], 3 => [box.hiX, box.loY], ENDCASE => ERROR ]; }; IPNextPointPair: PROC [slice: Slice, pointPairGen: PointPairGenerator] RETURNS [pointPairAndDone: GGModelTypes.PointPairAndDone] = { IF pointPairGen=NIL OR pointPairGen.toGo = 0 THEN { pointPairAndDone.done _ TRUE; RETURN; } ELSE { sliceD: SliceDescriptor _ pointPairGen.sliceD; ipData: IPData _ NARROW[sliceD.slice.data]; ipParts: IPParts _ NARROW[sliceD.parts]; box: BoundBox _ ipData.localTightBox; t: ImagerTransformation.Transformation _ ipData.transform; index: INTEGER _ -1; pointPairAndDone.done _ FALSE; FOR index _ pointPairGen.index, index+1 UNTIL index >=ipMaxEdges DO IF ipParts.edges[index] THEN EXIT; -- index will point to next availabe edge ENDLOOP; SELECT index FROM -- index in [0..ipMaxEdges) of next available edge 0 => { pointPairAndDone.lo _ ImagerTransformation.Transform[t, [box.loX, box.loY] ]; pointPairAndDone.hi _ ImagerTransformation.Transform[t, [box.loX, box.hiY] ]; }; 1 => { pointPairAndDone.lo _ ImagerTransformation.Transform[t, [box.loX, box.hiY] ]; pointPairAndDone.hi _ ImagerTransformation.Transform[t, [box.hiX, box.hiY] ]; }; 2 => { pointPairAndDone.lo _ ImagerTransformation.Transform[t, [box.hiX, box.hiY] ]; pointPairAndDone.hi _ ImagerTransformation.Transform[t, [box.hiX, box.loY] ]; }; 3 => { pointPairAndDone.lo _ ImagerTransformation.Transform[t, [box.hiX, box.loY] ]; pointPairAndDone.hi _ ImagerTransformation.Transform[t, [box.loX, box.loY] ]; }; ENDCASE => SIGNAL Problem[msg: "Broken Invariant"]; pointPairGen.toGo _ pointPairGen.toGo - 1; pointPairGen.index _ IF pointPairGen.toGo=0 THEN 99 ELSE index+1; -- bump to the next available pair in the generator }; }; IPClosestPoint: PROC [sliceD: SliceDescriptor, testPoint: Point, tolerance: REAL] RETURNS [bestPoint: Point, bestDist: REAL, bestNormal:Vector _ [0,-1], hitData: REF ANY, success: BOOL _ FALSE] = { index: NAT _ 9999; slice: Slice _ sliceD.slice; ipData: IPData _ NARROW[slice.data]; tightBox: BoundBox _ GGSliceOps.GetTightBox[slice]; toleranceBox: BoundBoxObj _ [tightBox.loX-tolerance, tightBox.loY-tolerance, tightBox.hiX+tolerance, tightBox.hiY+tolerance, FALSE, FALSE]; -- box in GG coordinates IF PointIsInBox[testPoint, toleranceBox] THEN { ipHitData: IPHitData; ipParts: IPParts _ NARROW[sliceD.parts]; localTestpoint: Point _ GGTransform.Transform[ipData.inverse, testPoint]; localTolerance: REAL _ ABS[tolerance*MAX[ipData.inverseScale.x, ipData.inverseScale.y]]; -- MAX function is not correct when text is skewed. [bestDist, index, bestPoint, success] _ GGBoundBox.NearestPoint[ipData.localTightBox, localTestpoint, localTolerance, ipParts.points]; IF success THEN { bestPoint _ GGTransform.Transform[ipData.transform, bestPoint]; bestDist _ Vectors2d.Distance[testPoint, bestPoint]; hitData _ ipHitData _ NEW[IPHitDataObj _ [point: index, edge: -1, hitPoint: bestPoint] ]; }; }; }; IPClosestSegment: PROC [sliceD: SliceDescriptor, testPoint: Point, tolerance: REAL] RETURNS [bestPoint: Point, bestDist: REAL, bestNormal: Vector _ [0,-1], hitData: REF ANY, success: BOOL _ FALSE] = { slice: Slice _ sliceD.slice; ipData: IPData _ NARROW[slice.data]; tightBox: BoundBox _ GGSliceOps.GetTightBox[slice]; toleranceBox: BoundBoxObj _ [tightBox.loX-tolerance, tightBox.loY-tolerance, tightBox.hiX+tolerance, tightBox.hiY+tolerance, FALSE, FALSE]; -- box in GG coordinates IF PointIsInBox[testPoint, toleranceBox] THEN { ipHitData: IPHitData; index: NAT _ 9999; ipData: IPData _ NARROW[slice.data]; ipParts: IPParts _ NARROW[sliceD.parts]; localTestpoint: Point _ GGTransform.Transform[ipData.inverse, testPoint]; -- transfrom point to "IP space" localTolerance: REAL _ ABS[tolerance*MAX[ipData.inverseScale.x, ipData.inverseScale.y]]; -- MAX function is not correct when text is skewed. [bestDist, index, bestPoint, success] _ GGBoundBox.NearestSegment[ipData.localTightBox, localTestpoint, localTolerance, ipParts.points]; IF success THEN { bestPoint _ GGTransform.Transform[ipData.transform, bestPoint]; bestDist _ Vectors2d.Distance[testPoint, bestPoint]; hitData _ ipHitData _ NEW[IPHitDataObj _ [point: -1, edge: index, hitPoint: bestPoint] ]; bestNormal _ Vectors2d.Sub[testPoint, bestPoint]; }; }; }; IPFilledPathsUnderPoint: PUBLIC PROC [slice: Slice, point: Point, tolerance: REAL] RETURNS [hitData: REF ANY _ NIL, moreHitDatas: LIST OF REF ANY _ NIL] = { ipData: IPData _ NARROW[slice.data]; pointBox: Point _ ImagerTransformation.Transform[ipData.inverse, point]; box: BoundBox _ ipData.localTightBox; success: BOOL _ FALSE; success _ ipData.localTightBox.infinite OR (pointBox.x >= box.loX AND pointBox.x <= box.hiX AND pointBox.y >= box.loY AND pointBox.y <= box.hiY); IF success THEN { bestDist: REAL; segNum: NAT; bestPoint: Point; [bestDist, segNum, bestPoint, success] _ GGBoundBox.NearestSegment[box, pointBox]; bestPoint _ GGTransform.Transform[ipData.transform, bestPoint]; hitData _ NEW[IPHitDataObj _ [point: -1, edge: segNum, hitPoint: bestPoint] ]; }; }; PointIsInBox: PROC [test: Point, box: BoundBoxObj] RETURNS [BOOL _ FALSE] = { RETURN[ NOT (test.x < box.loX OR test.x > box.hiX OR test.y < box.loY OR test.y > box.hiY) ]; }; MakeComplete: PROC [parts: SliceParts] = { ipParts: IPParts _ NARROW[parts]; ipParts.points _ ALL[TRUE]; ipParts.edges _ ALL[TRUE]; ipParts.innards _ TRUE; }; IsComplete: PROC [parts: SliceParts] RETURNS [BOOL _ FALSE] = { ipParts: IPParts _ NARROW[parts]; RETURN[ipParts#NIL AND ipParts.points=ALL[TRUE] AND ipParts.edges=ALL[TRUE] AND ipParts.innards=TRUE]; }; IsEmpty: PROC [parts: SliceParts] RETURNS [BOOL _ FALSE] = { ipParts: IPParts _ NARROW[parts]; RETURN[ipParts=NIL OR (ipParts.points=ALL[FALSE] AND ipParts.edges=ALL[FALSE] AND ipParts.innards = FALSE)]; }; NoOpIsEmptyParts: PUBLIC GGModelTypes.SliceIsEmptyPartsProc = { SIGNAL Problem [msg: "All slice classes must have this proc."]; RETURN[TRUE]; }; NoOpIsCompleteParts: PUBLIC GGModelTypes.SliceIsCompletePartsProc = { SIGNAL Problem [msg: "All slice classes must have this proc."]; RETURN[FALSE]; }; NoOpNewParts: PUBLIC GGModelTypes.SliceNewPartsProc = { SIGNAL Problem [msg: "All slice classes must have this proc."]; }; NoOpUnionParts: PUBLIC GGModelTypes.SliceUnionPartsProc = { SIGNAL Problem [msg: "All slice classes must have this proc."]; }; NoOpDifferenceParts: PUBLIC GGModelTypes.SliceDifferencePartsProc = { SIGNAL Problem [msg: "All slice classes must have this proc."]; }; NoOpMovingParts: PUBLIC GGModelTypes.SliceMovingPartsProc = { SIGNAL Problem [msg: "All slice classes must have this proc."]; }; NoOpAugmentParts: PUBLIC GGModelTypes.SliceAugmentPartsProc = { SIGNAL Problem [msg: "All slice classes must have this proc."]; }; NoOpAlterParts: PUBLIC GGModelTypes.SliceAlterPartsProc = { RETURN[NIL]; }; NoOpSetSelectedFields: PUBLIC GGModelTypes.SliceSetSelectedFieldsProc = { }; NoOpPointsInDescriptor: PUBLIC GGModelTypes.SlicePointsInDescriptorProc = { pointGen _ NIL; }; NoOpPointPairsInDescriptor: PUBLIC GGModelTypes.SlicePointPairsInDescriptorProc = { pointPairGen _ NIL; }; NoOpSegmentsInDescriptor: PUBLIC GGModelTypes.SliceSegmentsInDescriptorProc = { segGen _ NIL; }; NoOpWalkSegments: PUBLIC GGModelTypes.SliceWalkSegmentsProc = { sliceD _ GGSliceOps.NewParts[slice, NIL, none]; }; NoOpNextPoint: PUBLIC GGModelTypes.SliceNextPointProc = { pointAndDone.done _ TRUE; }; NoOpNextPointPair: PUBLIC GGModelTypes.SliceNextPointPairProc = { pointPairAndDone.done _ TRUE; }; NoOpNextSegment: PUBLIC GGModelTypes.SliceNextSegmentProc = { seg _ NIL; }; NoOpClosestPoint: PUBLIC GGModelTypes.SliceClosestPointProc = { ERROR Problem [msg: "All slice classes must have this proc."]; }; NoOpClosestJointToHitData: PUBLIC GGModelTypes.SliceClosestJointToHitDataProc = { [point, ----, normal, ----, ----] _ GGSliceOps.ClosestPoint[sliceD, mapPoint, GGUtility.plusInfinity]; jointD _ sliceD; }; NoOpClosestPointAndTangent: PUBLIC GGModelTypes.SliceClosestPointAndTangentProc = { success _ FALSE; tangent _ [1.0, 0.0]; bestDist _ Real.LargestNumber; bestPoint _ [0.0, 0.0]; }; NoOpClosestSegment: PUBLIC GGModelTypes.SliceClosestSegmentProc = { ERROR Problem [msg: "All slice classes must have this proc."]; }; NoOpFilledPathsUnderPoint: PUBLIC GGModelTypes.SliceFilledPathsUnderPointProc = { hitData _ NIL; moreHitDatas _ NIL; }; NoOpLineIntersection: PUBLIC GGModelTypes.SliceLineIntersectionProc = { points _ NIL; pointCount _ 0; }; NoOpCircleIntersection: PUBLIC GGModelTypes.SliceCircleIntersectionProc = { points _ NIL; pointCount _ 0; }; NoOpHitDataAsSimpleCurve: PUBLIC GGModelTypes.SliceHitDataAsSimpleCurveProc = { simpleCurve _ NIL; }; NoOpSetDefaults: PUBLIC GGModelTypes.SliceSetDefaultsProc = { }; NoOpSetStrokeWidth: PUBLIC GGModelTypes.SliceSetStrokeWidthProc = { }; NoOpGetStrokeWidth: PUBLIC GGModelTypes.SliceGetStrokeWidthProc = { }; NoOpSetStrokeEnd: PUBLIC GGModelTypes.SliceSetStrokeEndProc = { }; NoOpGetStrokeEnd: PUBLIC GGModelTypes.SliceGetStrokeEndProc = { }; NoOpSetStrokeJoint: PUBLIC GGModelTypes.SliceSetStrokeJointProc = { }; NoOpGetStrokeJoint: PUBLIC GGModelTypes.SliceGetStrokeJointProc = { }; NoOpSetStrokeColor: PUBLIC GGModelTypes.SliceSetStrokeColorProc = { }; NoOpGetStrokeColor: PUBLIC GGModelTypes.SliceGetStrokeColorProc = { }; NoOpSetFillColor: PUBLIC GGModelTypes.SliceSetFillColorProc = { }; NoOpGetFillColor: PUBLIC GGModelTypes.SliceGetFillColorProc = { color _ NIL; }; NoOpSetArrows: PUBLIC GGModelTypes.SliceSetArrowsProc = { }; NoOpGetArrows: PUBLIC GGModelTypes.SliceGetArrowsProc = { }; NoOpSetDashed: PUBLIC GGModelTypes.SliceSetDashedProc = { }; NoOpGetDashed: PUBLIC GGModelTypes.SliceGetDashedProc = { }; GetMaster: PROC [fullName: Rope.ROPE, router: MsgRouter] RETURNS [ipMaster: Interpress.Master, success: BOOL _ FALSE] = { val: SymTab.Val; [success, val] _ SymTab.Fetch[masterTable, fullName]; IF success THEN ipMaster _ NARROW[val] ELSE { [ipMaster, success] _ GGFileOps.OpenInterpressOrComplain["IP read", router, fullName]; IF success THEN [] _ SymTab.Insert[masterTable, fullName, ipMaster]; }; }; Init: PROC [] = { contextTable _ RefTab.Create[]; masterTable _ SymTab.Create[]; cache _ FunctionCache.Create[maxEntries: 100]; }; cache: FunctionCache.Cache; contextTable: RefTab.Ref; -- cache of filled ImagerMemory contexts masterTable: SymTab.Ref; -- cache of Interpress.IPMasters Init[]; END.  GGSliceImplF.mesa Copyright ำ 1988 by Xerox Corporation. All rights reserved. Pier, July 17, 1991 11:25 am PDT Bier, January 31, 1991 5:41 pm PST Contents: Implements IP slice classes in Gargoyle. Interpress-Only Data and Procs edge: [-1..ipMaxEdges) THIS DEF CAUSES A FATAL COMPILER ERROR IN PASS FIVE !! Fundamentals Drawing Transforming Textual Description Parts Part Generators Hit Testing Style The interpress master is assumed to be in units of pixelsPerUnit pixels. The memory context has 1 unit = 1/72.0 inch. IF ipMaster=NIL THEN RETURN; -- we need to make some sort of slice, even if the Interpress file is not found Following code required to get the context state to the default assumed by Interpress.DoPage The transformations should be set up so that MaskPixel[pa] will look correct in a context where the units are pixels if transform is concatenated before pa is drawn. In the common case, transform will be a pure translation. The memory context has 1 unit = 1/72.0 inch. Interpress Class Procs Fundamentals GGModelTypes.SliceBoundBoxProc IPs always keep a current bound box and tight box GGModelTypes.SliceBoundBoxProc IPs always keep a current bound box and tight box GGModelTypes.SliceCopyProc Just ignore parts and copy the whole box. GGModelTypes.SliceRestoreProc from and to must be nearly identically structured slices, probably because from was made as a copy of to. Restore the contents of "to", getting all non-REF values from "from". Good luck. to.nullDescriptor is always valid to.fullDescriptor is unused Drawing GGModelTypes.SliceDrawTransformProc GGModelTypes.SliceDrawSelectionFeedbackProc The feedback rectangle can be rotated. It is the tight bounding box of the rectangular interpress master. It is derived from the local box. Drawing Utilities Imager.Object.draw PROC NOT called with the dc transformation already set to object coordinates Transforming GGModelTypes.SliceTransformProc Textual Description GGModelTypes.SliceDescribeProc GGModelTypes.SliceFileoutProc Write a description of yourself onto stream f. Write the interpress into a Rope.ROPE. Send the rope to stream f. GGModelTypes.SliceFileinProc Read a description of yourself from stream f. next make an IPSlice and return it Hit Testing GGModelTypes.SliceEmptyPartsProc GGModelTypes.SliceNewPartsProc GGModelTypes.SliceUnionPartsProc GGModelTypes.SliceMovingPartsProc If anything is moving, everything is dragging. Otherwise on background. GGModelTypes.SliceAugmentPartsProc For every edge, add its corresponding points IP slices have only one "segment" whose entire purpose is to contain properties in seg.props IP slices have only one "segment" whose entire purpose is to contain properties in seg.props GGModelTypes.SliceClosestPointProc hitData _ NIL; -- automatically done by compiler GGModelTypes.SliceClosestSegmentProc hitData _ NIL; -- automatically done by compiler NoOp Parts Routines NoOp Style SIGNAL Problem [msg: "Unimplemented operation for this slice class. Proceed."]; SIGNAL Problem [msg: "Unimplemented operation for this slice class. Proceed."]; SIGNAL Problem [msg: "Unimplemented operation for this slice class. Proceed."]; SIGNAL Problem [msg: "Unimplemented operation for this slice class. Proceed."]; SIGNAL Problem [msg: "Unimplemented operation for this slice class. Proceed."]; SIGNAL Problem [msg: "Unimplemented operation for this slice class. Proceed."]; SIGNAL Problem [msg: "Unimplemented operation for this slice class. Proceed."]; SIGNAL Problem [msg: "All slice classes must have this proc."]; SIGNAL Problem [msg: "All slice classes must have this proc."]; SIGNAL Problem [msg: "Unimplemented operation for this slice class. Proceed."]; SIGNAL Problem [msg: "Unimplemented operation for this slice class. Proceed."]; SIGNAL Problem [msg: "Unimplemented operation for this slice class. Proceed."]; SIGNAL Problem [msg: "Unimplemented operation for this slice class. Proceed."]; SIGNAL Problem [msg: "Unimplemented operation for this slice class. Proceed."]; SIGNAL Problem [msg: "Unimplemented operation for this slice class. Proceed."]; ส1%˜codešœ™Kšœ<™[context: Imager.Context, strokeJoint: Imager.StrokeJoint]šœ!˜!K˜Kšœ"˜"šžœžœžœ˜šžœžœžœž˜'šžœžœžœž˜0Kšœ,˜,Kšœ,˜,Kšœ,˜,Kšœ,˜,Kšžœžœ˜——Kšžœ˜K˜—K˜—Kšœ"˜"K˜K˜—K™Kšœ ™ š œžœ$žœY˜‘K™Kšœžœ ˜$KšœL˜LKšœ?˜?KšœD˜DKšœ˜K˜K˜—K™Kšœ™š œžœžœ žœ˜HK™Kšœ žœ˜Kšœ˜Kšœžœ˜Kšžœžœžœžœ˜1Kšœ žœ˜šžœžœžœž˜%šžœžœ˜Kšœ˜Kšœ˜K˜—Kšžœ˜—šžœžœžœž˜$šžœžœ˜Kšœ˜Kšœ˜K˜—Kšžœ˜—Kšžœžœžœ"˜@Kšžœ žœ žœžœ˜@Kšœ.˜.K˜K˜—š  œžœžœžœžœ žœ˜RKšœžœ ˜'Kšœ žœ˜Kšžœžœ(˜BKšžœžœžœ%˜CKšžœžœ˜ Kšœ.˜.K˜K˜—š œžœžœžœ˜0K™K™.š œžœ˜)Kšœ#Ÿ7˜ZKšœ$˜$K˜—Kšœžœ ˜$Kšœ˜Kšœžœžœ˜Kšœ žœ˜Kšœžœ˜K˜Kšœ'˜'Kšœ4˜4Kšœ žœžœ˜Kšœ-˜-Kšœ žœžœ˜Kšœ(˜(Kšœ˜Kšœ/˜/Kšœ žœžœ˜K˜šžœžœ˜Kš !ะbk ™&Kšœžœžœ˜KšœQ˜QKšœ+˜+Kšœ˜Kšœ(˜(K–[base: ROPE]šœ%˜%˜Kš ™—Kšœ&˜&Kšœ˜K˜K˜—Kšœ˜Mšœ)žœ˜.Mš žœžœžœ,žœ žœžœŸ˜nKšœ˜K˜K˜—šœžœžœžœ žœ%žœžœ˜pK™Kšœžœ˜"Kšœžœ˜Kšœ žœžœ˜K˜/Kšœžœ˜Kšœ žœ˜Kšœžœ˜K˜"Kš œžœžœžœžœ˜K˜Kšœ˜Kšœ,˜,šžœžœ˜Kšœ%˜%šžœžœ˜Kšœ ˜ K˜—šžœ˜Kšœ2˜2Kšœ0Ÿ%˜UK˜—K˜—Kšžœžœ žœ˜+šžœžœ˜"šžœ žœ˜(Kšœ2˜2KšœžœŸ#˜8K˜—Kšœ žœžœ žœŸQ˜K˜—šžœžœ˜Kšœ žœ˜Kšœ*˜*Kšœ=˜=Kšžœžœ žœžœ˜K˜šžœžœ˜Kš -™-K˜$K˜Kšœ žœžœ˜-K˜Kšœ+žœ˜0K˜Kš "™"Kšœcžœ˜iK˜—šžœ˜KšœPžœ˜YK˜—K˜—šžœ˜KšœRžœ˜YK˜—šžœžœŸ˜3Kšœ žœžœ˜Kšœ$˜$Kšœ0˜0Kš œžœ žœžœžœ˜>Kšœ˜šžœžœžœ˜Kšœžœ˜.š žœžœžœžœžœžœž˜@Mšœ žœ˜(Mšžœ˜—K˜—K˜—K˜—K™Kšœ ™ šœžœžœž œ˜IKšœ ™ Kšžœ˜K˜K˜—šœžœžœž œ˜LKšžœ˜!K˜K˜—š  œžœžœžœžœ˜iKšœ™Kšœžœ ˜'Kš œžœžœžœ žœžœ˜Ošžœž˜˜ Kšžœžœ#ž˜AKšžœžœžœ!ž˜CKšžœžœ˜ K˜—šœ ˜ šžœžœ˜Kšœ"žœ˜'K˜—šžœ˜Kšœ žœžœ˜Kšœ˜Kšœ žœ˜K˜Kšœžœ ˜'KšœŸœŸœh˜sKšžœžœ žœžœ˜Kšœžœ ˜Kšžœžœžœ˜&Kšœ%žœ˜*K˜—Kšœžœ˜K˜—šœ žœžœ˜%Kšœ žœ˜%Kšœžœ˜K˜—šœ˜Kšœžœžœ˜Kšœžœžœ˜K˜—Kšœ/˜/šœ Ÿ˜ K˜—Kšžœžœ˜—Kšœ5˜5K˜K˜—š œžœ4žœ˜kKšœ ™ Kšœžœ˜)Kšœžœ˜)Kšœ˜Kš žœ žœžœ žœžœžœ˜+Kšžœžœžœžœ ˜*Kšžœžœžœžœ ˜*Kš œ žœžœžœ žœžœ˜Gšžœžœžœž˜%Kšœ(žœ˜>Kšžœ˜—šžœžœžœž˜$Kšœ&žœ˜;Kšžœ˜—Kšœ$žœ˜8Kšœ=˜=K˜K˜—š œžœ4žœ˜kKšœžœ˜)Kšœžœ˜)Kšœ˜Kš žœ žœžœ žœžœžœ˜+Kš žœžœžœžœžœžœ ˜@Kš œ žœžœžœ žœžœ˜Gšžœžœžœž˜%Kš œžœžœžœžœ˜NKšžœ˜—šžœžœžœž˜$Kš œžœžœžœžœ˜KKšžœ˜—Kš œžœžœžœžœ˜HKšœ>˜>K˜K˜—š œžœ|žœ9˜ฯKšœ!™!K™HKš œžœžœžœ žœžœ žœ˜^Kš œžœžœžœ žœžœ žœ˜dšžœžœŸ˜6Kšœžœžœ˜Kšœžœžœ˜Kšœžœ˜Kšœ˜—šžœŸ˜Kšœžœžœ˜#Kšœžœžœ˜$Kšœžœ˜ K˜—KšœA˜AKšœ6žœ˜;Kšœ5˜5Kšœ˜K˜—šœžœ8žœ˜oKšœ"™"Kšœžœ˜(KšœžœY˜pK™,šžœžœžœž˜$šžœžœžœž˜&Kšœžœžœ˜=Kšœžœžœ˜=Kšœžœžœ˜=Kšœžœžœ˜=—Kšžœžœ˜Kšžœ˜—Kšœ;˜;K˜—K˜šœžœžœ˜[Kšœžœ˜&Kšœ žœ$žœ˜9šžœžœžœž˜)Kšžœžœ#˜>Kšžœ˜—K˜K˜—šœžœ7˜UKšœžœ˜&Kšœžœ˜+Kšœžœžœ˜šžœžœžœž˜)šžœžœ˜Kšœ+˜+Kšžœžœžœ˜K˜—Kšžœ˜—K˜—K˜šœžœžœ'˜gKšœžœ˜&Kšœžœ(žœŸ˜\šžœžœžœž˜'Kšžœžœ+˜DKšžœ˜—K˜K˜—šœžœžœ˜]K™\Kšœžœ˜&Kš œ žœžœ žœžœ žœŸ˜hšžœžœžœž˜'KšžœžœŸ ˜JKšžœ˜—K˜K˜—šœžœ$žœ˜]K™\Kšœžœ ˜$Kšžœžœžœžœžœ žœžœ ˜jK˜K˜—š œžœ*žœ.˜rKšžœžœžœžœžœžœžœ˜6šžœ˜Kšœžœ˜2Kšœ˜Kšœ˜Kšœ˜—K˜K˜—š œžœ*žœ.˜pšžœ žœžœžœ˜+Kšœžœ˜Kšžœ˜K˜—šžœ˜Kšœ*˜*Kšœžœ˜+Kšœžœ˜(Kšœžœ˜Kšœžœ˜šžœ!žœž˜@KšžœžœžœŸ+˜OKšžœ˜—Kšœ/˜/Kšœ ˜ Kšœžœžœžœ Ÿ4˜nK˜—K˜K˜—š œžœžœžœ˜LKšœ%˜%šœ9žœž˜JK˜K˜K˜K˜Kšžœž˜Kšœ˜—K˜K˜—šœžœ2žœ6˜„šžœžœžœžœ˜3Kšœžœ˜Kšžœ˜K˜—šžœ˜Kšœ.˜.Kšœžœ˜+Kšœžœ˜(Kšœ%˜%Kšœ;˜;Kšœžœ˜Kšœžœ˜šžœ%žœž˜CKšžœžœžœŸ)˜LKšžœ˜—šžœžœŸ2˜Dšœ˜KšœM˜MKšœM˜MK˜—šœ˜KšœM˜MKšœM˜MK˜—šœ˜KšœM˜MKšœM˜MK˜—šœ˜KšœM˜MKšœM˜MK˜—Kšžœžœ"˜3—Kšœ*˜*Kšœžœžœžœ Ÿ3˜uK˜—K˜K˜—šœžœ8žœžœžœ'žœžœ žœžœ˜ลK™"Kšœ žœŸ!™1Kšœžœ˜Kšœ˜Kšœžœ ˜$Kšœ3˜3Kšœ}žœžœŸ˜คšžœ'žœ˜/Kšœ˜Kšœžœ˜(KšœI˜IKšœžœžœ žœ1Ÿ3˜ŒKšœ†˜†šžœ žœ˜Kšœ?˜?Kšœ4˜4Kšœžœ@˜YK˜—K˜—K˜K˜—šœžœ8žœžœžœ(žœžœ žœžœ˜ศKšœ$™$Kšœ žœŸ!™1Kšœ˜Kšœžœ ˜$Kšœ3˜3Kšœ}žœžœŸ˜คšžœ'žœ˜/Kšœ˜Kšœžœ˜Kšœžœ ˜$Kšœžœ˜(KšœJŸ ˜jKšœžœžœ žœ1Ÿ3˜ŒKšœˆ˜ˆšžœ žœ˜Kšœ?˜?Kšœ4˜4Kšœžœ@˜YKšœ1˜1K˜—K˜—K˜—K˜šœžœžœ)žœžœ žœžœžœžœžœžœžœžœ˜œKšœžœ ˜$Kšœฅœ@˜HKšœ%˜%Kšœ žœžœ˜K˜šœž˜ Kšœž˜ Kšœฅœž˜Kšœฅœž˜Kšœฅœž˜Kšœฅœ˜K˜—šžœ žœ˜Kšœ˜Kšœžœ˜ Kšœ˜K˜KšœMฅœ˜RKšœ?˜?Kšœ žœA˜NK˜—K˜K˜—š œžœ!žœž œ˜MKš žœžœžœžœžœ˜]K˜K˜—š œžœ˜+Kšœžœ˜!Kšœžœžœ˜Kšœžœžœ˜Kšœžœ˜K˜K˜—š œžœžœž œ˜?Kšœžœ˜!Kšžœ žœžœžœžœžœžœžœžœžœ˜fK˜K˜—š œžœžœžœžœ˜K˜—šœžœ0˜QKšœŸœŸœŸœF˜fKšœ˜K˜—šœžœ1˜SKšœ žœ˜K˜K˜K˜K˜—šœžœ)˜CKšžœ9˜>K˜—šœžœ0˜QKšœ žœ˜Kšœžœ˜K˜—šœžœ+˜GKšœ žœ˜K˜—šœžœ-˜KKšœ žœ˜K˜—šœžœ/˜OKšœžœ˜K˜—K™Kšœ ™ šœžœ&˜=KšžœI™OK˜—šœžœ)˜CKšžœI™OK˜—šœžœ)˜CKšžœI™OK˜—šœžœ'˜?KšžœI™OK˜—šœžœ'˜?KšžœI™OK˜—šœžœ)˜CKšžœI™OK˜—šœžœ)˜CKšžœI™OK˜—šœžœ)˜CKšžœ9™?K˜—šœžœ)˜CKšžœ9™?K˜—šœžœ'˜?KšžœI™OK˜—šœžœ'˜?KšžœI™OKšœžœ˜ K˜—š œžœ$˜9KšžœI™OK˜—š œžœ$˜9KšžœI™OK˜—š œžœ$˜9KšžœI™OK˜—š œžœ$˜9KšžœI™OK˜—K˜š  œžœžœžœ(žœžœ˜yMšœ˜Kšœ6˜6šžœ žœ žœžœ˜-KšœV˜VKšžœ žœ5˜DK˜—J˜—J˜šœžœ˜Jšœ˜Jšœ˜K–:[maxEntries: INT _ 10, maxTotalSize: INT _ 2147483647]˜.J˜—J˜K˜JšœŸ(˜BJšœŸ ˜9M˜J˜M˜Kšžœ˜—…—ญZ๎‰