// Convert.bcpl

// last modified by Butterfield, October 13, 1980 12:00 PM
// -
SetSCVTransform, resolutions 1X instead of 10X - 10/13/80

//12/13/77: ConvertAChar now puts out ORbit format characters
J.Maleson
// catalog number ???

//ConvertAWidth(ps,pc,p)
//
Convert the widths of a single character.
//
ps -> SplineWidth. Fills up pc -> CharWidth.
//
p -> Convert structure.
// returns true if bitmap for character is large
// (only routines which are prepared to deal with spline
//
output check the conservative return from ConvertAWidth)
//ConvertAChar(w,wo,pc,p,FSGet,FSPut)
//
Converts a single character. p -> Convert structure,
//
which describes some parameters.
//
w is window to read splines from; wo window to put
//
scan-converted characters into.
//
pc -> CharWidth structure to return goodies.
//
Returns an error code:
//
0 = All OK
//
1= not enough core in SCV
//
2,3 = SCV errors in splines
//
4 = too big to scan convert, try ScaleAChar
//
10= Spline description file illegal
//
11= Illegal Press font object
//
12= Scan convert screwed up -- not even # intersections
//
13= Bit intersection out of range
//
14= Conversion did not terminate properly.
//
FSGet,FSPut are routines for getting, releasing storage.
//ScaleAChar(w,wo,pc)
//
Converts a single character into scaled spline representation
//
w is window to read splines from; wo window to put
//
scaled splines into.
//
pc -> CharWidth structure to return goodies.

//SetSCVTransform(siz,rotation,incline,resolutionx,resolutiony)
//
Set up SCV transformation matrix.
//Cos(theta,lvsign,lvmag)
//
Computes the cosine of the angle "theta" (in minutes) and
//
returns sign (0 if positive, -1 if negative) and magnitude
//
(0 to #177777)


get "ix.dfs"
get "scv.dfs"

// outgoing procedures
external
[
ConvertAChar
ConvertAWidth
ScaleAChar
SetSCVTransform
Cos
]

// outgoing statics
external
[
convertThicken
convertOrbitized
]
static
[
convertThicken=false
convertOrbitized=false
]

// incoming procedures
external
[
//In SCV package
SCVInit
SCVBeginObject
SCVEndObject
SCVMoveToF
SCVDrawToF
SCVDrawCurve
SCVMatrix
SCVReadRuns
SCVTransformF
SCVFlush
Floor


//Window package
WindowSetPosition
WindowGetPosition
WindowReadBlock
WindowWriteBlock
WindowRead
WindowWrite
WindowCopy

//Block move, store
Zero; SetBlock; MoveBlock

//Misc
MulDiv
MulFull;DivFull
DoubleAddV

//Floating point
FLD;FST;FLDI;FNEG;FAD;FML;FDV
FCM;FLDV;FSTDP;FTR
]

// incoming statics

// internal statics

// File-wide structure and manifest declarations.

// Procedures

let

ConvertAWidth(s,c,p) = valof [

// s -> SplineWidth structure of char to compute widths.
// c -> CharWidth structure to receive results.
// p -> Convert structure that governs how things are done.
// Returns true if spline will be needed.

let spline=false
let pw=lv s>>SplineWidth.WX
test pw!0 eq 0 & pw!1 eq -1 then
[//Non existent char
c>>CharWidth.H=HNonExCode
]
or
[//Transform widths
SCVTransformF(lv s>>SplineWidth.WX,lv s>>SplineWidth.WY)
FSTDP(8,lv c>>CharWidth.WX)//and save for AC file
FSTDP(9,lv c>>CharWidth.WY)//as double-precision

//Calculate bounding box.

//Warning: the calculation for the bounding box is really not good enough.
// This is because the bounding box kept with the SD description is calculated
// a little differently than will be the endpoints of the splines when passed
// to SCV during conversion. As a result, small round-off errors will occur
// (partly because SCVDrawCurve uses ->relative<- numbers!). This can change
// 2E-9 into -2E-9, which will cause a different bounding box to be calculated.

test p>>Convert.BBGood then
[//Transform bounding box
SCVTransformF(lv s>>SplineWidth.XL,lv s>>SplineWidth.YB)
FLD(1,8)//Save left edge
let yb=Floor(9)//Y bottom

SCVTransformF(lv s>>SplineWidth.XR,lv s>>SplineWidth.YT)
let yh=Floor(9)

//Now swap ybottom and yheight if inverted (will happen if character is rotated).
if yb gr yh then [ let t=yb; yb=yh; yh=t ]
yh=yh-yb+1

//And same for x, but (1) check for empty character, and (2)
// in scan direction, things are assymetric, because of the kind of
// scan conversion we are doing.

let xl,xw=nil,nil
let sg=FCM(1,8)
switchon sg into
[
case 0:xl=0; xw=0; yb=0; yh=0//Character is empty
endcase
case 1:FLD(2,1);FLD(1,8);FLD(8,2) //Swap -- ac 1 < ac 8
case -1:xl=Floor(1)+1//Assymetry
xw=Floor(8)-xl+1
endcase
]
c>>CharWidth.XL=xl
c>>CharWidth.YB=yb
c>>CharWidth.H=yh
c>>CharWidth.W=xw

if p>>Convert.SplineOk & SplineNeeded(c,1) then
[
c>>CharWidth.H=HSplineCode
spline=true
]
] //end of "test BBGood"
or //rotated, can’t get bounding box, BUT: we can tell if it looks like
//splines are needed (since DP WX,WY are computed already)
[ if ((lv c>>CharWidth.WX)!0 gr 50)%
((lv c>>CharWidth.WY)!0 gr 50) then spline=true
]
] //end of good character
resultis spline
]

and

ConvertAChar(w,wo,pc,p,FSGet,FSPut) = valof [

//Assume w positioned at spline, wo positioned to receive it.
// pc -> CharWidth structure to receive results (bounding box only)
// p -> Convert structure that governs how things are done.

let originallen=32000//Generous estimate!
if p>>Convert.PressFontPart then originallen=p>>Convert.Len

SCVInit(FSGet,FSPut)
SCVBeginObject(not p>>Convert.Monotone,p>>Convert.Monotone)

let opos=vec 3//Remember old pos for spline
WindowGetPosition(w,opos)
WindowGetPosition(wo,opos+2)

let stuff=vec 12
let len=originallen

[
if len le 0 then break
let op=WindowRead(w)
switchon op into [

case DSplineFontMoveTo://MoveTo
WindowReadBlock(w,stuff,4)
SCVMoveToF(stuff,stuff+2)
len=len-5
endcase;
case DSplineFontDrawTo://DrawTo
WindowReadBlock(w,stuff,4)
SCVDrawToF(stuff,stuff+2)
len=len-5
endcase;
case DSplineFontDrawCurve://DrawCurve
WindowReadBlock(w,stuff,12)
SCVDrawCurve(stuff,stuff+2,stuff+4,stuff+6,
stuff+8,stuff+10)
len=len-13
endcase;
case DSplineFontEndObjects: //End
break
endcase
default:resultis 10//Illegal file format
]
] repeat

let v=vec (size SCV/16)
SCVEndObject(v)//Finish off
if v>>SCV.Error then [//Error -- return code
SCVFlush(v)
resultis v>>SCV.Error
]

//Compute offsets (ox,oy), width (ns) and height (nb)
let ns,nb=nil,nil
let ox=v>>SCV.Smin
test ox gr v>>SCV.Smax then [ ox=0;ns=0 ] or
ns=v>>SCV.Smax-ox+1
let oy=v>>SCV.Rmin
test oy gr v>>SCV.Rmax then [ oy=0;nb=0 ] or
nb=v>>SCV.Rmax-oy+1

if p>>Convert.PressFontPart then
[
if len ne 0 then
[
SCVFlush(v)
resultis 11
]
SCVTransformF(stuff,stuff+2)//Use last MOVETO
FSTDP(8,lv pc>>CharWidth.WX)// to compute widths
FSTDP(9,lv pc>>CharWidth.WY)
]
//Salt away goodies in the structure
pc>>CharWidth.XL=ox
pc>>CharWidth.YB=oy
pc>>CharWidth.W=ns
pc>>CharWidth.H=nb

//figure out how much space needed: if not enough, use splines
unless convertOrbitized do nb=(nb+15)𫙠
let bitsNeeded=vec 1
MulFull(ns,nb,bitsNeeded)
DoubleAddV(bitsNeeded,15)//take care of odd bit lengths
let sizeNeeded=DivFull(bitsNeeded,16)+1//add fudge for final tempPBits!1
let pbits=FSGet(sizeNeeded)

//Now decide if splines needed.
test p>>Convert.SplineOk&((pbits eq 0)%SplineNeeded(pc)) then
[ unless pbits eq 0 do FSPut(pbits)
WindowSetPosition(w,opos)//Restart
SCVFlush(v)//Release any storage
resultis 4//return error, so that caller will call ScaleAChar
]
or
[//Can convert OK

//The character can be converted into a bit matrix, and stored in
// the character definition file as such.

let sl=vec 2
test convertOrbitized then
[sl!0=-nb//height
sl!1=ns-1//width
WindowWriteBlock(wo,sl,2)
]
or
[ sl<<FHEAD.hw=nb rshift 4
sl<<FHEAD.ns=ns//Make up font header
WindowWrite(wo,sl)
]

if sizeNeeded eq 1 then [ FSPut(pbits);resultis 0] //null character

let Masks= table [
#177777; #077777; #037777; #017777; #007777;
#003777; #001777; #000777; #000377; #000177;
#000077; #000037; #000017; #000007; #000003;
#000001; #000000 ]

let Cycle=table [ #60000;#1401]
let CMasks=
table [#177777;#177776;#177774
#177770;#177760;#177740
#177700;#177600;#177400
#177000;#176000;#174000
#170000;#160000;#140000
#100000;0
]

let slbuf=vec 100//For making up scan lines
Zero(slbuf,100)

//Prepare to call SCVReadRuns to obtain all runs until the character
// exhausts.

let hw=(nb+15)/16
let ob=0
let tempPBits=pbits
let buf=vec 1000
let sl=v>>SCV.Smin
v>>SCV.Send=sl-1
[
v>>SCV.Sbegin=v>>SCV.Send+1//Move right
v>>SCV.Send=v>>SCV.Smax//Optimistic
SCVReadRuns(v,buf,1000)
let n=v>>SCV.IntCnt
if n eq 0 then break//No more intersections
let p=v>>SCV.IntPtr

for i=1 to n by 2 do
[
while sl ne p!0 do//Going to new scan line.
[
let leftMask=CMasks!(16-ob)
let rightMask=not leftMask
for j=0 to hw-1 do
[ let w=Cycle(slbuf!j,16-ob)
tempPBits!0=(tempPBits!0&(not Masks!ob))+(w&rightMask)
tempPBits!1=(tempPBits!1&(Masks!ob))+(w&leftMask)
tempPBits=tempPBits+1
] //end of "for j"
ob=ob+nb
tempPBits=tempPBits-hw+ob/16
ob=ob
Zero(slbuf,hw)
sl=sl+1
] //end of "while sl"
if p!2 ne sl then resultis 12
let yb=p!1-oy//Bottom y
let yt=p!3-oy+(convertThicken? 1,0)//Top y+1
p=p+4//Bump to next intersection

// Turn on bits from yb to yt-1 (inclusive)
if yb ls 0 % yt gr nb then resultis 13
if yt ge yb then//Only show non-zero runs
[
let LeftMask=(Masks!(yb))
let RightMask= not (Masks!(yt))
yb=yb rshift 4
let wc=(yt rshift 4)-yb//Word count
let w=slbuf+yb//Word address
let bw=(@w & (not LeftMask))%(-1 & LeftMask)
for i=0 to wc-1 do
[ w!i=bw; bw=-1 ]
w!wc=(w!wc & (not RightMask))%(bw & RightMask)
] //end of "if yt ge yb"
] //end of "for i"

] repeat

//and finish putting out the last line...
let leftMask=CMasks!(16-ob)
let rightMask=not leftMask
for j=0 to hw-1 do
[ let w=Cycle(slbuf!j,16-ob)
tempPBits!0=(tempPBits!0&(not Masks!ob))+(w&rightMask)
tempPBits!1=(tempPBits!1&(Masks!ob))+(w&leftMask)
tempPBits=tempPBits+1
]
WindowWriteBlock(wo,pbits,sizeNeeded)//Last line....
FSPut(pbits)
if sl ne v>>SCV.Smax then resultis 14
] //end of doing scan conversion

resultis 0
]

//The character will occupy too much space if scan-converted and
// saved in a character file as bits. We return an error code, and
// the calling program will have the smarts to call ScaleAChar,
// which will simply scale the spline definition,
// and place it in the character file. In addition,
// the CharWidth.H entry is changed to have a special code that
// indicates this character is described by splines, and XL and YB
// have in them the file position of the spline encoding.
and ScaleAChar(w,wo,pc) = valof
[ WindowGetPosition(wo,lv pc>>CharWidth.XL)
//Save pos in XL,YB
pc>>CharWidth.H=HSplineCode
let outLen=0
let stuff=vec 13

[let op=WindowRead(w)
stuff!0=op-1//GAAAAAAA!!! PreObjects uses different codes!!!!
switchon op into
[
case DSplineFontMoveTo:
case DSplineFontDrawTo:
WindowReadBlock(w,stuff+1,4)
SCVTransformF(stuff+1,stuff+3)
stuff!1=FTR(8);stuff!2=FTR(9)
WindowWriteBlock(wo,stuff,3)
outLen=outLen+3
endcase
case DSplineFontDrawCurve:
WindowReadBlock(w,stuff+1,12)
for p=1 to 12 by 4 do
[
SCVTransformF(stuff+p,stuff+p+2)
FST(8,stuff+p)
FST(9,stuff+p+2)
]
WindowWriteBlock(wo,stuff,13)
outLen=outLen+13
endcase
case DSplineFontNewObject:
case DSplineFontEndObjects:
break//done
] //end of "switchon op"
] repeat
pc>>CharWidth.W=outLen
resultis 0 //no errors
]

and

//Set up the SCV transformation matrix from parameters that are
// supplied for converting a font:
// siz
Size of the font in micas
// rotation
Rotation of the font in minutes
// incline
Incline of the font in percent
// resx,resy
Resolution of the output device
//
(if not supplied, FPAC’s 3&4 assumed set up)

SetSCVTransform(siz,rotation,incline,resx,resy; numargs n) be
[
FLDI(5, 2540);
FLDI(1,siz); FLDI(2,siz)
if n gr 3 then [ FLDI(3,resx); FLDI(4,resy) ]
FML(1,3); FML(2,4)
FDV(1,5); FDV(2,5)//x and y scales.
test rotation ne 0 then
[//Get sine and cosine
GetFloatingCos(rotation,3)
GetFloatingCos(rotation-90*60,4)
]
or
[
FLDI(3,1); FLDI(4,0)
]
FLD(5,3); FML(5,1)//m[1,1]
FLD(6,4); FML(6,1)//m[1,2]
FNEG(4)
FLDI(7,incline);FLDI(0,100);FDV(7,0)
FML(7,3);FAD(4,7);FML(4,2)//m[2,1]
FML(3,2)//m[2,2]
SCVMatrix(5,6,4,3)
]

and

//Return floating cosine in ac. Argument is minutes of arc.

GetFloatingCos(min,ac) be [
let v=vec 4
v!1=0//Exponent
v!3=0//Low mantissa
let one=table [ 0;0;#177777;0 ] //Normalization constant

Cos(min,v,v+2)
// Now normalize the number
if v!2 then
while ((v!2)𘚠) eq 0 do [ v!2=v!2 lshift 1; v!1=v!1-1 ]
FLDV(ac,v)
FLDV(0,one)
FDV(ac,0)
]

and

Cos(theta,lvsign,lvmag) be [
//Calculate the cosine of the given angle, and return the
// magnitude as a fraction of #177777 (largest number)
// Also return sign (0 if positive, -1 if negative)

if theta ls 0 then theta=-theta
@lvsign=-(((theta+90*60)/(180*60))&1)
let d=theta rem 90*60
if ((theta/(90*60))&1) ne 0 then d=90*60-d
let min=d rem 60//Minutes part
d=d/60//Degrees part
//Now d in range 0-90 degrees

let retrievecos(d,min) =valof [//0 le d le 45
let cosar=table [
#177777;
#177765; #177727; #177645; #177537; #177405;
#177227; #177026; #176601; #176330; #176033;
#175512; #175146; #174557; #174144; #173505;
#173024; #172317; #171567; #171014; #170216;
#167376; #166532; #165645; #164735; #164002;
#163026; #162030; #161007; #157746; #156662;
#155556; #154430; #153262; #152072; #150663;
#147432; #146162; #144672; #143362; #142032;
#140463; #137075; #135471; #134045; #132405;
#130743;//46 degrees because of interpolation
]

let a=cosar!d//First answer
if min ne 0 then//Must interpolate
[
let b=cosar!(d+1)
a=a-MulDiv(a-b,min,60)//Careful about signs
]
resultis a
]

test d gr 45 then
[//Use half-angle formulae
if (d&1) ne 0 then min=min+60 //Divide angle by 2
let a=retrievecos(d rshift 1,min rshift 1)
a=MulDiv(a,a,#177777)// cos↑2(theta/2)
a=a-#100000// cos↑2 -1/2
@lvmag=a lshift 1//2 cos↑2 -1
]
or@lvmag=retrievecos(d,min)

]


and

//Return true if character described by pc>>CharWidth...
// will be too big for a scan-converted form.

SplineNeeded(pc,boundFactor;numargs na) = valof
[
if na eq 1 then boundFactor=10
let nbw=(pc>>CharWidth.H+15)/16
resultis (nbw gr 10*boundFactor) %
(nbw*pc>>CharWidth.W gr 200*boundFactor)
]