GGBoxSliceImpl.mesa
Copyright © 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
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 needed for general case of rotated boxes
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] = {
pointGen ← NEW[PointGeneratorObj];
pointGen.sliceD ← sliceD;
pointGen.toGo ← 4;
pointGen.index ← 0; -- not used
pointGen.classSpecific ← NIL;
};
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 {
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;
};
};
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] = {
GGModelTypes.SliceDrawProc
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] = {
requires a bound box input with loX<=hiX AND loY<=hiY
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] = {
SHOULD THIS RETURN A COPY OF THE BOX ??
RETURN[IF slice.class.type#$Box THEN NIL ELSE NARROW[slice.data, BoxData].box];
};
Class Procedures
BoxBoundBox:
PROC [slice: Slice] = {
GGModelTypes.SliceBoundBoxProc
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] = {
GGModelTypes.SliceCopyProc
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.ConcatT[dc, boxData.transform];
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] = {
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.
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] = {
GGModelTypes.SliceDrawSelectionFeedbackProc
if not quick, draws a diagonal line thru the selected box, then highlites selected joints/edges
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] = {
GGModelTypes.SliceTransformProc
Permanently transforms the box. Depending on which parts are selected, the points are transformed and the box is grown/shrunk properly.
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;
requires a bound box result with loX<=hiX AND loY<=hiY
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];
};
Box
EmptyParts:
PROC [slice: Slice, parts: SliceParts]
RETURNS [
BOOL] = {
GGModelTypes.SliceEmptyPartsProc
boxParts: BoxParts ← NARROW[parts];
RETURN[boxParts.entire=FALSE AND boxParts.corners=NIL AND boxParts.edges=NIL];
};
Box
NewParts:
PROC [slice: Slice, mode: SelectMode]
RETURNS [parts: SliceParts] = {
GGModelTypes.SliceNewPartsProc
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]
};
Box
AddParts:
PROC [slice: Slice, parts: SliceParts, mode: ExtendMode]
RETURNS [newParts: SliceParts] = {
GGModelTypes.SliceAddPartsProc
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]
};
Box
RemoveParts:
PROC [slice: Slice, parts: SliceParts, mode: ExtendMode]
RETURNS [newParts: SliceParts] = {
GGModelTypes.SliceRemovePartsProc
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] = {
GGModelTypes.SliceClosestPointProc
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] = {
GGModelTypes.SliceClosestSegmentProc
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] = {
GGModelTypes.SliceFileoutProc
Write a description of yourself onto stream f.
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] = {
GGModelTypes.SliceFileinProc
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.