-- EdgeImpl.mesa
-- Last changed by Doug Wyatt, January 12, 1982 3:02 PM

DIRECTORY
Edge,
Vector USING [Vec, Add, Sub, Mul, Cross],
Poly USING [Handle, New, Put, NewArea, Free],
Area USING [Handle, Vertices, Free],
Pipe USING [Handle, Object, Procs, Put, Free],
Real USING [FComp],
RealFns USING [SqRt],
Memory USING [zone];

EdgeImpl: PROGRAM
IMPORTS Memory,Vector,Poly,Area,Pipe,Real,RealFns
EXPORTS Edge SHARES Edge,Pipe = {
OPEN Edge;

zone: UNCOUNTED ZONE = Memory.zone;

Vec: TYPE = Vector.Vec;

Line: TYPE = RECORD [SELECT tag: * FROM
u,d => [x: REAL], -- vertical edge
l,r => [y: REAL], -- horizontal edge
oblique => [u: Vec, d: REAL], -- oblique edge
ENDCASE
];
LineRef: TYPE = LONG POINTER TO Line;

Data: PUBLIC TYPE = RECORD [
line: LineRef
];
DataRef: TYPE = LONG POINTER TO Data;

procs: LONG POINTER TO READONLY Procs = zone.NEW[Procs = [
NewPipe: CNewPipe,
Free: CFree
]];

-- Procedure for creating an Edge object

New: PUBLIC PROC[v0,v1: Vector.Vec] RETURNS[Handle] = {
line: LineRef=NewLine[@v0,@v1];
d: DataRef = zone.NEW[Data ← [line: line]];
RETURN[zone.NEW[Object ← [procs: procs, data: d]]];
};

Normalize: PROC[v: Vec] RETURNS[Vec] = INLINE {
d: REAL=RealFns.SqRt[v.x*v.x+v.y*v.y];
RETURN[[x: v.x/d, y: v.y/d]]
};

NewLine: PROC[v0,v1: POINTER TO Vector.Vec]
RETURNS[LineRef] = --INLINE-- {
SELECT TRUE FROM
(v0.x=v1.x) => RETURN[zone.NEW[Line ←
IF v1.y>v0.y THEN [u[v0.x]] ELSE [d[v0.x]]]];
(v0.y=v1.y) => RETURN[zone.NEW[Line ←
IF v1.x>v0.x THEN [r[v0.y]] ELSE [l[v0.y]]]];
ENDCASE => {
u: Vec=Normalize[Vector.Sub[v1↑,v0↑]];
RETURN[zone.NEW[Line ← [oblique[u: u, d: Vector.Cross[v0↑,u]]]]];
};
};

Vertex: TYPE = RECORD[v: Vec, dis: REAL] ← [[0,0],0];

Distance: PROC[line: LineRef, v: Vector.Vec] RETURNS[REAL] = INLINE {
WITH l:line SELECT FROM
u => RETURN[l.x-v.x]; d => RETURN[v.x-l.x];
l => RETURN[l.y-v.y]; r => RETURN[v.y-l.y];
oblique => RETURN[l.d-Vector.Cross[v,l.u]];
ENDCASE => ERROR;
};

Intersect: PROC[line: LineRef, old,new: POINTER TO Vertex]
RETURNS[Vec] = --INLINE-- {
r: REAL=old.dis/(old.dis-new.dis);
w: Vec=Vector.Add[old.v,Vector.Mul[Vector.Sub[new.v,old.v],r]];
WITH l:line SELECT FROM
u,d => RETURN[[x: l.x, y: w.y]];
l,r => RETURN[[x: w.x, y: l.y]];
oblique => RETURN[w];
ENDCASE => ERROR;
};

-- Operations on an Edge

PData: PUBLIC TYPE = RECORD [
line: LineRef,
ipoly,opoly: Poly.Handle,
ipipe,opipe: Pipe.Handle
];
PDataRef: TYPE = LONG POINTER TO PData;

pprocs: LONG POINTER TO READONLY Pipe.Procs = zone.NEW[Pipe.Procs = [
Put: PPut,
Free: PFree
]];

CNewPipe: PROC[self: Handle, ipipe, opipe: Pipe.Handle]
RETURNS[Pipe.Handle] = {
d: DataRef=self.data;
p: PDataRef=zone.NEW[PData ← [
line: d.line,
ipoly: Poly.New[], opoly: Poly.New[],
ipipe: ipipe, opipe: opipe
]];
RETURN[zone.NEW[Pipe.Object ← [procs: pprocs, data: LOOPHOLE[p]]]];
};

PPut: PROC[self: Pipe.Handle, area: Area.Handle] = {
p: PDataRef=LOOPHOLE[self.data];
vfirst,vnew,vold: Vertex;
first: BOOLEAN←TRUE;
DoVertex: PROC[v: Vector.Vec] = {
vnew←[v: v, dis: Distance[p.line,v]];
IF first THEN { vfirst←vnew; first←FALSE }
ELSE DoEdge[p,@vold,@vnew];
vold←vnew;
};
Area.Vertices[area,DoVertex];
DoEdge[p,@vold,@vfirst];
Area.Free[@area];
Pipe.Put[p.ipipe,Poly.NewArea[p.ipoly]];
Pipe.Put[p.opipe,Poly.NewArea[p.opoly]];
};

DoEdge: PROC[p: PDataRef, old,new: POINTER TO Vertex] = {
OPut: PROC[v: Vec] = INLINE { Poly.Put[p.opoly,v] };
IPut: PROC[v: Vec] = INLINE { Poly.Put[p.ipoly,v] };
SELECT Real.FComp[old.dis,0] FROM
<0 => {
OPut[old.v];
SELECT Real.FComp[new.dis,0] FROM
<0 => NULL;
=0 => OPut[new.v];
>0 => { w: Vec←Intersect[p.line,old,new]; OPut[w]; IPut[w] };
ENDCASE;
};
=0 => {
SELECT Real.FComp[new.dis,0] FROM
<0 => OPut[old.v];
=0 => NULL;
>0 => IPut[old.v];
ENDCASE;
};
>0 => {
IPut[old.v];
SELECT Real.FComp[new.dis,0] FROM
<0 => { w: Vec←Intersect[p.line,old,new]; IPut[w]; OPut[w] };
=0 => IPut[new.v];
>0 => NULL;
ENDCASE;
};
ENDCASE;
};

PFree: PROC[self: Pipe.Handle] = {
p: PDataRef←LOOPHOLE[self.data];
Pipe.Free[@p.ipipe]; Pipe.Free[@p.opipe];
Poly.Free[@p.ipoly]; Poly.Free[@p.opoly];
zone.FREE[@p]; zone.FREE[@self];
};

CFree: PROC[self: Handle] = {
d: DataRef←self.data;
zone.FREE[@d.line];
zone.FREE[@d]; zone.FREE[@self];
};

}.