-- CGOutlinesImpl.mesa
--Written by Doug Wyatt, Nov-81
-- Last changed by J Warnock June 28, 1982 12:24 pm
DIRECTORY
ParametricMap,
CGCubic USING [Bezier],
GraphicsBasic USING [Vec],
CGVector USING [Add,Mul,Sub,Dot],
CGOutlines USING [Data, DataRep, ErrorType, NodeRep, Rep],
CGStorage USING [pZone, qZone],
RealFns USING [SqRt];
CGOutlinesImpl: PROGRAM
IMPORTS CGStorage,CGVector,ParametricMap,RealFns
EXPORTS CGOutlines = {
OPEN CGOutlines, GraphicsBasic, CGVector, CGCubic;
Ref: TYPE = REF Rep;
Error: PUBLIC ERROR[type: ErrorType] = CODE;
dataZone: ZONE = CGStorage.pZone;
nodeZone: ZONE = CGStorage.qZone;
boxZone: ZONE = CGStorage.qZone;
repZone: ZONE = CGStorage.qZone;
New: PUBLIC PROC[size: NAT] RETURNS[Ref] = {
self: Ref ← repZone.NEW[Rep ← [size: 0, data: NIL, fpi: 0, fp: [0,0], lp: [0,0], sum:0]];
[] ← GetData[self,size];
RETURN[self];
};
GetData: PROC[self: Ref, size: NAT, bump: NAT ← 0] RETURNS[Data] = INLINE {
data: Data ← self.data;
space: NAT ← IF data=NIL THEN 0 ELSE data.space;
IF space<size THEN data ← Grow[self,MAX[size,space+bump]];
RETURN[data];
};
Grow: PROC[self: Ref, size: NAT] RETURNS[Data] = {
old: Data ← self.data;
space: NAT ← IF old=NIL THEN 0 ELSE old.space;
new: Data ← dataZone.NEW[DataRep[size]];
FOR i: NAT IN[0..space) DO new[i] ← old[i] ENDLOOP;
FOR i: NAT IN[space..size) DO new[i] ← nodeZone.NEW[NodeRep ← nullNode] ENDLOOP;
self.data ← new;
RETURN[new];
};
nullNode:NodeRep=NodeRep[TRUE,0,0,0,0,Bezier[[0,0],[0,0],[0,0],[0,0]]];
MoveTo: PUBLIC PROC[self: Ref, p: Vec] = {
i: NAT ← self.size;
self.fpi←i;
self.fp ← p;
self.lp ← p;
};
LineTo: PUBLIC PROC[self: Ref, p: Vec] = {
l:REAL; -- length of vec
v:Vec;
size: NAT;
data: Data;
i: NAT ← self.size;
IF p = self.lp THEN RETURN; --ignore duplicated points.
size ← i + 1;
data ← GetData[self,size,MAX[8,MIN[size/2,1024]]];
v←Sub[p,self.lp];
l←Mag[v];
IF i=0
THEN {data[i]↑←NodeRep[TRUE,0,0,0,l,
Bezier[self.lp,Add[self.lp,Mul[v,1.0/3.0]],Add[self.lp,Mul[v,2.0/3.0]],p]];}
ELSE { s1,s2:REAL;
v1,v2,v3:Vec;
data[i]↑←NodeRep[TRUE,0,0,self.sum,self.sum+l,
Bezier[self.lp,Add[self.lp,Mul[v,1.0/3.0]],Add[self.lp,Mul[v,2.0/3.0]],p]];
v1←Norm[Sub[data[i-1].bz.b3,data[i-1].bz.b2]];
v2←Norm[v];
v3←Norm[Add[v1,v2]];
s1←Dot[Vec[v1.y,-v1.x],v3];
s2←Dot[Vec[v2.y,-v2.x],v3];
data[i-1].a2←s1/RealFns.SqRt[1-s1*s1]; --compute horz vec for angle between v1 & v3
data[i].a1←s2/RealFns.SqRt[1-s2*s2]; --compute horz vec for angle between v2 & v3
};
self.sum←self.sum+l;
self.lp ← p;
self.size ← size;
};
CurveTo: PUBLIC PROC[self: Ref, b1,b2,b3: Vec] = {
IF self.lp = b1
THEN
{IF b3 = b2
THEN
{IF b2 = b1
THEN RETURN --ignore degenerate cubic.
ELSE LineTo[self,b3];
}
ELSE
{IF b2 = b1
THEN LineTo[self,b3]
ELSE FourPoint[self,self.lp,b1,b2,b3];
};
}
ELSE
{IF b3 = b2
THEN
{IF b2 = b1 THEN LineTo[self,b3];}
ELSE FourPoint[self,self.lp,b1,b2,b3];
};
};
Close: PUBLIC PROC[self: Ref] = {
l:REAL; -- length of vec
p,p1,v:Vec;
i: NAT;
p1←self.fp;
p←self.data[self.fpi].bz.b1;
LineTo[self,p1];
v←Sub[p,p1];
i←self.size;
l←Mag[v];
IF i=0
THEN {ERROR}
ELSE {s1,s2:REAL;
v1,v2,v3:Vec;
v1←Norm[Sub[self.data[i-1].bz.b3,self.data[i-1].bz.b2]];
v2←Norm[v];
v3←Norm[Add[v1,v2]];
s1←Dot[Vec[v1.y,-v1.x],v3];
s2←Dot[Vec[v2.y,-v2.x],v3];
self.data[i-1].a2←s1/RealFns.SqRt[1-s1*s1]; --compute horz vec for angle
self.data[self.fpi].a1←s2/RealFns.SqRt[1-s2*s2]; --compute horz vec for angle
};
};
FourPoint:PROC[self:Ref,vec1,vec2,vec3,vec4:Vec]=
{data: Data;
l:REAL;
v:Vec;
i,size:NAT;
i←self.size;
size ← i + 1;
data ← GetData[self,size,MAX[8,MIN[size/2,1024]]];
v←Sub[vec2,vec1];
l←GetLength[vec1,vec2,vec3,vec4];
IF i=0
THEN {data[i]↑ ← NodeRep[FALSE,0,0,0,l,Bezier[vec1,vec2,vec3,vec4]];}
ELSE {s1,s2:REAL;
v1,v2,v3:Vec;
data[i]↑ ← [FALSE,0,0,self.sum,self.sum+l,Bezier[vec1,vec2,vec3,vec4]];
v1←Norm[Sub[data[i-1].bz.b3,data[i-1].bz.b2]];
v2←Norm[v];
v3←Norm[Add[v1,v2]];
s1←Dot[Vec[v1.y,-v1.x],v3]; --compute sin of angle
s2←Dot[Vec[v2.y,-v2.x],v3]; --compute sin of angle
self.data[i-1].a2←s1/RealFns.SqRt[1-s1*s1]; --compute horz vec for angle
data[i].a1←s2/RealFns.SqRt[1-s2*s2]; --compute horz vec for angle
};
self.sum←self.sum+l;
self.lp ← vec4;
self.size ← size;};
GetMappedVec: PUBLIC PROC[self:Ref,v:Vec] RETURNS [w:Vec]=
{a1,a2,x,t:REAL;
pos,dc:Vec;
bz:Bezier;
i:NAT;
i←0;
IF v.x > self.sum THEN v.x←self.sum;
UNTIL v.x IN [self.data[i].l1..self.data[i].l2] DO
i←i+1;
ENDLOOP;
bz←self.data[i].bz;
x←v.x-self.data[i].l1;
IF self.data[i].line
THEN
{t←x/(self.data[i].l2-self.data[i].l1)}
ELSE
{[,t]←ParametricMap.GetTforArc[@bz,x];};
[pos,dc]←ParametricMap.GetDirCos[t,@bz];
a1←self.data[i].a1;
a2←self.data[i].a2;
w←Add[pos,Mul[Add[Vec[-dc.y,dc.x],Mul[dc,a1+(a2-a1)*t]],v.y]];
};
GetLinkCount:PUBLIC PROC[self:Ref] RETURNS [c:NAT]=
{RETURN[self.size];};
GetLinkLength:PUBLIC PROC[self:Ref,i:NAT] RETURNS [l:REAL]=
{RETURN[self.data[i].l2-self.data[i].l1];};
Copy: PUBLIC PROC[self: Ref] RETURNS[Ref] = {
size: NAT ← self.size;
old: Data ← self.data;
copy: Ref ← New[size];
new: Data ← copy.data;
FOR i: NAT IN[0..size) DO new[i]↑ ← old[i]↑ ENDLOOP;
copy.size ← size;
copy.fpi ← self.fpi;
copy.fp ← self.fp;
copy.lp ← self.lp;
RETURN[copy];
};
Assign: PUBLIC PROC[self: Ref, copy: Ref] = {
size: NAT ← copy.size;
new: Data ← copy.data;
old: Data ← GetData[self,size];
FOR i: NAT IN[0..size) DO old[i]↑ ← new[i]↑ ENDLOOP;
self.size ← size;
self.fpi ← copy.fpi;
self.fp ← copy.fp;
self.lp ← copy.lp;
};
Mag:PROC[v:Vec] RETURNS [REAL]=INLINE
{RETURN[RealFns.SqRt[v.x*v.x+v.y*v.y]];};
Norm:PROC[v:Vec] RETURNS [Vec]=INLINE
{m:REAL←Mag[v];
IF m = 0 THEN RETURN [v] ELSE RETURN [Mul[v,1.0/m]];};
GetLength:PROC[v1,v2,v3,v4:Vec] RETURNS [REAL]=
{bz:CGCubic.Bezier;
bz←CGCubic.Bezier[v1,v2,v3,v4];
RETURN[ParametricMap.ArcLength[@bz]];};
}.