// PreObjects.bcpl

// last modified by Butterfield, October 22, 1980  4:08 PM
// - ShowObject, turn off rectangle checking temporarily - 10/22
// - ShowObject, if a rectangle, use extended rectangle if necessary - 10/15
// - ShowRectangle, have 15 extended rectangles - 10/15
// - ShowObject, let sdirmin eq sdirmax & rdirmin eq rdirmax - 9/10/80

//  errors 1000
//
//ShowRectangle(w,h)
//	Called to put a rectangle description in the band list.
//ShowObject(n)
//	Called with word count of object stuff in DL to put in
//	band list.
//	%%%% Note: variant needed to deal with characters that
//		were so big that spline encodings were left %%%%
//

get "PressInternals.df"
get "PressParams.df"
get "PressFile.df"
get "Scv.dfs"

// outgoing procedures
external
	[
	ShowRectangle
	ShowObject
	]

// outgoing statics
//external
//	[
//	]
//static
//	[
//	]

// incoming procedures
external
	[
//BAND
	BandWrite
	BandSync

//PRESCAN
	CoordsConvert
	CoordsConvertBox
	CoordsConvertF
	CoordsBound

//PARTS
	CheckAvailinPart
	SkipinPart

//WINDOWS
	WindowRead
	WindowReadBlock

//PRESS
	PressError
	PressErrorV
	FSGet; FSGetX; FSPut

//PRESSML
	DoubleCop
	DoubleAdd
	TGr
	Ugt

//OS
	MoveBlock; SetBlock; Zero

//FLOAT
	FLD; FST; FTR; FLDI; FNEG; FAD; FSB; FML; FDV;
	FCM; FSN; FLDV; FSTV; FLDDP; FSTDP 
	]

// incoming statics
external
	[
	Entity		//Entity we are working on

	DL
	PreScratchW		//Window on scratch file

	BandFree
	Report
	printerDevice	//for bits structure: ORbit only handles 12 bits of x
	]

// internal statics
static
	[
	//fontpass has these guys already defined
	nomoco		//True if no more core available
	sdirmin		//Current min and max values
	sdirmax
	rdirmin
	rdirmax

	lasts		//State for putsegment
	firstpiece	//True if new MoveTo
	firstpieceflag	//State for putsegment
	firstpiecep	//    "

	todolist	//list of all things to scan convert
	KlugeCycleColors
	]

// File-wide structure and manifest declarations.

// Procedures

let ShowRectangle(w,h) be
 [
   compileif ReportSw then [ Report>>REP.nObjects=Report>>REP.nObjects+1 ]
   let s,st,b,bt=nil,nil,nil,nil
   unless CoordsConvertBox(w,h,lv s,lv st,lv b,lv bt) then
	[ PressErrorV(1004); return ]
   if b ge 4096 then //not printable in ORbit worldd
     [ if b ge 8192 then //too big to handle
	[ PressErrorV(1006);return]
       if printerDevice le printerDurango then //ORbit, blown
	[ PressErrorV(1007);return]
     ]
   BandFree>>BERectangle.Type = (((b & #170000) eq 0)? BERectangleH,
    BEExtendedRectangleH - 1 + b rshift 12);
   BandFree>>BERectangle.Sr=s
   BandFree>>BERectangle.Bit=b
   BandFree>>BERectangle.nHeight=-(bt-b+1)
   BandFree>>BERectangle.widthM1=st-s
   BandWrite(s, size BERectangle/16)
 ]

and ShowObject(n,xoff,yoff;numargs na) = valof	//returns true is simple object (rectangle)
 [ let InFile=nil
   test na eq 1 then [ InFile=DL;xoff=Entity>>EH.Xe;yoff=Entity>>EH.Ye]
		or	[ InFile=PreScratchW]

   if InFile>>W.WhichByte then PressError(1005)	//Not word-aligned
   if KlugeCycleColors then
	[
	BandSync((KlugeCycleColors rem 256))	//Something non-white
	KlugeCycleColors=KlugeCycleColors+153
	]
   compileif ReportSw then [ Report>>REP.nObjects=Report>>REP.nObjects+1 ]
   if InFile eq DL then CheckAvailinPart(DL,1,n)		//Make sure n words avail.
//******* From SCVBeginObject
	todolist=0
	firstpiece=true			//Flag for MoveTo
	firstpiecep=FSGetX(4*(orac-esd+1))
	nomoco=(firstpiecep eq 0)		//True if no more fs
	rdirmin=plusinfinity;  sdirmin=plusinfinity
	rdirmax=minusinfinity; sdirmax=minusinfinity
//******* end from SCVBeginObject
   let v=vec 20
   FLDI(3,xoff)
   FLDI(4,yoff)

   while n gr 0 do
     [
      let a=WindowRead(InFile)
      n=n-1
      switchon a into
	[
	case DMoveTo:
		[
		unless firstpiece then ObjFlushit()
		WindowReadBlock(InFile,v,2)	//Get coords
		n=n-2
		FLDI(1,v!0); FLDI(2,v!1)
		FAD(1,3); FAD(2,4)	//Add Xe,Ye
		CoordsConvertF(1,2,osac,orac,true)
		FLD(fsac,osac)
		FLD(frac,orac)
		]
		endcase
	case DDrawTo:
		[
		WindowReadBlock(InFile,v,2)
		n=n-2
		FLDI(1,v!0); FLDI(2,v!1)
		FAD(1,3); FAD(2,4)	//Add Xe,Ye
		CoordsConvertF(1,2,csac,crac,true)
		putsegment(1)
		FLD(osac,csac)
		FLD(orac,crac)
		]
		endcase

	case DDrawCurve:
		[
		WindowReadBlock(InFile,v,12)
		n=n-12
		FLD(esd,osac)
		FLD(erd,orac)		//Get 0Th derivative
		for i=0 to 5 by 2 do
		   [
		   CoordsConvertF(v+i*2,v+2+i*2,esc+i,esc+i+1,false)
		   ]
		ObjDrawCurve()		//Do it.
		]
		endcase
	default:
		[
		PressError(1000)
		if InFile eq DL then SkipinPart(DL,1,n)	//Skip remaining words
		n=0
		]
	]
     ]

   if n ne 0 then PressError(1001)
//**** From SCVEndObject
   if nomoco then
	[
	PressError(1002)
	resultis true	//nothing done, so still simple
	]
   ObjFlushit()				//Flush object
   FSPut(firstpiecep)
//**** End from SCVEndObject

//If no object results, min>max.  CoordsBound might do the right thing
// if it were modified to use "true greater than", but it would probably
// expand several dimensions of the bounding box in the process, which
// is simply wasteful.  However, because SCV itself uses Bcpl gr tests,
// we may have a non-zero todolist and still have an illegal object.
   let GoodObj=(todolist ne 0)&
		(TGr(sdirmax+1,sdirmin))&(TGr(rdirmax+1,rdirmin))
   if GoodObj then GoodObj=CoordsBound(sdirmin,sdirmax,rdirmin,rdirmax)
   let s=sdirmin

//first, check for a rectangle
if 0 & GoodObj&(@todolist ne 0)&(@@todolist eq 0)&  // turned off
		(todolist>>HD.type eq LINEtype)&
		((@todolist)>>HD.type eq LINEtype)&
		(todolist>>HD.smin eq (@todolist)>>HD.smin)&
		(todolist>>HD.smax eq (@todolist)>>HD.smax)&
		((lv todolist>>LINE.dx)!0 eq 0)&
		((lv todolist>>LINE.dx)!1 eq 0)&
		((lv (@todolist)>>LINE.dx)!0 eq 0)&
		((lv (@todolist)>>LINE.dx)!1 eq 0) then //RECTANGLE!!!
 [ let b1=lv todolist>>LINE.x
   let b2=lv (@todolist)>>LINE.x
   test b1!1 ls 0 then b1=b1!0+1 or b1=b1!0	//round up if fraction>1/2
   test b2!1 ls 0 then b2=b2!0+1 or b2=b2!0
   if Ugt(b1, b2) then [ let t = b1; b1 = b2; b2 = t; ]
   BandFree>>BERectangle.Type = (((b1 & #170000) eq 0)? BERectangleH,
    BEExtendedRectangleH - 1 + b1 rshift 12);
   BandFree>>BERectangle.Type=BERectangleH
   BandFree>>BERectangle.Sr=todolist>>HD.smin
   BandFree>>BERectangle.Bit=b1
   BandFree>>BERectangle.nHeight=-(b2-b1+1)
   BandFree>>BERectangle.widthM1=todolist>>HD.smax-todolist>>HD.smin
   BandWrite(s, size BERectangle/16)
   while todolist do 
    [ let p=todolist
      todolist=p>>HD.next
      FSPut(p)
    ]
   resultis true	//still a simple page
 ]

//Now loop through all entries in todolist, putting out band stuff.
   while todolist do
     [
      let p=todolist
      todolist=p>>HD.next
      BandFree>>SH.Sbegin=p>>HD.smin
      BandFree>>SH.Send=p>>HD.smax

      test p>>HD.type eq LINEtype then
	[
	BandFree>>BELine.H=BELineH	//A line
	DoubleCop(lv BandFree>>BELine.Bit,lv p>>LINE.x)
	DoubleCop(lv BandFree>>BELine.dBit,lv p>>LINE.dx)
	if GoodObj then BandWrite(s,size BELine/16)
	] or
	[
	BandFree>>BESpline.H=BESplineH	//A spline
	MoveBlock(lv BandFree>>BESpline.Stuff,lv p>>RSPLINE.str,16)
	if GoodObj then BandWrite(s,size BESpline/16)
	]
      FSPut(p)
     ]

   BandFree>>BEEndObject.H=BEEndObjectH	//And a terminator
   test GoodObj then BandWrite(s,size BEEndObject/16)
	or PressErrorV(1003)
   resultis not GoodObj
]

and ObjDrawCurve() be
 [
//Now bust the spline into sections
// that are monotonic in the scan direction.
	//ROOT records the value of t in FPAC t1 as a spot on
	// the curve that is an internal extremum in scan direction.
	// it sorts this value into a table of floating point
	// numbers, pointed to by PTR.  PTR!0 is the number of roots
	// so far (initially 2, t=0 and t=1).
	// (If ptr=0, we are calculating r extrema)
	let root(ptr) be [
	   if FSN(t1) eq 1 & FCM(t1,table [ #40300;0]) eq -1 then
		test ptr ne 0 then
			[ //Root lies between 0 and 1
			let i=(@ptr)*2-1; let j=i
			while FCM(t1,ptr+i) eq -1 do i=i-2
			for k=j+1 to i+2 by -1 do ptr!(k+2)=ptr!k
			FST(t1,ptr+i+2)
			@ptr=@ptr+1
			]
			or
			[
			feval(1,t1,t2)		//get r value
			let r=Floor(t2)		//truncate
			if r ls rdirmin then rdirmin=r
			if r gr rdirmax then rdirmax=r
			]
	]

	and

	findextrema(ptr,d) be [
	   let esbd=esb+d
	   test FSN(esa+d) ne 0 then 
		[				//A ne 0
		FLDI(t2,-3); FML(t2,esa+d)	//-3A
		FLD(t1,esbd); FML(t1,esbd)
		FLD(t3,t2); FML(t3,esc+d); FAD(t1,t3) //B↑2-3AC
		let b=FSN(t1)			//Sign of discriminant
		if b ne -1 then [		//Possible root
		test b eq 0 then
		   [ FLD(t1,esbd); FDV(t1,t2) ]	//-B/3A
		or [				//Take square root
		     let a=vec 3; FSTV(t1,a);a!1=a!1/2;FLDV(t3,a)
		     for i=0 to 2 do
			[
			  FLD(t4,t1);FDV(t4,t3);FAD(t3,t4)
			  FLDI(t4,2);FDV(t3,t4)
			]
		     FDV(t3,t2)			//SQRT(b↑2-3ac)/-3a
		     FLD(t4,esbd);FDV(t4,t2)	//B/-3A
		     FLD(t1,t4);FSB(t1,t3)	//+root
		     root(ptr)
		     FLD(t1,t4);FAD(t1,t3)	//-root
		   ]
		root(ptr)			//Other root
			]			//Possible root
		]
	or
		[				//A=0
		if FSN(esbd) ne 0 then
			[
			FLD(t1,esc+d);FDV(t1,esbd);FNEG(t1)
			FLDI(t2,2);FDV(t1,t2)	//-C/2B
			root(ptr)
			]
		]
	]

//If we need bounding box, record extreme values of s and r
// at any interior extrema.  Putsegment will take care of endpoint
// extrema.
//***** Edits near here *****
	findextrema(0,1)

	let ptr=vec 9; Zero(ptr,9); ptr!0=2; ptr!3=#40300
	findextrema(ptr,0)
//***** End edits *****

//Now table ptr has values of t
// that cause junctions between monotonic segments.
//"Old" points already set up.
	FLDI(tmaxac,0)
	for i=1 to ptr!0-1 do
		[
		FLD(tminac,tmaxac)		//New tmin is old tmax
		FLD(tmaxac,ptr+i*2+1)		//Get junction from table
		feval(0,tmaxac,csac)		//Evaluate current points
		feval(1,tmaxac,crac)
		putsegment(0)			//Do the spline
		FLD(osac,csac)			//Set old points
		FLD(orac,crac)
		]
]

and

ObjFlushit() be [
	unless firstpiece then
		[
		FLD(csac,fsac)
		FLD(crac,frac)
		putsegment(2)	//Join up.
		putsegment(-1)		//Flush last piece.
		firstpiece=true
		]
]

and

//This function checks for closure and builds LINE and SPLINE
//blocks for later reference.
//Entry conditions:
//Line (lineflag=1 or 2):
//	Draw a line from (osac,orac) to (csac,crac)
//	Must leave csac,crac untouched because they are used
//	to reset the "old" point for next time.
//Spline (lineflag=0):
//	Draw a spline from (osac,orac) to (csac,crac) which
//	corresponds to values of t, tminac le t le tmaxac.
//	Coefficients are in esa...esd and era...erd
//	Must leave csac, crac unchanged.
//Finish up call (lineflag=-1):
//	There is global state in: firstpiece,
//		firstpiecep and firstpieceflag..
//	Firstpiece is true if this is the first segment of a closed curve.

putsegment(lineflag) be [

//	external TypeForm
//	let str=vec 2
//	TypeForm(2,csac,32,2,crac,1,str)

if lineflag ls 0 then
	[				//Restore accumulators
	lineflag=firstpieceflag
	let p=firstpiecep
	for i=(lineflag? csac,esd) to orac do
		[ FLDV(i,p); p=p+4 ]
	]

	let smin=Floor(osac)		//Value of S at tmin
	let smax=Floor(csac)		//Value of S at tmax

//Update r direction extrema (must do before checking smin=smax because
//  we only check "current" value of r direction, so we must check every
//  leg of the closed curve).
	let r=Floor(crac)
	if r ls rdirmin then rdirmin=r
	if r gr rdirmax then rdirmax=r

	if smin eq smax then return	//No intersections.
	if firstpiece then
		[
		firstpiece=false
		firstpieceflag=lineflag
		let p=firstpiecep
		for i=(lineflag? csac,esd) to orac do
			[ FSTV(i,p); p=p+4 ]
		lasts=smax		//This is what we need to know.
		return
		]

	let stop,sbot=nil,nil
//Thisdirection is 1 if increasing t gives decreasing s
	let thisdirection=(smin gr smax)?1,-1
	test thisdirection eq 1 then
		[ stop=lasts; sbot=smax+1 ]
	or
		[ stop=smax; sbot=lasts+1 ]

//Update s direction extrema	
	if sbot ls sdirmin then sdirmin=sbot
	if stop gr sdirmax then sdirmax=stop

//Save scan-line intersection state for next time.
	lasts=smax


//Get free storage block to hold this line or spline
	if nomoco then return		//None available, return
//***** Edits near here to remove cond assy, FSGet *****
	let nx=FSGet((lineflag? size LINE/16,size RSPLINE/16))
	if nx eq 0 then
		[			//Out of free storage
		nomoco=true		//Say so.
		let p=todolist		//
		while p do		//Release core
			[
			let np=p>>HD.next
			FSPut(p)
			p=np
			]
		return
		]
//***** End of edits *****

//Build description of line or spline segment

test lineflag then
	[ //LINE
	nx>>LINE.type=LINEtype
	FLD(t1,crac);FSB(t1,orac)		//Delta r
	FLD(t2,csac);FSB(t2,osac)		//Delta s
	FDV(t1,t2);FSTDP(t1,lv nx>>LINE.dx)	//Increment each scan line
	FLDI(t2,sbot);FSB(t2,osac)
	FML(t1,t2);FAD(t1,orac)			//Bottom point.
	FSTDP(t1,lv nx>>LINE.x)			//and save it.
	] //LINE
or
	[ //SPLINE
	nx>>RSPLINE.type=SPLINEtype

	test thisdirection ls 0 then
		[
		FLD(t1,tminac)			//t1 is at low s end
		FLD(t2,tmaxac)
		]
	or
		[
		FLD(t1,tmaxac)
		FLD(t2,tminac)
		]
	
// CEVAL computes and fills in F(tac) values and G(tac) values.
//	Indx=0 for s direction, 1 for r.
	let ceval(indx,tac,ptr) be [
		feval(indx,tac,t4)			//Get value of function.
		FSTDP(t4,ptr)			//Put down value.
		FLDI(t4,3);FML(t4,esa+indx);FML(t4,tac) //3At
		FAD(t4,esb+indx)			//3At+B
		FML(t4,t3)			//(dt↑2)(3At+B)
		FSTDP(t4,ptr+2)			//Put it down.
		]
	
	FLD(t3,t2);FSB(t3,t1);FML(t3,t3)	//(dt↑2)
	ceval(0,t1,lv nx>>RSPLINE.stl)
	ceval(1,t1,lv nx>>RSPLINE.rtl)
	ceval(0,t2,lv nx>>RSPLINE.str)
	ceval(1,t2,lv nx>>RSPLINE.rtr)
	] //SPLINE

//Record some common information in the block.
	nx>>HD.smin=sbot
	nx>>HD.smax=stop
	nx>>HD.next=todolist; todolist=nx
]

and

feval(indx,tac,rac) be [
//Evaluate the spline (indx=0, s direction; 1 r direction)
// tac=AC with value of t; rac=AC for result
	FLD(rac,esa+indx)
	FML(rac,tac)
	FAD(rac,esb+indx)
	FML(rac,tac)
	FAD(rac,esc+indx)
	FML(rac,tac)
	FAD(rac,esd+indx)
]

and

Floor(ac) = valof [			//Standard floor function
	let a=FTR(ac)
	if FSN(ac) ls 0 then
		[
		let s1=vec 3
		let s2=vec 3
		FSTV(ac,s1)	//Save some ac's
		FSTV(31,s2)
		a=-a+4		//Get number to make positive
		FLDI(31,a)
		FAD(ac,31)	//Make it positive
		a=FTR(ac)-a	//Take floor and subtract offset
		FLDV(31,s2)
		FLDV(ac,s1)
		]

	resultis a
]