// S C V M A I N -- SCAN CONVERTER    (PREPRESS)
// catalog number ???

//Modified by Lyle Ramshaw, June 25, 1980  3:14 PM:
//  The quadratic eqaution solver in the procedure 'findextrema"
//  was subtracting two numbers of roughly equal magnitude, and
//  hence loosing all the sigmificant bits.  I rewrote it so that
//  it (on Jim Boyce's suggetion) computes the root that doesn't
//  involve loss of significance in the normal way, and then 
//  computes the other root by division into (C/A), which is the
//  product of the roots.
 
//Scan Converter for PREPRESS and PRESS
// Throughout, the terminology "s" is for "scan-line direction" and
// "r" is for "run direction".  The distinction is simply
// one of how the sorting is done -- first on s, then on r.
// For an Alto screen, s=vertical(y), r=horizontal(x); for Slot,
// s=horizontal(x), r=vertical(y)

// INITIALIZATION, etc:
//SCVInit(getblock,putblock,error)
//	Begins operation.  The three arguments are the addresses of
//	three subroutines needed: for getting and releasing free-storage
//	blocks, and for error conditions.
//	This routine may be called any number of times.
//SCVMatrix(a,b,c,d)
//	Sets (scale) transformation matrix from floating
//	point numbers.  An argument of 0 is taken as short for 0.
//	Transformation is:
//		s' = a*s + c*r
//		r' = b*s + d*r
//SCVTransFormF(s,r [,v])
//	Carefully transform the points s and r by the matrix,
//	and return in v (optional):
//	v!0=s	v!1=r
//	also leaves results in FPac's 8,9.

// BUILDING OBJECT DESCRIPTIONS:
//SCVBeginObject([care, monotonic, segproc, makebothmonotone])
//	Called to initialize for a new object. "care" is true
//	if you wish careful scan-conversion to be done; default false.
//	"monotonic" is true if you know that all splines will be monotonic
//	in the s direction; default false.  "segproc," if not 0, is called
//	each time a boundary segment is ready (see code); default 0.
//	"makebothmonotone," if true, will cause segments to be monotonic
//	in r and s directions; default false.
//SCVMoveTo(s,r)  and SCVMoveToF(s,r)
//	Like PICO MOVETO, fixed and floating point versions
//SCVDrawTo(s,r) and SCVDrawToF(s,r)
//	Like PICO DRAWTO, fixed and floating point versions
//SCVDrawCurve(s',r',s''/2,r''/2,s'''/6,r'''/6)
//	Like PICO DRAWCURVE, except arguments are coefficients of
//	the polynomial, rather than derivatives.
//SCVEndObject(v)
//	Finishes out an object, and returns in v (structure SCV):
//	v>>SCV.Smin, v>>SCV.Smax	Extent of object in s.
//	v>>SCV.Rmin, v>>SCV.Rmax	Extent of object in r.
//	v>>SCV.Object	Object descriptor for the object (not to tamper!)
//	v>>SCV.Error	Non-zero if error
//		1= no more core
//		2,3= infinite loops in spline evaluations
//	NB: the r values are only filled in accurately for
//	     splines if "care" is true in SCVBeginObject!!!

// SCAN CONVERSION:
//SCVReadRuns(v,buffer,bufsize)
//	Calculate a bunch of intersections:
//	v>>SCV.Sbegin and v>>SCV.Send are the limits (inclusive) of the
//	region you are requesting scan-conversion of.  You
//	must proceed from min s to max s (as returned by
//	SCVEndObject in v>>SCV.Smin and v>>SCV.Smax).
//	"buffer" is the address of a buffer of "bufsize" words to
//	use to store intersections.
//	Returns in v:
//	v>>SCV.Send 	Maximum S value converted to
//			intersections (will be less than you
//			requested if there was insufficient buffer)
//	v>>SCV.IntPtr	Pointer to first intersection.
//	v>>SCV.IntCnt	Number of intersections returned (even no.).
//	v>>SCV.Object	Updated object descriptor (not to tamper!)
//SCVFlush(v)
//	If you get an object back, and do not wish to actually
//	go through with the scan conversion, you can return all
//	storage gracefully with SCVFlush(v).



//Comment about buffer layout internally.  
// 2 words	- infinity
// 2n words	intersections
// 2 words	+ infinity


get "scv.dfs"

// outgoing procedures
external
	[
	SCVInit
	SCVMatrix
	SCVTransformF

	SCVBeginObject
	SCVMoveTo
	SCVMoveToF
	SCVDrawTo
	SCVDrawToF
	SCVDrawCurve
	SCVEndObject

	SCVReadRuns
	SCVFlush
	Floor
	]

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

// incoming procedures
external
	[
	Zero; SetBlock; MoveBlock
	QuickSort
	FLD; FST; FTR; FLDI; FNEG; FAD; FSB; FML; FDV;
	FCM; FSN; FLDV; FSTV; FLDDP; FSTDP; DPAD; DPSB; DPSHR
	]

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

// internal statics
static
	[
	SCVGetB		//Routine to get free storage
	SCVPutB		//Routine to return free storage

	@careful		//True if careful scan conversion
	@monotone	//True if splines monotonic in s
	@makermonotone	//True if splines to be made monotone in r
	@segproc		//Non-zero if segment procedure exists --this is it
	@nomoco		//True if no more core available
	@simplescale	//True if off-diagnoal matrix elements zero
	@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
	]


// Procedures

let

SCVInit(getb,putb) be [
	SCVGetB=getb
	SCVPutB=putb
]

and

SCVMatrix(a,b,c,d) be [
	simplescale=false
	for i=0 to 3 do
		test (lv a)!i eq 0 then FLDI(sssac+i,0)
				   or   FLD(sssac+i,(lv a)!i)
	if FSN(srsac) eq 0 & FSN(ssrac) eq 0 then simplescale=true
]

and

SCVTransformF(s,r,v; numargs nargs) be [
	FLD(t1,s); FLD(t2,r)
	scaleit(t1,t2)			//Do transformation.
	if nargs eq 2 then return
	FLDI(t3,1); FLDI(t4,2); FDV(t3,t4); FLD(t4,t3)
	FAD(t3,t1); FAD(t4,t2);
	v!0=FTR(t3)
	v!1=FTR(t4)
]

and

scaleit(s,r) be [
	test simplescale 
	ifso 	[
		FML(s,sssac)
		FML(r,srrac)
		]
	ifnot	[
		FLD(t3,s); FML(t3,sssac)
		FLD(t4,r); FML(t4,srsac)
		FAD(t3,t4)		//T1=new s
		FLD(t4,s)
		FLD(s,t3)
		FML(t4,ssrac)
		FML(r,srrac)
		FAD(r,t4)
		]
]
and

SCVBeginObject(care,mono,seg,makemono; numargs nargs) be [
	switchon nargs into [		//Fill in defaults
	case 0:	care=false
	case 1:	mono=false
	case 2:	seg=0
	case 3:	makemono=false
		endcase
	]
	careful=care			//Save in statics
	monotone=mono
	segproc=seg
	makermonotone=makemono

	todolist=0
	firstpiece=true			//Flag for MoveTo
	firstpiecep=SCVGetB(4*(orac-esd+1))
	nomoco=(firstpiecep eq 0)		//True if no more fs
	rdirmin=plusinfinity;  sdirmin=plusinfinity
	rdirmax=minusinfinity; sdirmax=minusinfinity
]

and

SCVMoveTo(sdir,rdir) be [
	unless firstpiece then flushit()
	FLDI(osac,sdir)
	FLDI(orac,rdir)
	SCVMoveToF()			//..continue here..
]

and

SCVMoveToF(sac,rac; numargs n) be [
//Flush any previous closed curve and load the "old" point
	if n then
		[
		unless firstpiece then flushit()
		FLD(osac,sac)
		FLD(orac,rac)
		]
	scaleit(osac,orac)		//Do Matrix multiply
	FLD(fsac,osac)			//Save memory of first position.
	FLD(frac,orac)
]

and

SCVDrawTo(sdir,rdir) be [
	FLDI(csac,sdir)
	FLDI(crac,rdir)
	SCVDrawToF()			//..continue here..
]

and

SCVDrawToF(sac,rac; numargs n) be [
	if n then
		[
		FLD(csac,sac)
		FLD(crac,rac)
		]
	scaleit(csac,crac)		//Do Matrix multiply

//Now draw line from "old" point to "current" point.
	putsegment(1)		//Enter a description.
	FLD(osac,csac)			//Old←New
	FLD(orac,crac)
]

// Spline stuff.
and

SCVDrawCurve(s,nil,nil,nil,nil,nil) be [
compileif SplineHandling ne NoSplines then [
	FLD(esd,osac)
	FLD(erd,orac)		//Get 0Th derivative
	for i=0 to 5 do FLD(esc+i,(lv s)!i)
	for i=esc to esa by 2 do scaleit(i,i+1) //Transform.

//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); root(ptr) ]	//-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
		     //the two roots are now t4+t3 and t4-t3
		     //but computing one of them may involve a lot of
		     //cancellation of significance.  So, we first
		     //calculate the safe one:
		     switchon FSN(t3)*FSN(t4) into
			[
		      case 1: //same sign, so adding is safe
			FLD(t1,t4);FAD(t1,t3);root(ptr)
			docase 2
		      case -1: //opposite signs, so subtracting is safe
			FLD(t1,t4);FSB(t1,t3);root(ptr)
			docase 2
		      case 0: //at least one is zero, so either is safe
			FLD(t1,t4);FAD(t1,t3);root(ptr)
			FLD(t1,t4);FSB(t1,t3);root(ptr)
			endcase
		      case 2:  //one root is in t1;  now we
				//compute the other root by dividing into
				// (C/3A), which is product of roots
			FLD(t3,t1)	//save first root
			FLD(t1,esc+d); FDV(t1,t2); FNEG(t1) // C/3A
			FDV(t1,t3); root(ptr); endcase
			]
		   ]
			      ]			//End of 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.
	if careful then findextrema(0,1)

	let ptr=vec 13; Zero(ptr, 13); ptr!0=2; ptr!3=#40300
	if not monotone then
		[
		if makermonotone then findextrema(ptr,1)
		findextrema(ptr,0)
		]

//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)
		]
]					//Conditional assembly
]

and

SCVEndObject(v) be [
	flushit()
	SCVPutB(firstpiecep)
	v>>SCV.Smin=sdirmin
	v>>SCV.Smax=sdirmax
	v>>SCV.Rmin=rdirmin
	v>>SCV.Rmax=rdirmax
	v>>SCV.Object=todolist
	v>>SCV.Error=(nomoco ne 0? 1,0)
]

and

SCVFlush(v) be [
	let s=v>>SCV.Object
	while s do
		[
		let ns=s>>HD.next
		SCVPutB(s)
		s=ns
		]
	v>>SCV.Object=0
]

and

flushit() 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 segproc then
	[				//Lineflag=0,1 only!!!!
	segproc(lineflag)
	return
	]

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
	let nxsiz=nil
	compileif SplineHandling eq AdditiveSplines then [ nxsiz=size SPLINE/16 ]
	compileif SplineHandling eq RecursiveSplines then [ nxsiz=size RSPLINE/16 ]

	let nx=SCVGetB((lineflag? size LINE/16,nxsiz))
	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
			SCVPutB(p)
			p=np
			]
		return
		]

//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
compileif SplineHandling eq AdditiveSplines then [
	nx>>SPLINE.type=SPLINEtype

	test thisdirection ls 0 then 
		[
		FLD(t1,tminac)
		FLD(t2,tmaxac); FSB(t2,tminac)
		]
	or
		[
		FLD(t1,tmaxac)
		FLD(t2,tminac); FSB(t2,tmaxac)
		]
	FLDI(t3,((stop-sbot) rshift 1)+2)	//Nevals
	FDV(t2,t3)				//Delta T
	FST(t1,lv nx>>SPLINE.t0)
	FST(t2,lv nx>>SPLINE.dt)		//Save in block
	for i=0 to era-esd do FST(i+esd,(lv nx>>SPLINE.coeffs)+i*2)
]					//Conditional assy
compileif SplineHandling eq RecursiveSplines then [
	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)
]					//Conditional assy
	] //SPLINE

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

and 

//Here is the main routine for calculating intersections, and stuffing
// them in the buffer.  Calls QuickSort to sort intersections.

SCVReadRuns(v,buffer,bufsize) be [
	let ibufsize=(bufsize rshift 1)-2	//Max no. of intersections
						//(leave room for 2 guards)
	let LScan=v>>SCV.Sbegin
	let RScan=v>>SCV.Send
	let p,c,l,r=nil,nil,nil,nil

	[
	c=0
	p=v>>SCV.Object
	while p ne 0 do
		[				//See if enough room
		if p>>HD.smin le RScan then
			[			//We need to look at it
			l=p>>HD.smin
			if l ls LScan then l=LScan
			r=p>>HD.smax
			if r gr RScan then r=RScan
			c=c+r-l+1		//Number of intersections
			]
		p=p>>HD.next
		]
	test c gr ibufsize 
		ifso RScan=(LScan+RScan)/2
		ifnot break
	] repeat

//Now it is safe to scan from LScan to RScan inclusive --
// buffer will not overflow.

	let b=buffer+2				//Pointer to intersections.
	let pp=(lv v>>SCV.Object)-(lv 0>>HD.next)
	p=pp>>HD.next
	while p ne 0 do
		[				//Loop scan converting.
		if p>>HD.smin le RScan then
		[
		l=p>>HD.smin; if l ls LScan then l=LScan
		r=p>>HD.smax; if r gr RScan then r=RScan

		switchon p>>HD.type into
		[
		case LINEtype:
			for s=l to r do
				[
				b!0=s; b!1=(lv p>>LINE.x)!0
				b=b+2
				DPAD(lv p>>LINE.x,lv p>>LINE.dx)
				]
			endcase
		case SPLINEtype:
compileif SplineHandling eq AdditiveSplines then [
			for i=0 to era-esd do
			  FLD(i+esd,(lv p>>SPLINE.coeffs)+i*2)
			FLD(t1,lv p>>SPLINE.t0)
			FLD(tt1,lv p>>SPLINE.dt)
			FAD(FLD(t2,t1),tt1)
			feval(0,t1,t3)
			feval(0,t2,t4)

//Idea is to always have two points (say 0 and 1)
//that span the S value required.  Then we use secant approximation
//to calculate an intersection.  The initialization code tries
//to get:
//	AC t1 = T0 	**stored in block
//	AC t2 = T1
//	AC t3 = Fs(T0)
//	AC t4 = Fs(T1)
//	AC tt1 = dt	**stored in block

	for s=l to r do
		[				//Main loop
		FLDI(tt2,s)			//S sought
						//If s<Fs(T0) use T0
		test FCM(tt2,t3) ls 0 then FLD(tt2,t1) or
			[
			let cnt=-100
			while FCM(tt2,t4) eq 1 do
				[		//s>Fs(T1)
				FLD(t1,t2)	//T0←T1
				FLD(t3,t4)
				FAD(t2,tt1)	//T1←T1+delta T
				feval(0,t2,t4)	// new value
//Repeatedly adding delta T to T0 will only approximately get us
// back to the value Tn that has Floor(Fs(Tn))=p>>HD.smax, because of
// roundoff errors.  If we were VERY UNLUCKY, it might happen that
// Fs(T0+i*dT)<(p>>HD.smax) for all i, in which case this searching
// loop will dive right off the deep end.  To prevent that, here
// is what we do: (this patch by Lyle Ramshaw)
				if FCM(t4,t3) ls 0 then 
					[
					FLD(tt2, t1)  //biggest we can get
					goto PatchLabel
					]
//End of patch by Lyle Ramshaw
				cnt=cnt+1
				if cnt eq 0 then
					[
					v>>SCV.Error=2 //Infinite loop
					return
					]
				]
			FML(FSB(tt2,t3),tt1)	//Secant
			FSB(FLD(tt3,t4),t3)
			FAD(FDV(tt2,tt3),t1)
			]
//label in next line is the other component of above Ramshaw patch
PatchLabel:	feval(1,tt2,tt3)		//tt3 = Fr(T)
		let lx=Floor(tt3)
		b!0=s; b!1=lx			//All the work for 2 values!
		b=b+2
		]
			FST(t1,lv p>>SPLINE.t0)	//Save for more??
]					//Conditional assy
compileif SplineHandling eq RecursiveSplines then [
			let onehalf=table [ 0;#100000 ] //DP 1/2
			let tolerance=table [ 0;#100000 ] //DP 1/2

//Layout of "stack":  each entry is 16 words, divided into
//	4 DP numbers for S direction, followed by
//	4 DP numbers for R direction (see structure RSPLINE)
//Each block of four is:
//		 F(tright)
//		 G(tright,n)
//		 F(tleft)
//		 G(tleft,n)
// (see Sproull notes for meaning of F,G)
			let sp=vec 200		//Stacks.
			let se=sp-16		//End if you get here
			MoveBlock(sp,lv p>>RSPLINE.str,16)
			let s=l			//Scan-line number
			if sp!4 eq s then	//Begins exactly at s.
			  [
			   b!0=l; b!1=sp!12	//Intersection.
			   b=b+2; s=s+1
			  ]
		[	if sp eq se then break	//Stack empty
			test sp!0 ls s then	//Right edge < sl
			  [
			   sp=sp-16; 		//Pop stack, move right
			  ] or
			test sp!0 eq s &(valof [
				let v=vec 2
				v!0=sp!0; v!1=sp!1 //Larger S.
				DPSB(v,sp+4)	//minus smaller S.
				let a=DPSB(v,tolerance)
				if a ls 0 then resultis true //Small enough
				resultis false	//Search to finer detail
				]) then
			  [			//Output a point.
			   let ra=DPAD(sp+8,sp+12)	//Average two R's
			   b!0=s; b!1=(ra+1)/2	//R value (rounded)
			   b=b+2
			   if s eq r then break	//done
			   s=s+1		//Bump scan-line count
			   sp=sp-16		//Pop stack.
			  ] or
			  [			//Subdivide....
			   let x=sp		//Part of stack to subdivide
			   for i=0 to 1 do	//Two blocks on stack.
		[				//Subdivide stack "x":
						// x!0,1  F(tright)
						// x!2,3  G(tright,n)
						// x!4,5  F(tleft)
						// x!6,7  G(tleft,n)
			let x2=x+2; let x6=x+6; let x4=x+4
			DPSHR(x2); DPSHR(x2)	//Gright
			DPSHR(x6); DPSHR(x6)	//Gleft
			x!22=x!6;x!23=x!7	//Gleft in place
			DPAD(x6,x2)		//Gleft+Gright
			DPSHR(x6)		//Gmiddle
			x!18=x!6;x!19=x!7	// and again
			x!20=x!4;x!21=x!5	//Xleft in place
			DPAD(x4,x)		//Xleft+Xright
			DPSHR(x4)		// *.5
			DPSB(x4,x6)		//.5(Xleft+Xright)-Gmiddle
			x!16=x!4;x!17=x!5		//Xmiddle again

			x=x+8			//Do the other block next time.
		]

			  sp=sp+16		//Bump stacks.
			  ]			//Subdivide
		] repeat			//Recursive loop
			if s ne r then
				[
				v>>SCV.Error=3
				return
				]
]					//Conditional assy
			endcase
		]				//switchon
		if p>>HD.smax le RScan then
			[			//Exhausts
			pp>>HD.next=p>>HD.next
			SCVPutB(p)
			p=pp
			]
		]
		pp=p
		p=p>>HD.next			//Look at next
	]					//while p ne 0

	QuickSort(buffer,c)			//Sort the intersections

	v>>SCV.Send=RScan			//Speak to user
	v>>SCV.IntCnt=c
	v>>SCV.IntPtr=buffer+2
]

and

feval(indx,tac,rac) be [
compileif SplineHandling ne NoSplines then [
//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)
]					//Conditional assy
]

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
]