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
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:
BOOL ←
FALSE, 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:
BOOL ←
FALSE, 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:
BOOL ←
FALSE, 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:
BOOL ←
FALSE, 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.ROPE ← NARROW[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.ROPE ← NARROW[argument];
good ← Rope.Equal[thisName, camera.viewName, FALSE];
};
bBoxCache: FunctionCache.Cache ← assem.bBoxCache;
oldEntryAny: REF ANY;
oldEntry: BoundBoxEntry;
ok: BOOL ← FALSE;
[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:
REF ←
NIL]
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, eye
World: Point3d] = {
PutAssemblyOnQueue[assembly, surfaceQueue, camera.coordSys, eyeWorld];
};
AddPolygonsToBufferTransform:
PUBLIC
PROC [surfaceQueue: PriorityQueue.Ref, sliceD: SliceDescriptor, scene: Scene, camera: Camera, eye
World: 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:
BOOL ←
TRUE]
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:
BOOL ←
TRUE]
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, eye
World: 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, eye
World: 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:
BOOL ←
FALSE, 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, d
World: 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:
BOOL ←
FALSE] = {
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:
BOOL ←
FALSE, 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:
BOOL ←
FALSE] = {
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, point
World: Point3d, normal
World: 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, point
World: Point3d, normal
World: 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, point
World: Point3d, normal
World: 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, boundBlock
World: 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: BOOL ← TRUE;
name: Rope.ROPE ← SVScene.UniqueAssemblyNameFrom["Block", scene];
[sliceParts, mo] ← SVMasterObject.MakeBlockSlice[name, loX, loY, loZ, hiX, hiY, hiZ, corner];
BEGIN
moFound: BOOL ← TRUE;
addSucceeds: BOOL ← TRUE;
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.