xref: /plan9/sys/src/cmd/unix/drawterm/libmemdraw/ellipse.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1*7dd7cddfSDavid du Colombier #include "../lib9.h"
2*7dd7cddfSDavid du Colombier 
3*7dd7cddfSDavid du Colombier #include "../libdraw/draw.h"
4*7dd7cddfSDavid du Colombier #include "../libmemdraw/memdraw.h"
5*7dd7cddfSDavid du Colombier #include "../libmemlayer/memlayer.h"
6*7dd7cddfSDavid du Colombier 
7*7dd7cddfSDavid du Colombier /*
8*7dd7cddfSDavid du Colombier  * ellipse(dst, c, a, b, t, src, sp)
9*7dd7cddfSDavid du Colombier  *   draws an ellipse centered at c with semiaxes a,b>=0
10*7dd7cddfSDavid du Colombier  *   and semithickness t>=0, or filled if t<0.  point sp
11*7dd7cddfSDavid du Colombier  *   in src maps to c in dst
12*7dd7cddfSDavid du Colombier  *
13*7dd7cddfSDavid du Colombier  *   very thick skinny ellipses are brushed with circles (slow)
14*7dd7cddfSDavid du Colombier  *   others are approximated by filling between 2 ellipses
15*7dd7cddfSDavid du Colombier  *   criterion for very thick when b<a: t/b > 0.5*x/(1-x)
16*7dd7cddfSDavid du Colombier  *   where x = b/a
17*7dd7cddfSDavid du Colombier  */
18*7dd7cddfSDavid du Colombier 
19*7dd7cddfSDavid du Colombier typedef struct Param	Param;
20*7dd7cddfSDavid du Colombier typedef struct State	State;
21*7dd7cddfSDavid du Colombier 
22*7dd7cddfSDavid du Colombier static	void	bellipse(int, State*, Param*);
23*7dd7cddfSDavid du Colombier static	void	erect(int, int, int, int, Param*);
24*7dd7cddfSDavid du Colombier static	void	eline(int, int, int, int, Param*);
25*7dd7cddfSDavid du Colombier 
26*7dd7cddfSDavid du Colombier struct Param {
27*7dd7cddfSDavid du Colombier 	Memimage	*dst;
28*7dd7cddfSDavid du Colombier 	Memimage	*src;
29*7dd7cddfSDavid du Colombier 	Point			c;
30*7dd7cddfSDavid du Colombier 	int			t;
31*7dd7cddfSDavid du Colombier 	Point			sp;
32*7dd7cddfSDavid du Colombier 	Memimage	*disc;
33*7dd7cddfSDavid du Colombier };
34*7dd7cddfSDavid du Colombier 
35*7dd7cddfSDavid du Colombier /*
36*7dd7cddfSDavid du Colombier  * denote residual error by e(x,y) = b^2*x^2 + a^2*y^2 - a^2*b^2
37*7dd7cddfSDavid du Colombier  * e(x,y) = 0 on ellipse, e(x,y) < 0 inside, e(x,y) > 0 outside
38*7dd7cddfSDavid du Colombier  */
39*7dd7cddfSDavid du Colombier 
40*7dd7cddfSDavid du Colombier struct State {
41*7dd7cddfSDavid du Colombier 	int	a;
42*7dd7cddfSDavid du Colombier 	int	x;
43*7dd7cddfSDavid du Colombier 	vlong	a2;	/* a^2 */
44*7dd7cddfSDavid du Colombier 	vlong	b2;	/* b^2 */
45*7dd7cddfSDavid du Colombier 	vlong	b2x;	/* b^2 * x */
46*7dd7cddfSDavid du Colombier 	vlong	a2y;	/* a^2 * y */
47*7dd7cddfSDavid du Colombier 	vlong	c1;
48*7dd7cddfSDavid du Colombier 	vlong	c2;	/* test criteria */
49*7dd7cddfSDavid du Colombier 	vlong	ee;	/* ee = e(x+1/2,y-1/2) - (a^2+b^2)/4 */
50*7dd7cddfSDavid du Colombier 	vlong	dxe;
51*7dd7cddfSDavid du Colombier 	vlong	dye;
52*7dd7cddfSDavid du Colombier 	vlong	d2xe;
53*7dd7cddfSDavid du Colombier 	vlong	d2ye;
54*7dd7cddfSDavid du Colombier };
55*7dd7cddfSDavid du Colombier 
56*7dd7cddfSDavid du Colombier static
57*7dd7cddfSDavid du Colombier State*
58*7dd7cddfSDavid du Colombier newstate(State *s, int a, int b)
59*7dd7cddfSDavid du Colombier {
60*7dd7cddfSDavid du Colombier 	s->x = 0;
61*7dd7cddfSDavid du Colombier 	s->a = a;
62*7dd7cddfSDavid du Colombier 	s->a2 = (vlong)(a*a);
63*7dd7cddfSDavid du Colombier 	s->b2 = (vlong)(b*b);
64*7dd7cddfSDavid du Colombier 	s->b2x = (vlong)0;
65*7dd7cddfSDavid du Colombier 	s->a2y = s->a2*(vlong)b;
66*7dd7cddfSDavid du Colombier 	s->c1 = -((s->a2>>2) + (vlong)(a&1) + s->b2);
67*7dd7cddfSDavid du Colombier 	s->c2 = -((s->b2>>2) + (vlong)(b&1));
68*7dd7cddfSDavid du Colombier 	s->ee = -s->a2y;
69*7dd7cddfSDavid du Colombier 	s->dxe = (vlong)0;
70*7dd7cddfSDavid du Colombier 	s->dye = s->ee<<1;
71*7dd7cddfSDavid du Colombier 	s->d2xe = s->b2<<1;
72*7dd7cddfSDavid du Colombier 	s->d2ye = s->a2<<1;
73*7dd7cddfSDavid du Colombier 	return s;
74*7dd7cddfSDavid du Colombier }
75*7dd7cddfSDavid du Colombier 
76*7dd7cddfSDavid du Colombier /*
77*7dd7cddfSDavid du Colombier  * return x coord of rightmost pixel on next scan line
78*7dd7cddfSDavid du Colombier  */
79*7dd7cddfSDavid du Colombier static
80*7dd7cddfSDavid du Colombier int
81*7dd7cddfSDavid du Colombier step(State *s)
82*7dd7cddfSDavid du Colombier {
83*7dd7cddfSDavid du Colombier 	while(s->x < s->a) {
84*7dd7cddfSDavid du Colombier 		if(s->ee+s->b2x <= s->c1 ||	/* e(x+1,y-1/2) <= 0 */
85*7dd7cddfSDavid du Colombier 		   s->ee+s->a2y <= s->c2) {	/* e(x+1/2,y) <= 0 (rare) */
86*7dd7cddfSDavid du Colombier 			s->dxe += s->d2xe;
87*7dd7cddfSDavid du Colombier 			s->ee += s->dxe;
88*7dd7cddfSDavid du Colombier 			s->b2x += s->b2;
89*7dd7cddfSDavid du Colombier 			s->x++;
90*7dd7cddfSDavid du Colombier 			continue;
91*7dd7cddfSDavid du Colombier 		}
92*7dd7cddfSDavid du Colombier 		s->dye += s->d2ye;
93*7dd7cddfSDavid du Colombier 		s->ee += s->dye;
94*7dd7cddfSDavid du Colombier 		s->a2y -= s->a2;
95*7dd7cddfSDavid du Colombier 		if(s->ee-s->a2y <= s->c2) {	/* e(x+1/2,y-1) <= 0 */
96*7dd7cddfSDavid du Colombier 			s->dxe += s->d2xe;
97*7dd7cddfSDavid du Colombier 			s->ee += s->dxe;
98*7dd7cddfSDavid du Colombier 			s->b2x += s->b2;
99*7dd7cddfSDavid du Colombier 			return s->x++;
100*7dd7cddfSDavid du Colombier 		}
101*7dd7cddfSDavid du Colombier 		break;
102*7dd7cddfSDavid du Colombier 	}
103*7dd7cddfSDavid du Colombier 	return s->x;
104*7dd7cddfSDavid du Colombier }
105*7dd7cddfSDavid du Colombier 
106*7dd7cddfSDavid du Colombier void
107*7dd7cddfSDavid du Colombier memellipse(Memimage *dst, Point c, int a, int b, int t, Memimage *src, Point sp)
108*7dd7cddfSDavid du Colombier {
109*7dd7cddfSDavid du Colombier 	State in, out;
110*7dd7cddfSDavid du Colombier 	int y, inb, inx, outx, u;
111*7dd7cddfSDavid du Colombier 	Param p;
112*7dd7cddfSDavid du Colombier 
113*7dd7cddfSDavid du Colombier 	if(a < 0)
114*7dd7cddfSDavid du Colombier 		a = -a;
115*7dd7cddfSDavid du Colombier 	if(b < 0)
116*7dd7cddfSDavid du Colombier 		b = -b;
117*7dd7cddfSDavid du Colombier 	p.dst = dst;
118*7dd7cddfSDavid du Colombier 	p.src = src;
119*7dd7cddfSDavid du Colombier 	p.c = c;
120*7dd7cddfSDavid du Colombier 	p.t = t;
121*7dd7cddfSDavid du Colombier 	p.sp = subpt(sp, c);
122*7dd7cddfSDavid du Colombier 	p.disc = nil;
123*7dd7cddfSDavid du Colombier 
124*7dd7cddfSDavid du Colombier 	u = (t<<1)*(a-b);
125*7dd7cddfSDavid du Colombier 	if((b<a && u>b*b) || (a<b && -u>a*a)) {
126*7dd7cddfSDavid du Colombier /*	if(b<a&&(t<<1)>b*b/a || a<b&&(t<<1)>a*a/b)	# very thick */
127*7dd7cddfSDavid du Colombier 		bellipse(b, newstate(&in, a, b), &p);
128*7dd7cddfSDavid du Colombier 		return;
129*7dd7cddfSDavid du Colombier 	}
130*7dd7cddfSDavid du Colombier 
131*7dd7cddfSDavid du Colombier 	if(t < 0) {
132*7dd7cddfSDavid du Colombier 		inb = -1;
133*7dd7cddfSDavid du Colombier 		newstate(&out, a, y = b);
134*7dd7cddfSDavid du Colombier 	} else {
135*7dd7cddfSDavid du Colombier 		inb = b - t;
136*7dd7cddfSDavid du Colombier 		newstate(&out, a+t, y = b+t);
137*7dd7cddfSDavid du Colombier 	}
138*7dd7cddfSDavid du Colombier 	if(t > 0)
139*7dd7cddfSDavid du Colombier 		newstate(&in, a-t, inb);
140*7dd7cddfSDavid du Colombier 	inx = 0;
141*7dd7cddfSDavid du Colombier 	for( ; y>=0; y--) {
142*7dd7cddfSDavid du Colombier 		outx = step(&out);
143*7dd7cddfSDavid du Colombier 		if(y > inb) {
144*7dd7cddfSDavid du Colombier 			erect(-outx, y, outx, y, &p);
145*7dd7cddfSDavid du Colombier 			erect(-outx, -y, outx, -y, &p);
146*7dd7cddfSDavid du Colombier 			continue;
147*7dd7cddfSDavid du Colombier 		}
148*7dd7cddfSDavid du Colombier 		if(t > 0) {
149*7dd7cddfSDavid du Colombier 			inx = step(&in);
150*7dd7cddfSDavid du Colombier 			if(y == inb)
151*7dd7cddfSDavid du Colombier 				inx = 0;
152*7dd7cddfSDavid du Colombier 		} else if(inx > outx)
153*7dd7cddfSDavid du Colombier 			inx = outx;
154*7dd7cddfSDavid du Colombier 		erect(inx, y, outx, y, &p);
155*7dd7cddfSDavid du Colombier 		erect(inx, -y, outx, -y, &p);
156*7dd7cddfSDavid du Colombier 		erect(-outx, y, -inx, y, &p);
157*7dd7cddfSDavid du Colombier 		erect(-outx, -y, -inx, -y, &p);
158*7dd7cddfSDavid du Colombier 		inx = outx + 1;
159*7dd7cddfSDavid du Colombier 	}
160*7dd7cddfSDavid du Colombier }
161*7dd7cddfSDavid du Colombier 
162*7dd7cddfSDavid du Colombier static Point p00 = {0, 0};
163*7dd7cddfSDavid du Colombier 
164*7dd7cddfSDavid du Colombier /*
165*7dd7cddfSDavid du Colombier  * a brushed ellipse
166*7dd7cddfSDavid du Colombier  */
167*7dd7cddfSDavid du Colombier static
168*7dd7cddfSDavid du Colombier void
169*7dd7cddfSDavid du Colombier bellipse(int y, State *s, Param *p)
170*7dd7cddfSDavid du Colombier {
171*7dd7cddfSDavid du Colombier 	int t, ox, oy, x, nx;
172*7dd7cddfSDavid du Colombier 
173*7dd7cddfSDavid du Colombier 	t = p->t;
174*7dd7cddfSDavid du Colombier 	p->disc = allocmemimage(Rect(-t,-t,t+1,t+1), GREY1);
175*7dd7cddfSDavid du Colombier 	if(p->disc == nil)
176*7dd7cddfSDavid du Colombier 		return;
177*7dd7cddfSDavid du Colombier 	memfillcolor(p->disc, DTransparent);
178*7dd7cddfSDavid du Colombier 	memellipse(p->disc, p00, t, t, -1, memopaque, p00);
179*7dd7cddfSDavid du Colombier 	oy = y;
180*7dd7cddfSDavid du Colombier 	ox = 0;
181*7dd7cddfSDavid du Colombier 	nx = x = step(s);
182*7dd7cddfSDavid du Colombier 	do {
183*7dd7cddfSDavid du Colombier 		while(nx==x && y-->0)
184*7dd7cddfSDavid du Colombier 			nx = step(s);
185*7dd7cddfSDavid du Colombier 		y++;
186*7dd7cddfSDavid du Colombier 		eline(-x,-oy,-ox, -y, p);
187*7dd7cddfSDavid du Colombier 		eline(ox,-oy,  x, -y, p);
188*7dd7cddfSDavid du Colombier 		eline(-x,  y,-ox, oy, p);
189*7dd7cddfSDavid du Colombier 		eline(ox,  y,  x, oy, p);
190*7dd7cddfSDavid du Colombier 		ox = x+1;
191*7dd7cddfSDavid du Colombier 		x = nx;
192*7dd7cddfSDavid du Colombier 		y--;
193*7dd7cddfSDavid du Colombier 		oy = y;
194*7dd7cddfSDavid du Colombier 	} while(oy > 0);
195*7dd7cddfSDavid du Colombier }
196*7dd7cddfSDavid du Colombier 
197*7dd7cddfSDavid du Colombier /*
198*7dd7cddfSDavid du Colombier  * a rectangle with closed (not half-open) coordinates expressed
199*7dd7cddfSDavid du Colombier  * relative to the center of the ellipse
200*7dd7cddfSDavid du Colombier  */
201*7dd7cddfSDavid du Colombier static
202*7dd7cddfSDavid du Colombier void
203*7dd7cddfSDavid du Colombier erect(int x0, int y0, int x1, int y1, Param *p)
204*7dd7cddfSDavid du Colombier {
205*7dd7cddfSDavid du Colombier 	Rectangle r;
206*7dd7cddfSDavid du Colombier 
207*7dd7cddfSDavid du Colombier /*	print("R %d,%d %d,%d\n", x0, y0, x1, y1); */
208*7dd7cddfSDavid du Colombier 	r = Rect(p->c.x+x0, p->c.y+y0, p->c.x+x1+1, p->c.y+y1+1);
209*7dd7cddfSDavid du Colombier 	memdraw(p->dst, r, p->src, addpt(p->sp, r.min), memopaque, p00);
210*7dd7cddfSDavid du Colombier }
211*7dd7cddfSDavid du Colombier 
212*7dd7cddfSDavid du Colombier /*
213*7dd7cddfSDavid du Colombier  * a brushed point similarly specified
214*7dd7cddfSDavid du Colombier  */
215*7dd7cddfSDavid du Colombier static
216*7dd7cddfSDavid du Colombier void
217*7dd7cddfSDavid du Colombier epoint(int x, int y, Param *p)
218*7dd7cddfSDavid du Colombier {
219*7dd7cddfSDavid du Colombier 	Point p0;
220*7dd7cddfSDavid du Colombier 	Rectangle r;
221*7dd7cddfSDavid du Colombier 
222*7dd7cddfSDavid du Colombier /*	print("P%d %d,%d\n", p->t, x, y);	*/
223*7dd7cddfSDavid du Colombier 	p0 = Pt(p->c.x+x, p->c.y+y);
224*7dd7cddfSDavid du Colombier 	r = Rpt(addpt(p0, p->disc->r.min), addpt(p0, p->disc->r.max));
225*7dd7cddfSDavid du Colombier 	memdraw(p->dst, r, p->src, addpt(p->sp, r.min), p->disc, p->disc->r.min);
226*7dd7cddfSDavid du Colombier }
227*7dd7cddfSDavid du Colombier 
228*7dd7cddfSDavid du Colombier /*
229*7dd7cddfSDavid du Colombier  * a brushed horizontal or vertical line similarly specified
230*7dd7cddfSDavid du Colombier  */
231*7dd7cddfSDavid du Colombier static
232*7dd7cddfSDavid du Colombier void
233*7dd7cddfSDavid du Colombier eline(int x0, int y0, int x1, int y1, Param *p)
234*7dd7cddfSDavid du Colombier {
235*7dd7cddfSDavid du Colombier /*	print("L%d %d,%d %d,%d\n", p->t, x0, y0, x1, y1); */
236*7dd7cddfSDavid du Colombier 	if(x1 > x0+1)
237*7dd7cddfSDavid du Colombier 		erect(x0+1, y0-p->t, x1-1, y1+p->t, p);
238*7dd7cddfSDavid du Colombier 	else if(y1 > y0+1)
239*7dd7cddfSDavid du Colombier 		erect(x0-p->t, y0+1, x1+p->t, y1-1, p);
240*7dd7cddfSDavid du Colombier 	epoint(x0, y0, p);
241*7dd7cddfSDavid du Colombier 	if(x1-x0 || y1-y0)
242*7dd7cddfSDavid du Colombier 		epoint(x1, y1, p);
243*7dd7cddfSDavid du Colombier }
244