ImplicitTubeImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Bloomenthal, September 6, 1992 12:01 pm PDT
DIRECTORY G3dBasic, G3dDraw, G3dFunction, G3dMatrix, G3dSpline, G3dTube, G3dVector, Imager, ImplicitDefs, ImplicitTube, RealFns, ImplicitSurface;
ImplicitTubeImpl: CEDAR MONITOR
IMPORTS G3dDraw, G3dFunction, G3dSpline, G3dTube, G3dVector, RealFns, ImplicitSurface
EXPORTS ImplicitTube
~ BEGIN
Pair:    TYPE ~ G3dBasic.Pair;
Triple:   TYPE ~ G3dBasic.Triple;
Matrix:   TYPE ~ G3dMatrix.Matrix;
Tube:    TYPE ~ G3dTube.Tube;
TubeProc:  TYPE ~ G3dTube.TubeProc;
XSection:   TYPE ~ G3dTube.XSection;
NearSegment: TYPE ~ G3dVector.NearSegment;
Context:   TYPE ~ Imager.Context;
Sample:   TYPE ~ ImplicitDefs.Sample;
DistanceMode: TYPE ~ ImplicitDefs.DistanceMode;
ValueProc:  TYPE ~ ImplicitDefs.ValueProc;
PI:     REAL ~ 3.1415926535;
PrepareTube: PUBLIC PROC [tube: Tube, distanceMode: DistanceMode, measureMode: ATOM]
~ {
IF NOT tube.xSectionsValid THEN G3dTube.MakeXSections[tube, FALSE, TRUE];
IF NOT tube.lengthsValid THEN G3dTube.SetLengths[tube];
IF NOT tube.planeValid THEN G3dTube.SetPlanes[tube];
SELECT measureMode FROM
$Sum =>
G3dTube.SetDescendantRadii[tube, IF distanceMode=inverse THEN linear ELSE square];
ENDCASE;
};
StartPoint: PUBLIC PROC [
tube: Tube,
valueProc: ValueProc,
threshold: REAL ¬ 1.0,
measureMode: ATOM ¬ $Unspecified,
spread: REAL ¬ 0.75,
clientData: REF ANY ¬ NIL]
RETURNS [tt: Triple]
~ {
t: REAL ~ 0.5;
pIn, pOut, v: Triple;
factor: REAL;
factor ¬ SELECT measureMode FROM
$Segment => 2.0/spread,
--($Sum)-- ENDCASE => 1.5; -- 1.5 instead of 1.0, for some fudge
pIn ¬ G3dSpline.Position[tube.spline, t];
v ¬ G3dSpline.Velocity[tube.spline, t];
v ¬ G3dVector.Unit[G3dVector.Ortho[v]];
pOut ¬ G3dVector.ScaleRay[[pIn, v], factor*G3dTube.Radius[tube, t]];
tt ¬ ImplicitSurface.SurfacePoint[pIn, pOut, valueProc, threshold, clientData];
};
SampleTube: PUBLIC PROC [
q: Triple,
tube: Tube,
measureMode: ATOM,
distanceMode: DistanceMode,
tolerance, spread: REAL]
RETURNS [sample: Sample]
~ {
s: Sample ¬ sample ¬
SampleTubeBranch[q, tube, measureMode, distanceMode, tolerance, spread];
nearest: Tube ¬ G3dTube.NearestTube[q, tube].tube;
IF nearest.nearSpline.distance # 0.0
THEN {            -- useful sample information:
v: Triple ~ G3dVector.Sub[q, nearest.nearSpline.point];
radius: REAL ~ G3dTube.Radius[nearest, nearest.nearSpline.t];
d: REAL ~ nearest.nearSpline.distance;
sample.near ¬ G3dVector.ScaleRay[[nearest.nearSpline.point, v], radius/d];
}
ELSE sample.near ¬ nearest.nearSpline.point;
};
SampleTubeBranch: PUBLIC PROC [
q: Triple,
tube: Tube,
measureMode: ATOM,
distanceMode: DistanceMode,
tolerance, spread: REAL]
RETURNS [Sample]
~ {
segment: Sample ¬
SampleTubeSegment[q, tube, measureMode, distanceMode, tolerance, spread];
branches: Sample ¬
SampleTubeBranches[q, tube, measureMode, distanceMode, tolerance, spread];
Note about the following line: if tube has no next and no branches and q is too far away from tube, then both segment.value and branches.value may be zero; in this case, segment.near, segment.vector, etc. are defined, but branches.near, etc. are not; thus the benefit of the inequality goes to segment.
RETURN[IF branches.value > segment.value THEN branches ELSE segment];
};
SampleTubeBranch: PUBLIC PROC [
q: Triple,
tube: Tube,
sampleMode: SampleMode]
RETURNS [Sample]
~ {
If we want to debug the contribution of branches only:
RETURN[IF tube.prev = NIL THEN SampleTubeBranches[q, tube, sampleMode] ELSE SampleTubeSegment[q, tube, sampleMode]];
};
SampleTubeBranches: PUBLIC PROC [
q: Triple,
tube: Tube,
measureMode: ATOM,
distanceMode: DistanceMode,
tolerance, spread: REAL]
RETURNS [sample: Sample]
~ {
tubeProc: TubeProc ~ {
s: Sample ¬
SampleTubeBranch[q, tube, measureMode, distanceMode, tolerance, spread];
IF s.value > sample.value THEN sample ¬ s;
samples[nBranches] ¬ s;
nBranches ¬ nBranches+1;
};
nBranches: NAT ¬ 0;
samples: ARRAY [0..50) OF Sample;
G3dTube.ApplyToBranches[tube, tubeProc];
sample.value ¬ 0.0;
SELECT measureMode FROM
$Segment => {
FOR n: NAT IN [0..nBranches) DO
s: Sample ¬ samples[n];
FOR nn: NAT IN (n..nBranches) DO
ss: Sample ¬ samples[nn];
IF s.near = ss.near
THEN sample.value ¬ sample.value+s.value
ELSE {
aveRadius: REAL ¬ 0.5*(s.radius+ss.radius);
aveDistance: REAL ¬ 0.5*(s.distance+ss.distance);
aveRatio: REAL ¬ Ratio[aveDistance, aveRadius, spread];
SELECT aveRatio FROM
< 2.0 => {
near: NearSegment ¬
G3dVector.NearestToSegment[s.near, ss.near, q];
lineDistance: REAL ¬ SELECT near.w0 FROM
0.0 => ss.distance,
1.0 => s.distance,
ENDCASE => G3dVector.Distance[near.point, q];
lineValue: REAL ¬
G3dFunction.Wyvill[Ratio[lineDistance, aveRadius, spread]];
IF aveRatio < 1.0
THEN sample.value ¬ sample.value+lineValue
ELSE {
f: REAL ¬ 2.0-aveRatio;
sample.value ¬ sample.value+f*lineValue+(1.-f)*(s.value+ss.value);
};
};
ENDCASE => sample.value ¬ sample.value+s.value+ss.value;
};
ENDLOOP;
ENDLOOP;
IF nBranches > 2 THEN sample.value ¬ 2.0*sample.value/(nBranches*(nBranches-1));
};
--($Sum)-- ENDCASE =>
FOR n: NAT IN [0..nBranches) DO
sample.value ¬ sample.value+samples[n].value;
ENDLOOP;
};
SampleTubeSegment: PUBLIC PROC [
q: Triple,
tube: Tube,
measureMode: ATOM,
distanceMode: DistanceMode,
tolerance, spread: REAL]
RETURNS [sample: Sample]
~ {
SetBasics: PROC ~ {
tube.nearSpline ¬ G3dSpline.NearestPoint[q, tube.spline, 0.0, 1.0, tolerance];
sample.near ¬ tube.nearSpline.point;
sample.vector ¬ G3dVector.Sub[sample.near, q];
sample.radius ¬ G3dTube.Radius[tube, tube.nearSpline.t];
sample.squareDistance ¬ G3dVector.Square[sample.vector];
};
SetDistance: PROC ~ {
sample.distance ¬ RealFns.SqRt[sample.squareDistance];
sample.distanceSet ¬ TRUE;
};
sample.refAny ¬ tube;
SELECT measureMode FROM
$Segment => {
SetBasics[];
SetDistance[];
sample.ratio ¬ Ratio[sample.distance, sample.radius, spread];
sample.value ¬ G3dFunction.Wyvill[sample.ratio];
};
--($Sum)-- ENDCASE => {
SetBasics[];
IF distanceMode = inverse THEN SetDistance[];
sample.value ¬ IF distanceMode = inverse
THEN sample.radius/MAX[0.0001, sample.distance]
ELSE sample.radius*sample.radius/MAX[0.0001, sample.squareDistance];
};
};
Ratio: PROC [distance, radius, spread: REAL] RETURNS [REAL] ~ {
RETURN[IF radius = 0.0 THEN 2.0 ELSE spread*(distance-radius)/radius];
};
TextureOfTube: PUBLIC PROC [q: Triple, tube: Tube] RETURNS [texture: Pair] ~ {
nearest: Tube ¬ G3dTube.NearestTube[q, tube, FALSE].tube;
xSection: XSection ¬ G3dTube.GetXSection[nearest, nearest.nearSpline.t];
vector: Triple ¬ G3dVector.Sub[q, xSection.frame.position];
xProj: Triple ¬ G3dVector.Project[vector, xSection.frame.triad.n];
yProj: Triple ¬ G3dVector.Project[vector, xSection.frame.triad.b];
proj: Triple ¬ G3dVector.Unit[G3dVector.Add[xProj, yProj]];
texture.x ¬ G3dVector.AngleBetween[proj, xSection.frame.triad.n, FALSE]/PI;
texture.y ¬ SELECT nearest.nearSpline.t FROM
0.0 => tube.length0-
G3dVector.Length[G3dVector.Project[vector, xSection.frame.triad.v]],
1.0 => tube.length1+
G3dVector.Length[G3dVector.Project[vector, xSection.frame.triad.v]],
ENDCASE => nearest.length0+xSection.length;
};
DiagramTube: PUBLIC PROC [
context: Context,
view: Matrix,
viewport: G3dMatrix.Viewport,
q: Triple,
tube: Tube,
measureMode: ATOM,
distanceMode: DistanceMode,
tolerance, spread: REAL]
~ {
Info: TYPE ~ RECORD [tube: Tube, branches: BOOL, sample: Sample];
InfoTubeBranch: PROC [q: Triple, tube: Tube] RETURNS [Info] ~ {
segment: Info ¬ InfoTubeSegment[q, tube];
branches: Info ¬ InfoTubeBranches[q, tube];
RETURN[IF branches.sample.value > segment.sample.value THEN branches ELSE segment];
};
InfoTubeBranches: PROC [q: Triple, tube: Tube] RETURNS [info: Info] ~ {
s: Sample ¬SampleTubeSegment[q, tube, measureMode, distanceMode, tolerance, spread];
RETURN[[tube, TRUE, s]];
};
InfoTubeSegment: PROC [q: Triple, tube: Tube] RETURNS [Info] ~ {
s: Sample ¬SampleTubeSegment[q, tube, measureMode, distanceMode, tolerance, spread];
RETURN[[tube, FALSE, s]];
};
IF tube # NIL THEN SELECT measureMode FROM
$Segment => {
info: Info ¬ InfoTubeBranch[q, tube];
IF info.branches
THEN {
nBranches: NAT ¬ G3dTube.NBranches[info.tube]+1;
FOR n: NAT IN [0..nBranches) DO
b0: Tube ¬ G3dTube.GetBranch[info.tube, n];
p0: Triple ¬
G3dSpline.NearestPoint[q, b0.spline, 0.0, 1.0, tolerance].point;
FOR nn: NAT IN (n..nBranches) DO
b1: Tube ¬ G3dTube.GetBranch[info.tube, nn];
p1: Triple ¬
G3dSpline.NearestPoint[q, b1.spline, 0.0, 1.0, tolerance].point;
x: Triple ¬ G3dVector.NearestToSegment[p0, p1, q].point;
G3dDraw.Segment[context, p0, p1, view, viewport, solid];
G3dDraw.Segment[context, q, x, view, viewport, solid];
G3dDraw.Segment[context, q, p0, view, viewport, dotted];
G3dDraw.Segment[context, q, p1, view, viewport, dotted];
ENDLOOP;
ENDLOOP;
}
ELSE {
x: Triple ¬ G3dTube.NearestTube[q, info.tube].tube.nearSpline.point;
G3dDraw.Segment[context, q, x, view, viewport, solid];
};
};
ENDCASE;
};
END.
..
TextureOfTube: PUBLIC PROC [q: Triple, tube: Tube] RETURNS [texture: Pair] ~ {
Info: TYPE ~ RECORD [inverseDistance: REAL, texture: Pair];
tubeProc: TubeProc ~ {
xSection: XSection ¬ G3dTube.GetXSection[tube, tube.nearSpline.t];
vector: Triple ¬ G3dVector.Sub[q, frame.position];
xProj: Triple ¬ G3dVector.Project[vector, xSection.frame.triad.n];
yProj: Triple ¬ G3dVector.Project[vector, xSection.frame.triad.b];
proj: Triple ¬ G3dVector.Unit[G3dVector.Add[xProj, yProj]];
tx: REAL ¬ G3dVector.AngleBetween[proj, xSection.frame.triad.n, FALSE]/PI;
ty: REAL ¬ SELECT tube.nearSpline.t FROM
0.0 => tube.length0-
G3dVector.Length[G3dVector.Project[vector, xSection.frame.triad.v]],
1.0 => tube.length1+
G3dVector.Length[G3dVector.Project[vector, xSection.frame.triad.v]],
ENDCASE => tube.length0+frame.length;
info[nBranches] ¬ [1.0/tube.nearSpline.distance, [tx, ty]];
nBranches ¬ nBranches+1;
};
nBranches: NAT ¬ 0;
info: ARRAY [0..50) OF Info;
nearest: Tube ¬ G3dTube.NearestTube[q, tube, FALSE];
G3dTube.ApplyToSiblings[nearest, tubeProc];
IF nBranches = 1
THEN RETURN[info[0].texture]
ELSE {
inverseDistanceSum: REAL ¬ 0.0;
texture ¬ [0.0, 0.0];
FOR n: NAT IN [0..nBranches) DO
inverseDistanceSum ¬ inverseDistanceSum+info[n].inverseDistance;
ENDLOOP;
FOR n: NAT IN [0..nBranches) DO
texture ¬ Vector2.Add[texture, Vector2.Mul[info[n].texture, info[n].inverseDistance]];
ENDLOOP;
texture ¬ Vector2.Div[texture, inverseDistanceSum];
};
};
ValueOfTubeBranches: PUBLIC PROC [
q: Triple,
tube: Tube,
distanceMode: DistanceMode,
tolerance: REAL ¬ 0.0]
RETURNS [sample: Sample]
~ {
nBranches: NAT ¬ 0;
vSum: Triple ¬ [0.0, 0.0, 0.0];
maxValue: REAL ¬ -100000.0;
vectors: ARRAY [0..50) OF Triple;
samples: ARRAY [0..50) OF Sample;
weight: BOOL ¬ distanceMode = wtInverse OR distanceMode = wtInvSq;
tubeProc: G3dTube.TubeProc ~ {
IF tube # NIL THEN {
s: Sample ¬ ValueOfTubeBranch[q, tube, distanceMode, tolerance];
IF s.value > maxValue THEN {maxValue ¬ s.value; sample ¬ s};
IF (distanceMode=wtInverse OR distanceMode=wtInvSq) AND NOT s.distanceSet THEN {
s.distance ¬ Real.SqRt[s.squareDistance];
s.distanceSet ¬ TRUE;
};
samples[nBranches] ¬ s;
nBranches ¬ nBranches+1;
};
};
G3dTube.ApplyToBranches[tube, tubeProc];
SELECT distanceMode FROM
wtInverse, wtInvSq => {
Now a miracle occurs.
FOR n: NAT IN [0..nBranches) DO
v: Triple ¬ G3dVector.Sub[samples[n].nearest, q];
vectors[n] ¬ G3dVector.Div[v, samples[n].distance];
vSum ¬ G3dVector.Add[vSum, vectors[n]];
ENDLOOP;
vSum ¬ G3dVector.Unit[vSum];
FOR n: NAT IN [0..nBranches) DO
f: REAL ~ 0.5*(1.0-G3dVector.Dot[vSum, vectors[n]]);
sample.value ¬ sample.value+f*samples[n].value;
ENDLOOP;
};
ENDCASE => {
FOR n: NAT IN [0..nBranches) DO
sample.value ¬ sample.value+samples[n].value;
ENDLOOP;
};
};
ValueOfTubeSegment: PUBLIC PROC [
q: Triple,
tube: Tube,
distanceMode: DistanceMode,
tolerance: REAL ¬ 0.0]
RETURNS [s: Sample]
~ {
radius: REAL ~ G3dTube.Radius[tube, tube.nearSpline.t];
s ¬ ImplicitValue.ValueOfPoint[q, tube.nearSpline.point, radius, distanceMode, threshold];
};
mode: SampleMode.integral => {
IF tube.length = 0.0 THEN G3dTube.SetSectionLengths[tube];
{
Val: PROC RETURNS [REAL] ~ {
distance: REAL ¬ G3dVector.Length[[diff[0][0], diff[0][1], diff[0][2]]];
RETURN[Strength[Ratio[distance, radius, mode.spread]]];
};
radius: REAL ¬ tube.r0;
diff: Matrix ¬ G3dMatrix.ObtainMatrix[];
nSteps: NAT ¬ Real.Round[tube.length/mode.stepSize];
dRadius: REAL ¬ (tube.r1-tube.r0)/REAL[MAX[1, nSteps]];
diff^ ¬ tube.spline^;
diff[3] ¬ [diff[3][0]-q.x, diff[3][1]-q.y, diff[3][2]-q.z, 0.0];
diff ¬ G3dSpline.ForwardDifference[diff, nSteps];
sample.value ¬ Val[];
FOR n: NAT IN [0..nSteps) DO
radius ¬ radius+dRadius;
FOR j: NAT IN [0..3) DO
diff[0][j] ¬ diff[0][j]+diff[1][j];
diff[1][j] ¬ diff[1][j]+diff[2][j];
diff[2][j] ¬ diff[2][j]+diff[3][j];
ENDLOOP;
sample.value ¬ sample.value+Val[];
ENDLOOP;
G3dMatrix.ReleaseMatrix[diff];
};
};