-- MapperImpl.mesa
-- Last changed by Doug Wyatt, September 22, 1980 5:31 PM

DIRECTORY
Mapper,
Vector USING [Add, Sub, Det],
Memory USING [NewZone];

MapperImpl: PROGRAM
IMPORTS Memory,Mapper,Vector
EXPORTS Mapper SHARES Mapper = {
OPEN Mapper;

zone: UNCOUNTED ZONE = Memory.NewZone["MapperImpl"];

Data: PUBLIC TYPE = RECORD [
m: Matrix, -- transformation matrix
t: Vec, -- translation vector
det: REAL, -- determinant of m
ortho: BOOLEAN -- orthogonal flag
];
DataRef: TYPE = LONG POINTER TO Data;

tProcs: LONG POINTER TO READONLY Procs = zone.NEW[Procs = [
Map: TMap, InverseMap: TInv,
Translate: CTranslate, Concat: TConcat,
Ortho: COrtho, Read: CRead, Copy: CCopy, Free: CFree
]];

aProcs: LONG POINTER TO READONLY Procs = zone.NEW[Procs = [
Map: AMap, InverseMap: AInv,
Translate: CTranslate, Concat: AConcat,
Ortho: COrtho, Read: CRead, Copy: CCopy, Free: CFree
]];

sProcs: LONG POINTER TO READONLY Procs = zone.NEW[Procs = [
Map: SMap, InverseMap: SInv,
Translate: CTranslate, Concat: SConcat,
Ortho: COrtho, Read: CRead, Copy: CCopy, Free: CFree
]];

rProcs: LONG POINTER TO READONLY Procs = zone.NEW[Procs = [
Map: RMap, InverseMap: RInv,
Translate: CTranslate, Concat: RConcat,
Ortho: COrtho, Read: CRead, Copy: CCopy, Free: CFree
]];

eProcs: LONG POINTER TO READONLY Procs = zone.NEW[Procs = [
Map: RMap, InverseMap: EInv,
Translate: CTranslate, Concat: RConcat,
Ortho: COrtho, Read: CRead, Copy: CCopy, Free: CFree
]];


-- Procedure for creating a Mapper object

NewMapper: PUBLIC PROCEDURE[] RETURNS[Handle] = {
d: DataRef = zone.NEW[Data ← [
m: [1,0,0,1], t: [0,0], det: 1, ortho: TRUE
]];
RETURN[zone.NEW[Object ← [procs: tProcs, data: d]]];
};


-- Operations on a Mapper object

-- Translation only
TMap: PROCEDURE[self: Handle, v: POINTER TO Vec, t: BOOLEAN] = {
d: DataRef=self.data;
IF t THEN v↑←Vector.Add[v↑,d.t];
};

-- Alto screen transformation: scaling by [1,-1]
AMap: PROCEDURE[self: Handle, v: POINTER TO Vec, t: BOOLEAN] = {
d: DataRef=self.data;
w: Vec=[x: v.x, y: -v.y];
v↑←(IF t THEN Vector.Add[w,d.t] ELSE w);
};

-- Scaling, but no rotation
SMap: PROCEDURE[self: Handle, v: POINTER TO Vec, t: BOOLEAN] = {
d: DataRef=self.data;
w: Vec=[x: d.m.a11*v.x, y: d.m.a22*v.y];
v↑←(IF t THEN Vector.Add[w,d.t] ELSE w);
};

-- Rotation and more.. the most general transformation
RMap: PROCEDURE[self: Handle, v: POINTER TO Vec, t: BOOLEAN] = {
d: DataRef=self.data;
w: Vec=[x: d.m.a11*v.x+d.m.a12*v.y, y: d.m.a21*v.x+d.m.a22*v.y];
v↑←(IF t THEN Vector.Add[w,d.t] ELSE w);
};

MapperError: PUBLIC ERROR = CODE;

TInv: PROCEDURE[self: Handle, v: POINTER TO Vec, t: BOOLEAN] = {
d: DataRef=self.data;
IF t THEN v↑←Vector.Sub[v↑,d.t];
};

AInv: PROCEDURE[self: Handle, v: POINTER TO Vec, t: BOOLEAN] = {
d: DataRef=self.data;
w: Vec=IF t THEN Vector.Sub[v↑,d.t] ELSE v↑;
v↑←[x: w.x, y: -w.y];
};

SInv: PROCEDURE[self: Handle, v: POINTER TO Vec, t: BOOLEAN] = {
d: DataRef=self.data;
w: Vec=IF t THEN Vector.Sub[v↑,d.t] ELSE v↑;
v↑←[x: w.x/d.m.a11, y: w.y/d.m.a22];
};

RInv: PROCEDURE[self: Handle, v: POINTER TO Vec, t: BOOLEAN] = {
d: DataRef=self.data;
w: Vec=IF t THEN Vector.Sub[v↑,d.t] ELSE v↑;
v↑←[
x: (d.m.a22*w.x-d.m.a12*w.y)/d.det,
y: (d.m.a11*w.y-d.m.a21*w.x)/d.det
];
};

EInv: PROCEDURE[self: Handle, v: POINTER TO Vec, t: BOOLEAN] = {
ERROR MapperError;
};


Install: PROCEDURE[self: Handle] = {
d: DataRef=self.data;
d.det←Vector.Det[d.m]; -- compute new determinant
SELECT TRUE FROM
(d.det=0) => { self.procs←eProcs; d.ortho←FALSE };
(d.m.a12#0 OR d.m.a21#0) => { self.procs←rProcs; d.ortho←FALSE };
(d.m.a11=1 AND d.m.a22=-1) => { self.procs←aProcs; d.ortho←TRUE };
(d.m.a11#1 OR d.m.a22#1) => { self.procs←sProcs; d.ortho←TRUE };
ENDCASE => { self.procs←tProcs; d.ortho←TRUE };
};

TConcat: PROCEDURE[self: Handle, m: POINTER TO Matrix] = {
d: DataRef=self.data;
d.m←m↑;
Install[self];
};

AConcat: PROCEDURE[self: Handle, m: POINTER TO Matrix] = {
d: DataRef=self.data;
d.m←[a11: m.a11, a12: m.a12, a21: -m.a21, a22: -m.a22];
Install[self];
};

SConcat: PROCEDURE[self: Handle, m: POINTER TO Matrix] = {
d: DataRef=self.data;
mm: Matrix←[
a11: d.m.a11*m.a11, a12: d.m.a11*m.a12,
a21: d.m.a22*m.a21, a22: d.m.a22*m.a22
];
d.m←mm;
Install[self];
};

RConcat: PROCEDURE[self: Handle, m: POINTER TO Matrix] = {
d: DataRef=self.data;
mm: Matrix←[
a11: d.m.a11*m.a11 + d.m.a12*m.a21,
a12: d.m.a11*m.a12 + d.m.a12*m.a22,
a21: d.m.a21*m.a11 + d.m.a22*m.a21,
a22: d.m.a21*m.a12 + d.m.a22*m.a22
];
d.m←mm;
Install[self];
};

CTranslate: PROCEDURE[self: Handle, v: POINTER TO Vec] = {
d: DataRef=self.data;
d.t←Map[self,v↑];
};

COrtho: PROCEDURE[self: Handle] RETURNS[BOOLEAN] = {
d: DataRef=self.data;
RETURN[d.ortho];
};

CRead: PROCEDURE[self: Handle,
m: POINTER TO Matrix, v: POINTER TO Vec] = {
d: DataRef=self.data;
m↑←d.m; v↑←d.t;
};

CCopy: PROCEDURE[self: Handle] RETURNS[Handle] = {
d: DataRef=self.data;
dd: DataRef = zone.NEW[Data ← d↑];
RETURN[zone.NEW[Object ← [procs: self.procs, data: dd]]];
};

CFree: PROCEDURE[selfPtr: LONG POINTER TO Handle] = {
self: Handle←selfPtr↑;
d: DataRef←self.data;
selfPtr↑←NIL;
zone.FREE[@d];
zone.FREE[@self];
};

}.