ShapeTwiddleImpl.mesa
Last Edited by: Crow, October 2, 1986 5:24:58 pm PDT
DIRECTORY
Real     USING [ FixC ],
Rope     USING [ ROPE ],
CedarProcess   USING [ DoWithPriority ],
ImagerColor   USING [ RGB ],
Vector3d    USING [ Sub, Length ],
Matrix3d    USING [ Transform ],
ThreeDBasics  USING [ Context, PairSequence, Quad, ShadingSequence, ShapeInstance,
         ShapeSequence, ShadingValue, Triple, TripleSequence, Vertex,
         VertexSequence ],
ThreeDScenes  USING [ Error, FindShape, NewShape, PutShading, SetPosition ],
ThreeDSurfaces  USING [ PtrPatchSequence ],
ShapeTwiddle  USING [ NatPair ];
ShapeTwiddleImpl: CEDAR PROGRAM
IMPORTS Real, Vector3d, ThreeDScenes, CedarProcess, Matrix3d
EXPORTS ShapeTwiddle
~ BEGIN
Types
Context: TYPE ~ ThreeDBasics.Context;
ShapeTwiddleError: PUBLIC SIGNAL [reason: ATOM] = CODE;
NatPair: TYPE ~ ShapeTwiddle.NatPair;   -- RECORD [x, y: NAT];
PairSequence: TYPE ~ ThreeDBasics.PairSequence;
Triple: TYPE ~ ThreeDBasics.Triple;
TripleSequence: TYPE ~ ThreeDBasics.TripleSequence;
RGB: TYPE ~ ImagerColor.RGB;
NatSequence: TYPE ~ RECORD [SEQUENCE length: NAT OF NAT];
Global Variables
Shape Manipulations
WriteOutShape: PUBLIC PROC [context: REF Context, name, ext: Rope.ROPENIL,
          numbered: BOOLEANFALSE] ~ {
Caution!!! Writes old format!!!
shape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, name ];
surface: REF ThreeDSurfaces.PtrPatchSequence ← NARROW[shape.surface];
pInfo: REF ThreeDBasics.ShadingSequence ← NARROW[
ThreeDScenes.GetShading[ shape, $PatchColors ]
];
vtxNormals: BOOLEAN ← ThreeDScenes.GetShading[ shape, $VertexNormalsInFile ] # NIL;
vtxColors: BOOLEAN ← ThreeDScenes.GetShading[ shape, $VertexColorsInFile ] # NIL;
vtxTexture: BOOLEAN ← ThreeDScenes.GetShading[ shape, $VertexTextureInFile ] # NIL;
vtxTrans: BOOLEAN ← ThreeDScenes.GetShading[ shape, $VertexTransInFile ] # NIL;
ptchNormals: BOOLEAN ← ThreeDScenes.GetShading[ shape, $PatchNormalsInFile ] # NIL;
ptchColors: BOOLEAN ← ThreeDScenes.GetShading[ shape, $PatchColorsInFile ] # NIL;
ptchTrans: BOOLEAN ← ThreeDScenes.GetShading[ shape, $PatchTransInFile ] # NIL;
out: IO.STREAM;
IF name = NIL THEN SIGNAL ShapeTwiddleError[$UnNamedObject];
IF ext = NIL THEN ext ← "shape";
out ← FS.StreamOpen[
fileName: Rope.Cat[name, ".", ext],
accessOptions: create,
wDir: NARROW[ Atom.GetPropFromList[context.props, $WDir] ]
];
out.PutF[" %g - Created by: %g at %g\n\n",
IO.rope[name],
IO.rope[UserCredentials.Get[].name],
IO.time[]
];
out.PutRope["SurfaceType ~ "];
out.PutRope[Atom.GetPName[shape.type]];
IF shape.insideVisible THEN out.PutRope[", InsideVisible"];
out.PutRope["\n\n"];
out.PutRope["Vertices ~ "];
IF numbered THEN out.PutRope["index: integer, "];
out.PutRope["xyz: triple"];
IF vtxNormals THEN out.PutRope[", normal: triple"];
IF vtxColors THEN out.PutRope[", color: triple"];
IF vtxTrans THEN out.PutRope[", trans: real"];
IF vtxTexture THEN out.PutRope[", texture: triple"];
out.PutF[" -- %g Vertices %g Patches\n\n",
   IO
.int[shape.vertex.length], IO.int[shape.numSurfaces] ];
  
IF shape.positionInValid THEN ThreeDScenes.SetPosition[shape];   -- set position matrix
FOR i: NAT IN [0..shape.vertex.length) DO
OPEN shape.vertex[i];
s: REF ThreeDBasics.ShadingValue ← shape.shade[i];
tx, ty, tz: REAL;
[ [tx, ty, tz] ] ← Matrix3d.Transform[ [x, y, z], shape.position];
IF numbered THEN out.PutF["%g ", IO.int[i] ];
out.PutF["\t%g %g %g", IO.real[tx], IO.real[ty], IO.real[tz] ];
IF vtxNormals THEN out.PutF["\t%g %g %g", IO.real[s.xn], IO.real[s.yn], IO.real[s.zn] ];
IF vtxColors THEN out.PutF["\t%g %g %g", IO.real[s.r], IO.real[s.g], IO.real[s.b] ];
IF vtxTrans THEN out.PutF["\t%g", IO.real[s.t] ];
IF vtxTexture
THEN out.PutF["\t%g %g %g", IO.real[s.txtrX], IO.real[s.txtrY], IO.real[s.txtrZ] ];
out.PutRope["\n"];
ENDLOOP;
out.PutRope["\n"];
IF shape.type = $ConvexPolygon THEN out.PutRope["Polygons ~ "]
          ELSE out.PutRope["Patches ~ "];
IF numbered THEN out.PutRope["index: integer, "];
IF ptchNormals THEN out.PutRope["normal: triple, "];
IF ptchColors THEN out.PutRope["color: triple, "];
IF ptchTrans THEN out.PutRope["trans: real, "];
out.PutRope["vertices: nats\n\n"];
FOR i: NAT IN [0..shape.numSurfaces) DO
IF numbered THEN out.PutF["%g ", IO.int[i] ];
IF ptchNormals THEN
out.PutF["\t%g %g %g", IO.real[pInfo[i].xn], IO.real[pInfo[i].yn], IO.real[pInfo[i].zn] ];
IF ptchColors THEN
out.PutF["\t%g %g %g", IO.real[pInfo[i].r], IO.real[pInfo[i].g], IO.real[pInfo[i].b] ];
IF ptchTrans THEN out.PutF["\t%g", IO.real[pInfo[i].t] ];
out.PutRope["\t"];
FOR j: NAT IN [0..surface[i].nVtces) DO
out.PutF[" %g", IO.int[ surface[i][j] ] ];
ENDLOOP;
out.PutF["\n"];
ENDLOOP;
IO.Close[out];
};
ScaleShape: PUBLIC PROC [context: REF Context, name: Rope.ROPE, scale: REAL,
  xRatio, yRatio, zRatio: REAL ← 1.0] ~ {
shape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, name ];
IF shape.positionInValid THEN ThreeDScenes.SetPosition[shape];   -- set position matrix
FOR i: NAT IN [0..shape.vertex.length) DO
OPEN shape.vertex[i];
tx, ty, tz: REAL;
[ [tx, ty, tz] ] ← Matrix3d.Transform[ [x, y, z], shape.position];
x ← tx * scale * xRatio;
y ← ty * scale * yRatio;
z ← tz * scale * zRatio;
ENDLOOP;
shape.boundingRadius ← shape.boundingRadius * scale * MAX[xRatio, yRatio, zRatio];
ThreeDScenes.PutShading[
shape,
$Scale,
NEW[ ThreeDBasics.Quad ← [scale, xRatio, yRatio, zRatio] ]
];
};
ScaleTexture: PUBLIC PROC [context: REF Context, name: Rope.ROPE, scale: REAL,
   xRatio, yRatio, zRatio: REAL ← 1.0] ~ {
shape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, name ];
IF shape.positionInValid THEN ThreeDScenes.SetPosition[shape];   -- set position matrix
WITH shape.auxInfo SELECT FROM
pair: REF PairSequence => FOR i: NAT IN [0..pair.length) DO
pair[i].x ← pair[i].x * scale * xRatio;
pair[i].y ← pair[i].y * scale * yRatio;
ENDLOOP;
triple: REF TripleSequence => FOR i: NAT IN [0..triple.length) DO
triple[i].x ← triple[i].x * scale * xRatio;
triple[i].y ← triple[i].y * scale * yRatio;
triple[i].z ← triple[i].z * scale * zRatio;
ENDLOOP;
ENDCASE => SIGNAL ThreeDScenes.Error[[$Unimplemented, "Unknown texture type"]];
ThreeDScenes.PutShading[
shape,
$TextureScale,
NEW[ ThreeDBasics.Quad ← [scale, xRatio, yRatio, zRatio] ]
];
};
SortPair: TYPE ~ RECORD [vtx, next: NAT];
SortSequence: TYPE ~ RECORD[SEQUENCE length: NAT OF REF SortPair];
CleanUp: PUBLIC PROC [context: REF Context, name: Rope.ROPE, deSeam: BOOLEANFALSE,
  tolerance: REAL ← 0.0] ~ { 
delete excess vertices, join identical ones, etc.
currPos: NAT ← 0;
shape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, name ];
surface: REF ThreeDSurfaces.PtrPatchSequence ← NARROW[shape.surface];
table: REF NatSequence ← NEW[ NatSequence[shape.vertex.length] ];
buckets: REF SortSequence ← NEW[SortSequence[5 * shape.vertex.length / 4]];
nextBucket: NAT ← (shape.vertex.length / 4) + 1;
newVtx: REF ThreeDBasics.VertexSequence;
newShade: REF ThreeDBasics.ShadingSequence;
Action: PROC ~ {
IF deSeam THEN {
minX, maxX, scale: REAL;
[minX, maxX] ← Bounds[context, name];
scale ← (shape.vertex.length / 4) / (maxX - minX);
IF 1.0 / scale < tolerance THEN SIGNAL ShapeTwiddleError[$ToleranceTooLarge];
FOR i: NAT IN [0..shape.vertex.length) DO     -- bucket sort vertices
index: NAT ← Real.FixC[ (shape.vertex[i].x - minX) * scale ];
IF buckets[index] # NIL
THEN {
buckets[nextBucket] ← NEW[SortPair];
buckets[nextBucket].next ← buckets[index].next; -- put on next-to-head of list
buckets[index].next ← nextBucket;     -- reset first pointer
index ← nextBucket; nextBucket ← nextBucket + 1;
}
ELSE {
buckets[index] ← NEW[SortPair];    -- bucket hit for first time
buckets[index].next ← 0;
};
buckets[index].vtx ← i;         -- store vertex pointer
ENDLOOP;
FOR i: NAT IN [1..table.length) DO    -- Coalesce nearby vertices
vtx: REF ThreeDBasics.Vertex ← shape.vertex[i];
xBucket: NATMAX[1, MIN[shape.vertex.length-1, Real.FixC[ (vtx.x - minX)*scale]]];
table[i] ← i;           -- set to identity value
FOR j: NAT IN [xBucket-1 .. xBucket+1] DO-- search this and last and next buckets
index: NAT ← j;
WHILE buckets[index] # NIL DO
k: NAT ← buckets[index].vtx;
diff: REAL ← Vector3d.Length[ Vector3d.Sub[  -- check distance
[vtx.x, vtx.y, vtx.z],
[shape.vertex[k].x, shape.vertex[k].y, shape.vertex[k].z]
] ];
IF diff <= tolerance THEN IF k < table[i] THEN table[i] ← k; -- reset if smaller
index ← buckets[index].next; IF index = 0 THEN EXIT;
ENDLOOP;
ENDLOOP;
ENDLOOP;
FOR i: NAT IN [0..shape.numSurfaces) DO -- retarget vertex pointers
IF surface[i] # NIL THEN FOR j: NAT IN [0..surface[i].nVtces) DO
surface[i].vtxPtr[j] ← table[surface[i].vtxPtr[j]];
ENDLOOP;
ENDLOOP;
IF shape.type = $ConvexPolygon THEN {
FOR i: NAT IN [0..shape.numSurfaces) DO-- eliminate duplicated polygon vertices 
vtx: NAT ← 0;
IF surface[i] # NIL THEN FOR j: NAT IN [0..surface[i].nVtces) DO
k: NAT ← (j-1 + surface[i].nVtces) MOD surface[i].nVtces;
IF surface[i].vtxPtr[j] # surface[i].vtxPtr[k] THEN {
surface[i].vtxPtr[vtx] ← surface[i].vtxPtr[j];
vtx ← vtx + 1;
};
ENDLOOP;
surface[i].nVtces ← vtx;
ENDLOOP;
};
}; -- end deSeaming block
FOR i: NAT IN [0..table.length) DO table[i] ← 0; ENDLOOP;
FOR i: NAT IN [0..shape.numSurfaces) DO   -- build vertex reference count
IF surface[i] # NIL THEN FOR j: NAT IN [0..surface[i].nVtces) DO
table[surface[i].vtxPtr[j]] ← table[surface[i].vtxPtr[j]] + 1;
ENDLOOP;
ENDLOOP;
currPos ← 0;
FOR i: NAT IN [0..table.length) DO   -- collapse vertex array and keep audit trail
IF table[i] # 0 THEN {
shape.vertex[currPos] ← shape.vertex[i];
shape.shade[currPos] ← shape.shade[i];
table[i] ← currPos;
currPos ← currPos + 1;
};
ENDLOOP;
newVtx ← NEW[ ThreeDBasics.VertexSequence[currPos] ];
newShade ← NEW[ ThreeDBasics.ShadingSequence[currPos] ];
FOR i: NAT IN [0..currPos) DO   -- copy into new sequences of proper length
newVtx[i] ← shape.vertex[i];
newShade[i] ← shape.shade[i];
ENDLOOP;
shape.vertexnewVtx;
shape.shade ← newShade;
FOR i: NAT IN [0..shape.numSurfaces) DO   -- retarget vertex pointers
FOR j: NAT IN [0..surface[i].nVtces) DO
surface[i].vtxPtr[j] ← table[surface[i].vtxPtr[j]];
ENDLOOP;
ENDLOOP;
};
CedarProcess.DoWithPriority[background, Action];
};
CopyShape: PUBLIC PROC [context: REF Context, dstName, srcName: Rope.ROPE]
RETURNS[REF ThreeDBasics.ShapeInstance] ~ {
dstShape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.NewShape[dstName];
srcShape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[
     context.shapes, srcName ];
srcSurface: REF ThreeDSurfaces.PtrPatchSequence ← NARROW[srcShape.surface];
dstSurface: REF ThreeDSurfaces.PtrPatchSequence;
currPos: NAT ← 0;
shape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.NewShape[dstName];
dstShape.orientation ← srcShape.orientation;
dstShape.location ← srcShape.location;
dstShape.rotation ← srcShape.rotation;
dstShape.axisBase ← srcShape.axisBase;
dstShape.axisEnd ← srcShape.axisEnd;
dstShape.centroid ← srcShape.centroid;
dstShape.boundingRadius ← srcShape.boundingRadius;
dstShape.vertex ← NEW[ThreeDBasics.VertexSequence[srcShape.vertex.length] ];
FOR i: NAT IN [0..srcShape.vertex.length) DO
dstShape.vertex[i] ← srcShape.vertex[i];
ENDLOOP;
dstShape.shade ← NEW[ ThreeDBasics.ShadingSequence[srcShape.shade.length] ];
FOR i: NAT IN [0..srcShape.shade.length) DO
dstShape.shade[i] ← srcShape.shade[i];
ENDLOOP;
dstShape.surface ← NEW[ ThreeDSurfaces.PtrPatchSequence[srcShape.numSurfaces] ];
dstSurface ← NARROW[dstShape.surface, REF ThreeDSurfaces.PtrPatchSequence];
FOR i: NAT IN [0..srcShape.numSurfaces) DO
dstSurface[i] ← srcSurface[i];
ENDLOOP;
dstShape.numSurfaces ← srcShape.numSurfaces;
dstShape.shadingProps ← srcShape.shadingProps;
dstShape.props ← srcShape.props;
RETURN[dstShape];
};
Combine: PUBLIC PROC [context: REF Context, dstName, src1, src2: Rope.ROPE]
RETURNS[REF ThreeDBasics.ShapeInstance] ~ {
shape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.NewShape[dstName];
shape1: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, src1 ];
shape2: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, src2 ];
surface1: REF ThreeDSurfaces.PtrPatchSequence ← NARROW[shape1.surface];
surface2: REF ThreeDSurfaces.PtrPatchSequence ← NARROW[shape2.surface];
surface: REF ThreeDSurfaces.PtrPatchSequence;
shape.vertex ← NEW[     -- copy vertices
ThreeDBasics.VertexSequence[shape1.vertex.length + shape2.vertex.length] ];
FOR i: NAT IN [0..shape1.vertex.length) DO
shape.vertex[i] ← shape1.vertex[i];
[[shape.vertex[i].x, shape.vertex[i].y, shape.vertex[i].z]] ← Matrix3d.Transform[
[shape.vertex[i].x, shape.vertex[i].y, shape.vertex[i].z],
shape1.position
];
ENDLOOP;
FOR i: NAT IN [0..shape2.vertex.length) DO
shape.vertex[i+shape1.vertex.length] ← shape2.vertex[i];
[[shape.vertex[i].x, shape.vertex[i].y, shape.vertex[i].z]] ← Matrix3d.Transform[
[shape.vertex[i].x, shape.vertex[i].y, shape.vertex[i].z],
shape2.position
];
ENDLOOP;
shape.shade ← NEW[     -- copy shading values
ThreeDBasics.ShadingSequence[shape1.shade.length + shape2.shade.length] ];
FOR i: NAT IN [0..shape1.shade.length) DO
shape.shade[i] ← shape1.shade[i];
ENDLOOP;
FOR i: NAT IN [0..shape2.shade.length) DO
shape.shade[i+shape1.shade.length] ← shape2.shade[i];
ENDLOOP;
shape.numSurfaces ← shape1.numSurfaces + shape2.numSurfaces;
shape.surface ← NEW[    -- copy surfaces
ThreeDSurfaces.PtrPatchSequence[shape1.numSurfaces + shape2.numSurfaces] ];
surface ← NARROW[shape.surface, REF ThreeDSurfaces.PtrPatchSequence];
FOR i: NAT IN [0..shape1.numSurfaces) DO
surface[i] ← surface1[i];
ENDLOOP;
FOR i: NAT IN [0..shape2.numSurfaces) DO
base: NAT ← shape1.numSurfaces;
surface[i+base] ← surface2[i];
FOR j: NAT IN [0..surface[i+base].nVtces) DO  -- redirect vertex pointers
surface[i+base].vtxPtr[j] ← surface[i+base].vtxPtr[j] + shape1.vertex.length
ENDLOOP;
ENDLOOP;
shape.shadingProps ← shape1.shadingProps;
shape.props ← shape1.props;
RETURN[shape];
};
DeletePatches: PUBLIC PROC [context: REF Context, dstName, srcName: Rope.ROPE,
   patchList: LIST OF NatPair]
RETURNS[REF ThreeDBasics.ShapeInstance] ~ {
currPos: NAT ← 0;
oldShape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[
                   context.shapes, srcName ];
shape: REF ThreeDBasics.ShapeInstance ← CopyShape[context, dstName, srcName];
surface: REF ThreeDSurfaces.PtrPatchSequence;
surface ← NARROW[shape.surface, REF ThreeDSurfaces.PtrPatchSequence];
FOR list: LIST OF NatPair ← patchList, list.rest UNTIL list = NIL DO
FOR i: NAT IN [list.first.x..list.first.y] DO
surface[i] ← NIL;
ENDLOOP;
ENDLOOP;
FOR i: NAT IN [0..shape.numSurfaces) DO  -- collapse array
IF surface[i] # NIL THEN {
surface[currPos] ← surface[i];
currPos ← currPos + 1;
};
ENDLOOP;
shape.numSurfaces ← currPos;
RETURN[ shape ];
};
Bounds: PUBLIC PROC [context: REF Context, name: Rope.ROPE]
RETURNS
[xMin, xMax, yMin, yMax, zMin, zMax: REAL] ~ {
shape: REF ThreeDBasics.ShapeInstance ← ThreeDScenes.FindShape[ context.shapes, name ];
xMin ← xMax ← shape.vertex[0].x;
yMin ← yMax ← shape.vertex[0].y;
zMin ← zMax ← shape.vertex[0].z;
FOR i: NAT IN (0..shape.vertex.length) DO -- get min and max in x, y, and z
IF shape.vertex[i].x < xMin
THEN xMin ← shape.vertex[i].x
ELSE IF shape.vertex[i].x > xMax THEN xMax ← shape.vertex[i].x;
IF shape.vertex[i].y < yMin
THEN yMin ← shape.vertex[i].y
ELSE IF shape.vertex[i].y > yMax THEN yMax ← shape.vertex[i].y;
IF shape.vertex[i].z < zMin
THEN zMin ← shape.vertex[i].z
ELSE IF shape.vertex[i].z > zMax THEN zMax ← shape.vertex[i].z;
ENDLOOP;
};
END.