<> <> <> <> <> <> <<>> DIRECTORY GGBasicTypes, GGBoundBox, GGError, GGLines, GGModelTypes, GGInterfaceTypes, GGObjects, GGSegmentTypes, GGSelect, GGShapes, GGVector, Imager, ImagerTransformation, Real, RealFns; GGBoundBoxImpl: CEDAR PROGRAM IMPORTS GGBoundBox, GGError, GGLines, GGObjects, GGSelect, GGShapes, GGVector, Imager, ImagerTransformation, RealFns EXPORTS GGBoundBox = BEGIN BoundBox: TYPE = REF BoundBoxObj; BoundBoxObj: TYPE = GGBasicTypes.BoundBoxObj; Circle: TYPE = GGBasicTypes.Circle; Edge: TYPE = GGBasicTypes.Edge; EntityGenerator: TYPE = GGModelTypes.EntityGenerator; Joint: TYPE = GGModelTypes.Joint; Line: TYPE = GGBasicTypes.Line; MaskArray: TYPE = GGBoundBox.MaskArray; Outline: TYPE = GGModelTypes.Outline; OutlineDescriptor: TYPE = GGModelTypes.OutlineDescriptor; Point: TYPE = GGBasicTypes.Point; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; Sequence: TYPE = GGModelTypes.Sequence; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceParts: TYPE = GGModelTypes.SliceParts; Traj: TYPE = GGModelTypes.Traj; TrajGenerator: TYPE = GGModelTypes.TrajGenerator; Vector: TYPE = GGBasicTypes.Vector; emptyBoundBox: PUBLIC BoundBox _ NEW[GGBasicTypes.BoundBoxObj _ [loX: Real.LargestNumber, loY: Real.LargestNumber, hiX: Real.SmallestNormalizedNumber, hiY: Real.SmallestNormalizedNumber, null: TRUE, infinite: FALSE] ]; <> CreateBoundBox: PUBLIC PROC [loX, loY, hiX, hiY: REAL, null: BOOL _ FALSE, infinite: BOOL _ FALSE] RETURNS [bBox: BoundBox] = { bBox _ NEW[BoundBoxObj _ [loX, loY, hiX, hiY, null, infinite]]; }; NullBoundBox: PUBLIC PROC [] RETURNS [bBox: BoundBox] = { bBox _ NEW[BoundBoxObj _ [0,0,0,0, TRUE, FALSE]]; }; CopyBoundBox: PUBLIC PROC [bBox: BoundBox] RETURNS [copy: BoundBox] = { copy _ NEW[BoundBoxObj _ [bBox.loX, bBox.loY, bBox.hiX, bBox.hiY, bBox.null, bBox.infinite]]; }; UpdateCopyBoundBox: PUBLIC PROC [bBox: BoundBox, from: BoundBox] = { bBox^ _ from^; }; BoundBoxOfBoxes: PUBLIC PROC [list: LIST OF BoundBox] RETURNS [bigBox: BoundBox] = { bigBox _ NEW[BoundBoxObj]; WHILE list # NIL AND list.first.null DO list _ list.rest ENDLOOP; IF list = NIL THEN {bigBox.null _ TRUE; bigBox.infinite _ FALSE; RETURN}; CopyContents[bigBox, list.first]; FOR bBoxList: LIST OF BoundBox _ list.rest, bBoxList.rest UNTIL bBoxList = NIL DO IF bBoxList.first.infinite THEN {bigBox.null _ FALSE; bigBox.infinite _ TRUE; RETURN}; EnlargeByBox[bBox: bigBox, by: bBoxList.first]; ENDLOOP; }; BoundBoxOfBoundBox: PUBLIC PROC [box: BoundBox, transform: ImagerTransformation.Transformation] RETURNS [newBox: BoundBox] = { rect: Imager.Rectangle _ RectangleFromBoundBox[box]; rect _ ImagerTransformation.TransformRectangle[transform, rect]; newBox _ BoundBoxFromRectangle[rect]; }; UpdateBoundBoxOfBoundBox: PUBLIC PROC [bBox: BoundBox, localBox: BoundBox, transform: ImagerTransformation.Transformation] = { rect: Imager.Rectangle _ RectangleFromBoundBox[localBox]; rect _ ImagerTransformation.TransformRectangle[transform, rect]; UpdateBoundBoxFromRectangle[bBox, rect]; }; BoundBoxFromRectangle: PUBLIC PROC [rect: Imager.Rectangle] RETURNS [bBox: BoundBox] = { bBox _ NEW[BoundBoxObj _ [rect.x, rect.y, rect.x + rect.w, rect.y + rect.h, FALSE, FALSE]]; }; UpdateBoundBoxFromRectangle: PUBLIC PROC [bBox: BoundBox, rect: Imager.Rectangle] = { bBox^ _ [rect.x, rect.y, rect.x + rect.w, rect.y + rect.h, FALSE, FALSE]; }; RectangleFromBoundBox: PUBLIC PROC [bBox: BoundBox] RETURNS [rect: Imager.Rectangle] = { IF bBox.infinite THEN ERROR; IF bBox.null THEN rect _ [bBox.loX, bBox.loY, 0.0, 0.0] ELSE rect _ [bBox.loX, bBox.loY, bBox.hiX - bBox.loX, bBox.hiY - bBox.loY]; }; BoundBoxOfPixelArray: PUBLIC PROC [pa: Imager.PixelArray] RETURNS [bBox: BoundBox] = { xPixels, yPixels: REAL; xPixels _ pa.sSize; yPixels _ pa.fSize; bBox _ GGBoundBox.CreateBoundBox[0, 0, xPixels, yPixels]; bBox _ GGBoundBox.BoundBoxOfBoundBox[bBox, pa.m]; }; BoundBoxOfBitMap: PUBLIC PROC [base: LONG POINTER, wordsPerLine: NAT, sMin, fMin, sSize, fSize: NAT, tx, ty: INTEGER _ 0] RETURNS [bBox: BoundBox] = { <> }; <<>> <> AllowForJoints: PUBLIC PROC [box: BoundBox] = { cpHalf: REAL _ GGModelTypes.halfJointSize + 1; box^ _ [loX: box.loX-cpHalf, loY: box.loY-cpHalf, hiX: box.hiX+cpHalf, hiY: box.hiY+cpHalf, null: box.null, infinite: box.infinite]; }; EnlargeByBox: PUBLIC PROC [bBox: BoundBox, by: BoundBox] = { IF bBox.infinite OR by.null THEN RETURN; IF by.infinite THEN {bBox.infinite _ TRUE; bBox.null _ FALSE; RETURN}; IF bBox.null THEN {CopyContents[to: bBox, from: by]; RETURN}; bBox.loX _ MIN[bBox.loX, by.loX]; bBox.hiX _ MAX[bBox.hiX, by.hiX]; bBox.loY _ MIN[bBox.loY, by.loY]; bBox.hiY _ MAX[bBox.hiY, by.hiY]; }; EnlargeByPoint: PUBLIC PROC [bBox: BoundBox, point: Point] = { IF bBox.infinite THEN RETURN; IF bBox.null THEN { bBox.loX _ bBox.hiX _ point.x; bBox.loY _ bBox.hiY _ point.y; bBox.null _ FALSE; RETURN; }; bBox.loX _ MIN[bBox.loX, point.x]; bBox.hiX _ MAX[bBox.hiX, point.x]; bBox.loY _ MIN[bBox.loY, point.y]; bBox.hiY _ MAX[bBox.hiY, point.y]; }; EnlargeByVector: PUBLIC PROC [bBox: BoundBox, vector: Vector] = { <> IF bBox.null OR bBox.infinite THEN RETURN; IF vector.x <0.0 THEN bBox.loX _ bBox.loX + vector.x ELSE bBox.hiX _ bBox.hiX + vector.x; IF vector.y <0.0 THEN bBox.loY _ bBox.loY + vector.y ELSE bBox.hiY _ bBox.hiY + vector.y; }; EnlargeByOffset: PUBLIC PROC [bBox: BoundBox, offset: REAL] = { <> IF bBox.null OR bBox.infinite THEN RETURN; bBox.loX _ bBox.loX - offset; bBox.hiX _ bBox.hiX + offset; bBox.loY _ bBox.loY - offset; bBox.hiY _ bBox.hiY + offset; }; UpdateBoundBox: PUBLIC PROC [bBox: BoundBox, loX, loY, hiX, hiY: REAL] = { bBox.loX _ loX; bBox.loY _ loY; bBox.hiX _ hiX; bBox.hiY _ hiY; bBox.null _ FALSE; bBox.infinite _ FALSE; }; <> PointIsInBox: PUBLIC PROC [test: Point, box: GGBasicTypes.BoundBoxObj] RETURNS [BOOL] = { RETURN[ NOT (test.x < box.loX OR test.x > box.hiX OR test.y < box.loY OR test.y > box.hiY) ]; }; <> <<>> BoundBoxOfSelected: PUBLIC PROC [scene: Scene, selectClass: GGInterfaceTypes.SelectionClass _ normal] RETURNS [bigBox: BoundBox] = { entityGen: EntityGenerator; entity: REF ANY; nextBox: BoundBox; bigBox _ GGBoundBox.NullBoundBox[]; entityGen _ GGSelect.SelectedStuff[scene, selectClass]; FOR entity _ GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO WITH entity SELECT FROM sliceD: SliceDescriptor => { nextBox _ sliceD.slice.class.getBoundBox[sliceD.slice, sliceD.parts]; }; outlineD: OutlineDescriptor => { nextBox _ outlineD.slice.class.getBoundBox[outlineD.slice, outlineD.parts]; }; ENDCASE => ERROR; EnlargeByBox[bBox: bigBox, by: nextBox]; ENDLOOP; }; useSelected: BOOL _ FALSE; BoundBoxOfMoving: PUBLIC PROC [scene: Scene, selectClass: GGInterfaceTypes.SelectionClass _ normal] RETURNS [bigBox: BoundBox] = { <> entityGen: EntityGenerator; entity: REF ANY; nextBox: BoundBox; IF useSelected THEN RETURN[BoundBoxOfSelected[scene, selectClass]]; bigBox _ GGBoundBox.NullBoundBox[]; entityGen _ GGSelect.SelectedStuff[scene, selectClass]; FOR entity _ GGObjects.NextEntity[entityGen], GGObjects.NextEntity[entityGen] UNTIL entity = NIL DO WITH entity SELECT FROM sliceD: SliceDescriptor => { movingParts: SliceParts _ sliceD.slice.class.movingParts[sliceD.slice, sliceD.parts]; nextBox _ sliceD.slice.class.getBoundBox[sliceD.slice, movingParts]; }; outlineD: OutlineDescriptor => { movingParts: SliceParts _ outlineD.slice.class.movingParts[outlineD.slice, outlineD.parts]; nextBox _ outlineD.slice.class.getBoundBox[outlineD.slice, movingParts]; }; ENDCASE => ERROR; EnlargeByBox[bBox: bigBox, by: nextBox]; ENDLOOP; }; <> <<>> CopyContents: PROC [to: BoundBox, from: BoundBox] = { to^ _ [from.loX, from.loY, from.hiX, from.hiY, from.null, from.infinite]; }; <> <> <<>> <> <> <<[arrowHeight, arrowHalfWidth] _ GGShapes.ArrowSize[MAX[firstWidth, lastWidth]];>> < GGModelTypes.halfJointSize THEN {>> <> <> <> <> <> <<};>> <<};>> <<>> <<>> <> <<>> DrawBoundBox: PUBLIC PROC [dc: Imager.Context, bBox: BoundBox] = { GGShapes.DrawRectangle[dc, bBox.loX, bBox.loY, bBox.hiX, bBox.hiY]; }; EraseWithinBoundBox: PUBLIC PROC [dc: Imager.Context, bBox: BoundBox] = { EraseWithinBoundBoxAux: PROC = { rect: Imager.Rectangle _ [x: bBox.loX, y: bBox.loY, w: bBox.hiX-bBox.loX, h: bBox.hiY-bBox.loY]; Imager.SetColor[dc, Imager.white]; Imager.MaskRectangle[dc, rect]; }; IF bBox.null OR bBox.infinite THEN RETURN; Imager.DoSaveAll[dc, EraseWithinBoundBoxAux]; }; Clip: PUBLIC PROC [dc: Imager.Context, bBox: BoundBox] = { IF bBox.null THEN ERROR; IF bBox.infinite THEN RETURN; Imager.ClipRectangle[dc, [bBox.loX, bBox.loY, (bBox.hiX-bBox.loX), (bBox.hiY-bBox.loY)]]; }; <> <<>> NearestPoint: PUBLIC PROC [bBox: BoundBox, testPoint: Point, tolerance: REAL _ 1E6, mask: MaskArray _ ALL[TRUE] ] RETURNS [bestDist: REAL, bestJoint: NAT, bestPoint: Point, success: BOOL] = { <> thisPoint: Point; thisDist2, bestDist2: REAL; tolerance2: REAL _ tolerance*tolerance; index: NAT _ 0; IF bBox.null OR bBox.infinite THEN RETURN[bestDist: 99999.0, bestJoint: 999, bestPoint: [-1.0, -1.0], success: FALSE]; bestPoint _ [-1.0, -1.0]; bestDist2 _ 1E12; -- better be big enough bestJoint _ 9999; success _ FALSE; FOR index IN [0..4) DO IF mask[index] THEN { thisPoint _ BoxPoint[bBox, index]; thisDist2 _ GGVector.DistanceSquared[thisPoint, testPoint]; IF thisDist2 < bestDist2 THEN { bestDist2 _ thisDist2; bestJoint _ index; bestPoint _ thisPoint; IF bestDist2 < tolerance2 THEN success _ TRUE; }; }; ENDLOOP; bestDist _ RealFns.SqRt[bestDist2]; }; NearestSegment: PUBLIC PROC [bBox: BoundBox, testPoint: Point, tolerance: REAL _ 1E6, mask: MaskArray _ ALL[TRUE] ] RETURNS [bestDist: REAL, bestSeg: NAT, bestPoint: Point, success: BOOL] = { <> thisDist2, bestDist2: REAL; thisPoint: Point; tolerance2: REAL _ tolerance*tolerance; index: NAT _ 0; IF bBox.null OR bBox.infinite THEN RETURN[bestDist: 99999.0, bestSeg: 999, bestPoint: [-1.0, -1.0], success: FALSE]; bestPoint _ [-1.0, -1.0]; bestDist2 _ 1E12; -- better be big enough bestSeg _ 9999; success _ FALSE; FOR index IN [0..4) DO IF mask[index] THEN { FillBoxEdge[globalBoxEdge, bBox, index]; thisPoint _ GGLines.NearestPointOnEdge[testPoint, globalBoxEdge]; thisDist2 _ GGVector.DistanceSquared[thisPoint, testPoint]; IF thisDist2 < bestDist2 THEN { bestDist2 _ thisDist2; bestSeg _ index; bestPoint _ thisPoint; IF thisDist2 < tolerance2 THEN success _ TRUE; }; }; ENDLOOP; bestDist _ RealFns.SqRt[bestDist2]; }; LineMeetsBoundBox: PUBLIC PROC [bBox: BoundBox, line: Line] RETURNS [points: LIST OF Point, pointCount: NAT] = { noHit: BOOL; hits: ARRAY[0..3] OF BOOL; hitPoints: ARRAY[0..3] OF Point; hitPoint: Point; epsilon: REAL = 0.072; <> points _ NIL; pointCount _ 0; FOR i: NAT IN [0..3] DO hits[i] _ FALSE; ENDLOOP; FOR i: NAT IN [0..2] DO FillBoxEdge[globalEdge, bBox, i]; [hitPoints[i], noHit] _ GGLines.LineMeetsEdge[line, globalEdge]; -- global scratch edge IF noHit THEN LOOP; IF i>0 AND hits[i-1] THEN { IF ABS[hitPoints[i].x - hitPoints[i-1].x] < epsilon THEN LOOP; IF ABS[hitPoints[i].y - hitPoints[i-1].y] < epsilon THEN LOOP; }; points _ CONS[hitPoint, points]; pointCount _ pointCount + 1; hits[i] _ TRUE; REPEAT FINISHED => { FillBoxEdge[globalEdge, bBox, 3]; [hitPoints[3], noHit] _ GGLines.LineMeetsEdge[line, globalEdge]; IF noHit THEN GOTO End; IF hits[2] THEN { IF ABS[hitPoints[3].x - hitPoints[2].x] < epsilon THEN GOTO End; IF ABS[hitPoints[3].y - hitPoints[2].y] < epsilon THEN GOTO End; }; IF hits[0] THEN { IF ABS[hitPoints[3].x - hitPoints[0].x] < epsilon THEN GOTO End; IF ABS[hitPoints[3].y - hitPoints[0].y] < epsilon THEN GOTO End; }; points _ CONS[hitPoint, points]; pointCount _ pointCount + 1; hits[3] _ TRUE; EXITS End => NULL; }; ENDLOOP; IF pointCount > 2 THEN ERROR; }; CircleMeetsBoundBox: PUBLIC PROC [bBox: BoundBox, circle: Circle] RETURNS [points: LIST OF Point, pointCount: NAT] = { <> SIGNAL GGError.Problem[msg: "Not yet implemented"]; }; BoxPoint: PROC [bBox: BoundBox, index: NAT] RETURNS [point: Point] ~ { << [Artwork node; type 'ArtworkInterpress on' to command tool] >> <> IF bBox.null OR bBox.infinite THEN ERROR; SELECT index FROM 0 => point _ [bBox.loX, bBox.loY]; 1 => point _ [bBox.loX, bBox.hiY]; 2 => point _ [bBox.hiX, bBox.hiY]; 3 => point _ [bBox.hiX, bBox.loY]; ENDCASE => ERROR; }; <> <> <