DIRECTORY GGBasicTypes, GGBoxCluster, GGBoundBox, GGCluster, GGLines, GGInterfaceTypes, GGModelTypes, GGShapes, GGParseIn, GGParseOut, GGTransform, Imager, ImagerTransformation, IO; GGBoxClusterImpl: CEDAR PROGRAM IMPORTS GGBoundBox, GGCluster, GGLines, GGShapes, GGParseIn, GGParseOut, GGTransform, Imager, ImagerTransformation, IO EXPORTS GGBoxCluster = BEGIN Point: TYPE = GGBasicTypes.Point; Cluster: TYPE = GGModelTypes.Cluster; ClusterDescriptor: TYPE = GGModelTypes.ClusterDescriptor; ClusterDescriptorObj: TYPE = GGModelTypes.ClusterDescriptorObj; ClusterParts: TYPE = GGModelTypes.ClusterParts; ClusterObj: TYPE = GGModelTypes.ClusterObj; ClusterClass: TYPE = GGModelTypes.ClusterClass; ClusterClassObj: TYPE = GGModelTypes.ClusterClassObj; BoundBox: TYPE = GGModelTypes.BoundBox; SelectMode: TYPE = GGModelTypes.SelectMode; ExtendMode: TYPE = GGModelTypes.ExtendMode; Corner: TYPE = GGBoxCluster.Corner; Edge: TYPE = GGBoxCluster.Edge; BoxData: TYPE = REF BoxDataObj; BoxDataObj: TYPE = RECORD [ box: BoundBox, transform: ImagerTransformation.Transformation ]; BoxParts: TYPE = REF BoxPartsObj; BoxPartsObj: TYPE = RECORD [ entire: BOOL, -- means entire box is selected (traj or topLevel) corner: Corner, -- which corner of box is selected. edge: Edge -- which edge of box is 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 ]; BuildBoxClusterClass: PUBLIC PROC [] RETURNS [class: ClusterClass] = { class _ NEW[ClusterClassObj _ [ type: $Box, boundBox: BoxBoundBox, copy: BoxCopy, draw: BoxDraw, drawTransform: BoxDrawTransform, drawSelectionFeedback: BoxDrawSelectionFeedback, transform: BoxTransform, emptyParts: BoxEmptyParts, newParts: BoxNewParts, addParts: BoxAddParts, removeParts: BoxRemoveParts, closestPoint: BoxClosestPoint, closestPointAndTangent: NIL, closestSegment: BoxClosestSegment, fileout: BoxFileout, filein: BoxFilein ]]; }; MakeBoxCluster: PUBLIC PROC [box: BoundBox, corner: Corner, transform: ImagerTransformation.Transformation _ GGTransform.Identity[]] RETURNS [clusD: ClusterDescriptor] = { boxCluster: Cluster _ NIL; boxData: BoxData _ NEW[BoxDataObj _ [box: box, transform: transform] ]; boxParts: BoxParts _ NEW[BoxPartsObj _ [entire: FALSE, corner: corner, edge: none] ]; IF box.loX > box.hiX OR box.loY > box.hiY THEN ERROR; boxCluster _ NEW[ClusterObj _ [ class: GGCluster.FetchClusterClass[$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[boxCluster]; clusD _ NEW[ClusterDescriptorObj _ [boxCluster, boxParts] ]; }; GetBox: PUBLIC PROC [cluster: Cluster] RETURNS [box: BoundBox] = { RETURN[IF cluster.class.type#$Box THEN NIL ELSE NARROW[cluster.data, BoxData].box]; }; BoxBoundBox: PROC [cluster: Cluster] = { halfJoint: REAL = GGModelTypes.halfJointSize + 1; boxData: BoxData _ NARROW[cluster.data]; box: BoundBox _ GGBoundBox.BoundBoxOfBoundBox[boxData.box, boxData.transform]; GGBoundBox.UpdateBoundBox[cluster.boundBox, box.loX-halfJoint, box.loY-halfJoint, box.hiX+halfJoint, box.hiY+halfJoint] }; BoxCopy: PROC [cluster: Cluster] RETURNS [copy: Cluster] = { boxData: BoxData _ NARROW[cluster.data]; box: BoundBox _ GGBoundBox.CopyBoundBox[boxData.box]; transform: Imager.Transformation _ ImagerTransformation.Copy[boxData.transform]; copy _ MakeBoxCluster[box, none, transform].cluster; }; BoxDraw: PROC [cluster: Cluster, dc: Imager.Context, camera: GGInterfaceTypes.CameraData] = { boxData: BoxData _ NARROW[cluster.data]; DoDrawBox: PROC = { Imager.ConcatT[dc, boxData.transform]; GGBoundBox.DrawBoundBox[dc: dc, bBox: boxData.box]; }; Imager.DoSaveAll[dc, DoDrawBox]; }; BoxDrawTransform: PROC [cluster: Cluster, parts: ClusterParts, dc: Imager.Context, camera: GGInterfaceTypes.CameraData, transform: ImagerTransformation.Transformation] = { point, oppositePoint: ImagerTransformation.VEC; -- really points, not vectors boxData: BoxData _ NARROW[cluster.data]; boxParts: BoxParts _ NARROW[parts]; box: BoundBox _ boxData.box; inverse, totalTransform: ImagerTransformation.Transformation; DoDrawBox: PROC = { point _ [ box.loX, box.loY ]; oppositePoint _ [ box.hiX, box.hiY ]; Imager.ConcatT[dc, ImagerTransformation.Concat[boxData.transform, transform]]; GGShapes.DrawRectangle[dc, oppositePoint.x, oppositePoint.y, point.x, point.y]; }; 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.corner=none AND boxParts.edge=none THEN RETURN; -- no parts. Legal. IF boxParts.corner#none AND boxParts.edge#none THEN ERROR; -- both parts. Illegal IF boxParts.corner#none THEN { SELECT boxParts.corner 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.edge#none THEN { 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.edge 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 [cluster: Cluster, parts: ClusterParts, dc: Imager.Context, camera: GGInterfaceTypes.CameraData, quick: BOOL] = { boxData: BoxData _ NARROW[cluster.data]; boxParts: BoxParts _ NARROW[parts]; box: BoundBox _ boxData.box; DoDrawFeedback: PROC = { Imager.ConcatT[dc, boxData.transform]; IF NOT quick AND cluster.selectedInFull.normal THEN { 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 { GGShapes.DrawRectangle[dc, box.loX, box.loY, box.hiX, box.hiY, 2.0]; RETURN; }; SELECT boxParts.corner 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 => {}; -- maybe none SELECT boxParts.edge 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 ]; 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 ]; 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 ]; 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 ]; ENDCASE => {}; -- maybe none }; Imager.DoSaveAll[dc, DoDrawFeedback]; }; BoxTransform: PROC [cluster: Cluster, parts: ClusterParts, transform: ImagerTransformation.Transformation] = { point, oppositePoint: ImagerTransformation.VEC; -- really points, not vectors boxData: BoxData _ NARROW[cluster.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[cluster]; RETURN; }; IF boxParts.corner=none AND boxParts.edge=none THEN RETURN; -- no parts. Legal. IF boxParts.corner#none AND boxParts.edge#none THEN ERROR; -- both parts. Illegal IF boxParts.corner#none THEN { -- transform a corner SELECT boxParts.corner 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.edge#none 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.edge 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[cluster]; }; BoxEmptyParts: PROC [cluster: Cluster, parts: ClusterParts] RETURNS [BOOL] = { boxParts: BoxParts _ NARROW[parts]; RETURN[boxParts.entire=FALSE AND boxParts.corner=none AND boxParts.edge=none]; }; BoxNewParts: PROC [cluster: Cluster, mode: SelectMode] RETURNS [parts: ClusterParts] = { boxHitData: BoxHitData _ NARROW[cluster.hitData]; boxParts: BoxParts _ NEW[BoxPartsObj _ [entire: FALSE, corner: none, edge: none] ]; SELECT mode FROM joint => boxParts.corner _ boxHitData.corner; segment => boxParts.edge _ boxHitData.edge; traj, topLevel => boxParts.entire _ TRUE; ENDCASE => ERROR; parts _ boxParts; -- RETURN[boxParts] }; BoxAddParts: PROC [cluster: Cluster, parts: ClusterParts, mode: ExtendMode] RETURNS [newParts: ClusterParts] = { boxHitData: BoxHitData _ NARROW[cluster.hitData]; boxParts: BoxParts _ NARROW[parts]; newBoxParts: BoxParts _ NEW[BoxPartsObj _ [entire: FALSE, corner: none, edge: none] ]; SELECT mode FROM joint => newBoxParts.corner _ boxParts.corner; -- can't extend at joint level segment => newBoxParts.edge _ boxParts.edge; -- can't extend at segment level traj, topLevel => newBoxParts.entire _ TRUE; ENDCASE => ERROR; newParts _ newBoxParts; -- RETURN[newBoxParts] }; BoxRemoveParts: PROC [cluster: Cluster, parts: ClusterParts, mode: ExtendMode] RETURNS [newParts: ClusterParts] = { boxHitData: BoxHitData _ NARROW[cluster.hitData]; boxParts: BoxParts _ NARROW[parts]; newBoxParts: BoxParts _ NEW[BoxPartsObj _ [entire: boxParts.entire, corner: boxParts.corner, edge: boxParts.edge] ]; SELECT mode FROM joint => newBoxParts.corner _ IF boxHitData.corner=boxParts.corner THEN none ELSE boxParts.corner; segment => newBoxParts.edge _ IF boxHitData.edge=boxParts.edge THEN none ELSE boxParts.edge; traj, topLevel => newBoxParts.entire _ FALSE; ENDCASE => ERROR; newParts _ newBoxParts; -- RETURN[newBoxParts] }; BoxClosestPoint: PROC [cluster: Cluster, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = { index: NAT _ 99; scale: REAL; boxData: BoxData _ NARROW[cluster.data]; boxHitData: BoxHitData _ NARROW[cluster.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 [cluster: Cluster, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = { seg: NAT _ 99; scale: REAL; boxData: BoxData _ NARROW[cluster.data]; boxHitData: BoxHitData _ NARROW[cluster.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 [cluster: Cluster, f: IO.STREAM] = { boxData: BoxData _ NARROW[cluster.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 [cluster: Cluster] = { 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[1], p1[2], p2[1], p2[2] ]; cluster _ MakeBoxCluster[box, none, transform].cluster; }; END. ΦGGBoxClusterImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Last edited by Pier on May 22, 1986 6:10:09 pm PDT Contents: Implements the box cluster class. Bier, May 1, 1986 2:32:28 pm PDT N.B.: box has no "parts" data of its own. Make this work! transform needed for general case of non-rectilinear boxes requires a bound box input with loX<=hiX AND loY<=hiY SHOULD THIS RETURN A COPY OF THE BOX ?? SetBox: PUBLIC PROC [cluster: Cluster, box: BoundBox] = { boxData: BoxData _ NARROW[cluster.data]; boxHitData: BoxHitData _ NARROW[cluster.hitData]; IF cluster.class.type#$Box THEN RETURN; boxData.bBox _ box; boxHitData.corner _ none; boxHitData.edge _ none; WHAT TO DO ABOUT TRANSFORMATION ?? }; Class Procedures GGModelTypes.ClusterBoundBoxProc GGModelTypes.ClusterCopyProc GGModelTypes.ClusterDrawProc GGModelTypes.ClusterDrawTransformProc 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.ClusterDrawSelectionFeedbackProc if not quick, draws a diagonal line thru the selected box, then highlites selected joints/edges GGModelTypes.ClusterTransformProc 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.ClusterEmptyPartsProc GGModelTypes.ClusterNewPartsProc GGModelTypes.ClusterAddPartsProc GGModelTypes.ClusterRemovePartsProc GGModelTypes.ClusterClosestPointProc GGModelTypes.ClusterClosestSegmentProc GGModelTypes.ClusterFileoutProc Write a description of yourself onto stream f. GGModelTypes.ClusterFileinProc ΚΞ˜codešœ™Kšœ Οmœ1™˜>Kšœ7˜7K˜K˜K˜—Kšžœ˜—…—?BTζ