// C O N V E R T  -- read SD, create CD  (PREPRESS,PRESS)
// catalog number ???

//ConvertAWidth(ps,pc,p)
//	Convert the widths of a single character.
//	ps -> SplineWidth.  Fills up pc -> CharWidth.
//	p -> Convert structure.
//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
//		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.
//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
	SetSCVTransform
	Cos
	]

// outgoing statics
external
	[
	convertThicken
	]
static
	[
	convertThicken=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

//Floating point
	FLD;FST;FTR;FLDI;FNEG;FAD;FSB;FML;FDV;FCM;FSN
	FLDV;FSTV;FLDDP;FSTDP;DPAD;DPSB
	]

// incoming statics
//external
//	[
//	]

// internal statics
//static
//	[
//	]

// 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 CD 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.

	if 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) then
			[
			c>>CharWidth.H=HSplineCode
			spline=true
			]
		]
	   ]
	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

	let nbw=(nb+15) rshift 4		//Number of "words" high

//Now decide if splines needed.
	test p>>Convert.SplineOk & SplineNeeded(pc) then
	[

//The character will occupy too much space if scan-converted and
// saved in a character file as bits.  So, we 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.

		WindowSetPosition(w,opos)	//Restart
		WindowGetPosition(wo,opos+2)
		pc>>CharWidth.H=HSplineCode
		pc>>CharWidth.XL=opos!2		//Save position
		pc>>CharWidth.YB=opos!3
		len=originallen
	[	if len eq 0 then break		//Copy splines to wo
		let op=WindowRead(w)
		stuff!0=op
		let l = selecton op into [
		case DSplineFontMoveTo:
		case DSplineFontDrawTo:	4
		case DSplineFontDrawCurve: 12
		case DSplineFontNewObject:
		case DSplineFontEndObjects: 0
			]
		WindowReadBlock(w,stuff+1,l)
		for p=1 to l by 4 do	//Splines must be scaled, rot'd
			[
			SCVTransformF(stuff+p,stuff+p+2)
			FST(8,stuff+p)
			FST(9,stuff+p+2)
			]
		WindowWriteBlock(wo,stuff,l+1)
		if l eq 0 then break	//End
	] repeat
		SCVFlush(v)		//Release any storage
	]
or
	[				//Can convert OK

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

	let sl=nil
	sl<<FHEAD.hw=nbw
	sl<<FHEAD.ns=ns			//Make up font header
	WindowWrite(wo,sl)
	if sl eq 0 then resultis 0		//(Only if space)

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

	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 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.
			[
			WindowWriteBlock(wo,slbuf,nbw)
			Zero(slbuf,nbw)
			sl=sl+1
			]
		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&#17))
		let RightMask= not (Masks!(yt&#17))
		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)
		]
		]

] repeat
	WindowWriteBlock(wo,slbuf,nbw)	//Last line....
	if sl ne v>>SCV.Smax then resultis 14
	]

	resultis 0
]

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,25400)
	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)&#100000) 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) = valof [
	let nbw=(pc>>CharWidth.H+15)/16
	resultis (nbw gr 100) % (nbw*pc>>CharWidth.W gr 2000)
]