File: DisplayListToTreeImpl.mesa
Author: Eric Bier on January 1, 1983 11:43 pm
Last edited by Bier on July 5, 1983 5:03 pm
Contents: Code to extract a CSG Tree from the recursive assemblylist-of-assemblylist DisplayList structure.
DIRECTORY
CSG,
CSGGraphics,
Convert,
DisplayList3d,
IO,
Matrix3d,
Rope,
SVPrimitive,
SVTransforms;
DisplayListToTreeImpl: PROGRAM
IMPORTS Convert, CSG, IO, Rope, SVPrimitive, SVTransforms
EXPORTS DisplayList3d =
BEGIN
Scene: TYPE = REF SceneObj;
SceneObj: TYPE = DisplayList3d.SceneObj;
AssemblyList: TYPE = REF AssemblyListObj;
AssemblyListObj: TYPE = DisplayList3d.AssemblyListObj;
PointSetOp: TYPE = CSG.PointSetOp;-- {union, intersection, difference}
Assembly: TYPE = REF AssemblyObj;
AssemblyObj: TYPE = DisplayList3d.AssemblyObj;
Camera: TYPE = CSGGraphics.Camera;
MasterObject: TYPE = REF MasterObjectRec;
MasterObjectRec: TYPE = DisplayList3d.MasterObjectRec;
CSGTree: TYPE = REF CSGTreeObj;
CSGTreeObj: TYPE = CSG.CSGTreeObj;
Primitive: TYPE = REF PrimitiveObj;
PrimitiveObj: TYPE = CSG.PrimitiveObj;
Matrix4by4: TYPE = Matrix3d.Matrix4by4;
SurfaceArray: TYPE = REF SurfaceArrayObj;
SurfaceArrayObj: TYPE = CSG.SurfaceArrayObj;-- ARRAY[1..maxSceneDepth] OF Surface;
Surface: TYPE = REF ANY; -- may be RectSurface, TubeSurface, DiskSurface, or ShellSurface (of BasicObject3d), etc.
Composite: TYPE = REF CompositeObj;
CompositeObj: TYPE = CSG.CompositeObj;
SceneToTree: PUBLIC PROC [scene: Scene, camera: Camera] RETURNS [tree: CSGTree] = {
Here's the plan: Each assembly list has an associated PointSetOp. If this op is null, it defaults to "union". Intersection means the intersection of all objects on the assembly list. Union is the union of all of them. Difference is the first one minus all of the others.
topAssembly: Assembly;
topNode: REF ANY;
SVTransforms.TellAboutCameraAndWorld[scene.assembly, camera, scene];
Preprocess the scene to make sure all the assemblies know where they are so they can tell the primitives at PrimitiveFromAssembly time (see BasicObject3dImpl.mesa for example)
topAssembly ← scene.assembly;
topNode ← AssemblyToNode[topAssembly];
tree ← CSG.MakeCSGTree[topNode, scene.backgroundColor, scene.shadows];
};
AssemblyToNode: PROC [assembly: Assembly] RETURNS [node: REF ANY] = {
If assembly is a primitive assembly, return a primitive CSG node. Otherwise, call AssemblyListToNode on its children and return the resulting tree.
IF assembly = NIL THEN RETURN[ NIL ];
WITH assembly.object SELECT FROM
mo: MasterObject => {
newPrim: Primitive;
newPrim ← SVPrimitive.FromAssembly[assembly];
node ← newPrim;
RETURN};
alist: AssemblyList => {
node ← AssemblyListToNode[alist, assembly.name];
RETURN};
ENDCASE => ERROR;
}; -- end of AssemblyToNode
AssemblyListToNode: PROC [assemblyList: AssemblyList, name: Rope.ROPE] RETURNS [node: REF ANY] = {
list: LIST OF Assembly ← assemblyList.list;
treeList: LIST OF REF ANY;
We have a list of composite assemblies and/or primitive assemblies. Turn this into a list of Composites, Primitives and NILs:
treeList ← AssemblyListToTreeList[list];
Now we will inspect our list in order to combine the trees we have made.
If there are no children, then this node is NIL. It has no effect on the ray tracing.
IF treeList = NIL THEN RETURN[ NIL ];
If there is only one item in the list then the node for this assembly is the node produced for its son.
IF treeList.rest = NIL THEN {
node ← treeList.first;
RETURN}
ELSE { -- Inspect the treeList more carefully.
If all of the children are NIL, so is this node.
IF AllAreNil[treeList] THEN RETURN[NIL];
If the operation is intersection and any are NIL then so is the node.
IF assemblyList.pointSetOp = intersection AND AnyAreNIL[treeList] THEN RETURN[ NIL ];
IF the first is NIL and the operation is difference then the node is NIL.
IF treeList.first = NIL AND assemblyList.pointSetOp = difference THEN RETURN[ NIL ];
We have handled all cases which involve NILs. For all other cases, we should ignore them, so we will remove them now:
treeList ← SpliceOutNILs[treeList];
At this point, we must reconsider the possibility that we may have a null list or single member list.
IF treeList = NIL THEN RETURN[NIL];
IF treeList.rest = NIL THEN {
node ← treeList.first;
RETURN}
ELSE {
Since it is now clear that this node has more than one child, we make n-1 Composite nodes for it (if there are n children remaining).
SELECT assemblyList.pointSetOp FROM
union => node ← MakeUnionNode[treeList, name, 1];
intersection => node ← MakeIntersectionNode[treeList, name, 1];
difference => node ← MakeDifferenceNode[treeList, name, 1];
ENDCASE => ERROR;
}; -- end ELSE
}; -- end of ELSE
}; -- end of AssemblyListToNode
MakeUnionNode: PROC [treeList: LIST OF REF ANY, name: Rope.ROPE, level: NAT] RETURNS [node: REF ANY] = {
In the top level call to MakeUnionNode, treeList should have at least 2 members and none of its members should be NIL.
leftNode, rightNode: REF ANY;
intermediateName: Rope.ROPE;
IF treeList = NIL THEN RETURN[ NIL];
IF treeList.rest = NIL THEN RETURN [treeList.first];
leftNode ← treeList.first;
rightNode ← MakeUnionNode[treeList.rest, name, level + 1];
intermediateName ← Rope.Concat [name, Convert.ValueToRope[[unsigned[level]]]];
node ← NEW[CompositeObj ← [intermediateName, union, leftNode, rightNode]];
}; -- end of MakeUnionNode
MakeIntersectionNode: PROC [treeList: LIST OF REF ANY, name: Rope.ROPE, level: NAT] RETURNS [node: REF ANY] = {
In the top level call to MakeIntersectionNode, treeList should have at least 2 members and none of its members should be NIL.
leftNode, rightNode: REF ANY;
intermediateName: Rope.ROPE;
IF treeList = NIL THEN RETURN[ NIL];
IF treeList.rest = NIL THEN RETURN [treeList.first];
leftNode ← treeList.first;
rightNode ← MakeIntersectionNode[treeList.rest, name, level + 1];
intermediateName ← Rope.Concat [name, Convert.ValueToRope[[unsigned[level]]]];
node ← NEW[CompositeObj ← [intermediateName, intersection, leftNode, rightNode]];
}; -- end of MakeIntersectionNode
MakeDifferenceNode: PROC [treeList: LIST OF REF ANY, name: Rope.ROPE, level: NAT] RETURNS [node: REF ANY] = {
In the top level call to MakeDifferenceNode, treeList should have at least 2 members and none of its members should be NIL.
leftNode, rightNode: REF ANY;
intermediateName: Rope.ROPE;
leftNode ← treeList.first;
rightNode ← MakeUnionNode[treeList.rest, name, level + 1];
intermediateName ← Rope.Concat [name, Convert.ValueToRope[[unsigned[level]]]];
node ← NEW[CompositeObj ← [intermediateName, difference, leftNode, rightNode]];
}; -- end of MakeDifferenceNode
AssemblyListToTreeList: PROC [list: LIST OF Assembly] RETURNS [treeList: LIST OF REF ANY] = {
If there are no children, then this node is NIL. It has no effect on the ray tracing.
IF list = NIL THEN RETURN[ NIL ];
Go down the list turning each primitive or composite assembly into a Composite or Primitive.
treeList ← CONS[AssemblyToNode[list.first], AssemblyListToTreeList[list.rest]];
}; -- end of AssemblyListToNodeNoNILs
AllAreNil: PRIVATE PROC [list: LIST OF REF ANY] RETURNS [BOOL] = {
FOR l: LIST OF REF ANY ← list, l.rest UNTIL l = NIL DO
IF l.first # NIL THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE];
};
AnyAreNIL: PRIVATE PROC [list: LIST OF REF ANY] RETURNS [BOOL] = {
FOR l: LIST OF REF ANY ← list, l.rest UNTIL l = NIL DO
IF l.first = NIL THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE];
};
SpliceOutNILs: PRIVATE PROC [list: LIST OF REF ANY] RETURNS [compressed: LIST OF REF ANY] = {
IF list = NIL THEN RETURN[ NIL];
IF list.first # NIL THEN compressed ← CONS[list.first, SpliceOutNILs[list.rest]]
ELSE compressed ← SpliceOutNILs[list.rest];
};
---- ---- ---- ----
---- ---- ---- ----
ListTree: PUBLIC PROC [outHandle: IO.STREAM, tree: CSGTree] = {
indent: NAT ← 0;
node: REF ANY ← tree.son;
ListNode[outHandle, node, indent];
};
ListNode: PROC [outHandle: IO.STREAM, node: REF ANY, indent: NAT] = {
WITH node SELECT FROM
prim: Primitive => ListPrimitive[outHandle, prim, indent];
comp: Composite => ListComposite[outHandle, comp, indent];
ENDCASE => ERROR;
};
ListPrimitive: PROC [outHandle: IO.STREAM, prim: Primitive, indent: NAT] = {
FOR i: NAT IN[1..indent] DO outHandle.PutChar[IO.TAB] ENDLOOP;
outHandle.PutF["Primitive: %g\n",[rope[prim.name]]];
};
ListComposite: PROC [outHandle: IO.STREAM, comp: Composite, indent: NAT] = {
op: Rope.ROPE;
FOR i: NAT IN[1..indent] DO outHandle.PutChar[IO.TAB] ENDLOOP;
SELECT comp.operation FROM
union => op ← "union";
intersection => op ← "intersection";
difference => op ← "difference";
ENDCASE => ERROR;
outHandle.PutF["Composite: %g op: %g\n",[rope[comp.name]], [rope[op]]];
ListNode[outHandle, comp.leftSolid, indent + 1];
ListNode[outHandle, comp.rightSolid, indent + 1];
};
FindNodeFromName: PUBLIC PROC [tree: REF ANY, name: Rope.ROPE] RETURNS [node: REF ANY, success: BOOL] = {
WITH tree SELECT FROM
c: Composite => {[node, success] ← FindNodeFromNameInComposite[c, name]; RETURN};
p: Primitive => {[node, success] ← FindNodeFromNameInPrimitive[p, name]; RETURN};
ENDCASE => ERROR;
};
FindNodeFromNameInComposite: PROC [c: Composite, name: Rope.ROPE] RETURNS [node: REF ANY, success: BOOL] = {
IF Rope.Equal[c.name, name, TRUE] THEN RETURN[c, TRUE];
[node, success] ← FindNodeFromName[c.leftSolid, name];
IF success THEN RETURN;
[node, success] ← FindNodeFromName[c.rightSolid, name];
IF success THEN RETURN
ELSE RETURN[NIL, FALSE];
};
FindNodeFromNameInPrimitive: PROC [p: Primitive, name: Rope.ROPE] RETURNS [node: REF ANY, success: BOOL] = {
IF p.name = name THEN RETURN[p, TRUE]
ELSE RETURN[NIL, FALSE];
};
END.