SVAssemblyImpl.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Last edited by Bier on February 24, 1987 2:11:06 pm PST
Contents: Solidviews assemblies are like Gargoyle slices. They draw themselves as a unit. They update their bounding boxes. Their parts can be selected. This module is to Solidviews what GGSlice is to Gargoyle.
DIRECTORY
AtomButtonsTypes, Feedback, FunctionCache, GGBoundBox, Imager, ImagerColor, ImagerColorPrivate, PriorityQueue, Real, Rope, SV2d, SV3d, SVArtwork, SVAssembly, SVBasicTypes, SVBoundBox, SVCoordSys, SVGraphics, SVMasterObject, SVMatrix3d, SVMatrixOps, SVModelTypes, SVScene, SVSceneTypes, SVToolObject, SVTransforms, SVUtility, ViewerClasses;
SVAssemblyImpl: CEDAR PROGRAM
IMPORTS Feedback, FunctionCache, GGBoundBox, Imager, ImagerColor, ImagerColorPrivate, PriorityQueue, Rope, SVArtwork, SVBoundBox, SVCoordSys, SVGraphics, SVMasterObject, SVMatrix3d, SVMatrixOps, SVScene, SVToolObject, SVTransforms, SVUtility
EXPORTS SVAssembly
=
BEGIN
Artwork: TYPE = SVModelTypes.Artwork;
BoundBlock: TYPE = REF BoundBlockObj;
BoundBlockObj: TYPE = SVBasicTypes.BoundBlockObj;
BoundBox: TYPE = SVBasicTypes.BoundBox;
BoundHedron: TYPE = SVBasicTypes.BoundHedron;
Camera: TYPE = SVModelTypes.Camera;
Classification: TYPE = SVSceneTypes.Classification;
Color: TYPE = Imager.Color;
CoordSystem: TYPE = SVModelTypes.CoordSystem;
FeedbackData: TYPE = AtomButtonsTypes.FeedbackData;
LightSourceList: TYPE = SVSceneTypes.LightSourceList;
Line3d: TYPE = SV3d.Line3d;
MasterObject: TYPE = SVSceneTypes.MasterObject;
Matrix4by4: TYPE = SV3d.Matrix4by4;
PlanarSurface: TYPE = SVSceneTypes.PlanarSurface;
PlanarSurfaceList: TYPE = SVSceneTypes.PlanarSurfaceList;
Point2d: TYPE = SV2d.Point2d; 
Point3d: TYPE = SV3d.Point3d;
PointAndDone: TYPE = SVSceneTypes.PointAndDone;
PointPairAndDone: TYPE = SVSceneTypes.PointPairAndDone;
PointGenerator: TYPE = SVSceneTypes.PointGenerator;
PointPairGenerator: TYPE = SVSceneTypes.PointPairGenerator;
PointSetOp: TYPE = SVSceneTypes.PointSetOp; -- {union, intersection, difference}
Primitive: TYPE = SVSceneTypes.Primitive;
Ray: TYPE = SVSceneTypes.Ray;
Scene: TYPE = SVSceneTypes.Scene;
SelectionClass: TYPE = SVSceneTypes.SelectionClass;
SelectMode: TYPE = SVSceneTypes.SelectMode;
Shape: TYPE = SVSceneTypes.Shape;
ShapeObj: TYPE = SVSceneTypes.ShapeObj;
ShowAsType: TYPE = SVSceneTypes.ShowAsType;
Slice: TYPE = SVSceneTypes.Slice;
SliceList: TYPE = SVSceneTypes.SliceList;
SliceListObj: TYPE = SVSceneTypes.SliceListObj;
SliceObj: TYPE = SVSceneTypes.SliceObj;
SliceDescriptor: TYPE = SVSceneTypes.SliceDescriptor;
SliceDescriptorObj: TYPE = SVSceneTypes.SliceDescriptorObj;
SliceParts: TYPE = SVSceneTypes.SliceParts;
ToolData: TYPE = SVSceneTypes.ToolData;
Vector3d: TYPE = SV3d.Vector3d;
Viewer: TYPE = ViewerClasses.Viewer;
Hidden Surface
SurfaceSeq: TYPE = REF SurfaceSeqObj;
SurfaceSeqObj: TYPE = RECORD [
surfs: SEQUENCE len: NAT OF PlanarSurface
];
SurfaceGenerator: TYPE = REF SurfaceGeneratorObj;
SurfaceGeneratorObj: TYPE = RECORD [
surfs: SurfaceSeq
];
Problem: SIGNAL [msg: Rope.ROPE] = Feedback.Problem;
bBoxCacheSize: NAT = 3;
CreatePrimitive: PUBLIC PROC [name: Rope.ROPE, object: Rope.ROPE, scene: Scene, artwork: Artwork ← NIL, isTool: BOOLFALSE, toolData: ToolData ← NIL] RETURNS [prim: Slice, masterObjectFound: BOOL, success: BOOL] = {
masterObject: MasterObject;
toolMasterObject: MasterObject;
shape: Shape;
toolName: Rope.ROPE;
BEGIN
success ← TRUE;
IF SVScene.AssemblyNameIsPresent[name, scene] THEN GOTO NameAlreadyPresent;
IF toolData = NIL THEN toolMasterObject ← NIL
ELSE {
toolName ← SVCoordSys.NameWithSuffix[name, "$$tool", scene.coordSysRoot];
toolMasterObject ← SVToolObject.ToolMakeMasterObject[toolName, toolData];
};
[masterObject, masterObjectFound] ← SVScene.FindObjectFromName[object, scene];
IF NOT masterObjectFound THEN RETURN;
IF artwork = NIL THEN artwork ←
SVArtwork.CreateColorArtwork[ImagerColor.ColorFromRGB[[.6,.6,.8]], plastic]; -- light blue
shape ← NEW[ShapeObj];
shape.mo ← masterObject;
We will wire up the shape to a scalars coordinate frame below.
prim ← NEW[SliceObj ← [
name: name,
coordSys: NIL,
shape: shape,
bBoxCache: FunctionCache.Create[maxEntries: bBoxCacheSize],
artwork: artwork,
showAs: normal,
isTool: isTool,
toolMasterObject: toolMasterObject
]];
EXITS
NameAlreadyPresent => {
Feedback.AppendRaw[$Solidviews, Rope.Cat["Slice ", name, " already present."], oneLiner];
Feedback.BlinkRaw[$Solidviews];
RETURN[NIL, TRUE, FALSE];
};
END;
};
Create a primitive assembly, based on the master object named object, in scene. Its surface looks are described by artwork. It will look normal to start with (as opposed to as a "Tool" or invisible). If toolData is not provided, a close-fitting tool will be computed when needed.
Object must be on the master object list of scene. Use AddMasterObjectToScene below.
If success is FALSE, a message is written to the message window and nothing is done (NameAlreadyPresent and AttemptToAddSubassemblyToPrimitive are failure reasons).
AddPrimitive: PUBLIC PROC [prim: Slice, size: Vector3d, parent: Slice, mat: Matrix4by4, scene: Scene] RETURNS [success: BOOL] = {
Scale the primitive assembly by size and add it as a child of parent, so that assemblyparent will be mat.
scalarName: Rope.ROPE;
shape: Shape;
IF ISTYPE[parent.shape, SliceList] THEN { -- add to old SliceList
alist: SliceList ← NARROW[parent.shape];
cs: CoordSystem;
scalarCS: CoordSystem;
cs ← SVCoordSys.CreateCoordSysInTree[prim.name, mat, parent.coordSys, scene.coordSysRoot];
prim.coordSys ← cs;
scalarName ← SVCoordSys.NameWithSuffix[prim.name, "$$Scalars", scene.coordSysRoot];
scalarCS ← SVCoordSys.CreateScalarsOnlyCoordSysInTree[scalarName, size, cs, scene.coordSysRoot];
shape ← NARROW[prim.shape];
shape.coordSys ← scalarCS;
parent.shape ← SVUtility.AppendToAssemblyList[prim, alist];
success ← TRUE;
}
ELSE {
Feedback.AppendRaw[$Solidviews, "Attempt to add subassembly to primitive.", oneLiner];
Feedback.BlinkRaw[$Solidviews];
RETURN[FALSE];
};
};
CreatePrimitiveAtExistingCoordSys: PUBLIC PROC [name: Rope.ROPE, object: Rope.ROPE, size: Vector3d, scene: Scene, coordSys: CoordSystem, artwork: Artwork ← NIL, isTool: BOOLFALSE, toolData: ToolData ← NIL] RETURNS [assembly: Slice, masterObjectFound: BOOL] = {
masterObject: MasterObject;
toolMasterObject: MasterObject;
shape: Shape;
scalarCS: CoordSystem;
toolName, scalarName: Rope.ROPE;
IF toolData = NIL THEN toolMasterObject ← NIL
ELSE {
toolName ← SVCoordSys.NameWithSuffix[name, "$$tool", scene.coordSysRoot];
toolMasterObject ← SVToolObject.ToolMakeMasterObject[toolName, toolData];
};
[masterObject, masterObjectFound] ← SVScene.FindObjectFromName[object, scene];
IF NOT masterObjectFound THEN {
Feedback.AppendRaw[$Solidviews, Rope.Cat["Couldn't add assembly ", name, ". Master object ", object, " not found."], oneLiner];
Feedback.BlinkRaw[$Solidviews];
};
IF artwork = NIL THEN artwork ←
SVArtwork.CreateColorArtwork[ImagerColor.ColorFromRGB[[.6,.6,.8]], plastic]; -- light blue
shape ← NEW[ShapeObj];
shape.mo ← masterObject;
We will wire up the shape to a scalars coordinate frame below.
assembly ← NEW[SliceObj ← [
name: name,
coordSys: NIL,
shape: shape,
bBoxCache: FunctionCache.Create[maxEntries: bBoxCacheSize],
artwork: artwork,
showAs: normal,
isTool: isTool,
toolMasterObject: toolMasterObject
]];
Now wire up the assembly to its parent.
assembly.coordSys ← coordSys;
scalarName ← SVCoordSys.NameWithSuffix[assembly.name, "$$Scalars", scene.coordSysRoot];
scalarCS ← SVCoordSys.CreateScalarsOnlyCoordSysInTree[scalarName, size, coordSys, scene.coordSysRoot];
shape.coordSys ← scalarCS;
};
CreateCluster: PUBLIC PROC [name: Rope.ROPE, pointSetOp: PointSetOp ← union, scene: Scene, isTool: BOOLFALSE, toolData: ToolData ← NIL] RETURNS [assembly: Slice, success: BOOL] = {
Like CreatePrimitive but adds an assembly which can have children (can be passed as "parent" later.
artwork: Artwork ← SVArtwork.CreateColorArtwork[Imager.white, plastic]; -- white
toolMasterObject: MasterObject;
toolName: Rope.ROPE;
BEGIN
success ← TRUE;
IF SVScene.AssemblyNameIsPresent[name, scene] THEN GOTO NameInUse;
IF toolData = NIL THEN toolMasterObject ← NIL
ELSE {
toolName ← SVCoordSys.NameWithSuffix[name, "$$tool", scene.coordSysRoot];
toolMasterObject ← SVToolObject.ToolMakeMasterObject[toolName, toolData];
};
assembly ← NEW[SliceObj ← [
name: name,
coordSys: NIL,
shape: NEW[SliceListObj ← [NIL, pointSetOp]],
bBoxCache: FunctionCache.Create[maxEntries: bBoxCacheSize],
artwork: artwork,
showAs: normal,
isTool: isTool,
toolMasterObject: toolMasterObject
]];
EXITS
NameInUse => {
Feedback.AppendRaw[$Solidviews, Rope.Cat["Composite name ", name, " is already registered. Try another."], oneLiner];
Feedback.BlinkRaw[$Solidviews];
RETURN[NIL, FALSE];
};
END;
};
AddCluster: PUBLIC PROC [cluster: Slice, parent: Slice, mat: Matrix4by4, scene: Scene] RETURNS [success: BOOL] = {
Now wire up the assembly to its parent.
IF IsComposite[parent] THEN{ -- add to old SliceList
alist: SliceList ← NARROW[parent.shape];
cluster.coordSys ← SVCoordSys.CreateCoordSysInTree[cluster.name, mat, parent.coordSys, scene.coordSysRoot];
parent.shape ← SVUtility.AppendToAssemblyList[cluster, alist];
}
ELSE {
Feedback.AppendRaw[$Solidviews, "Attempt to add subassembly to primitive.", oneLiner];
Feedback.BlinkRaw[$Solidviews];
RETURN[FALSE];
};
};
CreateClusterAssemblyAtExistingCoordSys: PUBLIC PROC [name: Rope.ROPE, pointSetOp: PointSetOp, scene: Scene, coordSys: CoordSystem, isTool: BOOLFALSE, toolData: ToolData ← NIL] RETURNS [assembly: Slice] = {
artwork: Artwork ← SVArtwork.CreateColorArtwork[Imager.white, plastic]; -- white
toolMasterObject: MasterObject;
toolName: Rope.ROPE;
IF toolData = NIL THEN toolMasterObject ← NIL
ELSE {
toolName ← SVCoordSys.NameWithSuffix[name, "$$tool", scene.coordSysRoot];
toolMasterObject ← SVToolObject.ToolMakeMasterObject[toolName, toolData];
};
assembly ← NEW[SliceObj ← [
name: name,
coordSys: NIL,
shape: NEW[SliceListObj ← [NIL, pointSetOp]],
bBoxCache: FunctionCache.Create[maxEntries: bBoxCacheSize],
artwork: artwork,
showAs: normal,
isTool: isTool,
toolMasterObject: toolMasterObject
]];
assembly.coordSys ← coordSys;
};
AttemptToAddSubassemblyToPrimitive: PUBLIC SIGNAL = CODE;
ConnectAssemblyToParent: PUBLIC PROC [assembly: Slice, parent: Slice] = {
IF IsComposite[parent] THEN { -- add to old SliceList
alist: SliceList ← NARROW[parent.shape];
parent.shape ← SVUtility.AppendToAssemblyList[assembly, alist];
}
ELSE SIGNAL AttemptToAddSubassemblyToPrimitive;
};
Not for general use. Used in conjunction with Create*AtExistingCoordSys to wire up children to parents. Text filein uses this.
IsComposite: PUBLIC PROC [assem: Slice] RETURNS [BOOL] = {
RETURN[ISTYPE [assem.shape, SliceList] OR assem.shape = NIL];
};
Fundamentals
GetHedron: PUBLIC PROC [mo: MasterObject] RETURNS [hedron: BoundHedron] = {
Returns a bounding polyhedron in master object coordinates.
hedron ← mo.class.getHedron[mo];
};
GetBoundBlock: PUBLIC PROC [slice: Slice, parts: SliceParts ← NIL] RETURNS [bBlock: BoundBlock] = {
mo: MasterObject ← NARROW[slice.shape, Shape].mo;
hedron: BoundHedron ← GetHedron[mo];
point: Point3d;
shape: Shape ← NARROW[slice.shape];
shapeWorld: Matrix4by4 ← SVCoordSys.WRTWorld[shape.coordSys];
bBlock ← NEW[BoundBlockObj];
point ← hedron[0];
point ← SVMatrix3d.Update[point, shapeWorld];
bBlock.loX ← bBlock.hiX ← point[1];
bBlock.loY ← bBlock.hiY ← point[2];
bBlock.loZ ← bBlock.hiZ ← point[3];
bBlock.null ← bBlock.infinite ← FALSE;
FOR i: NAT IN [1..hedron.len) DO
point ← hedron[i];
point ← SVMatrix3d.Update[point, shapeWorld];
bBlock.loX ← MIN[bBlock.loX, point[1]];
bBlock.loY ← MIN[bBlock.loY, point[2]];
bBlock.loZ ← MIN[bBlock.loZ, point[3]];
bBlock.hiX ← MAX[bBlock.hiX, point[1]];
bBlock.hiY ← MAX[bBlock.hiY, point[2]];
bBlock.hiZ ← MAX[bBlock.hiZ, point[3]];
ENDLOOP;
};
BoundBoxEntry: TYPE = REF BoundBoxEntryObj;
BoundBoxEntryObj: TYPE = RECORD [
bBox: BoundBox,
ok: BOOL ← FALSE
];
GetBoundBox: PUBLIC PROC [assem: Slice, parts: SliceParts, camera: Camera] RETURNS [bBox: BoundBox] = {
CompareEntries2: PROC [argument: FunctionCache.Domain] RETURNS [good: BOOL] = {
thisName: Rope.ROPENARROW[argument];
good ← Rope.Equal[thisName, camera.viewName, FALSE];
};
bBoxCache: FunctionCache.Cache ← assem.bBoxCache;
oldEntryAny: REF ANY;
oldEntry: BoundBoxEntry;
ok: BOOL;
BEGIN
[oldEntryAny, ok] ← FunctionCache.Lookup[bBoxCache, CompareEntries2];
IF NOT ok THEN GOTO UpdateTheCache -- not in cache
ELSE {
oldEntry ← NARROW[oldEntryAny];
IF NOT oldEntry.ok THEN GOTO UpdateTheCache -- cache out of date
ELSE bBox ← oldEntry.bBox;
};
EXITS
UpdateTheCache => {
UpdateBoundBoxForCamera[assem, camera];
bBox ← GetBoundBox[assem, parts, camera]; -- recursive call
};
END;
};
UpdateBoundBoxForCamera: PUBLIC PROC [assem: Slice, camera: Camera] = {
A camera has moved, or we are using this camera for the first time. Update the entry for this camera (adding an entry if needed).
CompareEntries: PROC [argument: FunctionCache.Domain] RETURNS [good: BOOL] = {
thisName: Rope.ROPENARROW[argument];
good ← Rope.Equal[thisName, camera.viewName, FALSE];
};
bBoxCache: FunctionCache.Cache ← assem.bBoxCache;
oldEntryAny: REF ANY;
oldEntry: BoundBoxEntry;
ok: BOOLFALSE;
[oldEntryAny, ok] ← FunctionCache.Lookup[bBoxCache, CompareEntries];
IF ok THEN {
oldEntry ← NARROW[oldEntryAny];
FillBox[oldEntry.bBox, assem, camera];
oldEntry.ok ← TRUE;
}
ELSE {
newEntry: BoundBoxEntry ← NEW[BoundBoxEntryObj];
newEntry.bBox ← GGBoundBox.NullBoundBox[];
FillBox[newEntry.bBox, assem, camera];
newEntry.ok ← TRUE;
FunctionCache.Insert[bBoxCache, camera.viewName, newEntry, 0];
};
};
FillBox: PROC [bBox: BoundBox, slice: Slice, camera: Camera] = {
shape: Shape ← NARROW[slice.shape];
hedron: BoundHedron;
mo: MasterObject ← shape.mo;
hedron ← GetHedron[mo];
SVBoundBox.FillBoundBoxFromBoundHedron[hedron, camera, shape.coordSys, bBox];
EnlargeByOffset[bBox, 2.0]; -- allow for stroke width
};
EnlargeByOffset: 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;
};
UpdateBoundBoxes: PROC [slice: Slice, cameraList: LIST OF Camera ← NIL] = {
We have moved assembly, so its bounding boxes have changed in all views. For each camera in cameraList, update its entry to be correct (add an entry if necessary). Mark all other entries as incorrect.
oldEntry: BoundBoxEntry;
bBoxCache: FunctionCache.Cache ← slice.bBoxCache;
allCache: LIST OF FunctionCache.CacheEntry ← FunctionCache.GetList[bBoxCache];
FOR list: LIST OF FunctionCache.CacheEntry ← allCache, list.rest UNTIL list = NIL DO
oldEntry ← NARROW[list.first.value];
IF oldEntry # NIL THEN oldEntry.ok ← FALSE;
ENDLOOP;
FOR list: LIST OF Camera ← cameraList, list.rest UNTIL list = NIL DO
UpdateBoundBoxForCamera[slice, list.first];
ENDLOOP;
};
Drawing
DrawTransform: PUBLIC PROC [sliceD: SliceDescriptor, dc: Imager.Context, scene: Scene, camera: Camera, transform: Matrix4by4] = {
SELECT camera.style FROM
wire => DrawWireAssemblyTransform[sliceD, dc, camera, transform];
shaded, hiddenLine => ERROR; -- use AddPolygonsToBufferTransform
normals => {
assem: Slice ← sliceD.slice;
oldAssemblyWORLD: Matrix4by4 ← SVCoordSys.WRTWorld[assem.coordSys];
newAssemblyWORLD: Matrix4by4 ← SVMatrix3d.Mult[oldAssemblyWORLD, transform];
AbsTransf[assem, scene, newAssemblyWORLD];
DrawNormalsAssembly[dc, assem, scene, camera];
AbsTransf[assem, scene, oldAssemblyWORLD];
};
ENDCASE => SIGNAL NotYetImplemented;
};
DeeperPlanarPolygon: PROC [x,y: PriorityQueue.Item, data: REFNIL] RETURNS [BOOL] = {
xS: PlanarSurface ← NARROW[x];
yS: PlanarSurface ← NARROW[y];
RETURN[xS.depth < yS.depth];
};
DrawParts: PUBLIC PROC [slice: Slice, parts: SliceParts ← NIL, dc: Imager.Context, scene: Scene, camera: Camera, quick: BOOL] = {
IF parts = NIL THEN Draw[slice, dc, scene, camera];
};
Draw: PUBLIC PROC [assem: Slice, dc: Imager.Context, scene: Scene, camera: Camera] = {
sliceD: SliceDescriptor ← NewParts[assem, NIL, [0,0,0], slice];
DrawTransform[sliceD, dc, scene, camera, SVMatrix3d.Identity[]];
};
DrawWireAssembly: PROC [assembly: Slice, dc: Imager.Context, camera: Camera] = {
SetColor[dc, camera, Imager.black];
SELECT assembly.showAs FROM
tool => {
toolMasterObject: MasterObject ← assembly.toolMasterObject;
IF toolMasterObject = NIL THEN {
Feedback.AppendTypescriptRaw[$Solidviews, Rope.Cat["Slice ", assembly.name, " is a tool but has no tool data. Contact implementor."], oneLiner];
RETURN;
};
toolMasterObject.class.lineDraw[assembly, dc, camera];
};
normal => {
WITH assembly.shape SELECT FROM
assems: SliceList => {
FOR subassemblies: LIST OF Slice ← assems.list, subassemblies.rest
UNTIL subassemblies = NIL DO
DrawWireAssembly[subassemblies.first, dc, camera];
ENDLOOP;
};
shape: Shape => {
shape.mo.class.lineDraw[assembly, dc, camera];
};
ENDCASE => ERROR;
};
invisible => {};
both => ERROR Feedback.Problem[msg: "Can't have both"];
ENDCASE => ERROR;
};
DrawWireAssemblyTransform: PROC [sliceD: SliceDescriptor, dc: Imager.Context, camera: Camera, transform: Matrix4by4] = {
assembly: Slice ← sliceD.slice;
SetColor[dc, camera, Imager.black];
SELECT assembly.showAs FROM
normal => {
WITH assembly.shape SELECT FROM
assems: SliceList => {
SIGNAL Problem[msg: "LineDraw not implemented for composite assemblies"];
};
shape: Shape => {
shape.mo.class.lineDrawTransform[sliceD, dc, camera, transform];
};
ENDCASE => ERROR;
};
invisible, tool => {};
both => ERROR Feedback.Problem[msg: "Can't have both"];
ENDCASE => ERROR;
};
AddPolygonsToBuffer: PUBLIC PROC [surfaceQueue: PriorityQueue.Ref, assembly: Slice, camera: Camera, eyeWorld: Point3d] = {
PutAssemblyOnQueue[assembly, surfaceQueue, camera.coordSys, eyeWorld];
};
AddPolygonsToBufferTransform: PUBLIC PROC [surfaceQueue: PriorityQueue.Ref, sliceD: SliceDescriptor, scene: Scene, camera: Camera, eyeWorld: Point3d, transform: Matrix4by4] = {
PutAssemblyOnQueueTransform[sliceD, surfaceQueue, camera.coordSys, eyeWorld, transform];
};
DrawBuffer: PUBLIC PROC [dc: Imager.Context, surfaceQueue: PriorityQueue.Ref, scene: Scene, camera: Camera, hiddenLine: BOOL] = {
DoDrawBuffer: PROC = {
thisPlanarSurf: PlanarSurface;
mo: MasterObject;
FOR i: INT IN[1..PriorityQueue.Size[surfaceQueue]] DO
thisPlanarSurf ← NARROW[PriorityQueue.Remove[surfaceQueue]];
mo ← thisPlanarSurf.mo;
mo.class.drawSurf[dc, thisPlanarSurf, scene.lightSources, camera, eyeWorld, hiddenLine];
ENDLOOP;
};
eyeWorld: Point3d ← SVGraphics.LocalToWorld[[0,0,camera.focalLength], camera.coordSys];
Imager.DoSaveAll[dc, DoDrawBuffer];
};
DrawNormalsAssembly: PROC [dc: Imager.Context, assembly: Slice, scene: Scene, camera: Camera] = {
lightSources: LightSourceList ← scene.lightSources;
SetColor[dc, camera, Imager.black];
WITH assembly.shape SELECT FROM
assems: SliceList => {
FOR subassemblies: LIST OF Slice ← assems.list, subassemblies.rest
UNTIL subassemblies = NIL DO
DrawNormalsAssembly[dc, subassemblies.first, scene, camera];
ENDLOOP;
};
shape: Shape => {
shape.mo.class.normalsDraw[dc, shape.mo.shadeBody, camera, shape.coordSys];
};
ENDCASE => ERROR;
};
RayCast: PUBLIC PROC [cameraPoint: Point2d, localRay: Ray, sliceD: SliceDescriptor, prim: Primitive] RETURNS [class: Classification] = {
shape: Shape ← NARROW[sliceD.slice.shape];
mo: MasterObject ← shape.mo;
class ← mo.class.rayCast[cameraPoint, localRay, sliceD, prim];
};
RayCastNoBBoxes: PUBLIC PROC [localRay: Ray, sliceD: SliceDescriptor, prim: Primitive, positiveTOnly: BOOLTRUE] RETURNS [class: Classification] = {
shape: Shape ← NARROW[sliceD.slice.shape];
mo: MasterObject ← shape.mo;
class ← mo.class.rayCastNoBBoxes[localRay, sliceD, prim, positiveTOnly];
};
RayCastBoundingSpheres: PUBLIC PROC [localRay: Ray, sliceD: SliceDescriptor, prim: Primitive, positiveTOnly: BOOLTRUE] RETURNS [class: Classification] = {
shape: Shape ← NARROW[sliceD.slice.shape];
mo: MasterObject ← shape.mo;
class ← mo.class.rayCastBoundingSpheres[localRay, sliceD, prim, positiveTOnly];
};
DrawSelectionFeedback: PUBLIC PROC [slice: Slice, selectedParts: SliceParts, hotParts: SliceParts, dc: Imager.Context, camera: Camera, dragInProgress, caretIsMoving, hideHot, quick: BOOL] = {
shape: Shape ← NARROW[slice.shape];
mo: MasterObject ← shape.mo;
mo.class.drawSelectionFeedback[slice, selectedParts, hotParts, dc, camera, dragInProgress, caretIsMoving, hideHot, quick];
};
DrawAttractorFeedback: PUBLIC PROC [sliceD: SliceDescriptor, selectedParts: SliceParts, dragInProgress: BOOL, dc: Imager.Context, camera: Camera] = {
shape: Shape ← NARROW[sliceD.slice.shape];
mo: MasterObject ← shape.mo;
mo.class.drawAttractorFeedback[sliceD, selectedParts, dragInProgress, dc, camera];
};
CountSurfacesInAssembly: PROC [assembly: Slice] RETURNS [surfCount: NAT] = {
WITH assembly.shape SELECT FROM
assems: SliceList => surfCount ← CountSurfacesInAssemblyList[assems];
shape: Shape => surfCount ← shape.mo.class.countSurf[shape.mo];
ENDCASE => ERROR;
};
CountSurfacesInAssemblyList: PROC [asl: SliceList] RETURNS [surfCount: NAT] = {
surfCount ← 0;
FOR list: LIST OF Slice ← asl.list, list.rest UNTIL list = NIL DO
surfCount ← surfCount + CountSurfacesInAssembly[list.first];
ENDLOOP;
};
PutAssemblyOnQueue: PROC [assembly: Slice, q: PriorityQueue.Ref, cameraCS: CoordSystem, eyeWorld: Point3d] = {
surfList: PlanarSurfaceList;
SELECT assembly.showAs FROM
tool => {
toolMasterObject: MasterObject ← assembly.toolMasterObject;
surfList ← toolMasterObject.class.getSurf[assembly, cameraCS, eyeWorld];
FOR surfList ← surfList, surfList.rest UNTIL surfList = NIL DO
PriorityQueue.Insert[q, surfList.first];
ENDLOOP;
};
normal => {
WITH assembly.shape SELECT FROM
assems: SliceList => {
FOR list: LIST OF Slice ← assems.list, list.rest UNTIL list = NIL DO
PutAssemblyOnQueue[list.first, q, cameraCS, eyeWorld];
ENDLOOP;
};
shape: Shape => {
cameraShape: Matrix4by4;
cameraShape ← SVCoordSys.FindCameraInTermsOf[shape.coordSys, cameraCS];
surfList ← shape.mo.class.getSurf[assembly, cameraCS, eyeWorld];
FOR surfList ← surfList, surfList.rest UNTIL surfList = NIL DO
PriorityQueue.Insert[q, surfList.first];
ENDLOOP;
};
ENDCASE => ERROR;
};
invisible => {};
both => ERROR Feedback.Problem[msg: "Can't have both."];
ENDCASE => ERROR;
}; -- end of PutAssemblyOnQueue
PutAssemblyOnQueueTransform: PROC [sliceD: SliceDescriptor, q: PriorityQueue.Ref, cameraCS: CoordSystem, eyeWorld: Point3d, transform: Matrix4by4] = {
surfList: PlanarSurfaceList;
assembly: Slice ← sliceD.slice;
SELECT assembly.showAs FROM
tool => {};
normal => {
WITH assembly.shape SELECT FROM
assems: SliceList => {
FOR list: LIST OF Slice ← assems.list, list.rest UNTIL list = NIL DO
PutAssemblyOnQueue[list.first, q, cameraCS, eyeWorld];
ENDLOOP;
};
shape: Shape => {
surfList ← shape.mo.class.getSurfTransform[sliceD, cameraCS, eyeWorld, transform];
FOR surfList ← surfList, surfList.rest UNTIL surfList = NIL DO
PriorityQueue.Insert[q, surfList.first];
ENDLOOP;
};
ENDCASE => ERROR;
};
invisible => {};
both => ERROR Feedback.Problem[msg: "Can't have both."];
ENDCASE => ERROR;
}; -- end of PutAssemblyOnQueue
SetColor: PROC [dc: Imager.Context, camera: Camera, color: Color] = {
IF camera.colorFilm THEN Imager.SetColor[dc, color]
ELSE {
intensity: REAL ← ImagerColorPrivate.IntensityFromColor[NARROW[color]];
Imager.SetColor[dc, ImagerColor.ColorFromGray[1.0-intensity]]};
};
NotYetImplemented: PUBLIC SIGNAL = CODE;
Transforming
Transform: PUBLIC PROC [sliceD: SliceDescriptor, scene: Scene, transform: Matrix4by4] = {
shape: Shape ← NARROW[sliceD.slice.shape];
mo: MasterObject ← shape.mo;
mo.class.transform[sliceD, scene, transform];
UpdateBoundBoxes[sliceD.slice];
};
Four ways to move an assembly C. If d is NIL, the anchor is assumed to be WORLD.
IncTransf: PUBLIC PROC [c: Slice, scene: Scene, m: Matrix4by4, d: CoordSystem ← NIL, dFixed: BOOLFALSE, cameraList: LIST OF Camera ← NIL] = {
Incrementally transform C with respect to D by M. If D is a descendent of C, this could result in D moving as well. If this is not desired, set DFixed to TRUE and IncTransf makes sure that D doesn't move.
assemCS: CoordSystem ← c.coordSys;
IF d = NIL THEN d ← scene.coordSysRoot;
IncTransfCS[assemCS, d, m, dFixed];
UpdateBoundBoxes[c, cameraList];
};
IncTransfMatrix: PUBLIC PROC [c: Slice, dWorld: Matrix4by4, m: Matrix4by4, cameraList: LIST OF Camera ← NIL] = {
p: CoordSystem;
cWorld, pWorld: Matrix4by4;
assemCS: CoordSystem ← c.coordSys;
p ← SVCoordSys.Parent[assemCS];
IF p = NIL THEN ERROR; -- cannot translate WORLD for now
cWorld ← SVCoordSys.WRTWorld[assemCS];
pWorld ← SVCoordSys.WRTWorld[p];
SVCoordSys.SetMat[assemCS, SVMatrixOps.IncTransf[cWorld, pWorld, dWorld, m]];
UpdateBoundBoxes[c, cameraList];
};
IncTransfCS: PUBLIC PROC [c: CoordSystem, d: CoordSystem, m: Matrix4by4, dFixed: BOOLFALSE] = {
Incrementally transform C with respect to D by M. If D is a descendent of C, this could result in D moving as well. If this is not desired, set DFixed to TRUE and IncTransf makes sure that D doesn't move.
p: CoordSystem;
cWorld, dWorld, pWorld: Matrix4by4;
IF MatrixHasScaling[m] THEN...
p ← SVCoordSys.Parent[c];
IF p = NIL THEN ERROR; -- cannot translate WORLD for now
IF d = NIL THEN ERROR;
cWorld ← SVCoordSys.WRTWorld[c];
pWorld ← SVCoordSys.WRTWorld[p];
dWorld ← SVCoordSys.WRTWorld[d];
SVCoordSys.SetMat[c, SVMatrixOps.IncTransf[cWorld, pWorld, dWorld, m]];
IF dFixed THEN SVTransforms.AbsTransfMatrix[d, SVMatrix3d.Identity[], dWorld];
};
AbsTransf: PUBLIC PROC [c: Slice, scene: Scene, n: Matrix4by4, d: CoordSystem ← NIL, dFixed: BOOLFALSE, cameraList: LIST OF Camera ← NIL] = {
Absolutely transform C with respect to D by M. If D is a descendent of C, this could result in D moving as well. If this is not desired, set DFixed to TRUE and AbsTransf makes sure that D doesn't move.
assemCS: CoordSystem ← c.coordSys;
IF d = NIL THEN d ← scene.coordSysRoot;
AbsTransfCS[assemCS, d, n, dFixed];
UpdateBoundBoxes[c, cameraList];
};
AbsTransfCS: PUBLIC PROC [c: CoordSystem, d: CoordSystem, n: Matrix4by4, dFixed: BOOLFALSE] = {
Absolutely transform C with respect to D by M. If D is a descendent of C, this could result in D moving as well. If this is not desired, set DFixed to TRUE and AbsTransf makes sure that D doesn't move.
p: CoordSystem;
dWorld, pWorld: Matrix4by4;
IF Rope.Equal[SVCoordSys.Name[c], "SCREEN", TRUE] THEN {SVCoordSys.SetMat[c, n]; RETURN};
p ← SVCoordSys.Parent[c];
IF p = NIL THEN ERROR; -- cannot transform WORLD.
IF d = NIL THEN ERROR;
IF MatrixHasScaling[n] THEN {
SIGNAL Feedback.Problem[msg: "SVTransform.AbsTransf got illegal matrix. Ignored."];
RETURN}; -- commented out by Bier on May 27, 1987 3:27:32 pm PDT
dWorld ← SVCoordSys.WRTWorld[d];
pWorld ← SVCoordSys.WRTWorld[p];
SVCoordSys.SetMat[c, SVMatrixOps.AbsTransf[pWorld, dWorld, n]];
IF dFixed THEN SVTransforms.AbsTransfMatrix[d, SVMatrix3d.Identity[], dWorld];
};
TugTransf: PUBLIC PROC [c: Slice, scene: Scene, tugBoat: CoordSystem, n: Matrix4by4, d: CoordSystem ← NIL, cameraList: LIST OF Camera ← NIL] = {
Absolutely transforms tugBoat with respect to D and then C with respect to tugBoat.
assemCS: CoordSystem ← c.coordSys;
IF d = NIL THEN d ← scene.coordSysRoot;
TugTransfCS[assemCS, tugBoat, d, n];
UpdateBoundBoxes[c, cameraList];
};
TugTransfCS: PUBLIC PROC [C: CoordSystem, tugBoat: CoordSystem, D: CoordSystem, N: Matrix4by4] = {
F: Matrix4by4 ← SVCoordSys.FindAInTermsOfB[C, tugBoat];
AbsTransfCS[tugBoat, D, N];
AbsTransfCS[C, tugBoat, F];
};
TugTransfLeaveTug: PUBLIC PROC [c: Slice, scene: Scene, tugBoat: CoordSystem, n: Matrix4by4, d: CoordSystem ← NIL, cameraList: LIST OF Camera ← NIL] = {
Like TugTransf, but returns tugBoat to its original location.
assemCS: CoordSystem ← c.coordSys;
IF d = NIL THEN d ← scene.coordSysRoot;
TugTransfLeaveTugCS[assemCS, tugBoat, d, n];
UpdateBoundBoxes[c, cameraList];
};
TugTransfLeaveTugCS: PUBLIC PROC [C: CoordSystem, tugBoat: CoordSystem, D: CoordSystem, N: Matrix4by4] = {
Like TugTransf, but returns the Tugboat to its original position.
F: Matrix4by4;
saveTugMat: Matrix4by4 ← SVCoordSys.GetMat[tugBoat];
F ← SVCoordSys.FindAInTermsOfB[C, tugBoat];
AbsTransfCS[tugBoat, D, N];
AbsTransfCS[C, tugBoat, F];
SVCoordSys.SetMat[tugBoat, saveTugMat];
};
ScalePrimitive: PUBLIC PROC [c: Slice, sx, sy, sz: REAL, cameraList: LIST OF Camera ← NIL] = {
shape: Shape;
scalarCS: CoordSystem;
scalars: Vector3d;
IF NOT ISTYPE[c.shape, Shape] THEN ERROR;
shape ← NARROW[c.shape];
scalarCS ← shape.coordSys;
scalars ← SVCoordSys.GetScalars[scalarCS];
scalars[1] ← scalars[1]*sx;
scalars[2] ← scalars[2]*sy;
scalars[3] ← scalars[3]*sz;
SVCoordSys.SetScalars[scalarCS, scalars];
UpdateBoundBoxes[c, cameraList];
};
MatrixHasScaling: PRIVATE PROC [mat: Matrix4by4] RETURNS [BOOL] = {
sx, sy, sz: REAL;
almostZero: REAL ← 1.0e-4;
[sx, sy, sz] ← SVMatrix3d.ScaleFromMatrix[mat];
IF ABS[sx - 1.0] > almostZero OR ABS[sy-1.0] > almostZero OR ABS[sz - 1.0] > almostZero
THEN RETURN[TRUE]
ELSE RETURN [FALSE];
};
Describe: PUBLIC PROC [sliceD: SliceDescriptor] RETURNS [rope: Rope.ROPE] = {
shape: Shape ← NARROW[sliceD.slice.shape];
mo: MasterObject ← shape.mo;
RETURN[mo.class.describe[sliceD]];
};
DescribeHit: PUBLIC PROC [slice: Slice, hitData: REF ANY] RETURNS [rope: Rope.ROPE] = {
shape: Shape ← NARROW[slice.shape];
mo: MasterObject ← shape.mo;
RETURN[mo.class.describeHit[mo, hitData]];
};
Parts
IsEmptyParts: PUBLIC PROC [sliceD: SliceDescriptor] RETURNS [BOOL] = {
shape: Shape ← NARROW[sliceD.slice.shape];
mo: MasterObject ← shape.mo;
IF sliceD.parts = NIL THEN RETURN[TRUE];
RETURN[mo.class.isEmptyParts[sliceD]];
};
IsCompleteParts: PUBLIC PROC [sliceD: SliceDescriptor] RETURNS [BOOL] = {
shape: Shape ← NARROW[sliceD.slice.shape];
mo: MasterObject ← shape.mo;
IF sliceD.parts = NIL THEN RETURN[TRUE];
RETURN[mo.class.isCompleteParts[sliceD]];
};
NewParts: PUBLIC PROC [slice: Slice, hitData: REF ANY, hitPoint: Point3d, mode: SelectMode] RETURNS [sliceD: SliceDescriptor] = {
shape: Shape ← NARROW[slice.shape];
mo: MasterObject ← shape.mo;
RETURN[mo.class.newParts[slice, hitData, hitPoint, mode]];
};
DescriptorFromParts: PUBLIC PROC [slice: Slice, parts: SliceParts] RETURNS [sliceD: SliceDescriptor] = {
sliceD ← NEW[SliceDescriptorObj ← [slice, parts]];
};
UnionParts: PUBLIC PROC [partsA: SliceDescriptor, partsB: SliceDescriptor] RETURNS [aPlusB: SliceDescriptor] = {
shape: Shape;
mo: MasterObject;
IF partsA = NIL AND partsB = NIL THEN RETURN[NIL];
IF partsA = NIL THEN shape ← NARROW[partsB.slice.shape]
ELSE shape ← NARROW[partsA.slice.shape];
mo ← shape.mo;
RETURN[mo.class.unionParts[partsA, partsB]];
};
DifferenceParts: PUBLIC PROC [partsA: SliceDescriptor, partsB: SliceDescriptor] RETURNS [aMinusB: SliceDescriptor] = {
shape: Shape;
mo: MasterObject;
IF partsA = NIL AND partsB = NIL THEN RETURN[NIL];
IF partsA = NIL THEN shape ← NARROW[partsB.slice.shape]
ELSE shape ← NARROW[partsA.slice.shape];
mo ← shape.mo;
RETURN[mo.class.differenceParts[partsA, partsB]];
};
MovingParts: PUBLIC PROC [slice: Slice, selectedParts: SliceParts] RETURNS [background, overlay, rubber, drag: SliceDescriptor] = {
shape: Shape ← NARROW[slice.shape];
mo: MasterObject ← shape.mo;
[background, overlay, rubber, drag] ← mo.class.movingParts[slice, selectedParts];
};
AugmentParts: PUBLIC PROC [sliceD: SliceDescriptor, selectClass: SelectionClass] RETURNS [more: SliceDescriptor] = {
mo: MasterObject;
mo ← NARROW[sliceD.slice.shape, Shape].mo;
more ← mo.class.augmentParts[sliceD, selectClass];
};
Part Generators
PointsInDescriptor: PUBLIC PROC [sliceD: SliceDescriptor, centroids: BOOL] RETURNS [PointGenerator] = {
assembly: Slice;
mo: MasterObject;
shape: Shape;
assembly ← sliceD.slice;
shape ← NARROW[assembly.shape];
mo ← shape.mo;
RETURN[mo.class.pointsInDescriptor[sliceD, centroids]];
};
NextPoint: PUBLIC PROC [pointGen: PointGenerator] RETURNS [pointAndDone: PointAndDone] = {
assembly: Slice;
mo: MasterObject;
shape: Shape;
ptBlockandDone: PointAndDone;
blockWORLD: Matrix4by4;
assembly ← pointGen.sliceD.slice;
shape ← NARROW[assembly.shape];
mo ← shape.mo;
ptBlockandDone ← mo.class.nextPoint[pointGen];
IF ptBlockandDone.done THEN RETURN[[[0,0,0], TRUE]];
blockWORLD ← SVCoordSys.WRTWorld[shape.coordSys];
pointAndDone.point ← SVMatrix3d.Update[ptBlockandDone.point, blockWORLD];
pointAndDone.done ← FALSE;
};
PointPairsInDescriptor: PUBLIC PROC [sliceD: SliceDescriptor] RETURNS [pointPairGen: PointPairGenerator] = {
assembly: Slice;
mo: MasterObject;
shape: Shape;
assembly ← sliceD.slice;
shape ← NARROW[assembly.shape];
mo ← shape.mo;
RETURN[mo.class.pointPairsInDescriptor[sliceD]];
};
NextPointPair: PUBLIC PROC [pointPairGen: PointPairGenerator] RETURNS [pointPairAndDone: PointPairAndDone] = {
assembly: Slice;
mo: MasterObject;
shape: Shape;
pointPairShapeandDone: PointPairAndDone;
shapeWorld: Matrix4by4;
assembly ← pointPairGen.sliceD.slice;
shape ← NARROW[assembly.shape];
mo ← shape.mo;
pointPairShapeandDone ← mo.class.nextPointPair[pointPairGen];
IF pointPairShapeandDone.done THEN RETURN[[[0,0,0], [0,0,0], TRUE]];
shapeWorld ← SVCoordSys.WRTWorld[shape.coordSys];
pointPairAndDone.lo ← SVMatrix3d.Update[pointPairShapeandDone.lo, shapeWorld];
pointPairAndDone.hi ← SVMatrix3d.Update[pointPairShapeandDone.hi, shapeWorld];
pointPairAndDone.done ← FALSE;
};
Hit Testing
ClosestPointToPoint: PUBLIC PROC [sliceD: SliceDescriptor, testPoint: Point3d, t: REAL, centroids: BOOL] RETURNS [bestDist: REAL, pointWorld: Point3d, normalWorld: Vector3d, hitData: REF ANY, success: BOOL] = {
mo: MasterObject;
mo ← NARROW[sliceD.slice.shape, Shape].mo;
[bestDist, pointWorld, normalWorld, hitData, success] ← mo.class.closestPointToPoint[sliceD, testPoint, t, centroids];
};
ClosestPointToLine: PUBLIC PROC [sliceD: SliceDescriptor, cameraPoint: Point2d, line3d: Line3d, t: REAL, camera: Camera, centroids: BOOL] RETURNS [bestDist: REAL, pointWorld: Point3d, normalWorld: Vector3d, hitData: REF ANY, success: BOOL] = {
mo: MasterObject;
mo ← NARROW[sliceD.slice.shape, Shape].mo;
[bestDist, pointWorld, normalWorld, hitData, success] ← mo.class.closestPointToLine[sliceD, cameraPoint, line3d, t, camera, centroids];
};
ClosestSegmentToLine: PUBLIC PROC [sliceD: SliceDescriptor, cameraPoint: Point2d, line3d: Line3d, t: REAL, camera: Camera] RETURNS [bestDist: REAL, pointWorld: Point3d, normalWorld: Vector3d, hitData: REF ANY, success: BOOL] = {
mo: MasterObject;
mo ← NARROW[sliceD.slice.shape, Shape].mo;
[bestDist, pointWorld, normalWorld, hitData, success] ← mo.class.closestSegmentToLine[sliceD, cameraPoint, line3d, t, camera];
};
ClosestJointToHitData: PUBLIC PROC [sliceD: SliceDescriptor, worldPt: Point3d, normal: Vector3d, hitData: REF ANY] RETURNS [jointD: SliceDescriptor, jointPos: Point3d, jointNormal: Vector3d] = {
This is an incomplete implementation, just to get off the ground.
newHitData: REF ANY;
success: BOOL;
dist: REAL;
jointD ← sliceD;
[dist, jointPos, jointNormal, newHitData, success] ← ClosestPointToPoint[sliceD, worldPt, Real.LargestNumber, FALSE];
IF NOT success THEN ERROR;
jointNormal ← normal;
};
WithinBoundBlock: PUBLIC PROC [slice: Slice, boundBlockWorld: BoundBlock, parts: SliceParts ← NIL] RETURNS [withinParts: SliceDescriptor] = {
mo: MasterObject;
mo ← NARROW[slice.shape, Shape].mo;
withinParts ← mo.class.withinBoundBlock[slice, boundBlockWorld, parts];
};
Style
SetStrokeColor: PUBLIC PROC [slice: Slice, parts: SliceParts, color: Color ← NIL] = {
mo: MasterObject;
mo ← NARROW[slice.shape, Shape].mo;
mo.class.setStrokeColor[slice, parts, color];
};
GetStrokeColor: PUBLIC PROC [slice: Slice, parts: SliceParts] RETURNS [color: Color ← NIL] = {
mo: MasterObject;
mo ← NARROW[slice.shape, Shape].mo;
color ← mo.class.getStrokeColor[slice, parts];
};
SetFillColor: PUBLIC PROC [slice: Slice, parts: SliceParts, artwork: Artwork] = {
mo: MasterObject;
mo ← NARROW[slice.shape, Shape].mo;
mo.class.setFillColor[slice, parts, artwork];
};
GetFillColor: PUBLIC PROC [slice: Slice, parts: SliceParts] RETURNS [artwork: Artwork] = {
mo: MasterObject;
mo ← NARROW[slice.shape, Shape].mo;
artwork ← mo.class.getFillColor[slice, parts];
};
MakeBlockSlice: PUBLIC PROC [loX, loY, loZ, hiX, hiY, hiZ: REAL, corner: [0..7], transform: Matrix4by4, scene: Scene, feedback: FeedbackData] RETURNS [sliceD: SliceDescriptor] = {
sliceParts: SliceParts;
slice: Slice;
mo: MasterObject;
masterObjectFound, success: BOOLTRUE;
name: Rope.ROPE ← SVScene.UniqueAssemblyNameFrom["Block", scene];
[sliceParts, mo] ← SVMasterObject.MakeBlockSlice[name, loX, loY, loZ, hiX, hiY, hiZ, corner];
BEGIN
moFound: BOOLTRUE;
addSucceeds: BOOLTRUE;
SVScene.AddMasterObjectToScene[mo, scene
! SVScene.NameAlreadyPresent => {
Feedback.PutF[feedback, oneLiner, "%g name %g is already registered. Try another.", [rope[mo.class.name]], [rope[mo.name]]];
Feedback.Blink[feedback]; addSucceeds ← FALSE; CONTINUE}
];
IF NOT addSucceeds THEN RETURN;
END;
[slice, masterObjectFound, success] ← CreatePrimitive[name, name, scene];
IF NOT masterObjectFound OR NOT success THEN ERROR;
sliceD ← DescriptorFromParts[slice, sliceParts];
};
END.