-- 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]];}; }.