G3dBuildBImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Glassner, November 17, 1989 11:59:20 am PST
Bloomenthal, July 15, 1992 11:31 pm PDT
DIRECTORY Args, Ascii, Commander, Draw2d, FileNames, FS, G2dBasic, G3dBasic, G3dBuild, G3dDraw, G3dModel, G3dPlane, G3dPolygon, G3dQuaternion, G3dRender, G3dScene, G3dShape, G3dSpline, G3dSweep, G3dVector, Imager, IO, MessageWindow, Process, Random, RealFns, RefTab, Rope, RopeFile, RuntimeError, TiogaAccess, TiogaMenuOps, ViewerOps, ViewerTools;
G3dBuildBImpl: CEDAR PROGRAM
IMPORTS G2dBasic, G3dBasic, G3dBuild, G3dModel, G3dRender, G3dScene, G3dShape, G3dSweep, G3dVector, Random, RealFns
EXPORTS G3dBuild
~ BEGIN 
Imported Types
Arg:      TYPE ~ Args.Arg;
AxisAngle:    TYPE ~ G3dQuaternion.AxisAngle;
BlendType:    TYPE ~ G3dSweep.BlendType;
BoolSequence:   TYPE ~ G3dBasic.BoolSequence;
Context3d:    TYPE ~ G3dRender.Context3d;
NatSequence:    TYPE ~ G3dBasic.NatSequence;
NatSequenceRep:   TYPE ~ G3dBasic.NatSequenceRep;
ParseState:    TYPE ~ G3dScene.ParseState;
RandomStream:   TYPE ~ Random.RandomStream;
RealSequence:   TYPE ~ G3dBasic.RealSequence;
ROPE:      TYPE ~ Rope.ROPE;
SceneProc:    TYPE ~ G3dScene.SceneProc;
Shape:     TYPE ~ G3dShape.Shape;
ShapeRep:    TYPE ~ G3dShape.ShapeRep;
ShapeSequence:   TYPE ~ G3dShape.ShapeSequence;
SurfaceDescription:  TYPE ~ G3dSweep.SurfaceDescription;
SurfaceDescriptionRep: TYPE ~ G3dSweep.SurfaceDescriptionRep;
SurfaceSequence:  TYPE ~ G3dBasic.SurfaceSequence;
Triple:     TYPE ~ G3dBasic.Triple;
TripleSequence:   TYPE ~ G3dBasic.TripleSequence;
Vertex:     TYPE ~ G3dShape.Vertex;
VertexRep:    TYPE ~ G3dShape.VertexRep;
VertexSequence:   TYPE ~ G3dShape.VertexSequence;
Error:     PUBLIC SIGNAL [code: ATOM, reason: ROPE] = CODE;
Local Types
SweepPaths:    TYPE ~ G3dBuild.SweepPaths;
SweepPathsRep:   TYPE ~ G3dBuild.SweepPathsRep;
Field:      TYPE ~ G3dBuild.Field;
FieldRep:     TYPE ~ G3dBuild.FieldRep;
State:      TYPE ~ G3dBuild.State;
StateRep:     TYPE ~ G3dBuild.StateRep;
Constants
pi: REAL = 3.141593;
piOver2: REAL = 1.570796;
piTimes2: REAL = 6.283186;
degToRad: REAL = 1.745329e-2;
radToDeg: REAL = 57.29577;
Calls for Polygon-by-polygon Procedures
PolyStellate: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
factorA, basesA: Arg;
[factorA, basesA] ¬ G3dScene.GetArgs[ps, args, "%r-bases%b"];
StellateShape[ps.currentShape, factorA.real, basesA.ok];
};
PolyFrame: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
factorA: Arg;
[factorA] ¬ G3dScene.GetArgs[ps, args, "%r"];
FrameShape[ps.currentShape, factorA.real];
};
PolyDilate: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
factorA: Arg;
[factorA] ¬ G3dScene.GetArgs[ps, args, "%r"];
DilateShape[ps.currentShape, factorA.real];
};
PolyRotate: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
cyclesA: Arg;
[cyclesA] ¬ G3dScene.GetArgs[ps, args, "%r"];
RotateShape[ps.currentShape, cyclesA.real];
};
PolyExplode: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
factorA: Arg;
[factorA] ¬ G3dScene.GetArgs[ps, args, "%r"];
ExplodePShape[ps.currentShape, factorA.real, state.field];
};
PolyInscribe: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
factorA: Arg;
[factorA] ¬ G3dScene.GetArgs[ps, args, "%r"];
InscribeShape[ps.currentShape, factorA.real];
};
PolyCorners: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
alphaA: Arg;
[alphaA] ¬ G3dScene.GetArgs[ps, args, "%r"];
CutCornersShape[ps.currentShape, alphaA.real, TRUE];
};
PolyCutCorners: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
alphaA: Arg;
[alphaA] ¬ G3dScene.GetArgs[ps, args, "%r"];
CutCornersShape[ps.currentShape, alphaA.real, FALSE];
};
PolySplotch: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
sidesA, sizeA: Arg;
[sidesA, sizeA] ¬ G3dScene.GetArgs[ps, args, "%i-size%r"];
SplotchShape[ps.currentShape, sidesA.int, IF sizeA.ok THEN sizeA.real ELSE 1.0];
};
PolyFan: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
factorA: Arg;
[factorA] ¬ G3dScene.GetArgs[ps, args, "%r"];
FanShape[ps.currentShape, factorA.real];
};
PolyStar: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
factorA: Arg;
[factorA] ¬ G3dScene.GetArgs[ps, args, "%r"];
StarShape[ps.currentShape, factorA.real];
};
Impls for Polygon-by-polygon Procedures
place a pyramid on every polygon
StellateShape: PUBLIC PROC [shape: Shape, factor: REAL ¬ 1.0, bases: BOOL ¬ TRUE] ~ {
newSurfaces: SurfaceSequence ¬ NIL;
IF shape = NIL OR shape.surfaces = NIL THEN RETURN;
G3dModel.IsolateVertices[shape];
G3dShape.SetFaceNormals[shape];
FOR i: NAT IN [0..shape.surfaces.length) DO
tip: Vertex ¬ NEW[VertexRep ¬ []];
tipIndex: INT;
poly: NatSequence ¬ shape.surfaces[i].vertices;
center: Triple ¬ FindPolygonCenter[shape, i];
find the point at the tip of the pyramid
tip.point ¬ G3dVector.Add[center, G3dVector.SetVectorLength[shape.faces[i].normal, factor]];
tip.normal ¬ shape.faces[i].normal;
append this vertex
tipIndex ¬ shape.vertices.length;
shape.vertices ¬ G3dShape.AddToVertexSequence[shape.vertices, tip];
now build the new polygons
FOR j: NAT IN [0..poly.length) DO
k: INT ¬ (j+1) MOD poly.length;
nSequence: NatSequence ¬ NEW[NatSequenceRep[3]];
nSequence.length ¬ 3;
nSequence[0] ¬ poly[j];
nSequence[1] ¬ poly[k];
nSequence[2] ¬ tipIndex;
newSurfaces ¬ G3dBasic.AddToSurfaceSequence[newSurfaces, [NIL, nSequence]];
ENDLOOP;
add the base polys if desired
IF bases THEN {
nSequence: NatSequence ¬ G2dBasic.CopyNatSequence[poly];
newSurfaces ¬ G3dBasic.AddToSurfaceSequence[newSurfaces, [NIL, nSequence]];
};
ENDLOOP;
shape.surfaces ¬ newSurfaces;
shape.faces ¬ NIL;   -- necessary when replacing the surfaces
};
dilate the polygon and replace it with the difference from the original
FrameShape: PUBLIC PROC [shape: Shape, factor: REAL ¬ 1.0] ~ {
newVertices: VertexSequence ¬ NIL;
newSurfaces: SurfaceSequence ¬ NIL;
IF shape = NIL OR shape.surfaces = NIL THEN RETURN;
G3dModel.IsolateVertices[shape];
append the old vertices to the new vertex list
FOR i: NAT IN [0..shape.surfaces.length) DO
poly: NatSequence ¬ shape.surfaces[i].vertices;
FOR j: NAT IN [0..poly.length) DO
pv: Vertex ¬ shape.vertices[poly[j]];
v: Vertex ¬ NEW[VertexRep ¬ pv­];
newVertices ¬ G3dShape.AddToVertexSequence[newVertices, v];
ENDLOOP;
ENDLOOP;
append the new vertices to the new vertex list
FOR i: NAT IN [0..shape.surfaces.length) DO
poly: NatSequence ¬ shape.surfaces[i].vertices;
center: Triple ¬ FindPolygonCenter[shape, i];
FOR j: NAT IN [0..poly.length) DO
pv: Vertex ¬ shape.vertices[poly[j]];
v: Vertex ¬ NEW[VertexRep ¬ pv­];
relativePos: Triple ¬ G3dVector.Sub[pv.point, center];
relativePos ¬ G3dVector.Mul[relativePos, factor];
relativePos ¬ G3dVector.Add[relativePos, center];
v.point ¬ relativePos;
newVertices ¬ G3dShape.AddToVertexSequence[newVertices, v];
ENDLOOP;
ENDLOOP;
append the new polygons to the new polygon list
FOR i: NAT IN [0..shape.surfaces.length) DO
poly: NatSequence ¬ shape.surfaces[i].vertices;
FOR j: NAT IN [0..poly.length) DO
nSequence: NatSequence;
k: INT ¬ (j+1) MOD poly.length;
v0: INT ¬ poly[j];
v1: INT ¬ poly[k];
v1p: INT ¬ v1+shape.vertices.length;
v0p: INT ¬ v0+shape.vertices.length;
nSequence ¬ NEW[NatSequenceRep[3]];
nSequence.length ¬ 3;
nSequence[0] ¬ v0;
nSequence[1] ¬ v1;
nSequence[2] ¬ v1p;
newSurfaces ¬ G3dBasic.AddToSurfaceSequence[newSurfaces, [NIL, nSequence]];
nSequence ¬ NEW[NatSequenceRep[3]];
nSequence.length ¬ 3;
nSequence[0] ¬ v0;
nSequence[1] ¬ v1p;
nSequence[2] ¬ v0p;
newSurfaces ¬ G3dBasic.AddToSurfaceSequence[newSurfaces, [NIL, nSequence]];
ENDLOOP;
ENDLOOP;
shape.vertices ¬ newVertices;
shape.surfaces ¬ newSurfaces;
shape.faces ¬ NIL;
};
scale all polygons by the given factor wrt their center of gravity
DilateShape: PUBLIC PROC [shape: Shape, factor: REAL ¬ 1.0] ~ {
IF shape = NIL OR shape.surfaces = NIL THEN RETURN;
G3dModel.IsolateVertices[shape];
FOR i: NAT IN [0..shape.surfaces.length) DO
poly: NatSequence ¬ shape.surfaces[i].vertices;
center: Triple ¬ FindPolygonCenter[shape, i];
now move the vertices
FOR j: NAT IN [0..poly.length) DO
relativePos: Triple ¬ G3dVector.Sub[shape.vertices[poly[j]].point, center];
relativePos ¬ G3dVector.Mul[relativePos, factor];
relativePos ¬ G3dVector.Add[relativePos, center];
shape.vertices[poly[j]].point ¬ relativePos;
ENDLOOP;
ENDLOOP;
};
rotate all polygons by the given number of cycles wrt their center of gravity
RotateShape: PUBLIC PROC [shape: Shape, theta: REAL ¬ piOver2] ~ {
IF shape = NIL OR shape.surfaces = NIL THEN RETURN;
G3dModel.IsolateVertices[shape];
IF shape.faces = NIL OR NOT shape.faces.valid.normal THEN G3dShape.SetFaceNormals[shape];
FOR i: NAT IN [0..shape.surfaces.length) DO
poly: NatSequence ¬ shape.surfaces[i].vertices;
normal: Triple ¬ shape.faces[i].normal;
center: Triple ¬ FindPolygonCenter[shape, i];
now move the vertices
FOR j: NAT IN [0..poly.length) DO
relativePos: Triple ¬ G3dVector.Sub[shape.vertices[poly[j]].point, center];
relativePos ¬ G3dVector.RotateAbout[relativePos, normal, theta, FALSE];
shape.vertices[poly[j]].point ¬ G3dVector.Add[relativePos, center];
ENDLOOP;
ENDLOOP;
};
move all polygons away from the field center by factor
ExplodePShape: PUBLIC PROC [shape: Shape, factor: REAL ¬ 1.0, f: Field] ~ {
IF shape = NIL OR shape.surfaces = NIL THEN RETURN;
G3dModel.IsolateVertices[shape];
FOR i: NAT IN [0..shape.surfaces.length) DO
poly: NatSequence ¬ shape.surfaces[i].vertices;
find center of this poly
move, newCenter, relCenter, center: Triple ¬ [0.0, 0.0, 0.0];
center ¬ FindPolygonCenter[shape, i];
relCenter ¬ G3dVector.Sub[center, f.center];
newCenter ¬ G3dVector.Mul[relCenter, factor];
move ¬ G3dVector.Sub[newCenter, relCenter];
FOR j: NAT IN [0..poly.length) DO
shape.vertices[poly[j]].point ¬ G3dVector.Add[move, shape.vertices[poly[j]].point];
ENDLOOP;
ENDLOOP;
};
InscribeShape: PUBLIC PROC [shape: Shape, alpha: REAL ¬ 0.5] ~ {
IF shape = NIL OR shape.surfaces = NIL THEN RETURN;
G3dModel.IsolateVertices[shape];
FOR i: NAT IN [0..shape.surfaces.length) DO
poly: NatSequence ¬ shape.surfaces[i].vertices;
oldV0: Triple ¬ shape.vertices[poly[0]].point;
FOR j: NAT IN [0..poly.length-1) DO
shape.vertices[poly[j]].point ¬
G3dVector.Interp[alpha, shape.vertices[poly[j]].point, shape.vertices[poly[(j+1) MOD poly.length]].point];
ENDLOOP;
shape.vertices[poly[poly.length-1]].point ¬ G3dVector.Interp[alpha, shape.vertices[poly[poly.length-1]].point, oldV0];
ENDLOOP;
};
CutCornersShape: PUBLIC PROC [shape: Shape, alpha: REAL ¬ 0.5, corners: BOOL ¬ TRUE] ~ {
newVertices: VertexSequence ¬ NIL;
newSurfaces: SurfaceSequence ¬ NIL;
IF shape = NIL OR shape.surfaces = NIL THEN RETURN;
G3dModel.IsolateVertices[shape];
FOR i: NAT IN [0..shape.surfaces.length) DO
firstVertex: INT ¬ IF newVertices # NIL THEN newVertices.length ELSE 0;
poly: NatSequence ¬ shape.surfaces[i].vertices;
polyLen: INT ¬ poly.length;
polyLen2: INT ¬ 2*polyLen;
polyLen3: INT ¬ 3*polyLen;
create the new vertices
FOR j: INT IN [0 .. polyLen) DO
ov0: Vertex ¬ DuplicateVertex[shape.vertices[poly[j]]];
v0: Vertex ¬ DuplicateVertex[shape.vertices[poly[j]]];
v1: Vertex ¬ DuplicateVertex[shape.vertices[poly[(j+1) MOD polyLen]]];
lo: Triple ¬ G3dVector.Interp[alpha, v0.point, v1.point];
hi: Triple ¬ G3dVector.Interp[alpha, v1.point, v0.point];
newVertices ¬ G3dShape.AddToVertexSequence[newVertices, ov0];
v0.point ¬ lo;
v1.point ¬ hi;
newVertices ¬ G3dShape.AddToVertexSequence[newVertices, v0];
newVertices ¬ G3dShape.AddToVertexSequence[newVertices, v1];
ENDLOOP;
create the new polygons
IF corners
THEN {
FOR j: INT IN [0 .. polyLen) DO
p: INT ¬ 3*j;
nSequence: NatSequence ¬ NEW[NatSequenceRep[3]];
nSequence.length ¬ 3;
nSequence[0] ¬ firstVertex + (p);
nSequence[1] ¬ firstVertex + ((p+1) MOD polyLen3);
nSequence[2] ¬ firstVertex + ((p-1) MOD polyLen3);
newSurfaces ¬ G3dBasic.AddToSurfaceSequence[newSurfaces, [NIL, nSequence]];
ENDLOOP;
}
ELSE {
nSequence: NatSequence ¬ NEW[NatSequenceRep[polyLen2]];
index: INT ¬ 0;
nSequence.length ¬ polyLen2;
FOR j: INT IN [0 .. polyLen3) DO
IF (j MOD 3) # 0 THEN {
nSequence[index] ¬ firstVertex + j;
index ¬ index + 1;
};
ENDLOOP;
newSurfaces ¬ G3dBasic.AddToSurfaceSequence[newSurfaces, [NIL, nSequence]];
};
ENDLOOP;
shape.vertices ¬ newVertices;
shape.surfaces ¬ newSurfaces;
shape.faces ¬ NIL;
};
replace each poly by a new ngon somewhere within the poly
SplotchShape: PUBLIC PROC [shape: Shape, sides: INT ¬ 10, size: REAL ¬ 1.0] ~ {
newVertices: VertexSequence ¬ NIL;
newSurfaces: SurfaceSequence ¬ NIL;
basis1, basis2: Triple;
IF shape = NIL OR shape.surfaces = NIL OR sides < 3 THEN RETURN;
G3dModel.IsolateVertices[shape];
G3dShape.SetFaceNormals[shape];
G3dShape.SetFaceCenters[shape];
FOR i: NAT IN [0..shape.surfaces.length) DO
poly: NatSequence ¬ shape.surfaces[i].vertices;
radius: REAL ¬ 0.0;
firstVertex: INT ¬ 0;
nSequence: NatSequence;
center: Triple ¬ FindPolygonCenter[shape, i];
find the closest approach of each edge to the center
FOR j: NAT IN [0..poly.length) DO
p0: Triple ¬ shape.vertices[poly[j]].point;
p1: Triple ¬ shape.vertices[poly[(j+1) MOD poly.length]].point;
dist: REAL ¬ G3dVector.Distance[G3dVector.NearestToSegment[p0, p1, center].point, center];
IF j=0
THEN radius ¬ dist
ELSE radius ¬ MIN[radius, dist];
ENDLOOP;
find an orthonormal basis in the plane
basis1 ¬ G3dVector.Unit[G3dVector.Sub[shape.vertices[poly[0]].point, center]];
basis2 ¬ G3dVector.Unit[G3dVector.Cross[basis1, shape.faces[i].normal]];
firstVertex ¬ IF newVertices = NIL THEN 0 ELSE newVertices.length;
move the center if allowed
IF size < 1.0 THEN {
wiggle: REAL ¬ radius * (1.0-size);
theta: REAL ¬ piTimes2 * GetRandom[NIL];
length: REAL ¬ wiggle * GetRandom[NIL];
l1: REAL ¬ length * RealFns.Cos[theta];
l2: REAL ¬ length * RealFns.Sin[theta];
offset: Triple ¬ G3dVector.Add[G3dVector.Mul[basis1, l1], G3dVector.Mul[basis2, l2]];
center ¬ G3dVector.Add[center, offset];
};
buid the ngon vertices
radius ¬ radius * size;
FOR j: INT IN [0 .. sides) DO
theta: REAL ¬ piTimes2 * REAL[j] / REAL[sides-1];
l1: REAL ¬ radius * RealFns.Cos[theta];
l2: REAL ¬ radius * RealFns.Sin[theta];
v: Vertex ¬ NEW[VertexRep ¬ []];
offset: Triple ¬ G3dVector.Add[G3dVector.Mul[basis1, l1], G3dVector.Mul[basis2, l2]];
v.point ¬ G3dVector.Add[center, offset];
v.normal ¬ shape.faces[i].normal;
newVertices ¬ G3dShape.AddToVertexSequence[newVertices, v];
ENDLOOP;
build the polygon
nSequence ¬ NEW[NatSequenceRep[sides]];
nSequence.length ¬ sides;
FOR j: INT IN [0 .. sides) DO
nSequence[j] ¬ firstVertex + j;
ENDLOOP;
newSurfaces ¬ G3dBasic.AddToSurfaceSequence[newSurfaces, [NIL, nSequence]];
ENDLOOP;
shape.vertices ¬ newVertices;
shape.surfaces ¬ newSurfaces;
shape.faces ¬ NIL;
};
FanShape: PUBLIC PROC [shape: Shape, alpha: REAL ¬ 0.5] ~ {
newVertices: VertexSequence ¬ NIL;
newSurfaces: SurfaceSequence ¬ NIL;
centerVertex: Vertex;
firstVertex: INT ¬ 0;
IF shape = NIL OR shape.surfaces = NIL THEN RETURN;
G3dModel.IsolateVertices[shape];
G3dShape.SetFaceNormals[shape];
FOR i: NAT IN [0..shape.surfaces.length) DO
poly: NatSequence ¬ shape.surfaces[i].vertices;
radius: REAL ¬ 0.0;
center: Triple ¬ FindPolygonCenter[shape, i];
centerVertex ¬ NEW[VertexRep ¬ []];
centerVertex.point ¬ center;
centerVertex.normal ¬ shape.faces[i].normal;
firstVertex ¬ IF newVertices = NIL THEN 0 ELSE newVertices.length;
newVertices ¬ G3dShape.AddToVertexSequence[newVertices, centerVertex];
build new vertices
FOR j: NAT IN [0..poly.length) DO
p: Triple ¬
G3dVector.Interp[alpha, shape.vertices[poly[j]].point, shape.vertices[poly[(j+1) MOD poly.length]].point];
v0: Vertex ¬ DuplicateVertex[centerVertex];
v1: Vertex ¬ DuplicateVertex[centerVertex];
v0.point ¬ shape.vertices[poly[j]].point;
v1.point ¬ p;
newVertices ¬ G3dShape.AddToVertexSequence[newVertices, v0];
newVertices ¬ G3dShape.AddToVertexSequence[newVertices, v1];
ENDLOOP;
build new polygons
FOR j: NAT IN [0..poly.length) DO
nSequence: NatSequence ¬ NEW[NatSequenceRep[3]];
nSequence.length ¬ 3;
nSequence[0] ¬ firstVertex;
nSequence[1] ¬ firstVertex + 1 + (2*j);
nSequence[2] ¬ nSequence[1] + 1;
newSurfaces ¬ G3dBasic.AddToSurfaceSequence[newSurfaces, [NIL, nSequence]];
ENDLOOP;
ENDLOOP;
shape.vertices ¬ newVertices;
shape.surfaces ¬ newSurfaces;
shape.faces ¬ NIL;
};
StarShape: PUBLIC PROC [shape: Shape, factor: REAL ¬ 0.5] ~ {
newVertices: VertexSequence ¬ NIL;
newSurfaces: SurfaceSequence ¬ NIL;
centerVertex: Vertex;
firstVertex: INT ¬ 0;
IF shape = NIL OR shape.surfaces = NIL THEN RETURN;
G3dModel.IsolateVertices[shape];
G3dShape.SetFaceNormals[shape];
FOR i: NAT IN [0..shape.surfaces.length) DO
poly: NatSequence ¬ shape.surfaces[i].vertices;
polyLen2: INT ¬ 2 * poly.length;
radius: REAL ¬ 0.0;
center: Triple ¬ FindPolygonCenter[shape, i];
centerVertex ¬ NEW[VertexRep ¬ []];
centerVertex.point ¬ center;
centerVertex.normal ¬ shape.faces[i].normal;
firstVertex ¬ IF newVertices = NIL THEN 0 ELSE newVertices.length;
newVertices ¬ G3dShape.AddToVertexSequence[newVertices, centerVertex];
build new vertices
FOR j: NAT IN [0..poly.length) DO
p: Triple ¬
G3dVector.Interp[0.5, shape.vertices[poly[j]].point, shape.vertices[poly[(j+1) MOD poly.length]].point];
newp: Triple ¬ G3dVector.Add[G3dVector.Mul[G3dVector.Sub[p, center], factor], center];
v0: Vertex ¬ DuplicateVertex[shape.vertices[poly[j]]];
v1: Vertex ¬ DuplicateVertex[shape.vertices[poly[j]]];
v1.point ¬ newp;
newVertices ¬ G3dShape.AddToVertexSequence[newVertices, v0];
newVertices ¬ G3dShape.AddToVertexSequence[newVertices, v1];
ENDLOOP;
build new polygons
FOR j: INT IN [0..polyLen2+1) DO
nSequence: NatSequence ¬ NEW[NatSequenceRep[3]];
nSequence.length ¬ 3;
nSequence[0] ¬ firstVertex + (1 + j);
nSequence[1] ¬ firstVertex + ((2 + j) MOD polyLen2);
nSequence[2] ¬ firstVertex;
newSurfaces ¬ G3dBasic.AddToSurfaceSequence[newSurfaces, [NIL, nSequence]];
ENDLOOP;
ENDLOOP;
shape.vertices ¬ newVertices;
shape.surfaces ¬ newSurfaces;
shape.faces ¬ NIL;
};
Calls for Vertex-by-vertex Procedures
VertexExplode: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
factorA: Arg;
[factorA] ¬ G3dScene.GetArgs[ps, args, "%r"];
ExplodeVShape[ps.currentShape, factorA.real, state.field];
};
Impls for Vertex-by-vertex Procedures
move all vertices away from the field center by factor
ExplodeVShape: PUBLIC PROC [shape: Shape, factor: REAL ¬ 1.0, f: Field] ~ {
IF shape = NIL OR shape.surfaces = NIL THEN RETURN;
G3dModel.IsolateVertices[shape];
FOR i: NAT IN [0..shape.surfaces.length) DO
poly: NatSequence ¬ shape.surfaces[i].vertices;
FOR j: NAT IN [0..poly.length) DO
relVertex: Triple ¬ G3dVector.Sub[shape.vertices[poly[j]].point, f.center];
relVertex ¬ G3dVector.Mul[relVertex, factor];
shape.vertices[poly[j]].point ¬ G3dVector.Add[relVertex, f.center];
ENDLOOP;
ENDLOOP;
and reset the normals
G3dShape.SetFaceNormals[shape];
};
Calls for Field Procedures
FieldCenter: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
xA, yA, zA: Arg;
[xA, yA, zA] ¬ G3dScene.GetArgs[ps, args, "%rrr"];
state.field.center ¬ [xA.real, yA.real, zA.real];
};
FieldAmplitude: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
vA: Arg;
[vA] ¬ G3dScene.GetArgs[ps, args, "%r"];
state.field.amplitude ¬ vA.real;
};
FieldRadius: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
vA: Arg;
[vA] ¬ G3dScene.GetArgs[ps, args, "%r"];
state.field.radius ¬ vA.real;
};
FieldPower: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
vA: Arg;
[vA] ¬ G3dScene.GetArgs[ps, args, "%r"];
state.field.power ¬ vA.real;
};
FieldOffset: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
vA: Arg;
[vA] ¬ G3dScene.GetArgs[ps, args, "%r"];
state.field.offset ¬ vA.real;
};
Shape Modification Procedures
Extrude: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
shape: Shape ¬ ps.currentShape;
axisXA, axisYA, axisZA, newNameA, distanceA: Arg;
axis: Triple ¬ [0.0, 0.0, 1.0];
distance: REAL ¬ 1.0;
name: ROPE ¬ G3dBuild.AutoName[ps.context3d, "AutoExtrusion"];
[distanceA, axisXA, axisYA, axisZA, newNameA] ¬ G3dScene.GetArgs[ps, args, "-distance%r-axis%rrr-name%s"];
IF axisXA.ok THEN axis ¬ [axisXA.real, axisYA.real, axisZA.real];
IF distanceA.ok THEN distance ¬ distanceA.real;
shape ¬ G3dBuild.TranslateShape[ps.currentShape,
[0.0, 0.0, 0.0], G3dVector.Mul[axis, distance], TRUE];
IF newNameA.ok THEN name ¬ newNameA.rope;
shape.name ¬ name;
G3dBuild.LoadShape[ps, state, shape];
};
Revolve: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
shape: Shape ¬ ps.currentShape;
contourA, stepsA, axisXA, axisYA, axisZA, minThetaA, maxThetaA, radiansA, nameA: Arg;
steps: INT ¬ 10;
axis: Triple ¬ [0.0, 1.0, 0.0];
vertexCnt: INT ¬ shape.vertices.length;
newTotal: INT;
contour: BOOL ¬ FALSE;
minTheta: REAL ¬ 0.0;
maxTheta: REAL ¬ piTimes2;
vSequence: NatSequence ¬ NEW[NatSequenceRep];
[contourA, stepsA, axisXA, axisYA, axisZA, minThetaA, maxThetaA, radiansA, nameA] ¬ G3dScene.GetArgs[ps, args, "-contour%b-steps%i-axis%rrr-minTheta%r-maxTheta%r-radians%r-name%s"];
IF axisXA.ok AND axisYA.ok AND axisZA.ok THEN
axis ¬ [axisXA.real, axisYA.real, axisZA.real];
IF minThetaA.ok THEN
minTheta ¬ IF radiansA.ok THEN minThetaA.real ELSE minThetaA.real*degToRad;
IF maxThetaA.ok THEN
maxTheta ¬ IF radiansA.ok THEN maxThetaA.real ELSE maxThetaA.real*degToRad;
IF contourA.ok THEN contour ¬ TRUE;
IF stepsA.ok THEN steps ¬ stepsA.int;
newTotal ¬ vertexCnt * steps;
add new vertices
FOR step: INT IN [1 .. steps) DO
theta: REAL ¬ minTheta + ((maxTheta-minTheta) * step / REAL[steps]);
FOR i: INT IN [0 .. vertexCnt) DO
vtx: Vertex ¬ DuplicateVertex[shape.vertices[i]];
vtx.point ¬ G3dVector.RotateAbout[vtx.point, axis, theta, FALSE];
IF shape.vertices.valid.normal THEN {  
-- use the same transform on the normal - this is only valid for rotations!
vtx.normal ¬ G3dVector.RotateAbout[vtx.normal, axis, theta, FALSE];
};
shape.vertices ¬ G3dShape.AddToVertexSequence[shape.vertices, vtx];
ENDLOOP;
ENDLOOP;
add new polygons
IF contour THEN shape.surfaces ¬ NIL;
FOR step: INT IN [0 .. steps) DO
base: INT ¬ step * vertexCnt;
FOR i: INT IN [0 .. vertexCnt-1) DO
a: INT ¬ base+i;
b: INT ¬ base+i+1;
c: INT ¬ (a + vertexCnt) MOD newTotal;
d: INT ¬ (b + vertexCnt) MOD newTotal;
IF a >= newTotal OR b >= newTotal OR c >= newTotal OR d >= newTotal THEN
d ¬ d;
G3dSweep.AddGeneralQuadrilateralToShape[shape, a, b, d, c];
ENDLOOP;
ENDLOOP;
shape.name ¬
IF nameA.ok THEN nameA.rope ELSE G3dBuild.AutoName[ps.context3d, "AutoRevolve"];
};
MakeNormals: PUBLIC SceneProc ~ {G3dShape.SetVertexNormals[ps.currentShape]};
Utility Procedures
Isolate: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
G3dModel.IsolateVertices[ps.currentShape];
};
DuplicateVertex: PUBLIC PROC [old: Vertex] RETURNS [new: Vertex] ~ {
new ¬ NEW[VertexRep];
new­ ¬ old­;
};
FindPolygonCenter: PUBLIC PROC [shape: Shape, polyIndex: NAT] RETURNS [Triple] ~ {
center: Triple ¬ [0.0, 0.0, 0.0];
poly: NatSequence ¬ shape.surfaces[polyIndex].vertices;
FOR j: NAT IN [0..poly.length) DO
center ¬ G3dVector.Add[shape.vertices[poly[j]].point, center];
ENDLOOP;
center ¬ G3dVector.Div[center, poly.length];
RETURN[center];
};
GetRandom: PUBLIC PROC [rs: RandomStream] RETURNS [REAL] ~ {
RETURN[REAL[Random.NextInt[rs]]/REAL[LAST[INT]]];
};
Shape Merging Procs
MergeShapes: PUBLIC SceneProc ~ {
state: State ¬ G3dBuild.GetBuildState[ps];
newShape: Shape ¬ NEW[ShapeRep ¬ []];
nameA, s1A, s2A, s3A, s4A, s5A: Arg;
[nameA, s1A, s2A, s3A, s4A, s5A] ¬ G3dScene.GetArgs[ps, args, "%s[sssss"];
IF NOT s1A.ok THEN RETURN;
newShape ¬ G3dShape.CopyShape[G3dShape.FindShape[ps.context3d.shapes, s1A.rope]];
newShape.name ¬ nameA.rope;
IF s2A.ok THEN newShape ¬
G3dSweep.Merge2Shapes[newShape, G3dShape.FindShape[ps.context3d.shapes, s2A.rope]];
IF s3A.ok THEN newShape ¬
G3dSweep.Merge2Shapes[newShape, G3dShape.FindShape[ps.context3d.shapes, s3A.rope]];
IF s4A.ok THEN newShape ¬
G3dSweep.Merge2Shapes[newShape, G3dShape.FindShape[ps.context3d.shapes, s4A.rope]];
IF s5A.ok THEN newShape ¬
G3dSweep.Merge2Shapes[newShape, G3dShape.FindShape[ps.context3d.shapes, s5A.rope]];
G3dBuild.LoadShape[ps, state, newShape];
};
MergeAllShapes: PUBLIC SceneProc ~ {
Inner: PROC [context3d: Context3d, name: ROPE] ~ {
vertexNumber: INT ¬ 0;
shapes: ShapeSequence ¬ context3d.shapes;
newShape: Shape ¬ NEW[ShapeRep ¬ []];
newShape.name ¬ name;
newShape.type ¬ $ConvexPolygon;
FOR shapeNumber: INT IN [0 .. shapes.length) DO
thisShape: Shape ¬ G3dShape.CopyShape[shapes[shapeNumber]];
vertexList: VertexSequence ¬ thisShape.vertices;
G3dShape.TransformVertices[thisShape, thisShape.matrix];
FOR i: INT IN [0 .. vertexList.length) DO
newShape.vertices ¬ G3dShape.AddToVertexSequence[newShape.vertices, vertexList[i]];
ENDLOOP;
FOR surface: INT IN [0 .. thisShape.surfaces.length) DO
seq: NatSequence ¬ thisShape.surfaces[surface].vertices;
vSequence: NatSequence ¬ NEW[NatSequenceRep[seq.length]];
vSequence.length ¬ seq.length;
FOR j: INT IN [0 .. seq.length) DO
vSequence[j] ¬ seq[j] + vertexNumber;
ENDLOOP;
newShape.surfaces ¬ G3dBasic.AddToSurfaceSequence[newShape.surfaces, [NIL, vSequence]];
ENDLOOP;
vertexNumber ¬ vertexNumber + vertexList.length;
ENDLOOP;
newShape.vertices.valid ¬ [TRUE, FALSE, TRUE, FALSE];
G3dRender.AddShape[context3d, newShape];
};
state: State ¬ G3dBuild.GetBuildState[ps];
newNameA: Arg;
[newNameA] ¬ G3dScene.GetArgs[ps, args, "%s"];
Inner[ps.context3d, newNameA.rope];
};
Start Procedures
RegisterProcs: PROC ~ {
polygon procs 
G3dScene.RegisterProc["PolyStellate",  PolyStellate];
G3dScene.RegisterProc["PolyFrame",  PolyFrame];
G3dScene.RegisterProc["PolyDilate",  PolyDilate];
G3dScene.RegisterProc["PolyRotate",  PolyRotate];
G3dScene.RegisterProc["PolyExplode",  PolyExplode];
G3dScene.RegisterProc["PolyInscribe",  PolyInscribe];
G3dScene.RegisterProc["PolyCorners",  PolyCorners];
G3dScene.RegisterProc["PolyCutCorners", PolyCutCorners];
G3dScene.RegisterProc["PolySplotch",  PolySplotch];
G3dScene.RegisterProc["PolyFan",   PolyFan];
G3dScene.RegisterProc["PolyStar",   PolyStar];
vertex procs
G3dScene.RegisterProc["VertexExplode", VertexExplode];
field procs
G3dScene.RegisterProc["FieldCenter",  FieldCenter];
G3dScene.RegisterProc["FieldAmplitude", FieldAmplitude];
G3dScene.RegisterProc["FieldRadius",  FieldRadius];
G3dScene.RegisterProc["FieldPower",  FieldPower];
G3dScene.RegisterProc["FieldOffset",  FieldOffset];
modification procs
G3dScene.RegisterProc["Extrude",   Extrude];
G3dScene.RegisterProc["Revolve",   Revolve];
G3dScene.RegisterProc["MakeNormals", MakeNormals];
};
RegisterProcs[];
END.