G3dBuildAImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Glassner, November 17, 1989 12:38:07 pm PST
Bloomenthal, July 15, 1992 11:31 pm PDT
DIRECTORY Args, Ascii, Commander, Draw2d, FileNames, FS, G2dBasic, G2dVector, G3dBasic, G3dBuild, G3dCurve, G3dDraw, G3dMatrix, G3dPlane, G3dPolygon, G3dQuaternion, G3dRender, G3dScene, G3dShape, G3dSpline, G3dSweep, G3dTimeTrees, G3dVector, Imager, IO, MessageWindow, Process, Random, Real, RealFns, RefTab, Rope, RopeFile, RuntimeError, TiogaAccess, TiogaMenuOps, ViewerOps, ViewerTools;
G3dBuildAImpl: CEDAR PROGRAM
IMPORTS G2dBasic, G3dBasic, G3dRender, G3dScene, G3dShape, G3dSweep, G3dVector, IO, Real, RealFns, Random, Rope
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;
Material:     TYPE ~ G3dScene.Material;
NatSequence:    TYPE ~ G3dBasic.NatSequence;
NatSequenceRep:   TYPE ~ G3dBasic.NatSequenceRep;
ParseState:    TYPE ~ G3dScene.ParseState;
RealSequence:   TYPE ~ G3dBasic.RealSequence;
RenderStyle:    TYPE ~ G3dRender.RenderStyle;
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;
TextureStyle:    TYPE ~ G3dRender.TextureStyle;
Triple:     TYPE ~ G3dBasic.Triple;
TripleSequence:   TYPE ~ G3dBasic.TripleSequence;
Vertex:     TYPE ~ G3dShape.Vertex;
VertexRep:    TYPE ~ G3dShape.VertexRep;
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.1415926535;
piOver2: REAL ~ 0.5*pi;
piTimes2: REAL ~ 2.0*pi;
degToRad: REAL ~ 1.745329e-2;
radToDeg: REAL ~ 57.29577;
Get Build State
GetBuildState: PUBLIC PROC [ps: ParseState] RETURNS [State] ~ {
state: State;
IF ps.buildData = NIL
THEN {
state ¬ ps.buildData ¬ NEW[StateRep ¬ []];
state.field ¬ NEW[FieldRep ¬ []];
state.sweepPaths ¬ NEW[SweepPathsRep ¬ []];
state.randomStream ¬ Random.Create[];
}
ELSE state ¬ NARROW[ps.buildData];
RETURN[state];
};
Polygonal Surface Procedures
specialCaseSphere: BOOL ¬ TRUE;
MakeSphere: PUBLIC SceneProc ~ {
state: State ¬ GetBuildState[ps];
shape: Shape ¬ NEW[ShapeRep];
radius: REAL ¬ 1.0;
uRes, vRes: NAT ¬ 10;
minTheta: REAL ¬ 0.0;
maxTheta: REAL ¬ piTimes2;
minPsi: REAL ¬ 0.0;
maxPsi: REAL ¬ pi;
radiusA, uResA, vResA, minThetaA, maxThetaA, minPsiA, maxPsiA, nameA, radiansA: Arg;
sd: SurfaceDescription ← NEW[SurfaceDescriptionRep ← []]; -- unused
[radiusA, uResA, vResA, minThetaA, maxThetaA, minPsiA, maxPsiA, nameA, radiansA] ¬ G3dScene.GetArgs[ps, args,
"-r%r-ures%i-vres%i-mintheta%r-maxtheta%r-minpsi%r-maxpsi%r-name%s-radians%b"];
IF radiusA.ok THEN radius ¬ radiusA.real;
IF uResA.ok THEN uRes ¬ uResA.int;
IF vResA.ok THEN vRes ¬ vResA.int;
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 minPsiA.ok THEN
minPsi ¬ IF radiansA.ok THEN minPsiA.real ELSE minPsiA.real*degToRad;
IF maxPsiA.ok THEN
maxPsi ¬ IF radiansA.ok THEN maxPsiA.real ELSE maxPsiA.real*degToRad;
IF specialCaseSphere
THEN {
Surf: PROC [nVertices: NAT, i0, i1, i2, i3: NAT ¬ 0] RETURNS [s: G3dBasic.Surface] ~ {
s.vertices ¬ NEW[NatSequenceRep[nVertices]];
s.vertices.length ¬ nVertices;
s.vertices[0] ¬ i0; s.vertices[1] ¬ i1; s.vertices[2] ¬ i2;
IF nVertices = 4 THEN s.vertices[3] ¬ i3;
};
nVertices: NAT ¬ uRes*(vRes-2)+2;
nSurfaces: NAT ¬ uRes*(vRes-1);
lastRing: NAT ¬ 1+(vRes-3)*uRes;
shape.vertices ¬ NEW[G3dShape.VertexSequenceRep[nVertices]];
shape.vertices.length ¬ nVertices;
FOR n: NAT IN [0..nVertices) DO shape.vertices[n] ¬ NEW[VertexRep]; ENDLOOP;
shape.surfaces ¬ NEW[G3dBasic.SurfaceSequenceRep[nSurfaces]];
shape.surfaces.length ¬ nSurfaces;
shape.vertices[0].point ¬ [0.0, 0.0, radius];    -- north pole
shape.vertices[nVertices-1].point ¬ [0.0, 0.0, -radius]; -- south pole
FOR n: NAT IN [0..vRes-2) DO
offset: NAT ¬ 1+n*uRes;
a: REAL ¬ minPsi+REAL[n+1]*(maxPsi-minPsi)/REAL[vRes-1];
s: REAL ¬ radius*RealFns.Sin[a];
z: REAL ¬ radius*RealFns.Cos[a];
FOR nn: NAT IN [0..uRes) DO
aa: REAL ¬ minTheta+REAL[nn]*(maxTheta-minTheta)/REAL[uRes];
x: REAL ¬ s*RealFns.Cos[aa];
y: REAL ¬ s*RealFns.Sin[aa];
shape.vertices[offset+nn].point ¬ [x, y, z];
ENDLOOP;
ENDLOOP;
FOR u: NAT IN [0..uRes) DO
u0: NAT ¬ u;
u1: NAT ¬ (u0+1) MOD uRes;
shape.surfaces[u] ¬ Surf[3, 1+u1, 1+u0, 0];
shape.surfaces[shape.surfaces.length-u-1] ¬
Surf[3, lastRing+u0, lastRing+u1, nVertices-1];
ENDLOOP;
FOR v: NAT IN [1..vRes-2) DO
startRing1: NAT ¬ 1+v*uRes;
startRing0: NAT ¬ startRing1-uRes;
FOR u: NAT IN [0..uRes) DO
u0: NAT ¬ u;
u1: NAT ¬ (u+1) MOD uRes;
shape.surfaces[u+v*uRes] ¬
Surf[4, startRing0+u0, startRing0+u1, startRing1+u1, startRing1+u0];
ENDLOOP;
ENDLOOP;
}
ELSE shape ¬ ConstructSphere[uRes, vRes, minTheta, maxTheta, minPsi, maxPsi, radius];
shape.name ¬ IF nameA.ok THEN nameA.rope ELSE AutoName[ps.context3d, "AutoSphere"];
LoadShape[ps, state, shape];
};
MakeCylinder: PUBLIC SceneProc ~ {
state: State ¬ GetBuildState[ps];
MakeConeShape[ps, args, state, FALSE];
};
MakeCone: PUBLIC SceneProc ~ {
state: State ¬ GetBuildState[ps];
MakeConeShape[ps, args, state, TRUE];
};
MakeConeShape: PROC [ps: ParseState, args: ROPE, state: State, cone: BOOL ¬ TRUE] ~ {
shape: Shape;
steps: INT ¬ 10;
zLo: REAL ¬ -1.0;
zHi: REAL ¬ 1.0;
rLo: REAL ¬ 1.0;
rHi: REAL ¬ 1.0;
minTheta: REAL ¬ 0.0;
maxTheta: REAL ¬ piTimes2;
name: ROPE;
nameA, stepsA, lcapA, ucapA, radiusA, rLoA, rHiA, zLoA, zHiA, minThetaA, maxThetaA, radiansA: Arg;
sd: SurfaceDescription ¬ NEW[SurfaceDescriptionRep ¬ []];
IF cone
THEN {
[nameA, stepsA, lcapA, ucapA, rLoA, rHiA, zLoA, zHiA, minThetaA, maxThetaA, radiansA] ¬ G3dScene.GetArgs[ps, args, "-name%s-steps%i-lcap%b-ucap%b-rlo%r-rhi%r-zlo%r-zhi%r-minTheta%r-maxTheta%r-radians%b"];
IF rLoA.ok THEN rLo ¬ rLoA.real;
IF rHiA.ok THEN rHi ¬ rHiA.real;
name ¬ AutoName[ps.context3d, "AutoCone"];
}
ELSE {
[nameA, stepsA, lcapA, ucapA, radiusA, zLoA, zHiA, minThetaA, maxThetaA, radiansA] ¬ G3dScene.GetArgs[ps, args, "-name%s-steps%i-lcap%b-ucap%b-r%r-zlo%r-zhi%r-minTheta%r-maxTheta%r-radians%b"];
IF radiusA.ok THEN rLo ¬ rHi ¬ radiusA.real;
name ¬ AutoName[ps.context3d, "AutoCylinder"];
};
IF minThetaA.ok THEN {
minTheta ¬ minThetaA.real;
IF NOT radiansA.ok THEN minTheta ¬ minTheta * degToRad;
};
IF maxThetaA.ok THEN {
maxTheta ¬ maxThetaA.real;
IF NOT radiansA.ok THEN maxTheta ¬ maxTheta * degToRad;
};
IF nameA.ok THEN name ¬ nameA.rope;
IF zLoA.ok THEN zLo ¬ zLoA.real;
IF zHiA.ok THEN zHi ¬ zHiA.real;
IF stepsA.ok THEN steps ¬ stepsA.int;
shape ¬ ConstructCone[steps, rLo, rHi, zLo, zHi, lcapA.ok, ucapA.ok, minTheta, maxTheta];
shape.name ¬ name;
LoadShape[ps, state, shape];
};
MakeTorus: PUBLIC SceneProc ~ {
state: State ¬ GetBuildState[ps];
shape: Shape;
sd: SurfaceDescription ¬ NEW[SurfaceDescriptionRep ¬ []];
majorRA, minorRA, uResA, vResA, minThetaA, maxThetaA, minPsiA, maxPsiA, nameA, radiansA, lcapA, ucapA: Arg;
majorR: REAL ¬ 1.0;
minorR: REAL ¬ 0.1;
uRes, vRes: INT ¬ 10;
minTheta: REAL ¬ 0.0;
maxTheta: REAL ¬ piTimes2;
minPsi: REAL ¬ 0.0;
maxPsi: REAL ¬ piTimes2;
[majorRA, minorRA, uResA, vResA, minThetaA, maxThetaA, minPsiA, maxPsiA, nameA, radiansA, lcapA, ucapA] ¬ G3dScene.GetArgs[ps, args,
"-majorR%r-minorR%r-ures%i-vres%i-mintheta%r-maxtheta%r-minpsi%r-maxpsi%r-name%s-lcap%b-ucap%b"];
IF minorRA.ok THEN minorR ¬ minorRA.real;
IF majorRA.ok THEN majorR ¬ majorRA.real;
IF uResA.ok THEN uRes ¬ uResA.int;
IF vResA.ok THEN vRes ¬ vResA.int;
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 minPsiA.ok THEN
minPsi ¬ IF radiansA.ok THEN minPsiA.real ELSE minPsiA.real*degToRad;
IF maxPsiA.ok THEN
maxPsi ¬ IF radiansA.ok THEN maxPsiA.real ELSE maxPsiA.real*degToRad;
shape ¬ ConstructTorus[uRes, vRes, minorR, majorR, minTheta, maxTheta, minPsi, maxPsi, lcapA.ok, ucapA.ok];
shape.name ¬ IF nameA.ok THEN nameA.rope ELSE AutoName[ps.context3d, "AutoTorus"];
LoadShape[ps, state, shape];
};
MakeNGon: PUBLIC SceneProc ~ {
shape: Shape;
state: State ¬ GetBuildState[ps];
sidesA, radA, nameA, radialNormsA, zA, minThetaA, maxThetaA, radiansA: Arg;
rad: REAL ¬ 1.0;
z: REAL ¬ 0.0;
sides: INT ¬ 10;
radialNorms: BOOL ¬ FALSE;
minTheta: REAL ¬ 0.0;
maxTheta: REAL ¬ piTimes2;
name: ROPE ¬ AutoName[ps.context3d, "AutoNGon"];
[sidesA, radA, zA, radialNormsA, nameA, minThetaA, maxThetaA, radiansA] ¬ G3dScene.GetArgs[ps, args, "%i-r%r-z%r-radial%b-name%s-minTheta%r-maxTheta%r-radians%b"];
sides ¬ sidesA.int;
IF radA.ok THEN rad ¬ radA.real;
IF zA.ok THEN z ¬ zA.real;
IF nameA.ok THEN name ¬ nameA.rope;
IF radialNormsA.ok THEN radialNorms ¬ TRUE;
IF minThetaA.ok THEN {
minTheta ¬ minThetaA.real;
IF NOT radiansA.ok THEN minTheta ¬ minTheta * degToRad;
};
IF maxThetaA.ok THEN {
maxTheta ¬ maxThetaA.real;
IF NOT radiansA.ok THEN maxTheta ¬ maxTheta * degToRad;
};
shape ¬ ConstructNgonShape[rad, sides, z, radialNorms, TRUE, minTheta, maxTheta];
shape.name ¬ name;
LoadShape[ps, state, shape];
G3dRender.SetRenderStyle[ps.currentShape, faceted];
};
MakeBox: PUBLIC SceneProc ~ {
shape: Shape;
state: State ¬ GetBuildState[ps];
dxA, dyA, dzA, nameA: Arg;
dx, dy, dz: REAL ¬ -1.0;
name: ROPE ¬ AutoName[ps.context3d, "AutoBox"];
[dxA, dyA, dzA, nameA] ¬ G3dScene.GetArgs[ps, args, "%rrr-name%s"];
IF nameA.ok THEN name ¬ nameA.rope;
shape ¬ ConstructBox[dxA.real, dyA.real, dzA.real];
shape.name ¬ name;
LoadShape[ps, state, shape];
G3dRender.SetRenderStyle[ps.currentShape, faceted];
};
MakeAnnulus: PUBLIC SceneProc ~ {
shape: Shape;
state: State ¬ GetBuildState[ps];
stepsA, smallRA, bigRA, nameA, radialNormsA, zA, minThetaA, maxThetaA, radiansA: Arg;
smallR: REAL ¬ 0.5;
bigR: REAL ¬ 1.0;
z: REAL ¬ 0.0;
radialNorms: BOOL ¬ FALSE;
steps: INT ¬ 10;
minTheta: REAL ¬ 0.0;
maxTheta: REAL ¬ piTimes2;
name: ROPE ¬ AutoName[ps.context3d, "AutoAnnulus"];
[stepsA, smallRA, bigRA, zA, radialNormsA, nameA, minThetaA, maxThetaA, radiansA] ¬ G3dScene.GetArgs[ps, args, "-steps%i-minorR%r-majorR%r-z%r-radial%b-name%s-minTheta%r-maxTheta%r-radians%b"];
IF stepsA.ok THEN steps ¬ stepsA.int;
IF smallRA.ok THEN smallR ¬ smallRA.real;
IF bigRA.ok THEN bigR ¬ bigRA.real;
IF zA.ok THEN z ¬ zA.real;
IF minThetaA.ok THEN {
minTheta ¬ minThetaA.real;
IF NOT radiansA.ok THEN minTheta ¬ minTheta * degToRad;
};
IF maxThetaA.ok THEN {
maxTheta ¬ maxThetaA.real;
IF NOT radiansA.ok THEN maxTheta ¬ maxTheta * degToRad;
};
IF nameA.ok THEN name ¬ nameA.rope;
IF radialNormsA.ok THEN radialNorms ¬ TRUE;
shape ¬ ConstructAnnulus[smallR, bigR, steps, z, radialNorms, TRUE, minTheta, maxTheta];
shape.name ¬ name;
LoadShape[ps, state, shape];
G3dRender.SetRenderStyle[ps.currentShape, faceted];
};
Shape Saving and Book-keeping Procedures
ShapeFromTripleSequence: PUBLIC PROC [ts: TripleSequence] RETURNS [shape: Shape] ~ {
ns: NatSequence ¬ NEW[NatSequenceRep[ts.length]];
shape ¬ NEW[ShapeRep ¬ []];
FOR i: INT IN [0..ts.length) DO
v: Vertex ¬ NEW[VertexRep ¬ []];
v.point ¬ ts[i];
shape.vertices ¬ G3dShape.AddToVertexSequence[shape.vertices, v];
ENDLOOP;
ns.length ¬ ts.length;
FOR i: INT IN [0..ts.length) DO
ns[i] ¬ i;
ENDLOOP;
shape.surfaces ¬ G3dBasic.AddToSurfaceSequence[shape.surfaces, [NIL, ns]];
G3dShape.SetFaceNormals[shape];
G3dShape.SetFaceCenters[shape];
G3dShape.SetVertexNormals[shape];
};
ShapeFromTripleList: PUBLIC PROC [tl: LIST OF Triple] RETURNS [shape: Shape] ~ {
i: INT ¬ 0;
ns: NatSequence ¬ NIL;
shape ¬ NEW[ShapeRep ¬ []];
FOR t: LIST OF Triple ¬ tl, t.rest UNTIL t = NIL DO
v: Vertex ¬ NEW[VertexRep ¬ []];
v.point ¬ t.first;
shape.vertices ¬ G3dShape.AddToVertexSequence[shape.vertices, v];
ENDLOOP;
FOR t: LIST OF Triple ¬ tl, t.rest UNTIL t = NIL DO
ns ¬ G2dBasic.AddToNatSequence[ns, i];
i ¬ i+1;
ENDLOOP;
shape.surfaces ¬ G3dBasic.AddToSurfaceSequence[shape.surfaces, [NIL, ns]];
G3dShape.SetFaceNormals[shape];
G3dShape.SetFaceCenters[shape];
G3dShape.SetVertexNormals[shape];
};
ResampleTriples: PUBLIC PROC [
ts: TripleSequence,
steps: INT,
close: BOOL,
curved: BOOL ¬ TRUE]
RETURNS [TripleSequence]
~ {
-- ignore curved flag, since curved interpolation is just too slow right now
RETURN[LinearResampleTriples[ts, steps, close]];
};
LinearResampleTriples: PROC [ts: TripleSequence, steps: INT, close: BOOL]
RETURNS [TripleSequence]
~ {
arcLengths: RealSequence ¬ NIL;
totalArcLength: REAL ¬ 0.0;
newTriples: TripleSequence ¬ NIL;
arcLengths ¬ G2dBasic.AddToRealSequence[arcLengths, 0.0];
FOR i: INT IN [0 .. ts.length-1) DO
len: REAL ¬ G3dVector.Distance[ts[i], ts[i+1]];
totalArcLength ¬ totalArcLength + len;
arcLengths ¬ G2dBasic.AddToRealSequence[arcLengths, totalArcLength];
ENDLOOP;
IF close THEN {
len: REAL ¬ G3dVector.Distance[ts[0], ts[ts.length-1]];
totalArcLength ¬ totalArcLength + len;
arcLengths ¬ G2dBasic.AddToRealSequence[arcLengths, totalArcLength];
};
FOR i: INT IN [0 .. steps) DO
alpha: REAL ¬ IF close THEN REAL[i]/REAL[steps] ELSE REAL[i]/REAL[steps-1];
position: REAL ¬ alpha * totalArcLength;
segment: INT ¬ 0;
lerp, d: REAL;
newpt: Triple;
WHILE position > arcLengths[segment+1] DO
segment ¬ segment+1;
ENDLOOP;
d ¬ arcLengths[segment+1] - arcLengths[segment];
IF d = 0.0
THEN lerp ¬ 0.0
ELSE lerp ¬ (position - arcLengths[segment])/d;
newpt ¬ G3dVector.Interp[lerp, ts[segment], ts[(segment+1) MOD ts.length]];
newTriples ¬ G3dBasic.AddToTripleSequence[newTriples, newpt];
ENDLOOP;
RETURN[newTriples];
};
CurvedResampleTriples: PROC [ts: TripleSequence, steps: INT, close: BOOL]
RETURNS [TripleSequence]
~ {
-- yow! This is monstrously slow - don't use it yet!
output: TripleSequence ← NIL;
ss: G3dSpline.SplineSequence ← IF close
THEN G3dSpline.InterpolateCyclic[ts]
ELSE G3dSpline.Interpolate[ts];
c: G3dCurve.Curve ← G3dCurve.CurveFromSplines[ss];
FOR i: INT IN [0 .. steps) DO
alpha: REALREAL[i]/REAL[steps-1];
p: Triple ← G3dCurve.PositionA[c, alpha*c.totalLength];
output ← G3dBasic.AddToTripleSequence[output, p];
ENDLOOP;
RETURN[output];
RETURN[ts];
};
LoadShape: PUBLIC PROC [ps: ParseState, state: State, shape: Shape] ~ {
G3dRender.AddShape[ps.context3d, shape];
UpdateShape[ps, state, shape];
};
UpdateShape: PUBLIC PROC [ps: ParseState, state: State, shape: Shape] ~ {
SetTexture: PROC [name: ROPE, style: TextureStyle] ~ {
IF name # NIL THEN [] ¬ G3dRender.SetTextureMap[shape, name, style];
};
shape.vertices.valid ¬ [TRUE, FALSE, TRUE, FALSE];
shape.type ¬ $ConvexPolygon;
shape.fileName ¬ NIL;
G3dScene.AssignCtmToShape[ps, shape];
ps.currentShape ¬ shape;
G3dShape.UnitizeVertexNormals[shape];
G3dShape.SetFaceNormals[shape];
G3dShape.SetFaceCenters[shape];
G3dScene.InheritMaterial[ps];
};
AutoName: PUBLIC PROC [context3d: Context3d, type: ROPE] RETURNS [ROPE] ~ {
shapes: ShapeSequence ¬ context3d.shapes;
index: INT ¬ IF shapes = NIL THEN 0 ELSE shapes.length;
RETURN[IO.PutFR["%g%g", IO.rope[type], IO.int[index]]];
};
AutoUTexture: PUBLIC PROC [shape: Shape] ~ {
FOR i: INT IN [0 .. shape.vertices.length) DO
alpha: REAL ¬ IF shape.vertices.length > 1
THEN REAL[i]/REAL[shape.vertices.length-1]
ELSE 0.0;
shape.vertices[i].texture.x ¬ alpha;
ENDLOOP;
};
Sweep Surface Procedures
ClearSweepPaths: PUBLIC SceneProc ~ {
state: State ¬ GetBuildState[ps];
state.sweepPaths ¬ NEW[SweepPathsRep ¬ []];
};
ChangeSweepPaths: PUBLIC SceneProc ~ {
state: State ¬ GetBuildState[ps];
shapeA,  shapeBlendA,
scaleA,  scaleBlendA,
pathA,  pathBlendA,
translateA, translateBlendA,
rotateA,  rotateBlendA: Arg;
[shapeA, shapeBlendA, scaleA, scaleBlendA, pathA, pathBlendA, translateA, translateBlendA, rotateA, rotateBlendA] ¬ G3dScene.GetArgs[ps, args, "-shape%s-shapeBlend%s-scale%s-scaleBlend%s-path%s-pathBlend%s-translate%s-translateBlend%s-rotate%s-rotateBlend%s"];
IF shapeA.ok THEN state.sweepPaths.shapeFile ¬ shapeA.rope;
IF shapeBlendA.ok THEN
state.sweepPaths.shapeBlend ¬ BlendFromArg[shapeBlendA, smooth];
IF scaleA.ok THEN state.sweepPaths.scaleFile ¬ scaleA.rope;
IF scaleBlendA.ok THEN
state.sweepPaths.scaleBlend ¬ BlendFromArg[scaleBlendA, smooth];
IF pathA.ok THEN state.sweepPaths.pathFile ¬ pathA.rope;
IF pathBlendA.ok THEN
state.sweepPaths.pathBlend ¬ BlendFromArg[pathBlendA, smooth];
IF translateA.ok THEN state.sweepPaths.translateFile ¬ translateA.rope;
IF translateBlendA.ok THEN
state.sweepPaths.translateBlend ¬ BlendFromArg[translateBlendA, smooth];
IF rotateA.ok THEN state.sweepPaths.rotateFile ¬ rotateA.rope;
IF rotateBlendA.ok THEN
state.sweepPaths.rotateBlend ¬ BlendFromArg[rotateBlendA, smooth];
};
MakeSweep: PUBLIC SceneProc ~ {
shape: Shape;
state: State ¬ GetBuildState[ps];
tLo: REAL ¬ 0.0;
tHi: REAL ¬ 1.0;
steps: INT ¬ 10;
sp: SweepPaths ¬ state.sweepPaths;
onlyA, stepsA, anchorA, connectA, instanceA, localXSectionsA: Arg;
tubeTextureA, closeA, nameA, tLoA, tHiA, activeA: Arg;
[onlyA, activeA, stepsA, anchorA, connectA, instanceA, localXSectionsA, tubeTextureA, closeA, nameA, tLoA, tHiA] ¬ G3dScene.GetArgs[ps, args, "-onlyshape%s-activeShape%b-steps%i-anchor%s-connect%s-instance%s-localXSections%s-tubetexture%s-close%s-name%s-tlo%r-thi%r"];
shape ¬ G3dSweep.MakeSweepShape[
shapeProc:
IF sp.shapeFile # NIL
THEN G3dSweep.DefaultShapeBlendProc
ELSE NIL,
shapeData:
IF sp.shapeFile # NIL
THEN G3dSweep.SetupDefaultShapeBlendProc[sp.shapeFile, sp.shapeBlend]
ELSE IF onlyA.ok
THEN G3dShape.FindShape[ps.context3d.shapes, onlyA.rope]
ELSE ps.currentShape,
shapeBlend: sp.shapeBlend,
pathProc:
IF sp.pathFile # NIL
THEN G3dSweep.DefaultPathProc
ELSE NIL,
pathData:
IF sp.pathFile # NIL
THEN G3dSweep.SetupDefaultPathProc[sp.pathFile, sp.pathBlend]
ELSE NIL,
pathBlend: sp.pathBlend,
scaleProc:
IF sp.scaleFile # NIL
THEN G3dSweep.DefaultScaleProc
ELSE NIL,
scaleData:
IF sp.scaleFile # NIL
THEN G3dSweep.SetupDefaultScaleProc[sp.scaleFile, sp.scaleBlend]
ELSE NIL,
scaleBlend: sp.scaleBlend,
rotateProc:
IF sp.rotateFile # NIL
THEN G3dSweep.DefaultRotateProc
ELSE NIL,
rotateData:
IF sp.rotateFile # NIL
THEN G3dSweep.SetupDefaultRotateProc[sp.rotateFile, sp.rotateBlend]
ELSE NIL,
rotateBlend: sp.rotateBlend,
translateProc:
IF sp.translateFile # NIL
THEN G3dSweep.DefaultTranslateProc
ELSE NIL,
translateData:
IF sp.translateFile # NIL
THEN G3dSweep.SetupDefaultTranslateProc[sp.translateFile, sp.translateBlend]
ELSE NIL,
translateBlend: sp.translateBlend,
xformProc: NIL,
xformData: NIL,
xformBlend: smooth,
tLo: IF tLoA.ok THEN tLoA.real ELSE tLo,
tHi: IF tHiA.ok THEN tHiA.real ELSE tHi,
steps: IF stepsA.ok THEN stepsA.int ELSE steps,
anchor: BoolFromArg[anchorA, FALSE],
instance: BoolFromArg[instanceA, TRUE],
connect: BoolFromArg[connectA, TRUE],
localXSections: BoolFromArg[localXSectionsA, FALSE],
tubeTexture: BoolFromArg[tubeTextureA, TRUE],
close: BoolFromArg[closeA, TRUE]
];
shape.name ¬ IF nameA.ok THEN nameA.rope ELSE AutoName[ps.context3d, "AutoSweep"];
LoadShape[ps, state, shape];
};
Shape Creation Procedures
ConstructCone: PROC [steps: INT, rLo, rHi, zLo, zHi: REAL, lcap, ucap: BOOL, minTheta, maxTheta: REAL] RETURNS [Shape] ~ {
auxShape, shape: Shape;
auxShape ¬ ConstructNgonShape[1.0, steps, 0.0, FALSE, TRUE, minTheta, maxTheta];
shape ¬ G3dSweep.MakeSweepShape[
shapeProc: NIL,
shapeData: auxShape,
shapeBlend: straight,
scaleProc: G3dSweep.DefaultScaleProc,
scaleData: G3dSweep.SimpleSetupScaleProc[[rLo, rLo, 1.0], [rHi, rHi, 1.0]],
scaleBlend: straight,
translateProc: G3dSweep.DefaultTranslateProc,
translateData: G3dSweep.SimpleSetupTranslateProc[[0.0, 0.0, zLo], [0.0, 0.0, zHi]],
translateBlend: straight,
steps: 2,
firstInstance: lcap,
lastInstance: ucap,
reverseFirstInstance: FALSE,
reverseLastInstance: FALSE,
anchor: TRUE,
instance: FALSE,
connect: TRUE,
localXSections: FALSE,
tubeTexture: TRUE,
close: FALSE
];
RETURN[shape];
};
ConstructTorus: PROC [
uRes, vRes: INT,
minorR, majorR: REAL,
minTheta, maxTheta: REAL,
minPsi, maxPsi: REAL,
lcap, ucap: BOOL
] RETURNS [Shape] ~ {
auxShape, shape: Shape;
ts: TripleSequence;
sd: SurfaceDescription ¬ NEW[SurfaceDescriptionRep ¬ []];
objClosed: BOOL;
tmp: REAL;
FOR step: INT IN [0 .. vRes) DO
alpha: REAL ¬ REAL[step] / REAL[vRes];
theta: REAL ¬ LerpReal[alpha, minPsi, maxPsi];
t: Triple ¬ [minorR * RealFns.Cos[theta], 0.0, minorR * -RealFns.Sin[theta]];
t.x ¬ t.x + majorR;
ts ¬ G3dBasic.AddToTripleSequence[ts, t];
ENDLOOP;
auxShape ¬ ShapeFromTripleSequence[ts];
AutoUTexture[auxShape];
tmp ¬ maxPsi - minPsi;  -- is the contour closed?
sd.open ¬ NOT (Real.Floor[tmp/piTimes2] = (tmp/piTimes2));
auxShape.surfaces[0].clientData ¬ sd; 
tmp ¬ maxTheta - minTheta; -- is the torus itself closed?
objClosed ¬ Real.Floor[tmp/piTimes2] = (tmp/piTimes2);
shape ¬ G3dSweep.MakeSweepShape[
shapeProc: NIL,
shapeData: auxShape,
shapeBlend: straight,
rotateProc: G3dSweep.DefaultRotateProc,
rotateData: G3dSweep.SimpleSetupRotateProc[
[[0.0, 0.0, 1.0], minTheta],
[[0.0, 0.0, 1.0], maxTheta]
],
rotateBlend: straight,
steps: uRes,
anchor: FALSE,
instance: FALSE,
connect: TRUE,
localXSections: FALSE,
tubeTexture: TRUE,
close: objClosed,
firstInstance: lcap,
lastInstance: ucap,
reverseFirstInstance: FALSE,
reverseLastInstance: FALSE
];
RETURN[shape];
};
ConstructSphere: PROC [
uRes, vRes: INT,
minTheta, maxTheta: REAL,
minPsi, maxPsi: REAL,
radius: REAL
] RETURNS [Shape] ~ {
shape, auxShape: Shape;
ts: TripleSequence;
sd: SurfaceDescription ¬ NEW[SurfaceDescriptionRep ¬ []];
bs: BoolSequence ¬ NIL;
FOR step: INT IN [0 .. vRes) DO
alpha: REAL ¬ REAL[step] / REAL[vRes-1];
psi: REAL ¬ LerpReal[alpha, minPsi, maxPsi];
t: Triple ¬ [radius * RealFns.Sin[psi], 0.0, radius * -RealFns.Cos[psi]];
ts ¬ G3dBasic.AddToTripleSequence[ts, t];
ENDLOOP;
auxShape ¬ ShapeFromTripleSequence[ts];
AutoUTexture[auxShape];
IF minPsi = 0.0 OR maxPsi = pi THEN {
FOR step: INT IN [0 .. vRes) DO
bs ¬ G3dBasic.AddToBoolSequence[bs, FALSE];
ENDLOOP;
IF minPsi = 0.0 THEN bs[0] ¬ TRUE;
IF maxPsi = pi THEN bs[vRes-1] ¬ TRUE;
};
auxShape.clientData ¬ bs;
sd.open ¬ TRUE;
auxShape.surfaces[0].clientData ¬ sd;
shape ¬ RevolveShape[auxShape, uRes, minTheta, maxTheta, [0.0, 0.0, 1.0], FALSE, TRUE];
RETURN[shape];
};
a regular n-gon in the xy plane, whose front face is towards +z
ConstructNgonShape: PROC [r: REAL, n: INT, z: REAL, radialNorms: BOOL ¬ FALSE, positiveNormal: BOOL ¬ TRUE, loTheta: REAL ¬ 0.0, hiTheta: REAL ¬ piTimes2] RETURNS [shape: Shape] ~ { 
vSequence: NatSequence ¬ NIL;
shape ¬ NEW[ShapeRep ¬ []];
the vertices
FOR i: INT IN [0 .. n) DO
alpha: REAL ¬ REAL[i] / REAL[n];
theta: REAL ¬ loTheta + ((hiTheta - loTheta) * alpha);
vtx: Vertex ¬ NEW[VertexRep ¬ []];
cosTheta: REAL ¬ RealFns.Cos[theta];
sinTheta: REAL ¬ RealFns.Sin[theta];
vtx.point ¬ [r*cosTheta, -r*sinTheta, z];
IF radialNorms
THEN vtx.normal ¬ [cosTheta, -sinTheta, 0.0]
ELSE IF positiveNormal
THEN vtx.normal ¬ [0.0, 0.0, 1.0]
ELSE vtx.normal ¬ [0.0, 0.0, -1.0];
vtx.texture ¬ [alpha, alpha];
shape.vertices ¬ G3dShape.AddToVertexSequence[shape.vertices, vtx];
ENDLOOP;
the polygons
IF positiveNormal
THEN {
FOR i: INT IN [0 .. n) DO
vSequence ¬ G2dBasic.AddToNatSequence[vSequence, i];
ENDLOOP;
}
ELSE {
FOR i: INT DECREASING IN [0 .. n) DO
vSequence ¬ G2dBasic.AddToNatSequence[vSequence, i];
ENDLOOP;
};
shape.surfaces ¬ G3dBasic.AddToSurfaceSequence[shape.surfaces, [NIL, vSequence]];
};
ConstructBox: PROC [dx, dy, dz: REAL] RETURNS [shape: Shape] ~ { 
AddVert: PROC [x, y, z, u, v: REAL] ~ {
vtx: Vertex ¬ NEW[VertexRep ¬ [point: [x, y, z], normal: norm, texture: [u, v]]];
shape.vertices ¬ G3dShape.AddToVertexSequence[shape.vertices, vtx];
};
norm: Triple;
shape ¬ NEW[ShapeRep ¬ []];
we do each face individually to preserve unique texturing information for each face
near face
norm ¬ [ 0.0, -1.0, 0.0];
AddVert[-dx, -dy, -dz, 0.0, 1.0];  -- near lower left
AddVert[-dx, -dy,  dz, 0.0, 0.0];  -- near upper left
AddVert[ dx, -dy,  dz, 1.0, 0.0];  -- near upper right
AddVert[ dx, -dy, -dz, 1.0, 1.0];  -- near lower right
right face
norm ¬ [1.0, 0.0, 0.0];
AddVert[ dx, -dy, -dz, 0.0, 1.0];  -- near lower right
AddVert[ dx, -dy,  dz, 0.0, 0.0];  -- near upper right
AddVert[ dx,  dy,  dz, 1.0, 0.0];  -- far upper right
AddVert[ dx,  dy, -dz, 1.0, 1.0];  -- far lower right
upper face
norm ¬ [ 0.0, 0.0, 1.0];
AddVert[-dx, -dy,  dz, 0.0, 1.0];  -- near upper left
AddVert[-dx,  dy,  dz, 0.0, 0.0];  -- far upper left
AddVert[ dx,  dy,  dz, 1.0, 0.0];  -- far upper right
AddVert[ dx, -dy,  dz, 1.0, 1.0];  -- near upper right
left face
norm ¬ [-1.0, 0.0, 0.0];
AddVert[-dx,  dy, -dz, 0.0, 1.0];  -- far lower left
AddVert[-dx,  dy,  dz, 0.0, 0.0];  -- far upper left
AddVert[-dx, -dy,  dz, 1.0, 0.0];  -- near upper left
AddVert[-dx, -dy, -dz, 1.0, 1.0];  -- near lower left
far face
norm ¬ [ 0.0, 1.0, 0.0];
AddVert[ dx,  dy, -dz, 0.0, 1.0];  -- far lower right
AddVert[ dx,  dy,  dz, 0.0, 0.0];  -- far upper right
AddVert[-dx,  dy,  dz, 1.0, 0.0];  -- far upper left
AddVert[-dx,  dy, -dz, 1.0, 1.0];  -- far lower left
lower face
norm ¬ [ 0.0, 0.0, -1.0];
AddVert[-dx,  dy, -dz, 0.0, 1.0];  -- far lower left
AddVert[-dx, -dy, -dz, 0.0, 0.0];  -- near lower left
AddVert[ dx, -dy, -dz, 1.0, 0.0];  -- near lower right
AddVert[ dx,  dy, -dz, 1.0, 1.0];  -- far lower right
G3dSweep.AddGeneralQuadrilateralToShape[shape, 0, 1, 2, 3];  -- near
G3dSweep.AddGeneralQuadrilateralToShape[shape, 4, 5, 6, 7];  -- right
G3dSweep.AddGeneralQuadrilateralToShape[shape, 8, 9, 10, 11];  -- upper
G3dSweep.AddGeneralQuadrilateralToShape[shape, 12, 13, 14, 15]; -- left
G3dSweep.AddGeneralQuadrilateralToShape[shape, 16, 17, 18, 19]; -- far
G3dSweep.AddGeneralQuadrilateralToShape[shape, 20, 21, 22, 23]; -- lower
RETURN[shape];
};
ConstructAnnulus: PROC [smallR, bigR: REAL, steps: INT, z: REAL, radialNorms: BOOL ¬ FALSE, positiveNormal: BOOL ¬ TRUE, minTheta: REAL ¬ 0.0, maxTheta: REAL ¬ piTimes2] RETURNS [shape: Shape] ~ { 
shape ¬ NEW[ShapeRep ¬ []];
the vertices
FOR i: INT IN [0 .. steps) DO
alpha: REAL ¬ REAL[i] / REAL[steps];
theta: REAL ¬ minTheta + ((maxTheta - minTheta) * alpha);
vtx: Vertex ¬ NEW[VertexRep ¬ []];
cosTheta: REAL ¬ RealFns.Cos[theta];
sinTheta: REAL ¬ RealFns.Sin[theta];
vtx.point ¬ [bigR*cosTheta, -bigR*sinTheta, z];
IF radialNorms
THEN vtx.normal ¬ [cosTheta, -sinTheta, 0.0]
ELSE IF positiveNormal
THEN vtx.normal ¬ [0.0, 0.0, 1.0]
ELSE vtx.normal ¬ [0.0, 0.0, -1.0];
vtx.texture ¬ [alpha, alpha];
shape.vertices ¬ G3dShape.AddToVertexSequence[shape.vertices, vtx];
ENDLOOP;
FOR i: INT IN [0 .. steps) DO
theta: REAL ¬ piTimes2 * i / REAL[steps];
vtx: Vertex ¬ NEW[VertexRep ¬ []];
cosTheta: REAL ¬ RealFns.Cos[theta];
sinTheta: REAL ¬ RealFns.Sin[theta];
vtx.point ¬ [smallR*cosTheta, -smallR*sinTheta, z];
IF radialNorms
THEN vtx.normal ¬ [-cosTheta, sinTheta, 0.0]
ELSE IF positiveNormal
THEN vtx.normal ¬ [0.0, 0.0, 1.0]
ELSE vtx.normal ¬ [0.0, 0.0, -1.0];
vtx.texture ¬ [cosTheta, -sinTheta];
shape.vertices ¬ G3dShape.AddToVertexSequence[shape.vertices, vtx];
ENDLOOP;
the polygons
FOR i: INT IN [0 .. steps) DO
nexti: INT ¬ (i+1) MOD steps;
iprime: INT ¬ i + steps;
nextiprime: INT ¬ nexti + steps;
G3dSweep.AddTriangleToShape[shape, i, nexti, iprime];
G3dSweep.AddTriangleToShape[shape, nexti, nextiprime, iprime];
ENDLOOP;
IF NOT positiveNormal THEN G3dShape.ReversePolygons[shape];
RETURN[shape];
};
Primitive Sweep Convenience Procedures
TranslateShapeAlongZ: PUBLIC PROC [
shape: Shape,
distance: REAL ¬ 1.0,   
anchor: BOOL ¬ FALSE
] RETURNS [Shape] ~ {
z: Triple ¬ [0.0, 0.0, 1.0];
RETURN[
TranslateShape[shape, [0.0, 0.0, 0.0], G3dVector.Mul[z, distance], anchor]
];
};
TranslateShape: PUBLIC PROC [
shape: Shape,
lo, hi: Triple,
anchor: BOOL ¬ FALSE
] RETURNS [Shape] ~ {
RETURN[
G3dSweep.MakeSweepShape[
shapeProc: NIL,
shapeData: shape,
shapeBlend: straight,
translateProc: G3dSweep.DefaultTranslateProc,
translateData: G3dSweep.SimpleSetupTranslateProc[lo, hi],
translateBlend: straight,
steps: 2,
anchor: anchor,
instance: FALSE,
connect: TRUE,
localXSections: FALSE,
tubeTexture: TRUE,
close: FALSE
]
];
};
RevolveShape: PUBLIC PROC [
shape: Shape,
steps: INT ¬ 10,
loAngle: REAL ¬ 0.0,   -- angle extreme in degrees
hiAngle: REAL ¬ 360.0,   -- angle extreme in degrees
axis: Triple ¬ [0.0, 0.0, 1.0], -- z axis by default
anchor: BOOL ¬ FALSE,
objClosed: BOOL ¬ FALSE
] RETURNS [Shape] ~ {
RETURN[
G3dSweep.MakeSweepShape[
shapeProc: NIL,
shapeData: shape,
shapeBlend: straight,
rotateProc: G3dSweep.DefaultRotateProc,
rotateData: G3dSweep.SimpleSetupRotateProc[[axis, loAngle], [axis, hiAngle]],
rotateBlend: straight,
steps: steps,
anchor: anchor,
instance: FALSE,
connect: TRUE,
localXSections: FALSE,
tubeTexture: TRUE,
close: objClosed
]
];
};
Utility Procedures
BlendFromArg: PUBLIC PROC [a: Arg, default: BlendType] RETURNS [BlendType] ~ {
IF a.ok THEN RETURN[BlendFromRope[a.rope]] ELSE RETURN [default];
};
BlendFromRope: PUBLIC PROC [r: ROPE] RETURNS [BlendType] ~ {
RE: PROC [a, b: ROPE] RETURNS [BOOL] ~ { RETURN[Rope.Equal[a, b, FALSE]]; };
IF RE[r, "straight"] OR RE[r, "linear"] THEN RETURN [straight];
IF RE[r, "smooth"] OR RE[r, "cubic"] THEN RETURN [smooth];
IF RE[r, "cyclic"] OR RE[r, "closed"] THEN RETURN [cyclic];
RETURN [smooth];
};
BoolFromArg: PUBLIC PROC [a: Arg, default: BOOL] RETURNS [BOOL] ~ {
IF a.ok THEN RETURN[BoolFromRope[a.rope]] ELSE RETURN [default];
};
BoolFromRope: PUBLIC PROC [r: ROPE] RETURNS [BOOL] ~ {
RETURN[Rope.Equal[r, "Yes", FALSE] OR Rope.Equal[r, "True", FALSE]];
};
RopeFromBool: PUBLIC PROC [b: BOOL] RETURNS [ROPE] ~ {
IF b THEN RETURN["True"] ELSE RETURN["False"];
};
LerpReal: PROC [a, lo, hi: REAL] RETURNS [REAL] ~ {
RETURN[lo + (a * (hi-lo))];
};
Start Procedures
RegisterProcs: PROC ~ {
primitive objects  
G3dScene.RegisterProc["Sphere",    MakeSphere];
G3dScene.RegisterProc["Cylinder",    MakeCylinder];
G3dScene.RegisterProc["Cone",     MakeCone];
G3dScene.RegisterProc["Torus",    MakeTorus];
G3dScene.RegisterProc["NGon",    MakeNGon];
G3dScene.RegisterProc["Annulus",   MakeAnnulus];
G3dScene.RegisterProc["Box",     MakeBox];
sweep objects
G3dScene.RegisterProc["ClearSweepPaths", ClearSweepPaths];
G3dScene.RegisterProc["ChangeSweepPaths", ChangeSweepPaths];
G3dScene.RegisterProc["Sweep",    MakeSweep];
};
RegisterProcs[];
END.