// ChatDisCurve.bcpl  --  P.Baudelaire
// Copyright Xerox Corporation 1979
// January 31, 1978  9:52 AM

get "Chat.d"
get "ChatDis.d"

// outgoing procedures:

external [
	CurveSetup
	DrawCurve
	]


// incoming procedures:

external [
	Gets			// SYSTEM
	Zero
	DoubleAdd

	BitBlt			// CHATDISOPS
	]


// incoming statics:

external [
	SS			// CHAT
	]


// local statics:

static [
	DCbbc
	// current & next computed points
	DColdX
	DColdY
	DCx
	DCy
	DCnewX
	DCnewY
	// clipping limits
	DCminX
	DCmaxX
	DCminY
	DCmaxY
	// brush height (brush width DCbw is a constant)
	DCbh
	]

// local definitions:

manifest [
	DCbw=16
	]


let CurveSetup(r, drawMode, brushShape, brushWidth) be [CurveSetup
	let brush=GetBrush(brushShape, brushWidth)
	// clipping information (statics)
	DCbh=brush!0
	DCminX=r>>REG.Left - DCbw
	DCmaxX=r>>REG.Right
	DCminY=r>>REG.Top - DCbh
	DCmaxY=r>>REG.Bottom
	// setup BitBlt control block
	DCbbc=SS>>DISV.CurveBbc
	DCbbc>>BBC.SBCA=brush+1
	DCbbc>>BBC.Function=drawMode & 3
	]CurveSetup


and DrawCurve(x0, y0, x1, y1, d1X, d1Y, d2X, d2Y, d3X, d3Y, n) be [DrawCurve
	// d*X & d*Y are "double precision" finite differences in special format:
	// bit 0 is sign
	// bit 1 to 31 is 2's complement fractional part
	// starting point, with offset for brush size:
	DCx=x0-DCbw/2
	DCy=y0-DCbh/2
	DColdX, DColdY = DCx, DCy
	DCnewX, DCnewY = DCx, DCy
	DrawPoint()
	let x=vec 2; x!0=DCx; x!1=#100000	// for rounding
	let y=vec 2; y!0=DCy; y!1=#100000
	let dX=vec 2; let dY=vec 2
	for j=1 to n do [
		dX!1=(d1X!0) lshift 1;  dX!0=not((d1X!0) ge 0)
		dY!1=(d1Y!0) lshift 1;  dY!0=not((d1Y!0) ge 0)
		DoubleAdd(x, dX); DoubleAdd(y, dY)
		DoubleAdd(d1X, d2X); DoubleAdd(d1Y, d2Y)
		DoubleAdd(d2X, d3X); DoubleAdd(d2Y, d3Y)
		DCnewX, DCnewY = x!0, y!0
		// check consecutive points in L pattern
		unless ((DCnewX gr DColdX) ? DCnewX-DColdX, DColdX-DCnewX) le 1 &
		  ((DCnewY gr DColdY) ? DCnewY-DColdY, DColdY-DCnewY) le 1 then [
			DrawPoint()
			DColdX, DColdY = DCx, DCy
			]
		DCx, DCy = DCnewX, DCnewY
		]
	DrawPoint()
	DCx=x1-DCbw/2
	DCy=y1-DCbh/2
	DrawPoint()
	]DrawCurve



and DrawPoint() be [DrawPoint
	// draw point (DCx,DCy) iff inside clipping window
	if (DCx le DCminX) % (DCx gr DCmaxX)
	 % (DCy le DCminY) % (DCy gr DCmaxY) return
	test DCx ls (DCminX+DCbw)
	ifso [
		DCbbc>>BBC.DLX= DCminX+DCbw
		DCbbc>>BBC.DW= DCx-DCminX
		DCbbc>>BBC.SLX= DCminX+DCbw-DCx
		]
	ifnot [
		DCbbc>>BBC.DLX= DCx
		DCbbc>>BBC.DW= ((DCx gr (DCmaxX-DCbw+1)) ? (DCmaxX-DCx+1), DCbw)
		DCbbc>>BBC.SLX= 0
		]
	test DCy ls (DCminY+DCbh)
	ifso [
		DCbbc>>BBC.DTY= DCminY+DCbh
		DCbbc>>BBC.DH= DCy-DCminY
		DCbbc>>BBC.STY= DCminY+DCbh-DCy
		]
	ifnot [
		DCbbc>>BBC.DTY= DCy
		DCbbc>>BBC.DH= (DCy gr (DCmaxY-DCbh+1)) ? (DCmaxY-DCy+1), DCbh
		DCbbc>>BBC.STY= 0
		]
	BitBlt(DCbbc)
	]DrawPoint



and GetBrush(brushShape, brushSize) = valof [
	// returns a pointer to the brush pattern (16 wide, H height)
	// The brush pattern is stored as:
	//			H word
	//			bitPattern↑1,H word
	//
	// Values for brushShape are:
	//		0= round brush
	//		1= rectangular brush
	//		2= horizontal bar brush
	//		3= vertical bar brush
	//		4= diagonal bar brush
	//
	// brushSize is any value between 1 and 16, which will be rounded
	// to the values 1, 2, 4, 8, 16.

	let brushTable= table [
		// dot 1:
		1; #200;
		// dot 2:
		2; #600; #600;
		// dot 4:
		4; #600; #1700; #1700; #600;
		// dot 8:
		8; #1700; #3740; #7760; #7760; #7760; #7760; #3740; #1700;
		// dot 16:
		16; #3740; #17770; #37774; #77776; #77776; #177777; #177777; #177777; 
		#177777; #177777; #177777; #77776; #77776; #37774; #17770; #3740;

		// rect 4:
		4; #1700; #1700; #1700; #1700;
		// rect 8:
		8; #7760; #7760; #7760; #7760; #7760; #7760; #7760; #7760;
		// rect 16:
		16; #177777; #177777; #177777; #177777; #177777; #177777; #177777; #177777; 
		#177777; #177777; #177777; #177777; #177777; #177777; #177777; #177777;

		// hor 2:
		1; #600;
		// hor 4:
		1; #1700;
		// hor 8:
		1; #7760;
		// hor 16:
		1; #17777;

		// ver 2:
		2; #400; #400;
		// ver 4:
		4; #400; #400; #400; #400;
		// ver 8:
		8; #400; #400; #400; #400; #400; #400; #400; #400;
		// ver 16:
		16; #400; #400; #400; #400; #400; #400; #400; #400;
		#400; #400; #400; #400; #400; #400; #400; #400;

		// diag 2:
		2; #200; #400;
		// diag 4;
		4; #100; #200; #400; #1000;
		// diag 8:
		8; #20; #40; #100; #200; #400; #1000; #2000; #4000;
		// diag 16:
		16; 1; 2; 4; #10; #20; #40; #100; #200;
		#400; #1000; #2000; #4000; #10000; #20000; #40000; #100000
		]

	manifest [
		dot1=0; dot2=dot1+2; dot4=dot2+3; dot8=dot4+5; dot16=dot8+9
		rect1=dot1; rect2=dot2; rect4=dot16+17; rect8=rect4+5; rect16=rect8+9
		hor1=dot1; hor2=rect16+17; hor4=hor2+2; hor8=hor4+2; hor16=hor8+2
		ver1=dot1; ver2=hor16+2; ver4=ver2+3; ver8=ver4+5; ver16=ver8+9
		diag1=dot1; diag2=ver16+17; diag4=diag2+3; diag8=diag4+5; diag16=diag8+9
		]

	let brushIndexTable = table [
		dot1; dot2; dot4; dot8; dot16;
		rect1; rect2; rect4; rect8; rect16;
		hor1; hor2; hor4; hor8; hor16;
		ver1; ver2; ver4; ver8; ver16;
		diag1; diag2; diag4; diag8; diag16;
		]

	let widthRoundingTable = table [
		0; 0; 1; 1; 2; 2; 2; 3; 3; 3; 3; 3; 4; 4; 4; 4; 4 ]

	if brushSize gr 16 then brushSize=16
	if (brushShape ls 0) % (brushShape gr 4) then brushShape=0
	resultis (brushTable +
		brushIndexTable!(5*brushShape + widthRoundingTable!brushSize))
	]