G3dDrawImpl.mesa
Copyright Ó 1985, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, April 14, 1993 4:22 pm PDT
DIRECTORY Basics, CedarProcess, G2dVector, G3dBasic, G3dClip, G3dCurve, G3dDraw, G3dMatrix, G3dNats, G3dPatch, G3dPlane, G3dPolygon, G3dSpline, G3dVector, G3dView, Draw2d, Imager, ImagerBackdoor, ImagerCG6Context, ImagerColor, ImagerPath, ImagerShapeStroke, IO, MessageWindow, PreDebug, Process, Prop, Real, RealFns, Rope, Vector2;
G3dDrawImpl: CEDAR MONITOR
IMPORTS Basics, CedarProcess, G2dVector, G3dClip, G3dMatrix, G3dNats, G3dPatch, G3dPlane, G3dPolygon, G3dSpline, G3dVector, G3dView, Draw2d, Imager, ImagerBackdoor, ImagerCG6Context, ImagerColor, ImagerPath, ImagerShapeStroke, IO, PreDebug, Process, Prop, MessageWindow, Real, RealFns, Vector2
EXPORTS G3dDraw
~ BEGIN
This module is designed to work with the ImagerCG6Context interface; calls to Imager or Draw2d will fail (ie, imaging will be misplaced) if made from within an ImagerC6Context.DoSaveCG6. CG6 calls are enabled during a call to WrapInCG6. WrapInCG6 places the $CG6 property on the context property list, removing it upon completion.
All imaging procedures in this module should determine if $CG6 is on the context property list (Imager.GetProp[context, $CG6] # NIL); if so, imaging should be made via ImagerCG6; otherwise, calls should be made to Imager or Draw2d.
Must Imager and ImagerCG6Context be implemented in mutually exclusive ways?
Type Declarations
DrawType:     TYPE ~ Draw2d.DrawType;
MarkType:     TYPE ~ Draw2d.MarkType;
VEC:       TYPE ~ Draw2d.VEC;
IntegerPair:    TYPE ~ G3dBasic.IntegerPair;
IntegerPairSequence: TYPE ~ G3dBasic.IntegerPairSequence;
NatSequence:    TYPE ~ G3dBasic.NatSequence;
Pair:       TYPE ~ G3dBasic.Pair;
PairSequence:    TYPE ~ G3dBasic.PairSequence;
PairSequenceRep:   TYPE ~ G3dBasic.PairSequenceRep;
Quad:      TYPE ~ G3dBasic.Quad;
Screen:      TYPE ~ G3dBasic.Screen;
SurfaceSequence:   TYPE ~ G3dBasic.SurfaceSequence;
Triple:      TYPE ~ G3dBasic.Triple;
TripleSequence:   TYPE ~ G3dBasic.TripleSequence;
TripleSequenceRep:  TYPE ~ G3dBasic.TripleSequenceRep;
PointProc:     TYPE ~ G3dDraw.PointProc;
TriangleInfo:    TYPE ~ G3dDraw.TriangleInfo;
Matrix:      TYPE ~ G3dMatrix.Matrix;
Viewport:     TYPE ~ G3dMatrix.Viewport;
Bezier:      TYPE ~ G3dSpline.Bezier;
Spline:      TYPE ~ G3dSpline.Spline;
SplineSequence:   TYPE ~ G3dSpline.SplineSequence;
PairClip:      TYPE ~ G3dView.PairClip;
Context:      TYPE ~ Imager.Context;
ROPE:      TYPE ~ Rope.ROPE;
origin:      Triple ~ G3dBasic.origin;
2d Foundation
UseCG6: TYPE ~ G3dDraw.UseCG6;
CG6Available: PUBLIC PROC RETURNS [BOOL] ~ {
RETURN[Basics.IsBound[ImagerCG6Context.DoSaveCG6]];
};
GetUseCG6: PROC [context: Context] RETURNS [UseCG6] ~ {
RETURN[IF Imager.GetProp[context, $CG6] # NIL THEN y ELSE n];
};
WrapInCG6: PUBLIC PROC [context: Context, action: PROC] ~ {
Reject: PROC [reason: ROPE] RETURNS [BOOL ¬ FALSE] ~ {MessageWindow.Append[reason]};
ProtectedWrapInCG6: PROC ~ {ImagerCG6Context.DoSaveCG6[context, DoWithCG6]};
DoWithCG6: PROC ~ {
box: Draw2d.IntegerBox ¬ Draw2d.BoxFromContext[context];
context.propList ¬ Prop.Put[context.propList, $CG6, $CG6]; -- enable CG6 in other procs
ImagerCG6Context.SetCG6SubScreen[context, box.x, box.y-1, box.w, box.h];
the above seems to kill the ability to use the Imager to print text with colors other than
those with an r, g, or b component equal to 0 or 1. Weird.
ImagerCG6Context.SetCG6RGBColor[context, 0.0, 0.0, 0.0];
action[];
context.propList ¬ Prop.Rem[context.propList, $CG6]; -- disable CG6 in other procs
};
[] ¬ PreDebug.Protect[ProtectedWrapInCG6, Reject];
};
Rnd: PROC [r: REAL] RETURNS [i: INT] ~ INLINE {i ¬ Real.Round[r]};
RndPair: PROC [p: Pair] RETURNS [IntegerPair] ~ INLINE {
RETURN[[Real.Round[p.x], Real.Round[p.y]]];
};
RndPairAndVP: PROC [p: Pair, vp: Viewport] RETURNS [IntegerPair] ~ {
RETURN[RndPair[G3dMatrix.TransformByViewport[p, vp]]];
};
GetStrokeWidth: PROC [context: Context] RETURNS [w: REAL ¬ 0.0] ~ {
w ¬ ImagerBackdoor.GetReal[context, strokeWidth ! Imager.Error => CONTINUE];
};
Clear: PUBLIC PROC [context: Context, color: Triple, useCG6: UseCG6 ¬ maybe] ~ {
IF useCG6 = maybe THEN useCG6 ¬ GetUseCG6[context];
IF useCG6 = y
THEN {
box: Draw2d.IntegerBox ¬ Draw2d.BoxFromContext[context];
SetColor[context, color];
ImagerCG6Context.RectangleCG6[context, 0, 0, box.w, box.h];
}
ELSE Draw2d.Clear[context, ImagerColor.ColorFromRGB[[color.x, color.y, color.z]]];
};
Line2d: PUBLIC PROC [
context: Context,
p1, p2: IntegerPair,
drawType: DrawType,
useCG6: UseCG6 ¬ maybe]
~ {
ENABLE UNCAUGHT => GOTO Bad; -- occasional large values
IF useCG6 = maybe THEN useCG6 ¬ GetUseCG6[context];
IF useCG6 = y
THEN {
Line: PROC [x1, y1, x2, y2: INT] ~ {
IF thick
THEN ImagerCG6Context.QuadrangleCG6[
context, x1, y1, x1-v.x, y1-v.y, x2-v.x, y2-v.y, x2, y2]
ELSE ImagerCG6Context.LineCG6[context, x1, y1, x2, y2];
};
v: IntegerPair;
width: REAL ¬ GetStrokeWidth[context];
thick: BOOL ¬ width # 0.0 AND width # 1.0;
IF thick THEN {
tmp: IntegerPair ¬ v ¬ [p2.x-p1.x, p2.y-p1.y];
f: REAL ¬ width/RealFns.SqRt[v.x*v.x+v.y*v.y];
v ¬ RndPair[[-v.y*f, v.x*f]];
};
SELECT drawType FROM
solid => Line[p1.x, p1.y, p2.x, p2.y];
dashed => {
delta: Pair ¬ [p2.x-p1.x, p2.y-p1.y];
nGaps: INTEGER ¬ Rnd[0.10*RealFns.SqRt[delta.x*delta.x+delta.y*delta.y]];
IF nGaps > 0 THEN {
v: Pair ¬ [p1.x, p1.y];
nPieces: REAL ¬ nGaps+nGaps+1;
drawD: Imager.VEC ¬ [delta.x/nPieces, delta.y/nPieces];
stepD: Imager.VEC ¬ [2*drawD.x, 2*drawD.y];
FOR i: INTEGER IN [0..nGaps] DO
Line[Rnd[v.x], Rnd[v.y], Rnd[v.x+drawD.x], Rnd[v.y+drawD.y]];
v ¬ [v.x+stepD.x, v.y+stepD.y];
ENDLOOP;
}; 
};
ENDCASE;
}
ELSE Draw2d.Line[context, [p1.x, p1.y], [p2.x, p2.y], drawType];
EXITS Bad => NULL;
};
Mark2d: PUBLIC PROC [
context: Context,
p: IntegerPair, type:
MarkType,
useCG6: UseCG6 ¬ maybe]
~ {
IF useCG6 = maybe THEN useCG6 ¬ GetUseCG6[context];
IF useCG6 = y
THEN {
X: PROC ~ {
ImagerCG6Context.LineCG6[context, p.x-4, p.y-4, p.x+4, p.y+4];
ImagerCG6Context.LineCG6[context, p.x+4, p.y-4, p.x-4, p.y+4];
};
Cross: PROC ~ {
ImagerCG6Context.LineCG6[context, p.x-5, p.y, p.x+5, p.y];
ImagerCG6Context.LineCG6[context, p.x, p.y-5, p.x, p.y+5];
};
SELECT type FROM
dot => ImagerCG6Context.PointCG6[context, p.x, p.y];
x => X[];
cross => Cross[];
asterisk => {Cross[]; X[]};
ENDCASE => NULL;
}
ELSE Draw2d.Mark[context, [p.x, p.y], type];
};
Arrow2d: PUBLIC PROC [
context: Context,
tail, head: IntegerPair,
type: DrawType,
useCG6: UseCG6 ¬ maybe]
~ {
v: VEC ¬ [head.x-tail.x, head.y-tail.y];
mul: REAL ¬ IF v.x*v.x+v.y*v.y > 75.0*75.0 THEN -0.175*75.0/Vector2.Length[v] ELSE -0.175;
vv: VEC ¬ Vector2.Mul[v, mul];
p0: VEC ¬ Vector2.Add[Vector2.Add[[head.x, head.y], vv], [0.5*vv.y, -0.5*vv.x]];
p1: VEC ¬ Vector2.Add[p0, [-vv.y, vv.x]];
IF useCG6 = maybe THEN useCG6 ¬ GetUseCG6[context];
Line2d[context, head, tail, type, useCG6];
Line2d[context, head, RndPair[p0], type, useCG6];
Line2d[context, head, RndPair[p1], type, useCG6];
};
Quadrangle: PUBLIC PROC [
context: Context,
x0, y0, x1, y1, x2, y2, x3, y3: INTEGER,
useCG6: UseCG6 ¬ maybe]
~ {
IF useCG6 = maybe THEN useCG6 ¬ GetUseCG6[context];
IF useCG6 = y
THEN ImagerCG6Context.QuadrangleCG6[context, x0, y0, x1, y1, x2, y2, x3, y3]
ELSE Imager.MaskFillTrajectory[context,
ImagerPath.LineTo[
ImagerPath.LineTo[
ImagerPath.LineTo[
ImagerPath.MoveTo[[x0, y0]], [x1, y1]], [x2, y2]], [x3, y3]]];
};
Color
SetColor: PUBLIC PROC [context: Context, color: Triple, useCG6: G3dDraw.UseCG6 ¬ maybe] ~ {
IF useCG6 = maybe THEN useCG6 ¬ GetUseCG6[context];
IF useCG6 = y THEN ImagerCG6Context.SetCG6RGBColor[context, color.x, color.y, color.z];
Imager.SetColor[context, ImagerColor.ColorFromRGB[[color.x, color.y, color.z]]];
};
The following version would work if, within a DrawProc, Imager (ImagerColor) and
ImagerCG6Context calls were never mixed, but presently they are (for example, in Vector[]).
SetColor: PUBLIC PROC [context: Context, color: Triple, useCG6: UseCG6 ¬ maybe] ~ {
r: REF ANY ¬ Imager.GetProp[context, $Color3d];
IF r # NIL AND NARROW[r, REF Triple]^ = color THEN RETURN;
context.propList ¬ Prop.Put[context.propList, $Color3d, NEW[Triple ¬ color]];
IF useCG6 = maybe THEN useCG6 ¬ GetUseCG6[context];
IF useCG6 = y
THEN ImagerCG6Context.SetCG6RGBColor[context, color.x, color.y, color.z]
ELSE Imager.SetColor[context, ImagerColor.ColorFromRGB[[color.x, color.y, color.z]]];
};
Point Drawing
Mark: PUBLIC PROC [
context: Context,
point: Triple,
view: Matrix,
viewport: Viewport,
label: ROPE ¬ NIL,
markType: MarkType ¬ cross]
~ {
IF markType # none OR label # NIL THEN {
pc: PairClip ~ G3dView.TransformAndClipInZ[point, view, viewport];
IF pc.clipped THEN RETURN;
IF markType # none THEN Mark2d[context, RndPair[pc.pair], markType];
IF label # NIL THEN Draw2d.Label[context, [pc.pair.x+6, pc.pair.y], label];
};
};
MarkHex: PUBLIC PROC [
context: Context,
point: Triple,
view: Matrix,
viewport: Viewport,
fill: BOOL ¬ TRUE,
radius: INT ¬ 4,
useCG6: UseCG6 ¬ maybe]
~ {
DrawHex: PROC [x, y: INT] ~ {
r1: INT ¬ radius-2;
r2: INT ¬ radius-1;
r3: INT ¬ radius+1;
x1: INT ¬ x-radius; x2: INT ¬ x-r1; x3: INT ¬ x+r1; x4: INT ¬ x+radius;
y1: INT ¬ y-r2; y2: INT ¬ y+r2; y3: INT ¬ y1-r3; y4: INT ¬ y2+r3;
IF useCG6 = maybe THEN useCG6 ¬ GetUseCG6[context];
IF fill
THEN {
Quadrangle[context, x1, y, x2, y1, x3, y1, x4, y, useCG6];
Quadrangle[context, x1, y, x2, y2, x3, y2, x4, y, useCG6];
}
ELSE {
Line2d[context, [x1, y], [x2, y1], solid, useCG6];
Line2d[context, [x2, y1], [x3, y1], solid, useCG6];
Line2d[context, [x3, y1], [x4, y], solid, useCG6];
Line2d[context, [x1, y], [x2, y2], solid, useCG6];
Line2d[context, [x2, y2], [x3, y2], solid, useCG6];
Line2d[context, [x3, y2], [x4, y], solid, useCG6];
};
};
pp: Pair ¬ G3dView.TransformAndClipInZ[point, view, viewport].pair;
DrawHex[Real.Round[pp.x], Real.Round[pp.y]];
};
Box: PUBLIC PROC [
context: Context,
point: Triple,
view: Matrix,
viewport: Viewport,
w, h: INT,
label: ROPE ¬ NIL]
~ {
pc: PairClip ~ G3dView.TransformAndClipInZ[point, view, viewport];
IF pc.clipped THEN RETURN;
IF Imager.GetProp[context, $CG6] # NIL
THEN
ImagerCG6Context.RectangleCG6[context, Rnd[pc.pair.x-w/2], Rnd[pc.pair.y-h/2], w, h]
ELSE Imager.MaskRectangle[context, [pc.pair.x-w/2, pc.pair.y-h/2, w, h]];
IF label # NIL THEN Draw2d.Label[context, [pc.pair.x+w+6, pc.pair.y], label];
};
DoWithPoint: PUBLIC PROC [
context: Context,
point: Triple,
pointProc: PointProc,
view: Matrix,
viewport: Viewport ¬ []]
~ {
pc: PairClip ~ G3dView.TransformAndClipInZ[point, view, viewport];
IF NOT pc.clipped THEN pointProc[context, pc.pair];
};
Straight Line Drawing
Segment: PUBLIC PROC [
context: Context,
point0, point1: Triple,
view: Matrix,
viewport: Viewport,
drawType: DrawType ¬ solid]
~ {
hasPersp: BOOL ¬ G3dMatrix.HasPerspective[view];
s0: Screen ¬ G3dMatrix.GetScreen[point0, view, hasPersp, viewport];
s1: Screen ¬ G3dMatrix.GetScreen[point1, view, hasPersp, viewport];
SELECT TRUE FROM
NOT hasPersp OR (s0.visible AND s1.visible) =>
Line2d[context, s0.intPos, s1.intPos, drawType];
(s0.l AND s1.l)OR(s0.r AND s1.r)OR(s0.b AND s1.b)OR(s0.t AND s1.t)OR(s0.n AND s1.n)
=> NULL;
ENDCASE => {
ct: G3dClip.ClippedTriple ¬ G3dClip.FrustumH[s0.quad, s1.quad];
IF NOT ct.off THEN {
p0: Pair ¬ [viewport.scale.x*ct.c0.x+viewport.translate.x, viewport.scale.y*ct.c0.y+viewport.translate.y];
p1: Pair ¬ [viewport.scale.x*ct.c1.x+viewport.translate.x, viewport.scale.y*ct.c1.y+viewport.translate.y];
Line2d[context, RndPair[p0], RndPair[p1], drawType];
};
};
};
TaperedSegment: PUBLIC PROC [
context: Context,
point0, point1: Triple,
radius0, radius1: REAL,
view: Matrix,
viewInverse: Matrix ¬ NIL,
viewport: Viewport ¬ []]
~ {
Inner: PROC [p0, p1: Pair, t0, t1: Triple] ~ {
rad0: REAL ¬ ScreenRadiusFromSphere[[t0, radius0], view, inverse, viewport];
rad1: REAL ¬ ScreenRadiusFromSphere[[t1, radius1], view, inverse, viewport];
ImagerShapeStroke.TaperedStroke[context, p0, p1, rad0, rad1];
};
inverse: Matrix ¬ IF viewInverse # NIL
THEN viewInverse ELSE G3dMatrix.Invert[view, G3dMatrix.ObtainMatrix[]];
hasPersp: BOOL ¬ G3dMatrix.HasPerspective[view];
s0: Screen ¬ G3dMatrix.GetScreen[point0, view, hasPersp, viewport];
s1: Screen ¬ G3dMatrix.GetScreen[point1, view, hasPersp, viewport];
SELECT TRUE FROM
Imager.GetProp[context, $CG6] # NIL => NULL; -- no Imager operations during CG6
NOT s0.visible AND NOT s1.visible => NULL;
NOT hasPersp OR (s0.visible AND s1.visible) => Inner[s0.pos, s1.pos, point0, point1];
(s0.l AND s1.l)OR(s0.r AND s1.r)OR(s0.b AND s1.b)OR(s0.t AND s1.t)OR(s0.n AND s1.n) => NULL;
ENDCASE => {
off: BOOL;
p0, p1: Pair;
a0, a1: REAL;
[a0, a1, off] ¬ G3dClip.Alphas[s0.quad, s1.quad];
IF NOT off THEN {
dif: Quad ¬
[s1.quad.x-s0.quad.x, s1.quad.y-s0.quad.y, s1.quad.z-s0.quad.z, s1.quad.w-s0.quad.w];
w0: REAL ¬ 1.0/(s0.quad.w+a0*dif.w);
w1: REAL ¬ 1.0/(s0.quad.w+a1*dif.w);
c0: Pair ¬ [w0*(s0.quad.x+a0*dif.x), w0*(s0.quad.y+a0*dif.y)];
c1: Pair ¬ [w1*(s0.quad.x+a1*dif.x), w1*(s0.quad.y+a1*dif.y)];
t0: Triple ¬ G3dVector.Interp[w0, point0, point1];
t1: Triple ¬ G3dVector.Interp[w1, point0, point1];
p0 ¬ [viewport.scale.x*c0.x+viewport.translate.x, viewport.scale.y*c0.y+viewport.translate.y];
p1 ¬ [viewport.scale.x*c1.x+viewport.translate.x, viewport.scale.y*c1.y+viewport.translate.y];
Inner[p0, p1, t0, t1];
};
};
IF viewInverse = NIL THEN G3dMatrix.ReleaseMatrix[inverse];
};
ForwardFacingSegment: PUBLIC PROC [
context: Context,
point0, point1: Triple,
view: Matrix,
viewport: Viewport,
drawType: DrawType ¬ solid]
~ {
VP: PROC [p: Pair] RETURNS [IntegerPair] ~ INLINE {RETURN[RndPairAndVP[p, viewport]]};
IF G3dMatrix.HasPerspective[view]
THEN {
q1: Quad ¬ G3dMatrix.TransformH[point1, view];
q0: Quad ¬ G3dMatrix.TransformH[point0, view];
IF q0.z/q0.w < q1.z/q1.w THEN {
clip: G3dClip.ClippedPair ¬ G3dClip.NearH[q0, q1];
IF NOT clip.off THEN Line2d[context, VP[clip.c0], VP[clip.c1], drawType];
};
}
ELSE {
c0: Triple ¬ G3dMatrix.Transform[point0, view];
c1: Triple ¬ G3dMatrix.Transform[point1, view];
IF c0.z < c1.z THEN Line2d[context, VP[[c0.x, c0.y]], VP[[c1.x, c1.y]], drawType];
};
};
Vector: PUBLIC PROC [
context: Context,
base, vector: Triple,
view: Matrix,
viewport: Viewport,
label: ROPE ¬ NIL,
scale: REAL ¬ 0.2,
absolute2dLen: REAL ¬ 0.0,
markType: MarkType ¬ none,
drawType: DrawType ¬ solid]
~ {
off: BOOL;
c0, c1, d: Pair;
ic0: IntegerPair;
end: Triple ¬ G3dVector.Add[base, vector];
useCG6: UseCG6 ¬ GetUseCG6[context];
IF G3dMatrix.HasPerspective[view]
THEN {
[[c0, c1, off]] ¬ G3dClip.NearH[
G3dMatrix.TransformH[base, view], G3dMatrix.TransformH[end, view]];
IF off THEN RETURN;
c0 ¬ G3dMatrix.TransformByViewport[c0, viewport];
c1 ¬ G3dMatrix.TransformByViewport[c1, viewport];
ic0 ¬ RndPair[c0];
IF markType # none THEN Mark[context, base, view, viewport,, markType];
}
ELSE {
c0 ¬ G3dMatrix.TransformByViewport[G3dMatrix.TransformD[base, view], viewport];
c1 ¬ G3dMatrix.TransformByViewport[G3dMatrix.TransformD[end, view], viewport];
ic0 ¬ RndPair[c0];
IF markType # none THEN Mark2d[context, ic0, markType, useCG6];
};
d ¬ [scale*(c1.x-c0.x), scale*(c1.y-c0.y)];
c1 ¬ [c0.x+d.x, c0.y+d.y];
IF absolute2dLen # 0.0 THEN c1 ¬ G2dVector.Add[
c0, G2dVector.SetVectorLength[G2dVector.Sub[c1, c0], ABS[absolute2dLen]]];
Arrow2d[context, ic0, RndPair[c1], drawType, useCG6];
IF label # NIL -- AND useCG6 = n -- THEN {
p: Pair ¬ IF d=[0,0] THEN c1 ELSE Vector2.Add[[c1.x,c1.y-4],Vector2.Mul[Vector2.Unit[d],6]];
Draw2d.Label[context, p, label];
};
};
Axes: PUBLIC PROC [
context: Context,
view: Matrix,
viewport: Viewport,
scale: REAL ¬ 1.0,
drawType: DrawType ¬ solid,
label: BOOL ¬ TRUE]
~ {
Vector[context,[],[1,0,0],view, viewport, IF label THEN "X" ELSE NIL, scale,,, drawType];
Vector[context,[],[0,1,0],view, viewport, IF label THEN "Y" ELSE NIL, scale,,, drawType];
Vector[context,[],[0,0,1],view, viewport, IF label THEN "Z" ELSE NIL, scale,,, drawType];
};
ConnectedPairs: PUBLIC PROC [
context: Context,
pairs: PairSequence,
closed: BOOL ¬ TRUE,
drawType: DrawType ¬ solid]
~ {
useCG6: UseCG6 ¬ GetUseCG6[context];
IF pairs # NIL AND pairs.length > 0 THEN {
p1: IntegerPair ¬ RndPair[pairs[0]];
FOR n: INT IN [1..pairs.length) DO
p0: IntegerPair ¬ p1;
p1 ¬ RndPair[pairs[n]];
Line2d[context, p0, p1, drawType, useCG6];
ENDLOOP;
IF closed THEN
Line2d[context, RndPair[pairs[pairs.length-1]], RndPair[pairs[0]], drawType, useCG6];
};
};
ConnectedPoints: PUBLIC PROC [
context: Context,
points: TripleSequence,
view: Matrix ¬ NIL,
viewport: Viewport,
closed: BOOL ¬ TRUE,
drawType: DrawType ¬ solid]
~ {
Get: PROC [n: INT] RETURNS [Triple] ~ {RETURN[points[n]]};
IF points # NIL AND points.length > 0 THEN
DrawWithConnectedPoints[context, points.length, Get, view, viewport, closed, drawType];
};
DrawWithConnectedPoints: PUBLIC PROC [
context: Context,
nPoints: INT,
getPoint: PROC [n: INT] RETURNS [Triple],
view: Matrix ¬ NIL,
viewport: Viewport,
closed: BOOL ¬ TRUE,
drawType: DrawType ¬ solid]
~ {
useCG6: UseCG6 ¬ GetUseCG6[context];
IF getPoint = NIL OR nPoints = 0 THEN RETURN;
IF view = NIL
THEN {
GetPair: PROC [t: Triple] RETURNS [i: IntegerPair] ~ INLINE {i ¬ RndPair[[t.x, t.y]]};
p0: IntegerPair ¬ GetPair[getPoint[IF closed THEN nPoints-1 ELSE 0]];
FOR n: INT IN [(IF closed THEN 0 ELSE 1)..nPoints) DO
p1: IntegerPair ¬ GetPair[getPoint[n]];
Line2d[context, p0, p1, drawType, useCG6];
p0 ¬ p1;
ENDLOOP;
}
ELSE {
hasPersp: BOOL ¬ G3dMatrix.HasPerspective[view];
s0: Screen ¬ G3dMatrix.GetScreen[
getPoint[IF closed THEN nPoints-1 ELSE 0], view, hasPersp, viewport];
FOR n: INT IN [(IF closed THEN 0 ELSE 1)..nPoints) DO
s1: Screen ¬ G3dMatrix.GetScreen[getPoint[n], view, hasPersp, viewport];
SELECT TRUE FROM
NOT s0.visible AND NOT s1.visible => NULL;
NOT hasPersp OR (s0.visible AND s1.visible) =>
Line2d[context, s0.intPos, s1.intPos, drawType, useCG6];
(s0.l AND s1.l)OR(s0.r AND s1.r)OR(s0.b AND s1.b)OR(s0.t AND s1.t)OR(s0.n AND s1.n) => NULL;
ENDCASE => {
ct: G3dClip.ClippedTriple ¬ G3dClip.FrustumH[s0.quad, s1.quad];
IF NOT ct.off THEN {
p0: Pair ¬ [viewport.scale.x*ct.c0.x+viewport.translate.x, viewport.scale.y*ct.c0.y+viewport.translate.y];
p1: Pair ¬ [viewport.scale.x*ct.c1.x+viewport.translate.x, viewport.scale.y*ct.c1.y+viewport.translate.y];
Line2d[context, RndPair[p0], RndPair[p1], drawType, useCG6];
};
};
s0 ¬ s1;
ENDLOOP;
};
};
Patch Drawing
Patch: PUBLIC PROC [
context: Context,
view: Matrix,
viewport: Viewport,
patch: G3dPatch.Patch,
nCurves, nSegments: INT] ~ {
FOR n: INT IN [0..nCurves) DO
t: REAL ¬ REAL[n]/REAL[nCurves-1];
Curve[context, G3dPatch.SCurve[patch, t], view, viewport, nSegments];
Curve[context, G3dPatch.TCurve[patch, t], view, viewport, nSegments];
ENDLOOP;
};
Curve Drawing
ConnectedCurve: PUBLIC PROC [
context: Context,
c: G3dCurve.Curve,
view: Matrix ¬ NIL,
viewport: Viewport ¬ [],
nSegments: INT ¬ 0,
dotted: BOOL ¬ FALSE,
forInterpress: BOOL ¬ FALSE]
~ {
IF c # NIL THEN
FOR n: INT IN [0..c.length) DO
CedarProcess.CheckAbort[];
IF dotted
THEN DotCurve[context, c[n].spline, view, viewport]
ELSE Curve[context, c[n].spline, view, viewport, 0, forInterpress];
ENDLOOP;
};
Curves: PUBLIC PROC [
context: Context,
curves: SplineSequence,
view: Matrix ¬ NIL,
viewport: Viewport,
dotted: BOOL ¬ FALSE,
forInterpress: BOOL ¬ FALSE]
~ {
IF curves # NIL THEN FOR n: INT IN [0..curves.length) DO
Process.CheckForAbort[];
IF dotted
THEN DotCurve[context, curves[n], view, viewport]
ELSE Curve[context, curves[n], view, viewport, 0, forInterpress];
ENDLOOP;
};
Curve: PUBLIC PROC [
context: Context,
curve: Spline,
view: Matrix ¬ NIL,
viewport: Viewport,
nSegments: INT ¬ 0,
forInterpress: BOOL ¬ FALSE]
~ {
VP: PROC [p: Pair] RETURNS [x: Pair] ~ INLINE {
x ¬ G3dMatrix.TransformByViewport[p, viewport];
};
IF curve # NIL THEN {
useCG6: UseCG6 ¬ GetUseCG6[context];
persp: BOOL ¬ G3dMatrix.HasPerspective[view];
xc: Spline ¬ IF view = NIL
THEN curve
ELSE G3dMatrix.Mul[curve, view, G3dMatrix.ObtainMatrix[]];
nSegs: INT ¬ IF nSegments # 0
THEN nSegments
ELSE IF forInterpress THEN 50 ELSE G3dSpline.Resolution[xc, 1.0/viewport.scale.y];
dif: Spline ¬ G3dSpline.ForwardDifference[xc, nSegs, G3dMatrix.ObtainMatrix[]];
nCoords: INT ¬ IF persp THEN 4 ELSE 2;
p0, p1: Pair ¬ [dif[0][0], dif[0][1]];
vp0, vp1: Pair ¬ VP[p0];
q0, q1: Quad ¬ [dif[0][0], dif[0][1], dif[0][2], dif[0][3]];
t: Imager.Trajectory ¬ NIL;
FOR i: INTEGER IN [0..nSegs) DO
IF forInterpress AND NOT persp THEN t ¬ ImagerPath.MoveTo[vp0];
IF persp
THEN q1 ¬ [q0.x+dif[1][0], q0.y+dif[1][1], q0.z+dif[1][2], q0.w+dif[1][3]]
ELSE vp1 ¬ VP[p1 ¬ [p0.x+dif[1][0], p0.y+dif[1][1]]];
FOR j: INTEGER IN [0..nCoords) DO
dif[1][j] ¬ dif[1][j]+dif[2][j];
dif[2][j] ¬ dif[2][j]+dif[3][j];
ENDLOOP;
IF persp
THEN {
clip: G3dClip.ClippedPair ¬ G3dClip.NearH[q0, q1];
q0 ¬ q1;
IF clip.off THEN LOOP;
IF forInterpress
THEN Imager.MaskVector[context, VP[clip.c0], VP[clip.c1]]
ELSE Line2d[context, RndPair[VP[clip.c0]], RndPair[VP[clip.c1]], solid, useCG6];
}
ELSE {
IF forInterpress
THEN t ¬ ImagerPath.LineTo[t, vp1]
ELSE Line2d[context, RndPair[vp0], RndPair[vp1], solid, useCG6];
p0 ¬ p1;
vp0 ¬ vp1;
};
ENDLOOP;
IF forInterpress AND NOT persp THEN {
Imager.SetStrokeJoint[context, round];
Imager.MaskStrokeTrajectory[context, t];
};
IF view # NIL THEN G3dMatrix.ReleaseMatrix[xc];
G3dMatrix.ReleaseMatrix[dif];
};
};
DotCurve: PUBLIC PROC [
context: Context,
curve: Spline,
view: Matrix ¬ NIL,
viewport: Viewport,
nSegments: INT ¬ 0]
~ {
useCG6: UseCG6 ¬ GetUseCG6[context];
IF curve # NIL THEN {
clipped: BOOL ¬ FALSE;
persp: BOOL ¬ G3dMatrix.HasPerspective[view];
xc: Spline ¬ IF view = NIL THEN curve ELSE G3dMatrix.Mul[curve, view];
nSegs: INTEGER ¬ IF nSegments # 0
THEN nSegments
ELSE 4*G3dSpline.Resolution[xc, 1.0/viewport.scale.y];
dif: Spline ¬ G3dSpline.ForwardDifference[xc, nSegs];
nCoords: INT ¬ IF persp THEN 4 ELSE 2;
p: Pair ¬ [dif[0][0], dif[0][1]];
q: Quad ¬ [dif[0][0], dif[0][1], dif[0][2], dif[0][3]];
FOR i: INTEGER IN [0..nSegs) DO
IF persp AND NOT (clipped ¬ q.w = 0.0 OR q.z+q.w < 0.0) THEN p ¬ [q.x/q.w, q.y/q.w];
IF NOT clipped THEN {
p ¬ G3dMatrix.TransformByViewport[p, viewport];
IF useCG6 = y
THEN ImagerCG6Context.PointCG6[context, Rnd[p.x], Rnd[p.y]]
ELSE Imager.MaskRectangle[context, [p.x, p.y, 1, 1]];
};
IF persp
THEN q ¬ [q.x+dif[1][0], q.y+dif[1][1], q.z+dif[1][2], q.w+dif[1][3]]
ELSE p ¬ [p.x+dif[1][0], p.y+dif[1][1]];
FOR j: INTEGER IN [0..nCoords) DO
dif[1][j] ¬ dif[1][j]+dif[2][j];
dif[2][j] ¬ dif[2][j]+dif[3][j];
ENDLOOP;
ENDLOOP;
};
};
BezierPolygon: PUBLIC PROC [
context: Context,
bezier: Bezier,
view: Matrix ¬ NIL,
viewport: Viewport,
drawType: DrawType ¬ solid,
close: BOOL ¬ FALSE]
~ {
Segment[context, bezier.b0, bezier.b1, view, viewport, drawType];
Segment[context, bezier.b1, bezier.b2, view, viewport, drawType];
Segment[context, bezier.b2, bezier.b3, view, viewport, drawType];
IF close THEN Segment[context, bezier.b0, bezier.b3, view, viewport, drawType];
};
Triangle Drawing
Triangle: PUBLIC PROC [
context: Context,
p0, p1, p2: Triple,
view: Matrix,
viewport: Viewport,
drawType: DrawType ¬ solid]
~ {
useCG6: UseCG6 ¬ GetUseCG6[context];
pp0: IntegerPair ¬ RndPairAndVP[G3dMatrix.TransformD[p0, view], viewport];
pp1: IntegerPair ¬ RndPairAndVP[G3dMatrix.TransformD[p1, view], viewport];
pp2: IntegerPair ¬ RndPairAndVP[G3dMatrix.TransformD[p2, view], viewport];
Line2d[context, pp0, pp1, drawType, useCG6];
Line2d[context, pp1, pp2, drawType, useCG6];
Line2d[context, pp2, pp0, drawType, useCG6];
};
FrontFacingTriangle: PUBLIC PROC [
context: Context,
p0, p1, p2: Triple,
view: Matrix,
inverseView: Matrix ¬ NIL,
viewport: Viewport,
normal: Triple ¬ origin,
drawType: DrawType ¬ solid]
~ {
nrm: Triple ¬ IF normal = origin THEN G3dPolygon.TriangleNormal[p0, p1, p2] ELSE normal;
forward: BOOL ¬ IF G3dMatrix.HasPerspective[view]
THEN G3dVector.FrontFacingWithPerspective[
nrm, p0, IF inverseView = NIL THEN G3dMatrix.Invert[view] ELSE inverseView]
ELSE G3dVector.FrontFacingNoPerspective[nrm, view];
IF forward THEN Triangle[context, p0, p1, p2, view, viewport, drawType];
};
FillTriangle: PUBLIC PROC [
context: Context,
triangle: REF TriangleInfo,
color: Triple,
view: Matrix,
viewport: Viewport]
~ {
Path: Imager.PathProc ~ {moveTo[[p1.x, p1.y]]; lineTo[[p2.x, p2.y]]; lineTo[[p3.x, p3.y]]};
p1: IntegerPair ¬ RndPairAndVP[G3dMatrix.TransformD[triangle.p1, view], viewport];
p2: IntegerPair ¬ RndPairAndVP[G3dMatrix.TransformD[triangle.p2, view], viewport];
p3: IntegerPair ¬ RndPairAndVP[G3dMatrix.TransformD[triangle.p3, view], viewport];
useCG6: UseCG6 ¬ GetUseCG6[context];
SetColor[context, color, useCG6];
IF useCG6 = y
THEN ImagerCG6Context.TriangleCG6[context, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y]
ELSE Imager.MaskFill[context, Path];
};
ShadeTriangle: PUBLIC PROC [
context: Context,
triangle: REF TriangleInfo,
lightVector: Triple,
view: Matrix,
viewport: Viewport,
frontColor, backColor: Triple]
~ {
dot: REAL ¬ G3dVector.Dot[triangle.normal, lightVector];
color: Triple ¬ G3dVector.Mul[IF dot < 0.0 THEN backColor ELSE frontColor, dot];
FillTriangle[context, triangle, color, view, viewport];
};
ShadeFrontFacingTriangle: PUBLIC PROC [
context: Context,
triangle: REF TriangleInfo,
lightVector: Triple,
view: Matrix,
inverseView: Matrix ¬ NIL,
viewport: Viewport,
color: Triple]
~ {
forward: BOOL ¬ IF G3dMatrix.HasPerspective[view]
THEN G3dVector.FrontFacingWithPerspective[
triangle.normal,
triangle.p1,
IF inverseView = NIL THEN G3dMatrix.Invert[view] ELSE inverseView]
ELSE G3dVector.FrontFacingNoPerspective[triangle.normal, view];
IF forward THEN ShadeTriangle[context, triangle, lightVector, view, viewport, color, color];
};
Polygon Drawing
Polygon: PUBLIC PROC [
context: Context,
poly: NatSequence,
pairs: PairSequence ¬ NIL,
triples: TripleSequence ¬ NIL,
view: Matrix ¬ NIL,
viewport: Viewport,
drawType: DrawType ¬ solid]
~ {
VP: PROC [p: Pair] RETURNS [x: Pair] ~ INLINE {
x ¬ G3dMatrix.TransformByViewport[p, viewport];
};
IF poly # NIL AND poly.length > 0
THEN {
IF pairs = NIL AND view # NIL AND triples # NIL THEN {
pairs ¬ NEW[PairSequenceRep[triples.length]];
FOR n: INT IN [0..poly.length) DO
i: INT ~ poly[n];
pairs[i] ¬ VP[G3dMatrix.TransformD[triples[i], view]];
ENDLOOP;
};
IF pairs # NIL THEN {
ip: IntegerPairSequence ← G3dNats.ObtainScratchIntegerPairs[triples.length];
useCG6: UseCG6 ¬ GetUseCG6[context];
i0: INT ¬ 0;
FOR i: INT IN [0..triples.length) DO ip[i] ¬ RndPair[pairs[i]]; ENDLOOP;
FOR i1: INT IN [1..poly.length) DO
Line2d[context, ip[poly[i0]], ip[poly[i1]], drawType, useCG6];
i0 ¬ i1;
ENDLOOP;
Line2d[context, ip[poly[poly.length-1]], ip[poly[0]], drawType, useCG6];
G3dNats.ReleaseScratchIntegerPairs[ip];
};
}
ELSE
IF triples # NIL
THEN ConnectedPoints[context, triples, view, viewport, TRUE, drawType]
ELSE ConnectedPairs[context, pairs, TRUE, drawType];
};
FrontFacingPolygon: PUBLIC PROC [
context: Context,
poly: NatSequence,
view: Matrix,
inverseView: Matrix ¬ NIL,
viewport: Viewport,
normal: Triple ¬ origin,
pairs: PairSequence ¬ NIL,
triples: TripleSequence ¬ NIL,
drawType: DrawType ¬ solid]
~ {
forward: BOOL;
IF normal = origin AND triples = NIL THEN RETURN;
IF normal = origin THEN {
vertices: TripleSequence ¬ NEW[TripleSequenceRep[poly.length]];
FOR n: INT IN [0..poly.length) DO vertices[n] ¬ triples[poly[n]]; ENDLOOP;
normal ¬ G3dPolygon.PolygonNormal[vertices];
};
forward ¬ IF G3dMatrix.HasPerspective[view]
THEN G3dVector.FrontFacingWithPerspective[
normal,
triples[poly[0]],
IF inverseView = NIL THEN G3dMatrix.Invert[view] ELSE inverseView]
ELSE G3dVector.FrontFacingNoPerspective[normal, view];
IF forward THEN Polygon[context, poly, pairs, triples, view, viewport, drawType];
};
Polygons: PUBLIC PROC [
context: Context,
pairs: PairSequence ¬ NIL,
triples: TripleSequence ¬ NIL,
polygons: SurfaceSequence,
view: Matrix ¬ NIL,
viewport: Viewport,
drawType: DrawType ¬ solid]
~ {
VP: PROC [p: Pair] RETURNS [x: Pair] ~ INLINE {
x ¬ G3dMatrix.TransformByViewport[p, viewport];
};
IF pairs = NIL AND view # NIL AND triples # NIL THEN {
pairs ¬ NEW[PairSequenceRep[triples.length]];
FOR n: INT IN [0..triples.length) DO
pairs[n] ¬ VP[G3dMatrix.TransformD[triples[n], view]];
ENDLOOP;
};
IF polygons # NIL AND pairs # NIL THEN {
ip: IntegerPairSequence ¬ G3dNats.ObtainScratchIntegerPairs[triples.length];
useCG6: UseCG6 ¬ GetUseCG6[context];
FOR i: INT IN [0..triples.length) DO ip[i] ¬ RndPair[pairs[i]]; ENDLOOP;
FOR n: INT IN [0..polygons.length) DO
poly: NatSequence ~ polygons[n].vertices;
stop: CARDINAL ~ poly.length-1;
Process.CheckForAbort[];
IF poly = NIL OR poly.length < 1 THEN LOOP;
FOR i: INT IN[0..stop) DO
Line2d[context, ip[poly[i]], ip[poly[i+1]], drawType, useCG6];
ENDLOOP;
Line2d[context, ip[poly[stop]], ip[poly[0]], drawType, useCG6];
ENDLOOP;
G3dNats.ReleaseScratchIntegerPairs[ip];
};
};
FrontFacingPolygons: PUBLIC PROC [
context: Context,
pairs: PairSequence ¬ NIL,
triples: TripleSequence ¬ NIL,
polygons: SurfaceSequence,
view: Matrix,
inverseView: Matrix ¬ NIL,
viewport: Viewport,
normals: TripleSequence,
drawType: DrawType ¬ solid]
~ {
IF polygons # NIL AND view # NIL AND normals # NIL AND (pairs # NIL OR triples # NIL)
THEN {
VP: PROC [p: Pair] RETURNS [x: Pair] ~ INLINE {
x ¬ G3dMatrix.TransformByViewport[p, viewport];
};
ip: IntegerPairSequence;
cg6: UseCG6 ¬ GetUseCG6[context];
inv: Matrix ¬ IF inverseView # NIL THEN inverseView ELSE G3dMatrix.Invert[view];
q: Quad ¬ [inv[3][0], inv[3][1], inv[3][2], inv[3][3]];
camera: Triple ¬ IF q.w = 1.0 THEN [q.x, q.y, q.z] ELSE [q.x/q.w, q.y/q.w, q.z/q.w];
IF pairs = NIL THEN {
pairs ¬ NEW[PairSequenceRep[triples.length]];
FOR n: INT IN [0..triples.length) DO
pairs[n] ¬ VP[G3dMatrix.TransformD[triples[n], view]];
ENDLOOP;
};
FOR i: INT IN [0..triples.length) DO ip[i] ¬ RndPair[pairs[i]]; ENDLOOP;
FOR n: INT IN [0..polygons.length) DO
poly: NatSequence ¬ polygons[n].vertices;
Process.CheckForAbort[];
IF poly # NIL AND poly.length > 2 AND
G3dVector.Dot[G3dVector.Sub[camera, triples[poly[0]]], normals[n]] >= 0.0 THEN
FOR i: INT IN [0..poly.length) DO
Line2d[context, ip[poly[i]], ip[poly[(i+1) MOD poly.length]], drawType, cg6];
ENDLOOP;
ENDLOOP;
};
};
Miscellaneous Drawing
ScreenRadiusFromSphere: PUBLIC PROC [
sphere: G3dBasic.Sphere,
view: Matrix,
viewInverse: Matrix ¬ NIL,
viewport: Viewport ¬ []]
RETURNS [s: REAL]
~ {
x: Matrix ¬ IF viewInverse # NIL
THEN viewInverse ELSE G3dMatrix.Invert[G3dMatrix.ObtainMatrix[]];
eyePt: Triple ¬ G3dMatrix.Transform[[0.0, 0.0, 0.0], x];
v: Triple ¬ G3dVector.Unit[G3dVector.Ortho[G3dVector.Sub[sphere.center, eyePt]]];
pp: Triple ¬ G3dVector.Add[sphere.center, G3dVector.Mul[v, sphere.radius]];
s ¬ G2dVector.Distance[
G3dMatrix.TransformD[sphere.center, view], G3dMatrix.TransformD[pp, view]];
IF viewInverse = NIL THEN G3dMatrix.ReleaseMatrix[x];
IF viewport # [] THEN s ¬ 0.5*viewport.scale.x;
};
Plane: PUBLIC PROC [
context: Context,
plane: G3dPlane.Plane,
view: Matrix,
viewport: Viewport,
size: REAL ¬ 0.1,
drawType: DrawType ¬ solid]
~ {
VP: PROC [p: Pair] RETURNS [x: IntegerPair] ~ INLINE {x ¬ RndPairAndVP[p, viewport]};
z: Triple ¬ [plane.x, plane.y, plane.z];
useCG6: UseCG6 ¬ GetUseCG6[context];
IF NOT plane.normalized THEN z ¬ G3dVector.Unit[z];
IF z # origin THEN {
a: ARRAY [0..4) OF IntegerPair;
q: ARRAY [0..4) OF Pair ¬ [[-size, size], [size, size], [size, -size], [-size, -size]];
crosser: Triple ¬ IF z # [0.0, 1.0, 0.0] THEN [0.0, 1.0, 0.0] ELSE [1.0, 0.0, 0.0];
x: Triple ¬ G3dVector.Unit[G3dVector.Cross[z, crosser]];
y: Triple ¬ G3dVector.Unit[G3dVector.Cross[z, x]];
o: Triple ¬ G3dPlane.CenterOfPlane[plane];
m: Matrix ¬ G3dMatrix.ObtainMatrix[];
m ¬ G3dMatrix.MakeFromTriad[x, y, z, o, FALSE, m];
m ¬ G3dMatrix.Mul[m, view];
FOR i: INT IN [0..4) DO a[i] ¬ VP[G3dMatrix.TransformPairD[q[i], m]]; ENDLOOP;
FOR i: INT IN [0..4) DO Line2d[context, a[i], a[(i+1) MOD 4], drawType, useCG6]; ENDLOOP;
Vector[context, o, z, view, viewport];
G3dMatrix.ReleaseMatrix[m];
};
};
Circle: PUBLIC PROC [
context: Context,
p0, p1, p2: Triple,
nVertices: INT,
view: Matrix,
viewport: Viewport,
drawType: DrawType]
~ {
VP: PROC [p: Pair] RETURNS [x: Pair] ~ INLINE {
x ¬ G3dMatrix.TransformByViewport[p, viewport];
};
plane: G3dPlane.Plane ¬ G3dPlane.FromThreePoints[p0, p1, p2];
n: Triple ¬ [plane.x, plane.y, plane.z];
v0: Triple ¬ G3dVector.Unit[G3dVector.Cross[G3dVector.Sub[p0, p1], n]];
v1: Triple ¬ G3dVector.Unit[G3dVector.Cross[G3dVector.Sub[p1, p2], n]];
dot: REAL ¬ G3dVector.Dot[v0, v1];
IF dot # 1.0 THEN {
m0: Triple ¬ G3dVector.Midpoint[p0, p1];
m1: Triple ¬ G3dVector.Midpoint[p1, p2];
dm: Triple ¬ G3dVector.Sub[m1, m0];
t: REAL ¬ (dot*G3dVector.Dot[v0, dm]-G3dVector.Dot[v1, dm])/(1.-dot*dot);
center: Triple ¬ G3dVector.ScaleRay[[m1, v1], t];
radius: REAL ¬ G3dVector.Distance[center, p0];
x: Triple ¬ G3dVector.Sub[center, p0];
y: Triple ¬ G3dVector.Mul[G3dVector.Unit[G3dVector.Cross[x, n]], radius];
PI: REAL ~ 3.1415926535;
dAngle: REAL ¬ 2.0*PI/nVertices;
angle: REAL ¬ dAngle;
pp0: Triple ¬ G3dVector.Add[center, x];
FOR n: INT IN [0..nVertices) DO
cos: REAL ¬ RealFns.Cos[angle];
sin: REAL ¬ RealFns.SqRt[1.0-cos*cos];
pp1: Triple ¬ pp0;
IF angle > PI THEN sin ¬ -sin;
pp0 ¬ [center.x+cos*x.x+sin*y.x,center.y+cos*x.y+sin*y.y,center.z+cos*x.z+sin*y.z];
Segment[context, pp0, pp1, view, viewport, drawType];
angle ¬ angle+dAngle;
ENDLOOP;
};
};
Cube: PUBLIC PROC [
context: Context,
center: Triple,
size: REAL,
view: Matrix,
viewport: Viewport,
drawType: DrawType ¬ solid]
~ {
VP: PROC [p: Pair] RETURNS [x: IntegerPair] ~ INLINE {x ¬ RndPairAndVP[p, viewport]};
rad: REAL ¬ 0.5*size;
l: REAL ¬ center.x-rad;
r: REAL ¬ center.x+rad;
t: REAL ¬ center.y-rad;
b: REAL ¬ center.y+rad;
n: REAL ¬ center.z-rad;
f: REAL ¬ center.z+rad;
lbn: IntegerPair ¬ VP[G3dMatrix.TransformD[[l, b, n], view]];
lbf: IntegerPair ¬ VP[G3dMatrix.TransformD[[l, b, f], view]];
ltn: IntegerPair ¬ VP[G3dMatrix.TransformD[[l, t, n], view]];
ltf: IntegerPair ¬ VP[G3dMatrix.TransformD[[l, t, f], view]];
rbn: IntegerPair ¬ VP[G3dMatrix.TransformD[[r, b, n], view]];
rbf: IntegerPair ¬ VP[G3dMatrix.TransformD[[r, b, f], view]];
rtn: IntegerPair ¬ VP[G3dMatrix.TransformD[[r, t, n], view]];
rtf: IntegerPair ¬ VP[G3dMatrix.TransformD[[r, t, f], view]];
useCG6: UseCG6 ¬ GetUseCG6[context];
Line2d[context, lbn, lbf, drawType, useCG6]; -- lb edge
Line2d[context, ltn,  ltf, drawType, useCG6]; -- lt edge
Line2d[context, lbn, ltn, drawType, useCG6]; -- ln edge
Line2d[context, lbf,  ltf, drawType, useCG6]; -- lf edge
Line2d[context, lbn, rbn, drawType, useCG6]; -- bn edge
Line2d[context, ltn,  rtn, drawType, useCG6]; -- tn edge
Line2d[context, lbf,  rbf, drawType, useCG6]; -- bf edge
Line2d[context, ltf,  rtf, drawType, useCG6]; -- tf edge
Line2d[context, rbn, rtn, drawType, useCG6]; -- rn edge
Line2d[context, rbf,  rtf, drawType, useCG6]; -- rf edge
Line2d[context, rbn, rbf, drawType, useCG6]; -- rb edge
Line2d[context, rtn,  rtf, drawType, useCG6]; -- rt edge
};
DoGetSpherePoints: PROC [center: Triple, radius: REAL, nCircles: INT, points: TripleSequence] ~ {
delta: REAL ¬ 2.0/REAL[nCircles-1];
points.length ¬ 2+nCircles*(nCircles-2);
points[0] ¬ [center.x, center.y, center.z-radius];
points[points.length-1] ¬ [center.x, center.y, center.z+radius];
FOR i: INT IN [0..(nCircles/2)-1) DO
offset: REAL ¬ REAL[i+1]*delta;
offsetZ: REAL ¬ radius*offset;
z1: REAL ¬ center.z-radius+offsetZ;
z2: REAL ¬ center.z+radius-offsetZ;
nOffset: INT ¬ i*nCircles;
index1: INT ¬ 1+nOffset;
index2: INT ¬ points.length-1-nCircles-nOffset;
sin: REAL ¬ 1.0-offset;
s: REAL ¬ radius*RealFns.SqRt[1.0-sin*sin];
FOR j: INT IN [0..nCircles) DO
points[index1+j] ¬ [s, 0.0, z1];
points[index2+j] ¬ [s, 0.0, z2];
ENDLOOP;
ENDLOOP;
FOR i: INT IN [0..nCircles-2) DO
index: INT ¬ 1+i*nCircles;
middle: BOOL ¬ (nCircles MOD 2) = 1 AND i = (nCircles/2)-1;
s: REAL ¬ IF middle THEN radius ELSE points[index].x;
z: REAL ¬ IF middle THEN center.z ELSE points[index].z;
FOR j: INT IN [0..nCircles) DO
aa: REAL ¬ REAL[j]*360.0/REAL[nCircles];
x: REAL ¬ center.x+s*RealFns.CosDeg[aa];
y: REAL ¬ center.y+s*RealFns.SinDeg[aa];
points[index+j] ¬ [x, y, z];
ENDLOOP;
ENDLOOP;
};
Sphere: PUBLIC PROC [
context: Context,
center: Triple,
radius: REAL,
nCircles: INT,
view: Matrix,
viewport: Viewport,
points: TripleSequence ¬ NIL,
drawType: DrawType ¬ solid]
~ {
pts: TripleSequence ¬ IF points # NIL THEN points ELSE ObtainTriples[2+nCircles*(nCircles-2)];
IF points = NIL THEN DoGetSpherePoints[center, radius, nCircles, pts];
FOR i: INT IN [0..nCircles-2) DO
index: INT ¬ 1+i*nCircles;
p0: Triple ¬ pts[index+nCircles-1];
FOR j: INT IN [0..nCircles) DO
p1: Triple ¬ pts[index+j];
Segment[context, p0, p1, view, viewport, drawType];
p0 ¬ p1;
ENDLOOP;
ENDLOOP;
FOR i: INT IN [0..nCircles) DO
p0: Triple ¬ pts[0];
FOR j: INT IN [0..nCircles-2) DO
p1: Triple ¬ pts[1+nCircles*j+i];
Segment[context, p0, p1, view, viewport, drawType];
p0 ¬ p1;
ENDLOOP;
Segment[context, p0, pts[pts.length-1], view, viewport, drawType];
ENDLOOP;
IF points = NIL THEN ReleaseTriples[pts];
};
nScratchTriples: INT ~ 6;
scratchTriples: ARRAY [0..nScratchTriples) OF TripleSequence ¬ ALL[NIL];
ObtainTriples: ENTRY PROC [nTriples: CARD] RETURNS [TripleSequence] ~ {
FOR i: INT IN [0..nScratchTriples) DO
triples: TripleSequence ~ scratchTriples[i];
IF triples = NIL OR triples.maxLength <= nTriples THEN LOOP;
scratchTriples[i] ¬ NIL;
RETURN[triples];
ENDLOOP;
RETURN[NEW[TripleSequenceRep[nTriples]]];
};
ReleaseTriples: ENTRY PROC [triples: TripleSequence] ~ {
FOR i: INT IN [0..nScratchTriples) DO
IF scratchTriples[i] # NIL THEN LOOP;
scratchTriples[i] ¬ triples;
RETURN;
ENDLOOP;
};
GetSpherePoints: PUBLIC PROC [
center: Triple,
radius: REAL,
nCircles: INT]
RETURNS [points: TripleSequence]
~ {
DoGetSpherePoints[center, radius, nCircles, NEW[TripleSequenceRep[2+nCircles*(nCircles-2)]]];
};
Labelling
Label: PUBLIC PROC [
context: Context,
point: Triple,
rope: ROPE,
view: Matrix,
viewport: Viewport,
offset: Pair ¬ [6, -8]]
~ {
p: Pair ¬ G3dView.TransformAndClipInZ[point, view, viewport].pair;
Draw2d.Label[context, [p.x+offset.x, p.y+offset.y], rope];
};
LabelPairs: PUBLIC PROC [context: Context, pairs: PairSequence] ~ {
IF GetUseCG6[context] # y AND pairs # NIL THEN
FOR n: INT IN[0..pairs.length) DO
Draw2d.Label[context, pairs[n], IO.PutFR1["%g", IO.int[n]]];
ENDLOOP;
};
Pendant: PUBLIC PROC [
context: Context,
view: Matrix,
size: REAL ¬ .07,
wPos, hPos: REAL ¬ .7,
names: ARRAY [0..6) OF ROPE ¬ ALL[NIL]]
~ {
mx: REAL ¬ 0.0;
v: ARRAY [0..6) OF V;
V: TYPE ~ RECORD[rope: ROPE, vec: Triple];
axes: ARRAY [0..6) OF Triple ~ [[1, 0, 0], [0, 1, 0], [0, 0, 1], [-1, 0, 0], [0, -1, 0], [0, 0, -1]];
c: Pair;
rect: Imager.Rectangle ¬ [0.0, 0.0, 600.0, 400.0];
IF GetUseCG6[context] = y THEN RETURN; -- can't call Draw2d.Label (actually, maybe can)
rect ¬ ImagerBackdoor.GetBounds[context ! Imager.Error => CONTINUE];
c ¬ [rect.w*(1+wPos)/2, rect.h*(1+hPos)/2];
IF names = ALL[NIL] THEN names ¬ ["x", "y", "z", , , ];
FOR n: INT IN [0..6) DO
v[n] ¬ [names[n], G3dMatrix.TransformVec[axes[n], view]];
ENDLOOP;
FOR n: INT IN [0..6) DO mx ¬ MAX[mx, Vector2.Square[[v[n].vec.x, v[n].vec.y]]]; ENDLOOP;
mx ¬ MAX[rect.w, rect.h]*size/RealFns.SqRt[mx];
FOR n: INT IN [0..6) DO v[n].vec ¬ G3dVector.Mul[v[n].vec, mx]; ENDLOOP;
DO -- order v by increasing z:
d: BOOL ¬ FALSE;
FOR n: INT IN [0..5) DO
IF v[n].vec.z<v[n+1].vec.z THEN {t: V ¬ v[n]; v[n] ¬ v[n+1]; v[n+1] ¬ t; d ¬ TRUE};
ENDLOOP;
IF NOT d THEN EXIT;
ENDLOOP;
FOR n: INT IN [0..6) DO
IF v[n].rope = NIL THEN LOOP;
Imager.SetColor[context, Imager.white];
Imager.MaskRectangle[context, [c.x+v[n].vec.x, c.y+v[n].vec.y-4, 8, 8]];
Imager.SetColor[context, Imager.black];
Draw2d.Label[context, [c.x+v[n].vec.x, c.y+v[n].vec.y-4], v[n].rope];
ENDLOOP;
FOR n: INT IN [0..6) DO
IF v[n].rope # NIL THEN {
a: Pair ¬ IF v[n].vec.x = 0 AND v[n].vec.y = 0
THEN [0, 0] ELSE Vector2.Mul[Vector2.Unit[[v[n].vec.x, v[n].vec.y]], 6.0];
Arrow2d[context, RndPair[c], RndPair[[c.x+v[n].vec.x-a.x, c.y+v[n].vec.y-a.y]], solid, n];
};
ENDLOOP;
};
Asterisk3d: PUBLIC PROC [
context: Context,
p: Triple,
size: REAL,
view: Matrix,
viewport: Viewport,
drawType: DrawType ¬ solid]
~ {
Segment[context, [p.x-size, p.y, p.z], [p.x+size, p.y, p.z], view, viewport, drawType];
Segment[context, [p.x, p.y-size, p.z], [p.x, p.y+size, p.z], view, viewport, drawType];
Segment[context, [p.x, p.y, p.z-size], [p.x, p.y, p.z+size], view, viewport, drawType];
};
END.
..
To be used sometime after Cedar7.0:
TransformPointToDisplay: PUBLIC PROC [context3d: Context3d, point: Triple]
RETURNS [screenPoint: Triple] ~ {
xpoint: Triple;
[xpoint, ] ← ThreeDScenes.XfmPtToEyeSpace[context3d, point];
screenPoint ← ThreeDScenes.XfmPtToDisplay[context3d, NIL, xpoint];
};
PerspPP: PUBLIC PROC [p0, p1: Triple, context: Context, focalLength, tanHalfFov: REAL] ~ {
Draw segment p0p1 with perspective determined by focalLength and the tangent of 1/2 the field of view.
ClipZ: PROC [p0, p1: Triple, zLim: REAL] RETURNS [c0, c1: Triple, off: BOOL] ~ {
dx, dy, dz: REAL;
a0: REAL ← 0.0;
a1: REAL ← 1.0;
IF p0.z < zLim THEN {
IF p1.z < zLim THEN RETURN[c0, c1, TRUE];
a0 ← p0.z/(p0.z-p1.z);
}
ELSE IF p1.z < zLim THEN a1 ← p0.z/(p0.z-p1.z);
IF a0 > a1 THEN RETURN[c0, c1, TRUE];
dx ← p1.x-p0.x;
dy ← p1.y-p0.y;
dz ← p1.z-p0.z;
RETURN[[p0.x+a0*dx,p0.y+a0*dy,p0.z+a0*dz], [p0.x+a1*dx,p0.y+a1*dy,p0.z+a1*dz], FALSE];
};
off: BOOL ← FALSE;
c0, c1: Triple;
[c0, c1, off] ← ClipZ[p0, p1, -focalLength+0.01];
IF NOT off THEN {
f0: REAL ← 1.0/(1.0+c0.z*tanHalfFov);
f1: REAL ← 1.0/(1.0+c1.z*tanHalfFov);
Draw2d.Solid[context, [f0*c0.x, f0*c0.y], [f1*c1.x, f1*c1.y]];
};
};