G3dPlaneImpl.mesa
Copyright Ó 1984, 1992 by Xerox Corporation. All rights reserved.
Bloomenthal, October 9, 1992 10:52 am PDT
Andrew Glassner February 19, 1991 4:06 pm PST
Ken Fishkin, November 6, 1991 1:32 pm PST
DIRECTORY G3dBasic, G3dMatrix, G3dPlane, G3dPolygon, G3dVector, Real, RealFns, Rope;
G3dPlaneImpl:
CEDAR
PROGRAM
IMPORTS G3dMatrix, G3dPolygon, G3dVector, Real, RealFns
EXPORTS G3dPlane
~
BEGIN
Error:
PUBLIC
ERROR [reason:
ROPE] ~
CODE;
Circle: TYPE ~ G3dBasic.Circle;
NatSequence: TYPE ~ G3dBasic.NatSequence;
Pair: TYPE ~ G3dBasic.Pair;
PairSequence: TYPE ~ G3dBasic.PairSequence;
PairSequenceRep: TYPE ~ G3dBasic.PairSequenceRep;
Quad: TYPE ~ G3dBasic.Quad;
Sign: TYPE ~ G3dBasic.Sign;
Triple: TYPE ~ G3dBasic.Triple;
TripleSequence: TYPE ~ G3dBasic.TripleSequence;
Matrix: TYPE ~ G3dMatrix.Matrix;
MajorPlane: TYPE ~ G3dPlane.MajorPlane;
Plane: TYPE ~ G3dPlane.Plane;
PlaneSequence: TYPE ~ G3dPlane.PlaneSequence;
PlaneSequenceRep: TYPE ~ G3dPlane.PlaneSequenceRep;
Ray: TYPE ~ G3dPlane.Ray;
ROPE: TYPE ~ Rope.ROPE;
Plane Creations
FromPolygon:
PUBLIC
PROC [
points: TripleSequence,
polygon: NatSequence ¬ NIL]
RETURNS [p: Plane]
~ {
normal: Triple ~ G3dPolygon.PolygonNormal[points, polygon, TRUE];
p ¬ [normal.x, normal.y, normal.z, DFromNormalAndPoints[normal, points, polygon], TRUE];
};
FromThreePoints:
PUBLIC
PROC [p1, p2, p3: Triple, unitize:
BOOL ¬
FALSE]
RETURNS [plane: Plane]
~ {
normal: Triple ¬ G3dVector.Cross[G3dVector.Sub[p2, p1], G3dVector.Sub[p3, p1]];
IF G3dVector.Null[normal] THEN ERROR Error["points colinear"];
IF G3dVector.Null[normal] THEN normal ¬ G3dVector.Ortho[G3dVector.Sub[p2, p1]];
plane ¬ FromPointAndNormal[p1, normal, unitize];
plane ¬ [normal.x, normal.y, normal.z,
-(p1.x*(p2.y*p3.z-p3.y*p2.z)-p1.y*(p2.x*p3.z-p3.x*p2.z)+p1.z*(p2.x*p3.y-p3.x*p2.y))];
IF unitize THEN plane ¬ Unit[plane];
};
FromThreePointsAndBehind:
PUBLIC
PROC [
p1, p2, p3, behind: Triple,
unitize: BOOL ¬ FALSE]
RETURNS [plane: Plane]
~ {
plane ¬ FromThreePoints[p1,p2, p3, unitize];
IF DistanceToPoint[behind, plane] > 0.0 THEN plane ¬ Negate[plane];
};
FromPointAndNormal:
PUBLIC
PROC
[point, normal: Triple, unitize:
BOOL ¬
FALSE]
RETURNS [plane: Plane]
~ {
plane ¬ [normal.x, normal.y, normal.z, -normal.x*point.x-normal.y*point.y-normal.z*point.z];
IF unitize THEN plane ¬ Unit[plane];
};
DFromNormalAndPoints:
PUBLIC
PROC [
normal: Triple,
points: TripleSequence,
polygon: NatSequence ¬ NIL]
RETURNS [REAL]
~ {
nPoints: NAT ¬ IF polygon = NIL THEN points.length ELSE polygon.length;
GetPointN:
PROC [n:
NAT]
RETURNS [Triple] ~
INLINE {
RETURN[IF polygon = NIL THEN points[n] ELSE points[polygon[n]]];
};
min: REAL ¬ 100000.0;
max: REAL ¬ -100000.0;
FOR n:
NAT
IN [0..nPoints)
DO
distance: REAL ¬ G3dVector.Dot[GetPointN[n], normal];
max ¬ MAX[max, distance];
min ¬ MIN[min, distance];
ENDLOOP;
RETURN[-0.5*(min+max)];
};
NormalFromPlane:
PUBLIC
PROC [plane: Plane, unit:
BOOL ¬
FALSE]
RETURNS [Triple] ~ {
t: Triple ¬ [plane.x, plane.y, plane.z];
RETURN[IF unit AND NOT plane.normalized THEN G3dVector.Unit[t] ELSE t];
};
Intersections
IntersectionOf3:
PUBLIC
PROC [p1, p2, p3: Plane]
RETURNS [Triple] ~ {
Intersection is the determinant of a matrix formed from the three planes.
w: REAL ¬ p1.y*(p2.x*p3.z-p3.x*p2.z)-p1.x*(p2.y*p3.z-p3.y*p2.z)-p1.z*(p2.x*p3.y-p3.x*p2.y);
IF w = 0.0 THEN ERROR Error["no intersection"];
RETURN[[
(p1.y*(p2.z*p3.w-p3.z*p2.w)-p1.z*(p2.y*p3.w-p3.y*p2.w)+p1.w*(p2.y*p3.z-p3.y*p2.z))/w,
-(p1.x*(p2.z*p3.w-p3.z*p2.w)-p1.z*(p2.x*p3.w-p3.x*p2.w)+p1.w*(p2.x*p3.z-p3.x*p2.z))/w,
(p1.x*(p2.y*p3.w-p3.y*p2.w)-p1.y*(p2.x*p3.w-p3.x*p2.w)+p1.w*(p2.x*p3.y-p3.x*p2.y))/w]];
};
IntersectionOf2:
PUBLIC
PROC [plane1, plane2: Plane]
RETURNS [line: Ray] ~ {
line.axis ¬ G3dVector.Cross[[plane1.x, plane1.y, plane1.z], [plane2.x, plane2.y, plane2.z]];
IF G3dVector.Null[line.axis] THEN ERROR Error["no intersection"];
line.base ¬ IntersectionOf3[plane1, plane2, [line.axis.x, line.axis.y, line.axis.z, 0.0]];
};
IntersectWithLine:
PUBLIC
PROC
[plane: Plane, line: Ray]
RETURNS [Triple] ~ {
alpha: REAL;
pdDot: REAL ¬ G3dVector.Dot[[plane.x, plane.y, plane.z], line.axis];
IF pdDot = 0.0 THEN ERROR Error["no intersection"];
alpha ¬ (-plane.w - G3dVector.Dot[[plane.x, plane.y, plane.z], line.base])/pdDot;
RETURN[G3dVector.ScaleRay[line, alpha]];
};
Projections
ProjectPointToPlane:
PUBLIC
PROC [point: Triple, plane: Plane]
RETURNS [Triple] ~ {
Assumes the plane normal is unit length.
normal: Triple ~ [plane.x, plane.y, plane.z];
distance: REAL ~ G3dVector.Dot[point, normal]+plane.w;
RETURN[G3dVector.Sub[point, G3dVector.Mul[normal, distance]]];
};
ProjectVectorToPlane:
PUBLIC
PROC [v: Triple, plane: Plane]
RETURNS [Triple] ~ {
vv: Triple ¬ G3dVector.Project[v, [plane.x, plane.y, plane.z]];
RETURN[[v.x-vv.x, v.y-vv.y, v.z-vv.z]];
};
ProjectPointToMajorPlane:
PUBLIC
PROC [p: Triple, majorPlane: MajorPlane]
RETURNS [Pair]
~ {
Note: Ned Greene claims to should be called "principal" planes.
Jules Bloomenthal notes:
RETURN[
SELECT majorPlane
FROM
xy => [p.x, p.y],
yz => [p.y, p.z],
ENDCASE => [p.z, p.x]]; -- was [p.x, p.z]
};
ProjectPointsToXYPlane:
PUBLIC
PROC [points: TripleSequence]
RETURNS [pairs: PairSequence]
~ {
pairs ¬ NEW[PairSequenceRep[points.length]];
pairs.length ¬ points.length;
FOR n:
NAT
IN [0..points.length)
DO
pairs[n] ¬ [points[n].x, points[n].y];
ENDLOOP;
};
ProjectPointsToXZPlane:
PUBLIC
PROC [points: TripleSequence]
RETURNS [pairs: PairSequence]
~ {
pairs ¬ NEW[PairSequenceRep[points.length]];
pairs.length ¬ points.length;
FOR n:
NAT
IN [0..points.length)
DO
pairs[n] ¬ [points[n].x, points[n].z];
ENDLOOP;
};
ProjectPointsToYZPlane:
PUBLIC
PROC [points: TripleSequence]
RETURNS [pairs: PairSequence]
~ {
pairs ¬ NEW[PairSequenceRep[points.length]];
pairs.length ¬ points.length;
FOR n:
NAT
IN [0..points.length)
DO
pairs[n] ¬ [points[n].y, points[n].z];
ENDLOOP;
};
ProjectPointsToMajorPlane:
PUBLIC
PROC [points: TripleSequence, majorPlane: MajorPlane]
RETURNS [pairs: PairSequence]
~ {
pairs ¬
SELECT majorPlane
FROM
xy => ProjectPointsToXYPlane[points],
xz => ProjectPointsToXZPlane[points],
ENDCASE => ProjectPointsToYZPlane[points];
};
Miscellany
Side:
PUBLIC
PROC [point: Triple, plane: Plane]
RETURNS [Sign] ~ {
s: REAL ¬ point.x*plane.x+point.y*plane.y+point.z*plane.z+plane.w;
RETURN[IF s > 0.0 THEN positive ELSE negative];
};
Negate:
PUBLIC
PROC [plane: Plane]
RETURNS [Plane] ~ {
RETURN[[-plane.x, -plane.y, -plane.z, -plane.w]];
};
AlignWithXYPlane:
PUBLIC
PROC [plane: Plane, out: Matrix ¬
NIL]
RETURNS [Matrix] ~ {
v: Triple ¬ [plane.x, plane.y, plane.z];
sqLen: REAL ¬ G3dVector.Square[v];
normal: Triple ¬ IF sqLen = 1.0 THEN v ELSE G3dVector.Div[v, RealFns.SqRt[sqLen]];
axis: Triple ¬ G3dVector.Cross[normal, G3dBasic.zAxis];
degrees: REAL ¬ G3dVector.AngleBetween[normal, G3dBasic.zAxis];
center: Triple ¬ CenterOfPlane[plane];
out ¬ G3dMatrix.MakeRotate[axis, degrees, TRUE, out];
out ¬ G3dMatrix.Translate[out, G3dVector.Negate[center], out];
RETURN[out];
};
ClosestPair:
PUBLIC
PROC [pairs: PairSequence, pair: Pair]
RETURNS [index:
NAT] ~ {
min: REAL ¬ 1000000.0;
IF pairs = NIL THEN RETURN[0];
FOR n:
NAT
IN [0..pairs.length)
DO
p: Pair ~ pairs[n];
dx: REAL ~ p.x-pair.x;
dy: REAL ~ p.y-pair.y;
d: REAL ~ dx*dx+dy*dy;
IF d < min THEN {index ¬ n; min ¬ d};
ENDLOOP;
};
GetMajorPlane:
PUBLIC
PROC [plane: Plane]
RETURNS [MajorPlane] ~ {
x: REAL ~ ABS[plane.x];
y: REAL ~ ABS[plane.y];
z: REAL ~ ABS[plane.z];
RETURN[
SELECT
MAX[x, y, z]
FROM
x => yz,
y => xz,
ENDCASE => xy];
};
CenterOfPlane:
PUBLIC
PROC [plane: Plane]
RETURNS [Triple] ~ {
s: REAL ~ -plane.w/(plane.x*plane.x+plane.y*plane.y+plane.z*plane.z);
RETURN[[s*plane.x, s*plane.y, s*plane.z]];
};
DistanceToPoint:
PUBLIC
PROC [point: Triple, plane: Plane, unitize:
BOOL ¬
TRUE]
RETURNS [d: REAL]
~ {
d ¬ point.x*plane.x+point.y*plane.y+point.z*plane.z+plane.w;
IF unitize
THEN {
length: REAL ¬ G3dVector.Length[[plane.x, plane.y, plane.z]];
IF length = 0.0 THEN ERROR Error["null plane normal"];
d ¬ d/length;
};
};
CircleFromPoints:
PUBLIC
PROC [p1, p2, p3: Triple]
RETURNS [circle: Circle] ~ {
plane: Plane ¬ FromThreePoints[p1, p2, p3];
v0: Triple ¬ G3dVector.Unit[
G3dVector.Cross[G3dVector.Sub[p1, p2], [plane.x, plane.y, plane.z]]];
v1: Triple ¬ G3dVector.Unit[
G3dVector.Cross[G3dVector.Sub[p2, p3], [plane.x, plane.y, plane.z]]];
dot: REAL ¬ G3dVector.Dot[v0, v1];
IF dot = 1.0
THEN ERROR Error["points colinear"]
ELSE {
m0: Triple ¬ G3dVector.Midpoint[p1, p2];
m1: Triple ¬ G3dVector.Midpoint[p2, p3];
dm: Triple ¬ G3dVector.Sub[m1, m0];
t:
REAL ¬
(dot*G3dVector.Dot[v0, dm]-G3dVector.Dot[v1, dm])/(1.0-dot*dot);
circle.center ¬ G3dVector.ScaleRay[[m1, v1], t];
circle.radius ¬ G3dVector.Distance[circle.center, p1];
};
};
Plane Sequences
CopyPlaneSequence:
PUBLIC
PROC [planes: PlaneSequence]
RETURNS [PlaneSequence] ~ {
copy: PlaneSequence ¬ NIL;
IF planes #
NIL
THEN {
copy ¬ NEW[PlaneSequenceRep[planes.length]];
copy.length ¬ planes.length;
FOR n: NAT IN [0..planes.length) DO copy[n] ¬ planes[n]; ENDLOOP;
};
RETURN[copy];
};
AddToPlaneSequence:
PUBLIC
PROC [planes: PlaneSequence, plane: Plane]
RETURNS [PlaneSequence]
~ {
IF planes = NIL THEN planes ¬ NEW[PlaneSequenceRep[1]];
IF planes.length = planes.maxLength THEN planes ¬ LengthenPlaneSequence[planes];
planes[planes.length] ¬ plane;
planes.length ¬ planes.length+1;
RETURN[planes];
};
LengthenPlaneSequence:
PUBLIC
PROC [planes: PlaneSequence, amount:
REAL ¬ 1.3]
RETURNS [new: PlaneSequence] ~ {
newLength: NAT ¬ MAX[Real.Ceiling[amount*planes.maxLength], 3];
new ¬ NEW[PlaneSequenceRep[newLength]];
FOR i: NAT IN [0..planes.length) DO new[i] ¬ planes[i]; ENDLOOP;
new.length ¬ planes.length;
};