DIRECTORY GGBasicTypes, GGBoxSlice, GGBoundBox, GGSlice, GGLines, GGInterfaceTypes, GGModelTypes, GGShapes, GGParseIn, GGParseOut, GGTransform, Imager, ImagerTransformation, IO, Rope; GGBoxSliceImpl: CEDAR PROGRAM IMPORTS GGBoundBox, GGSlice, GGLines, GGShapes, GGParseIn, GGParseOut, GGTransform, Imager, ImagerTransformation, IO EXPORTS GGBoxSlice = BEGIN Point: TYPE = GGBasicTypes.Point; PointGenerator: TYPE = REF PointGeneratorObj; PointGeneratorObj: TYPE = GGModelTypes.PointGeneratorObj; PointPairGenerator: TYPE = REF PointPairGeneratorObj; PointPairGeneratorObj: TYPE = GGModelTypes.PointPairGeneratorObj; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorObj: TYPE = GGModelTypes.SliceDescriptorObj; SliceParts: TYPE = GGModelTypes.SliceParts; SliceObj: TYPE = GGModelTypes.SliceObj; SliceClass: TYPE = GGModelTypes.SliceClass; SliceClassObj: TYPE = GGModelTypes.SliceClassObj; BoundBox: TYPE = GGModelTypes.BoundBox; SelectMode: TYPE = GGModelTypes.SelectMode; ExtendMode: TYPE = GGModelTypes.ExtendMode; Corner: TYPE = GGBoxSlice.Corner; Edge: TYPE = GGBoxSlice.Edge; BoxData: TYPE = REF BoxDataObj; BoxDataObj: TYPE = RECORD [ box: BoundBox, transform: ImagerTransformation.Transformation, strokeWidths: ARRAY [1..4] OF REAL -- in order left, top, right, bottom ]; BoxParts: TYPE = REF BoxPartsObj; BoxPartsObj: TYPE = RECORD [ entire: BOOL, -- means entire box is selected (traj or topLevel) corners: LIST OF Corner, -- which corners of box are selected. edges: LIST OF Edge -- which edges of box are selected. ]; BoxHitData: TYPE = REF BoxHitDataObj; BoxHitDataObj: TYPE = RECORD [ corner: Corner, -- which corner of box is hit. Filled in by BoxClosestPoint, BoxClosestSegment edge: Edge -- which edge of box is hit. Filled in by BoxClosestPoint, BoxClosestSegment ]; Problem: PUBLIC SIGNAL [msg: Rope.ROPE] = CODE; BuildBoxSliceClass: PUBLIC PROC [] RETURNS [class: SliceClass] = { class _ NEW[SliceClassObj _ [ type: $Box, boundBox: BoxBoundBox, copy: BoxCopy, draw: BoxDraw, drawTransform: BoxDrawTransform, drawSelectionFeedback: BoxDrawSelectionFeedback, transform: BoxTransform, emptyParts: BoxEmptyParts, newParts: BoxNewParts, addParts: BoxAddParts, removeParts: BoxRemoveParts, pointsInDescriptor: BoxPointsInDescriptor, pointPairsInDescriptor: BoxPointPairsInDescriptor, nextPoint: BoxNextPoint, nextPointPair: BoxNextPointPair, closestPoint: BoxClosestPoint, closestPointAndTangent: NIL, closestSegment: BoxClosestSegment, fileout: BoxFileout, filein: BoxFilein, setStrokeWidth: BoxSetStrokeWidth, setStrokeColor: BoxSetStrokeColor, setFillColor: BoxSetFillColor ]]; }; BoxPointsInDescriptor: PROC [sliceD: SliceDescriptor] RETURNS [pointGen: PointGenerator] = { parts: BoxParts _ NARROW[sliceD.parts]; pointGen _ NEW[PointGeneratorObj _ [sliceD, 0, 0, NIL] ]; -- toGo and index now used IF parts.entire THEN pointGen.toGo _ 4 ELSE FOR corner: LIST OF Corner _ parts.corners, corner.rest UNTIL corner = NIL DO pointGen.toGo _ pointGen.toGo + 1; ENDLOOP; }; BoxPointPairsInDescriptor: PROC [sliceD: SliceDescriptor] RETURNS [pointPairGen: PointPairGenerator] = { parts: BoxParts _ NARROW[sliceD.parts]; pointPairGen _ NEW[PointPairGeneratorObj _ [sliceD, 0, 0, NIL] ]; -- toGo and index now used IF parts.entire THEN pointPairGen.toGo _ 4 ELSE FOR edge: LIST OF Edge _ parts.edges, edge.rest UNTIL edge = NIL DO pointPairGen.toGo _ pointPairGen.toGo + 1; ENDLOOP; }; BoxNextPoint: PROC [pointGen: PointGenerator] RETURNS [pointAndDone: GGModelTypes.PointAndDone] = { sliceD: SliceDescriptor _ pointGen.sliceD; slice: Slice _ sliceD.slice; boxData: BoxData _ NARROW[slice.data]; t: ImagerTransformation.Transformation _ boxData.transform; IF pointGen.toGo = 0 THEN { pointAndDone.done _ TRUE; } ELSE { boxParts: BoxParts _ NARROW[sliceD.parts]; corner: LIST OF Corner _ boxParts.corners; pointAndDone.done _ FALSE; IF boxParts.entire THEN corner _ LIST[ll,ul,ur,lr]; THROUGH [0..pointGen.index) DO corner _ corner.rest; ENDLOOP; SELECT corner.first FROM ll => pointAndDone.point _ ImagerTransformation.Transform[t, [boxData.box.loX, boxData.box.loY]]; ul => pointAndDone.point _ ImagerTransformation.Transform[t, [boxData.box.loX, boxData.box.hiY]]; ur => pointAndDone.point _ ImagerTransformation.Transform[t, [boxData.box.hiX, boxData.box.hiY]]; lr => pointAndDone.point _ ImagerTransformation.Transform[t, [boxData.box.hiX, boxData.box.loY]]; none => NULL; ENDCASE => SIGNAL Problem[msg: "Broken Invariant"]; pointGen.toGo _ pointGen.toGo - 1; pointGen.index _ pointGen.index + 1; }; }; BoxNextPointPair: PROC [pointPairGen: PointPairGenerator] RETURNS [pointPairAndDone: GGModelTypes.PointPairAndDone] = { sliceD: SliceDescriptor _ pointPairGen.sliceD; slice: Slice _ sliceD.slice; boxData: BoxData _ NARROW[slice.data]; t: ImagerTransformation.Transformation _ boxData.transform; IF pointPairGen.toGo = 0 THEN { pointPairAndDone.done _ TRUE; } ELSE { boxParts: BoxParts _ NARROW[sliceD.parts]; edge: LIST OF Edge _ boxParts.edges; pointPairAndDone.done _ FALSE; IF boxParts.entire THEN edge _ LIST[left,top,right,bottom]; THROUGH [0..pointPairGen.index) DO edge _ edge.rest; ENDLOOP; SELECT edge.first FROM left => { pointPairAndDone.lo _ ImagerTransformation.Transform[t, [boxData.box.loX, boxData.box.loY]]; pointPairAndDone.hi _ ImagerTransformation.Transform[t, [boxData.box.loX, boxData.box.hiY]]; }; top => { pointPairAndDone.lo _ ImagerTransformation.Transform[t, [boxData.box.loX, boxData.box.hiY]]; pointPairAndDone.hi _ ImagerTransformation.Transform[t, [boxData.box.hiX, boxData.box.hiY]]; }; right => { pointPairAndDone.lo _ ImagerTransformation.Transform[t, [boxData.box.hiX, boxData.box.hiY]]; pointPairAndDone.hi _ ImagerTransformation.Transform[t, [boxData.box.hiX, boxData.box.loY]]; }; bottom => { pointPairAndDone.lo _ ImagerTransformation.Transform[t, [boxData.box.hiX, boxData.box.loY]]; pointPairAndDone.hi _ ImagerTransformation.Transform[t, [boxData.box.loX, boxData.box.loY]]; }; none => NULL; ENDCASE => SIGNAL Problem[msg: "Broken Invariant"]; pointPairGen.toGo _ pointPairGen.toGo - 1; pointPairGen.index _ pointPairGen.index + 1; }; }; BoxDraw: PROC [slice: Slice, dc: Imager.Context, camera: GGInterfaceTypes.CameraData] = { boxData: BoxData _ NARROW[slice.data]; box: BoundBox _ boxData.box; DoDrawBox: PROC = { Imager.ConcatT[dc, boxData.transform]; Imager.SetColor[dc, Imager.black]; Imager.SetStrokeWidth[dc, boxData.strokeWidths[1]]; Imager.MaskVector[dc, [box.loX, box.loY], [box.loX, box.hiY]]; Imager.SetStrokeWidth[dc, boxData.strokeWidths[2]]; Imager.MaskVector[dc, [box.loX, box.hiY], [box.hiX, box.hiY]]; Imager.SetStrokeWidth[dc, boxData.strokeWidths[3]]; Imager.MaskVector[dc, [box.hiX, box.hiY], [box.hiX, box.loY]]; Imager.SetStrokeWidth[dc, boxData.strokeWidths[4]]; Imager.MaskVector[dc, [box.hiX, box.loY], [box.loX, box.loY]]; }; Imager.DoSaveAll[dc, DoDrawBox]; }; BoxSetStrokeColor: PROC [slice: Slice, parts: SliceParts, color: Imager.Color] = {}; BoxSetFillColor: PROC [slice: Slice, color: Imager.Color] = {}; BoxSetStrokeWidth: PROC [slice: Slice, parts: SliceParts, strokeWidth: REAL] = { boxParts: BoxParts _ NARROW[parts]; boxData: BoxData _ NARROW[slice.data]; IF boxParts.entire THEN { FOR i: NAT IN [1..4] DO boxData.strokeWidths[i] _ strokeWidth; ENDLOOP; } ELSE { FOR part: LIST OF Edge _ boxParts.edges, part.rest UNTIL part=NIL DO SELECT part.first FROM left => boxData.strokeWidths[1] _ strokeWidth; top => boxData.strokeWidths[2] _ strokeWidth; right => boxData.strokeWidths[3] _ strokeWidth; bottom => boxData.strokeWidths[4] _ strokeWidth; none => NULL; ENDCASE => ERROR; ENDLOOP; }; }; MakeBoxSlice: PUBLIC PROC [box: BoundBox, corner: Corner, transform: ImagerTransformation.Transformation _ GGTransform.Identity[], strokeWidth: REAL _ 1.0] RETURNS [sliceD: SliceDescriptor] = { boxSlice: Slice _ NIL; boxData: BoxData _ NEW[BoxDataObj _ [box: box, transform: transform, strokeWidths: [strokeWidth, strokeWidth, strokeWidth, strokeWidth]] ]; boxParts: BoxParts _ NEW[BoxPartsObj _ [entire: FALSE, corners: LIST[corner], edges: NIL] ]; IF box.loX > box.hiX OR box.loY > box.hiY THEN ERROR; boxSlice _ NEW[SliceObj _ [ class: GGSlice.FetchSliceClass[$Box], data: boxData, children: NIL, parent: NIL, selectedInFull: [FALSE, FALSE, FALSE, FALSE], boundBox: GGBoundBox.CreateBoundBox[0,0,0,0], hitData: NEW[BoxHitDataObj _ [corner: corner, edge: none ] ] ]]; BoxBoundBox[boxSlice]; sliceD _ NEW[SliceDescriptorObj _ [boxSlice, boxParts] ]; }; GetBox: PUBLIC PROC [slice: Slice] RETURNS [box: BoundBox] = { RETURN[IF slice.class.type#$Box THEN NIL ELSE NARROW[slice.data, BoxData].box]; }; BoxBoundBox: PROC [slice: Slice] = { halfJoint: REAL = GGModelTypes.halfJointSize + 1; boxData: BoxData _ NARROW[slice.data]; box: BoundBox _ GGBoundBox.BoundBoxOfBoundBox[boxData.box, boxData.transform]; GGBoundBox.UpdateBoundBox[slice.boundBox, box.loX-halfJoint, box.loY-halfJoint, box.hiX+halfJoint, box.hiY+halfJoint] }; BoxCopy: PROC [slice: Slice] RETURNS [copy: Slice] = { copyData: BoxData; boxData: BoxData _ NARROW[slice.data]; box: BoundBox _ GGBoundBox.CopyBoundBox[boxData.box]; transform: Imager.Transformation _ ImagerTransformation.Copy[boxData.transform]; copy _ MakeBoxSlice[box, none, transform].slice; copyData _ NARROW[copy.data]; FOR i: NAT IN [1..4] DO copyData.strokeWidths[i] _ boxData.strokeWidths[i] ENDLOOP; }; BoxDrawSelected: PRIVATE PROC [slice: Slice, dc: Imager.Context, camera: GGInterfaceTypes.CameraData] = { boxData: BoxData _ NARROW[slice.data]; box: BoundBox _ boxData.box; DoDrawBox: PROC = { Imager.SetColor[dc, Imager.black]; Imager.SetStrokeWidth[dc, 2*boxData.strokeWidths[1]]; Imager.MaskVector[dc, [box.loX, box.loY], [box.loX, box.hiY]]; Imager.SetStrokeWidth[dc, 2*boxData.strokeWidths[2]]; Imager.MaskVector[dc, [box.loX, box.hiY], [box.hiX, box.hiY]]; Imager.SetStrokeWidth[dc, 2*boxData.strokeWidths[3]]; Imager.MaskVector[dc, [box.hiX, box.hiY], [box.hiX, box.loY]]; Imager.SetStrokeWidth[dc, 2*boxData.strokeWidths[4]]; Imager.MaskVector[dc, [box.hiX, box.loY], [box.loX, box.loY]]; }; Imager.DoSaveAll[dc, DoDrawBox]; }; BoxDrawTransform: PROC [slice: Slice, parts: SliceParts, dc: Imager.Context, camera: GGInterfaceTypes.CameraData, transform: ImagerTransformation.Transformation] = { point, oppositePoint: ImagerTransformation.VEC; -- really points, not vectors boxData: BoxData _ NARROW[slice.data]; boxParts: BoxParts _ NARROW[parts]; box: BoundBox _ boxData.box; inverse, totalTransform: ImagerTransformation.Transformation; DoDrawBox: PROC = { Imager.ConcatT[dc, ImagerTransformation.Concat[boxData.transform, transform]]; GGShapes.DrawRectangle[dc, box.hiX, box.hiY, box.loX, box.loY]; }; DoDrawRectangle: PROC = { Imager.ConcatT[dc, boxData.transform]; GGShapes.DrawRectangle[dc, oppositePoint.x, oppositePoint.y, point.x, point.y]; }; IF box.null OR box.infinite THEN ERROR; IF boxParts.entire THEN { Imager.DoSaveAll[dc, DoDrawBox]; RETURN; }; IF boxParts.corners=NIL AND boxParts.edges=NIL THEN RETURN; -- no parts. Legal. IF (boxParts.corners#NIL AND boxParts.corners.rest#NIL) OR (boxParts.edges#NIL AND boxParts.edges.rest#NIL) THEN { -- more than one corner or more than one edge. Full transform. Imager.DoSaveAll[dc, DoDrawBox]; RETURN; }; IF boxParts.corners#NIL AND boxParts.edges#NIL THEN ERROR; -- can't mix corners and edges while dragging. Doesn't make sense; must be a selection error. IF boxParts.corners#NIL THEN { -- one corner. Transform it. SELECT boxParts.corners.first FROM ll => { point _ [ box.loX, box.loY ]; oppositePoint _ [ box.hiX, box.hiY ]; }; ul => { point _ [ box.loX, box.hiY ]; oppositePoint _ [ box.hiX, box.loY ]; }; lr => { point _ [ box.hiX, box.loY ]; oppositePoint _ [ box.loX, box.hiY ]; }; ur => { point _ [ box.hiX, box.hiY ]; oppositePoint _ [ box.loX, box.loY ]; }; ENDCASE => ERROR; inverse _ ImagerTransformation.Invert[m: boxData.transform]; totalTransform _ ImagerTransformation.Cat[boxData.transform, transform, inverse]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; -- transform the dragging point } ELSE IF boxParts.edges#NIL THEN { -- one edge. Transform it. f: ImagerTransformation.FactoredTransformation _ ImagerTransformation.Factor[transform]; globalTranslate: ImagerTransformation.VEC _ f.t; mInverse: ImagerTransformation.Transformation _ ImagerTransformation.Invert[boxData.transform]; localTranslate: ImagerTransformation.VEC _ ImagerTransformation.TransformVec[mInverse, globalTranslate]; totalTransform: ImagerTransformation.Transformation _ ImagerTransformation.Translate[localTranslate]; SELECT boxParts.edges.first FROM left => { point _ [ box.loX, 0 ]; oppositePoint _ [ box.hiX, box.hiY ]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; point _ [ point.x, box.loY]; -- result point is transformed in x only }; right => { point _ [ box.hiX, 0 ]; oppositePoint _ [ box.loX, box.loY ]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; point _ [ point.x, box.hiY]; -- result point is transformed in x only }; bottom => { point _ [ 0, box.loY ]; oppositePoint _ [ box.hiX, box.hiY ]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; point _ [ box.loX, point.y]; -- result point is transformed in y only }; top => { point _ [ 0, box.hiY ]; oppositePoint _ [ box.loX, box.loY ]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; point _ [ box.hiX, point.y]; -- result point is transformed in y only }; ENDCASE => ERROR; } ELSE ERROR; Imager.DoSaveAll[dc, DoDrawRectangle]; }; BoxDrawSelectionFeedback: PROC [slice: Slice, parts: SliceParts, dc: Imager.Context, camera: GGInterfaceTypes.CameraData, quick: BOOL] = { boxData: BoxData _ NARROW[slice.data]; boxParts: BoxParts _ NARROW[parts]; box: BoundBox _ boxData.box; DoDrawFeedback: PROC = { Imager.ConcatT[dc, boxData.transform]; IF NOT quick AND slice.selectedInFull.normal THEN { -- draw diagonal thru box to mark it line: GGLines.Line _ GGLines.LineFromPoints[ [box.loX, box.loY], [box.hiX, box.hiY] ]; GGShapes.DrawLine[dc: dc, line: line, clippedBy: [box.loX, box.loY, box.hiX-box.loX, box.hiY-box.loY], strokeWidth: 2.0 ]; -- uses DoSaveAll }; IF boxParts.entire THEN { BoxDrawSelected[slice, dc, camera]; -- uses DoSaveAll RETURN; }; FOR corner: LIST OF Corner _ boxParts.corners, corner.rest UNTIL corner=NIL DO SELECT corner.first FROM ll => GGShapes.DrawSelectedJoint[dc, [box.loX, box.loY] ]; ul => GGShapes.DrawSelectedJoint[dc, [box.loX, box.hiY] ]; lr => GGShapes.DrawSelectedJoint[dc, [box.hiX, box.loY] ]; ur => GGShapes.DrawSelectedJoint[dc, [box.hiX, box.hiY] ]; ENDCASE => ERROR; ENDLOOP; FOR edge: LIST OF Edge _ boxParts.edges, edge.rest UNTIL edge=NIL DO SELECT edge.first FROM left => GGShapes.DrawLine[dc: dc, line: GGLines.LineFromPoints[ [box.loX, box.loY], [box.loX, box.hiY] ], clippedBy: [box.loX, box.loY, box.hiX-box.loX, box.hiY-box.loY], strokeWidth: 2.0*boxData.strokeWidths[1] ]; top => GGShapes.DrawLine[dc: dc, line: GGLines.LineFromPoints[ [box.loX, box.hiY], [box.hiX, box.hiY] ], clippedBy: [box.loX, box.loY, box.hiX-box.loX, box.hiY-box.loY], strokeWidth: 2.0*boxData.strokeWidths[2] ]; right => GGShapes.DrawLine[dc: dc, line: GGLines.LineFromPoints[ [box.hiX, box.loY], [box.hiX, box.hiY] ], clippedBy: [box.loX, box.loY, box.hiX-box.loX, box.hiY-box.loY], strokeWidth: 2.0*boxData.strokeWidths[3] ]; bottom => GGShapes.DrawLine[dc: dc, line: GGLines.LineFromPoints[ [box.loX, box.loY], [box.hiX, box.loY] ], clippedBy: [box.loX, box.loY, box.hiX-box.loX, box.hiY-box.loY], strokeWidth: 2.0*boxData.strokeWidths[4] ]; ENDCASE => ERROR; ENDLOOP; }; Imager.DoSaveAll[dc, DoDrawFeedback]; }; BoxTransform: PROC [slice: Slice, parts: SliceParts, transform: ImagerTransformation.Transformation] = { point, oppositePoint: ImagerTransformation.VEC; -- really points, not vectors boxData: BoxData _ NARROW[slice.data]; boxParts: BoxParts _ NARROW[parts]; box: BoundBox _ boxData.box; inverse, totalTransform: ImagerTransformation.Transformation; IF box.null OR box.infinite THEN ERROR; IF boxParts.entire THEN { boxData.transform _ ImagerTransformation.Concat[boxData.transform, transform]; BoxBoundBox[slice]; RETURN; }; IF boxParts.corners=NIL AND boxParts.edges=NIL THEN RETURN; -- no parts. Legal. IF (boxParts.corners#NIL AND boxParts.corners.rest#NIL) OR (boxParts.edges#NIL AND boxParts.edges.rest#NIL) THEN { -- more than one corner or more than one edge. Full transform. boxData.transform _ ImagerTransformation.Concat[boxData.transform, transform]; BoxBoundBox[slice]; RETURN; }; IF boxParts.corners#NIL AND boxParts.edges#NIL THEN ERROR; -- can't mix corners and edges while dragging. Doesn't make sense; must be a selection error. IF boxParts.corners#NIL THEN { -- one corner. Transform it. SELECT boxParts.corners.first FROM ll => { point _ [ box.loX, box.loY ]; oppositePoint _ [ box.hiX, box.hiY ]; }; ul => { point _ [ box.loX, box.hiY ]; oppositePoint _ [ box.hiX, box.loY ]; }; lr => { point _ [ box.hiX, box.loY ]; oppositePoint _ [ box.loX, box.hiY ]; }; ur => { point _ [ box.hiX, box.hiY ]; oppositePoint _ [ box.loX, box.loY ]; }; ENDCASE => ERROR; inverse _ ImagerTransformation.Invert[m: boxData.transform]; totalTransform _ ImagerTransformation.Cat[boxData.transform, transform, inverse]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; -- transform the dragging point } ELSE IF boxParts.edges#NIL THEN { -- transform an edge f: ImagerTransformation.FactoredTransformation _ ImagerTransformation.Factor[transform]; globalTranslate: ImagerTransformation.VEC _ f.t; mInverse: ImagerTransformation.Transformation _ ImagerTransformation.Invert[boxData.transform]; localTranslate: ImagerTransformation.VEC _ ImagerTransformation.TransformVec[mInverse, globalTranslate]; totalTransform: ImagerTransformation.Transformation _ ImagerTransformation.Translate[localTranslate]; SELECT boxParts.edges.first FROM left => { point _ [ box.loX, 0 ]; oppositePoint _ [ box.hiX, box.hiY ]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; point _ [ point.x, box.loY]; -- result point is transformed in x only }; right => { point _ [ box.hiX, 0 ]; oppositePoint _ [ box.loX, box.loY ]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; point _ [ point.x, box.hiY]; -- result point is transformed in x only }; bottom => { point _ [ 0, box.loY ]; oppositePoint _ [ box.hiX, box.hiY ]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; point _ [ box.loX, point.y]; -- result point is transformed in y only }; top => { point _ [ 0, box.hiY ]; oppositePoint _ [ box.loX, box.loY ]; point _ ImagerTransformation.Transform[m: totalTransform, v: point]; point _ [ box.hiX, point.y]; -- result point is transformed in y only }; ENDCASE => ERROR; } ELSE ERROR; box^ _ [MIN[point.x, oppositePoint.x], MIN[point.y, oppositePoint.y], MAX[point.x, oppositePoint.x], MAX[point.y, oppositePoint.y], FALSE, FALSE]; BoxBoundBox[slice]; }; BoxEmptyParts: PROC [slice: Slice, parts: SliceParts] RETURNS [BOOL] = { boxParts: BoxParts _ NARROW[parts]; RETURN[boxParts.entire=FALSE AND boxParts.corners=NIL AND boxParts.edges=NIL]; }; BoxNewParts: PROC [slice: Slice, mode: SelectMode] RETURNS [parts: SliceParts] = { boxHitData: BoxHitData _ NARROW[slice.hitData]; boxParts: BoxParts _ NEW[BoxPartsObj _ [entire: FALSE, corners: NIL, edges: NIL] ]; SELECT mode FROM joint => boxParts.corners _ LIST[boxHitData.corner]; segment => boxParts.edges _ LIST[boxHitData.edge]; traj, slice, topLevel => boxParts.entire _ TRUE; ENDCASE => ERROR; parts _ boxParts; -- RETURN[boxParts] }; BoxAddParts: PROC [slice: Slice, parts: SliceParts, mode: ExtendMode] RETURNS [newParts: SliceParts] = { boxHitData: BoxHitData _ NARROW[slice.hitData]; boxParts: BoxParts _ NARROW[parts]; newBoxParts: BoxParts _ NEW[BoxPartsObj _ [entire: FALSE, corners: boxParts.corners, edges: boxParts.edges] ]; SELECT mode FROM joint => newBoxParts.corners _ CONS[boxHitData.corner, boxParts.corners]; segment => newBoxParts.edges _ CONS[boxHitData.edge, boxParts.edges]; traj, topLevel => newBoxParts.entire _ TRUE; ENDCASE => ERROR; newParts _ newBoxParts; -- RETURN[newBoxParts] }; BoxRemoveParts: PROC [slice: Slice, parts: SliceParts, mode: ExtendMode] RETURNS [newParts: SliceParts] = { CornerRemove: PROC [corner: Corner, list: LIST OF Corner] RETURNS [newList: LIST OF Corner _ NIL] = { FOR nextCorner: LIST OF Corner _ list, nextCorner.rest UNTIL nextCorner = NIL DO IF nextCorner.first#corner THEN newList _ CONS[nextCorner.first, newList]; ENDLOOP; }; EdgeRemove: PROC [edge: Edge, list: LIST OF Edge] RETURNS [newList: LIST OF Edge _ NIL] = { FOR nextEdge: LIST OF Edge _ list, nextEdge.rest UNTIL nextEdge = NIL DO IF nextEdge.first#edge THEN newList _ CONS[nextEdge.first, newList]; ENDLOOP; }; boxHitData: BoxHitData _ NARROW[slice.hitData]; boxParts: BoxParts _ NARROW[parts]; newBoxParts: BoxParts _ NEW[BoxPartsObj _ [entire: boxParts.entire, corners: boxParts.corners, edges: boxParts.edges] ]; SELECT mode FROM joint => newBoxParts.corners _ CornerRemove[boxHitData.corner, boxParts.corners]; segment => newBoxParts.edges _ EdgeRemove[boxHitData.edge, boxParts.edges]; traj, topLevel => newBoxParts.entire _ FALSE; ENDCASE => ERROR; newParts _ newBoxParts; -- RETURN[newBoxParts] }; BoxClosestPoint: PROC [slice: Slice, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = { index: NAT _ 99; scale: REAL; boxData: BoxData _ NARROW[slice.data]; boxHitData: BoxHitData _ NARROW[slice.hitData]; inverse: ImagerTransformation.Transformation; inverse _ ImagerTransformation.Invert[boxData.transform]; testPoint _ GGTransform.Transform[inverse, testPoint]; [bestDist, index, bestPoint, success] _ GGBoundBox.NearestPoint[boxData.box, testPoint]; bestPoint _ GGTransform.Transform[boxData.transform, bestPoint]; scale _ ImagerTransformation.Factor[boxData.transform].s.x; bestDist _ bestDist*(1/scale); boxHitData.corner _ IF success THEN SELECT index FROM 0 => Corner.ll, 1 => Corner.ul, 2 => Corner.ur, 3 => Corner.lr, ENDCASE => ERROR ELSE Corner.none; }; BoxClosestSegment: PROC [slice: Slice, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = { seg: NAT _ 99; scale: REAL; boxData: BoxData _ NARROW[slice.data]; boxHitData: BoxHitData _ NARROW[slice.hitData]; inverse: ImagerTransformation.Transformation; inverse _ ImagerTransformation.Invert[boxData.transform]; testPoint _ GGTransform.Transform[inverse, testPoint]; [bestDist, seg, bestPoint, success] _ GGBoundBox.NearestSegment[boxData.box, testPoint, tolerance]; bestPoint _ GGTransform.Transform[boxData.transform, bestPoint]; scale _ ImagerTransformation.Factor[boxData.transform].s.x; bestDist _ bestDist*(1/scale); boxHitData.edge _ IF success THEN SELECT seg FROM 0 => Edge.left, 1 => Edge.top, 2 => Edge.right, 3 => Edge.bottom, ENDCASE => ERROR ELSE Edge.none; }; BoxFileout: PROC [slice: Slice, f: IO.STREAM] = { boxData: BoxData _ NARROW[slice.data]; GGParseOut.WritePoint[f, [ boxData.box.loX, boxData.box.loY] ]; f.PutChar[IO.SP]; GGParseOut.WritePoint[f, [ boxData.box.hiX, boxData.box.hiY] ]; f.PutChar[IO.SP]; GGParseOut.WriteTransformation[f, boxData.transform ]; }; BoxFilein: PROC [f: IO.STREAM, version: REAL] RETURNS [slice: Slice] = { box: BoundBox; p1, p2: Point; transform: ImagerTransformation.Transformation; GGParseIn.ReadBlank[f]; p1 _ GGParseIn.ReadPoint[f]; GGParseIn.ReadBlank[f]; p2 _ GGParseIn.ReadPoint[f]; GGParseIn.ReadBlank[f]; transform _ GGParseIn.ReadTransformation[f]; box _ GGBoundBox.CreateBoundBox[ p1.x, p1.y, p2.x, p2.y ]; slice _ MakeBoxSlice[box, none, transform].slice; }; END. ΖGGBoxSliceImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Last edited by Pier on June 10, 1986 6:04:32 pm PDT Contents: Implements the box slice class. Bier, June 3, 1986 2:02:06 pm PDT transform needed for general case of rotated boxes BoxPointsInDescriptor: PROC [sliceD: SliceDescriptor] RETURNS [pointGen: PointGenerator] = { pointGen _ NEW[PointGeneratorObj]; pointGen.sliceD _ sliceD; pointGen.toGo _ 4; pointGen.index _ 0; -- not used pointGen.classSpecific _ NIL; }; BoxNextPoint: PROC [pointGen: PointGenerator] RETURNS [pointAndDone: GGModelTypes.PointAndDone] = { sliceD: SliceDescriptor _ pointGen.sliceD; slice: Slice _ sliceD.slice; boxData: BoxData _ NARROW[slice.data]; t: ImagerTransformation.Transformation _ boxData.transform; IF pointGen.toGo = 0 THEN { pointAndDone.done _ TRUE; } ELSE { pointAndDone.done _ FALSE; SELECT pointGen.toGo FROM 4 => pointAndDone.point _ ImagerTransformation.Transform[t, [boxData.box.loX, boxData.box.loY]]; 3 => pointAndDone.point _ ImagerTransformation.Transform[t, [boxData.box.loX, boxData.box.hiY]]; 2 => pointAndDone.point _ ImagerTransformation.Transform[t, [boxData.box.hiX, boxData.box.hiY]]; 1 => pointAndDone.point _ ImagerTransformation.Transform[t, [boxData.box.hiX, boxData.box.loY]]; ENDCASE => SIGNAL Problem[msg: "Broken Invariant"]; pointGen.toGo _ pointGen.toGo - 1; }; }; GGModelTypes.SliceDrawProc requires a bound box input with loX<=hiX AND loY<=hiY SHOULD THIS RETURN A COPY OF THE BOX ?? Class Procedures GGModelTypes.SliceBoundBoxProc GGModelTypes.SliceCopyProc Imager.ConcatT[dc, boxData.transform]; GGModelTypes.SliceDrawTransformProc This is what makes boxes behave specially. Depending on which parts are selected, the points are transformed and the box is rubberbanded properly. The box data itself is not modified. GGModelTypes.SliceDrawSelectionFeedbackProc if not quick, draws a diagonal line thru the selected box, then highlites selected joints/edges GGModelTypes.SliceTransformProc Permanently transforms the box. Depending on which parts are selected, the points are transformed and the box is grown/shrunk properly. requires a bound box result with loX<=hiX AND loY<=hiY GGModelTypes.SliceEmptyPartsProc GGModelTypes.SliceNewPartsProc GGModelTypes.SliceAddPartsProc GGModelTypes.SliceRemovePartsProc GGModelTypes.SliceClosestPointProc GGModelTypes.SliceClosestSegmentProc GGModelTypes.SliceFileoutProc Write a description of yourself onto stream f. GGModelTypes.SliceFileinProc Κž˜codešœ™Kšœ Οmœ1™Kšœžœžœ‘#˜7K˜—K˜Kšœ žœžœ˜%šœžœžœ˜Kšœ‘N˜^Kšœ ‘L˜WK˜—K˜Kš Οnœžœžœ žœžœ˜/K˜š’œžœžœžœ˜Bšœžœ˜K˜ Kšœ˜K˜Kšœ˜K˜ K˜0Kšœ˜K˜K˜K˜K˜K˜*K˜2K˜K˜ Kšœ˜Kšœžœ˜Kšœ"˜"K˜K˜K˜"K˜"K˜K˜—šœ˜K˜——š’œžœžœ™\Kšœ žœ™"Kšœ™Kšœ™Kšœ™Kšœžœ™K™K™—š’œžœžœ˜\Kšœžœ˜'Kšœ žœ$žœ‘˜TKšžœžœ˜&š žœžœ žœžœ%žœ žœž˜RK˜"Kšžœ˜—K˜K˜—š’œžœžœ'˜hKšœžœ˜'Kšœžœ(žœ‘˜\Kšžœžœ˜*š žœžœžœžœžœžœž˜HKšœ*˜*Kšžœ˜—K˜K˜—š’ œžœžœ.™cKšœ*™*Kšœ™Kšœžœ ™&Kšœ;™;šžœžœ™Kšœžœ™K™—šžœ™Kšœžœ™Kšžœž™Kšœ`™`Kšœ`™`Kšœ`™`Kšœ`™`Kšžœžœ"™3Kšœ"™"K™—K™K™—š’ œžœžœ.˜cKšœ*˜*Kšœ˜Kšœžœ ˜&Kšœ;˜;šžœžœ˜Kšœžœ˜K˜—šžœ˜Kšœžœ˜*Kšœžœžœ˜*Kšœžœ˜Kšžœžœ žœ˜3Kšžœžœžœ˜=šžœžœ˜Kšœa˜aKšœa˜aKšœa˜aKšœa˜aKšœžœ˜ Kšžœžœ"˜3—Kšœ"˜"Kšœ$˜$K˜—K˜K˜—š’œžœ$žœ6˜wKšœ.˜.Kšœ˜Kšœžœ ˜&Kšœ;˜;šžœžœ˜Kšœžœ˜K˜—šžœ˜Kšœžœ˜*Kšœžœžœ˜$Kšœžœ˜Kšžœžœžœ˜;Kšžœžœžœ˜=šžœ žœ˜šœ ˜ Kšœ\˜\Kšœ\˜\K˜—šœ˜Kšœ\˜\Kšœ\˜\K˜—šœ ˜ Kšœ\˜\Kšœ\˜\K˜—šœ ˜ Kšœ\˜\Kšœ\˜\K˜—Kšœžœ˜ Kšžœžœ"˜3—Kšœ*˜*Kšœ,˜,K˜—K˜K˜—š’œžœL˜YKšœ™Kšœžœ ˜&Kšœ˜š’ œžœ˜Kšœ&˜&Kšœ"˜"Kšœ3˜3Kšœ>˜>Kšœ3˜3Kšœ?˜?Kšœ3˜3Kšœ@˜@Kšœ3˜3Kšœ@˜@K˜—K˜ K˜K˜—š’œžœ=˜TK˜—š’œžœ*˜?K˜—š’œžœ0žœ˜PKšœžœ˜#Kšœžœ ˜&šžœžœ˜Kš žœžœžœžœ(žœ˜GK˜—šžœ˜š žœžœžœ"žœžœž˜Dšžœ ž˜Kšœ.˜.Kšœ-˜-Kšœ/˜/Kšœ0˜0Kšœžœ˜ Kšžœžœ˜—Kšžœ˜—K˜—˜K˜——š ’ œžœžœwžœžœ˜ΑK™5Kšœžœ˜Kšœžœu˜‹Kš œžœžœ žœžœ˜\Kšžœžœžœžœ˜5šœ žœ ˜K˜%Kšœ˜Kšœ žœ˜Kšœžœ˜ Kš œžœžœžœžœ˜-Kšœ-˜-Kšœ žœ0˜K™'Kš žœžœžœžœžœžœ˜OKšœ˜K˜—K™•StartOfExpansion# -- [cluster: GGModelTypes.Cluster]š’ œžœ˜$Kšœ™Kšœ žœ"˜1Kšœžœ ˜&KšœN˜NKšœv˜vK˜K˜—š’œžœžœ˜6Kšœ™Kšœ˜Kšœžœ ˜&Kšœ5˜5KšœP˜PKšœ0˜0Kšœ žœ ˜Kš žœžœžœžœ4žœ˜SK˜K˜—š’œž œL˜iKšœžœ ˜&Kšœ˜š’ œžœ˜Kšœ&™&Kšœ"˜"Kšœ5˜5Kšœ>˜>Kšœ5˜5Kšœ?˜?Kšœ5˜5Kšœ@˜@Kšœ5˜5Kšœ@˜@K˜—K˜ K˜K˜—šŸœžœ˜₯Kšœ#™#K™ΈKšœ+žœ‘˜MKšœžœ ˜&Kšœžœ˜#Kšœ˜Kšœ=˜=š’ œžœ˜KšœN˜NKšœ?˜?K˜—š’œžœ˜Kšœ&˜&KšœO˜OK˜—Kšžœ žœžœžœ˜'šžœžœ˜K˜ Kšžœ˜K˜—Kš žœžœžœžœžœžœ‘˜Ošžœžœžœžœžœžœžœžœžœžœ‘>˜±K˜ Kšžœ˜K˜—Kš žœžœžœžœžœžœ‘]˜˜šžœžœžœ‘˜;šžœž˜"KšœN˜NKšœN˜NKšœN˜NKšœN˜NKšžœžœ˜—Kšœ<˜˜±KšœN˜NKšœ˜Kšžœ˜K˜—Kš žœžœžœžœžœžœ‘]˜˜šžœžœžœ‘˜;šžœž˜"KšœN˜NKšœN˜NKšœN˜NKšœN˜NKšžœžœ˜—Kšœ<˜