xref: /plan9/sys/src/libdraw/bezier.c (revision ac57dd0bdfb9d49ce3ebb32937bb07f2cec3da6c)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <draw.h>
47dd7cddfSDavid du Colombier 
57dd7cddfSDavid du Colombier #define	PINC	32		/* realloc granularity */
67dd7cddfSDavid du Colombier 
77dd7cddfSDavid du Colombier typedef struct Plist Plist;
87dd7cddfSDavid du Colombier struct Plist
97dd7cddfSDavid du Colombier {
107dd7cddfSDavid du Colombier 	Point *p;
117dd7cddfSDavid du Colombier 	int np;			/* -1 if malloc/realloc failed */
127dd7cddfSDavid du Colombier };
137dd7cddfSDavid du Colombier 
147dd7cddfSDavid du Colombier static void
appendpt(Plist * l,Point p)157dd7cddfSDavid du Colombier appendpt(Plist *l, Point p)
167dd7cddfSDavid du Colombier {
177dd7cddfSDavid du Colombier 	if(l->np == -1)
187dd7cddfSDavid du Colombier 		return;
197dd7cddfSDavid du Colombier 	if(l->np == 0)
207dd7cddfSDavid du Colombier 		l->p = malloc(PINC*sizeof(Point));
217dd7cddfSDavid du Colombier 	else if(l->np%PINC == 0)
227dd7cddfSDavid du Colombier 		l->p = realloc(l->p, (l->np+PINC)*sizeof(Point));
237dd7cddfSDavid du Colombier 	if(l->p == 0){
247dd7cddfSDavid du Colombier 		l->np = -1;
257dd7cddfSDavid du Colombier 		return;
267dd7cddfSDavid du Colombier 	}
277dd7cddfSDavid du Colombier 	l->p[l->np++] = p;
287dd7cddfSDavid du Colombier }
297dd7cddfSDavid du Colombier 
307dd7cddfSDavid du Colombier static int
normsq(Point p)317dd7cddfSDavid du Colombier normsq(Point p)
327dd7cddfSDavid du Colombier {
337dd7cddfSDavid du Colombier 	return p.x*p.x+p.y*p.y;
347dd7cddfSDavid du Colombier }
357dd7cddfSDavid du Colombier 
367dd7cddfSDavid du Colombier static int
psdist(Point p,Point a,Point b)377dd7cddfSDavid du Colombier psdist(Point p, Point a, Point b)
387dd7cddfSDavid du Colombier {
397dd7cddfSDavid du Colombier 	int num, den;
407dd7cddfSDavid du Colombier 
417dd7cddfSDavid du Colombier 	p = subpt(p, a);
427dd7cddfSDavid du Colombier 	b = subpt(b, a);
437dd7cddfSDavid du Colombier 	num = p.x*b.x + p.y*b.y;
447dd7cddfSDavid du Colombier 	if(num <= 0)
457dd7cddfSDavid du Colombier 		return normsq(p);
467dd7cddfSDavid du Colombier 	den = normsq(b);
477dd7cddfSDavid du Colombier 	if(num >= den)
487dd7cddfSDavid du Colombier 		return normsq(subpt(b, p));
497dd7cddfSDavid du Colombier 	return normsq(subpt(divpt(mulpt(b, num), den), p));
507dd7cddfSDavid du Colombier }
517dd7cddfSDavid du Colombier 
527dd7cddfSDavid du Colombier /*
537dd7cddfSDavid du Colombier  * Convert cubic Bezier curve control points to polyline
547dd7cddfSDavid du Colombier  * vertices.  Leaves the last vertex off, so you can continue
557dd7cddfSDavid du Colombier  * with another curve.
567dd7cddfSDavid du Colombier  */
577dd7cddfSDavid du Colombier static void
bpts1(Plist * l,Point p0,Point p1,Point p2,Point p3,int scale)587dd7cddfSDavid du Colombier bpts1(Plist *l, Point p0, Point p1, Point p2, Point p3, int scale)
597dd7cddfSDavid du Colombier {
607dd7cddfSDavid du Colombier 	Point p01, p12, p23, p012, p123, p0123;
617dd7cddfSDavid du Colombier 	Point tp0, tp1, tp2, tp3;
627dd7cddfSDavid du Colombier 	tp0=divpt(p0, scale);
637dd7cddfSDavid du Colombier 	tp1=divpt(p1, scale);
647dd7cddfSDavid du Colombier 	tp2=divpt(p2, scale);
657dd7cddfSDavid du Colombier 	tp3=divpt(p3, scale);
667dd7cddfSDavid du Colombier 	if(psdist(tp1, tp0, tp3)<=1 && psdist(tp2, tp0, tp3)<=1){
677dd7cddfSDavid du Colombier 		appendpt(l, tp0);
687dd7cddfSDavid du Colombier 		appendpt(l, tp1);
697dd7cddfSDavid du Colombier 		appendpt(l, tp2);
707dd7cddfSDavid du Colombier 	}
717dd7cddfSDavid du Colombier 	else{
727dd7cddfSDavid du Colombier 		/*
737dd7cddfSDavid du Colombier 		 * if scale factor is getting too big for comfort,
747dd7cddfSDavid du Colombier 		 * rescale now & concede the rounding error
757dd7cddfSDavid du Colombier 		 */
767dd7cddfSDavid du Colombier 		if(scale>(1<<12)){
777dd7cddfSDavid du Colombier 			p0=tp0;
787dd7cddfSDavid du Colombier 			p1=tp1;
797dd7cddfSDavid du Colombier 			p2=tp2;
807dd7cddfSDavid du Colombier 			p3=tp3;
817dd7cddfSDavid du Colombier 			scale=1;
827dd7cddfSDavid du Colombier 		}
837dd7cddfSDavid du Colombier 		p01=addpt(p0, p1);
847dd7cddfSDavid du Colombier 		p12=addpt(p1, p2);
857dd7cddfSDavid du Colombier 		p23=addpt(p2, p3);
867dd7cddfSDavid du Colombier 		p012=addpt(p01, p12);
877dd7cddfSDavid du Colombier 		p123=addpt(p12, p23);
887dd7cddfSDavid du Colombier 		p0123=addpt(p012, p123);
897dd7cddfSDavid du Colombier 		bpts1(l, mulpt(p0, 8), mulpt(p01, 4), mulpt(p012, 2), p0123, scale*8);
907dd7cddfSDavid du Colombier 		bpts1(l, p0123, mulpt(p123, 2), mulpt(p23, 4), mulpt(p3, 8), scale*8);
917dd7cddfSDavid du Colombier 	}
927dd7cddfSDavid du Colombier }
937dd7cddfSDavid du Colombier 
947dd7cddfSDavid du Colombier static void
bpts(Plist * l,Point p0,Point p1,Point p2,Point p3)957dd7cddfSDavid du Colombier bpts(Plist *l, Point p0, Point p1, Point p2, Point p3)
967dd7cddfSDavid du Colombier {
977dd7cddfSDavid du Colombier 	bpts1(l, p0, p1, p2, p3, 1);
987dd7cddfSDavid du Colombier }
997dd7cddfSDavid du Colombier 
1007dd7cddfSDavid du Colombier static void
bezierpts(Plist * l,Point p0,Point p1,Point p2,Point p3)1017dd7cddfSDavid du Colombier bezierpts(Plist *l, Point p0, Point p1, Point p2, Point p3)
1027dd7cddfSDavid du Colombier {
1037dd7cddfSDavid du Colombier 	bpts(l, p0, p1, p2, p3);
1047dd7cddfSDavid du Colombier 	appendpt(l, p3);
1057dd7cddfSDavid du Colombier }
1067dd7cddfSDavid du Colombier 
1077dd7cddfSDavid du Colombier static void
_bezsplinepts(Plist * l,Point * pt,int npt)108*ac57dd0bSDavid du Colombier _bezsplinepts(Plist *l, Point *pt, int npt)
1097dd7cddfSDavid du Colombier {
1107dd7cddfSDavid du Colombier 	Point *p, *ep;
1117dd7cddfSDavid du Colombier 	Point a, b, c, d;
1127dd7cddfSDavid du Colombier 	int periodic;
1137dd7cddfSDavid du Colombier 
1147dd7cddfSDavid du Colombier 	if(npt<3)
1157dd7cddfSDavid du Colombier 		return;
1167dd7cddfSDavid du Colombier 	ep = &pt[npt-3];
1177dd7cddfSDavid du Colombier 	periodic = eqpt(pt[0], ep[2]);
1187dd7cddfSDavid du Colombier 	if(periodic){
1197dd7cddfSDavid du Colombier 		a = divpt(addpt(ep[1], pt[0]), 2);
1207dd7cddfSDavid du Colombier 		b = divpt(addpt(ep[1], mulpt(pt[0], 5)), 6);
1217dd7cddfSDavid du Colombier 		c = divpt(addpt(mulpt(pt[0], 5), pt[1]), 6);
1227dd7cddfSDavid du Colombier 		d = divpt(addpt(pt[0], pt[1]), 2);
1237dd7cddfSDavid du Colombier 		bpts(l, a, b, c, d);
1247dd7cddfSDavid du Colombier 	}
1257dd7cddfSDavid du Colombier 	for(p=pt; p<=ep; p++){
1267dd7cddfSDavid du Colombier 		if(p==pt && !periodic){
1277dd7cddfSDavid du Colombier 			a = p[0];
1287dd7cddfSDavid du Colombier 			b = divpt(addpt(p[0], mulpt(p[1], 2)), 3);
1297dd7cddfSDavid du Colombier 		}
1307dd7cddfSDavid du Colombier 		else{
1317dd7cddfSDavid du Colombier 			a = divpt(addpt(p[0], p[1]), 2);
1327dd7cddfSDavid du Colombier 			b = divpt(addpt(p[0], mulpt(p[1], 5)), 6);
1337dd7cddfSDavid du Colombier 		}
1347dd7cddfSDavid du Colombier 		if(p==ep && !periodic){
1357dd7cddfSDavid du Colombier 			c = divpt(addpt(mulpt(p[1], 2), p[2]), 3);
1367dd7cddfSDavid du Colombier 			d = p[2];
1377dd7cddfSDavid du Colombier 		}
1387dd7cddfSDavid du Colombier 		else{
1397dd7cddfSDavid du Colombier 			c = divpt(addpt(mulpt(p[1], 5), p[2]), 6);
1407dd7cddfSDavid du Colombier 			d = divpt(addpt(p[1], p[2]), 2);
1417dd7cddfSDavid du Colombier 		}
1427dd7cddfSDavid du Colombier 		bpts(l, a, b, c, d);
1437dd7cddfSDavid du Colombier 	}
1447dd7cddfSDavid du Colombier 	appendpt(l, d);
1457dd7cddfSDavid du Colombier }
1467dd7cddfSDavid du Colombier 
1477dd7cddfSDavid du Colombier int
bezsplinepts(Point * pt,int npt,Point ** pp)148*ac57dd0bSDavid du Colombier bezsplinepts(Point *pt, int npt, Point **pp)
149*ac57dd0bSDavid du Colombier {
150*ac57dd0bSDavid du Colombier 	Plist l;
151*ac57dd0bSDavid du Colombier 	l.np = 0;
152*ac57dd0bSDavid du Colombier 	l.p = nil;
153*ac57dd0bSDavid du Colombier 	_bezsplinepts(&l, pt, npt);
154*ac57dd0bSDavid du Colombier 	*pp  = l.p;
155*ac57dd0bSDavid du Colombier 	return l.np;
156*ac57dd0bSDavid du Colombier }
157*ac57dd0bSDavid du Colombier 
158*ac57dd0bSDavid du Colombier int
bezier(Image * dst,Point p0,Point p1,Point p2,Point p3,int end0,int end1,int radius,Image * src,Point sp)1597dd7cddfSDavid du Colombier bezier(Image *dst, Point p0, Point p1, Point p2, Point p3, int end0, int end1, int radius, Image *src, Point sp)
1607dd7cddfSDavid du Colombier {
161*ac57dd0bSDavid du Colombier 	return bezierop(dst, p0, p1, p2, p3, end0, end1, radius, src, sp, SoverD);
162*ac57dd0bSDavid du Colombier }
163*ac57dd0bSDavid du Colombier 
164*ac57dd0bSDavid du Colombier int
bezierop(Image * dst,Point p0,Point p1,Point p2,Point p3,int end0,int end1,int radius,Image * src,Point sp,Drawop op)165*ac57dd0bSDavid du Colombier bezierop(Image *dst, Point p0, Point p1, Point p2, Point p3, int end0, int end1, int radius, Image *src, Point sp, Drawop op)
166*ac57dd0bSDavid du Colombier {
1677dd7cddfSDavid du Colombier 	Plist l;
1687dd7cddfSDavid du Colombier 
1697dd7cddfSDavid du Colombier 	l.np = 0;
1707dd7cddfSDavid du Colombier 	bezierpts(&l, p0, p1, p2, p3);
1717dd7cddfSDavid du Colombier 	if(l.np == -1)
1727dd7cddfSDavid du Colombier 		return 0;
1737dd7cddfSDavid du Colombier 	if(l.np != 0){
174*ac57dd0bSDavid du Colombier 		polyop(dst, l.p, l.np, end0, end1, radius, src, addpt(subpt(sp, p0), l.p[0]), op);
1757dd7cddfSDavid du Colombier 		free(l.p);
1767dd7cddfSDavid du Colombier 	}
1777dd7cddfSDavid du Colombier 	return 1;
1787dd7cddfSDavid du Colombier }
1797dd7cddfSDavid du Colombier 
1807dd7cddfSDavid du Colombier int
bezspline(Image * dst,Point * pt,int npt,int end0,int end1,int radius,Image * src,Point sp)1817dd7cddfSDavid du Colombier bezspline(Image *dst, Point *pt, int npt, int end0, int end1, int radius, Image *src, Point sp)
1827dd7cddfSDavid du Colombier {
183*ac57dd0bSDavid du Colombier 	return bezsplineop(dst, pt, npt, end0, end1, radius, src, sp, SoverD);
184*ac57dd0bSDavid du Colombier }
185*ac57dd0bSDavid du Colombier 
186*ac57dd0bSDavid du Colombier int
bezsplineop(Image * dst,Point * pt,int npt,int end0,int end1,int radius,Image * src,Point sp,Drawop op)187*ac57dd0bSDavid du Colombier bezsplineop(Image *dst, Point *pt, int npt, int end0, int end1, int radius, Image *src, Point sp, Drawop op)
188*ac57dd0bSDavid du Colombier {
1897dd7cddfSDavid du Colombier 	Plist l;
1907dd7cddfSDavid du Colombier 
1917dd7cddfSDavid du Colombier 	l.np = 0;
192*ac57dd0bSDavid du Colombier 	_bezsplinepts(&l, pt, npt);
1937dd7cddfSDavid du Colombier 	if(l.np==-1)
1947dd7cddfSDavid du Colombier 		return 0;
1957dd7cddfSDavid du Colombier 	if(l.np != 0){
196*ac57dd0bSDavid du Colombier 		polyop(dst, l.p, l.np, end0, end1, radius, src, addpt(subpt(sp, pt[0]), l.p[0]), op);
1977dd7cddfSDavid du Colombier 		free(l.p);
1987dd7cddfSDavid du Colombier 	}
1997dd7cddfSDavid du Colombier 	return 1;
2007dd7cddfSDavid du Colombier }
2017dd7cddfSDavid du Colombier 
2027dd7cddfSDavid du Colombier int
fillbezier(Image * dst,Point p0,Point p1,Point p2,Point p3,int w,Image * src,Point sp)2037dd7cddfSDavid du Colombier fillbezier(Image *dst, Point p0, Point p1, Point p2, Point p3, int w, Image *src, Point sp)
2047dd7cddfSDavid du Colombier {
205*ac57dd0bSDavid du Colombier 	return fillbezierop(dst, p0, p1, p2, p3, w, src, sp, SoverD);
206*ac57dd0bSDavid du Colombier }
207*ac57dd0bSDavid du Colombier 
208*ac57dd0bSDavid du Colombier int
fillbezierop(Image * dst,Point p0,Point p1,Point p2,Point p3,int w,Image * src,Point sp,Drawop op)209*ac57dd0bSDavid du Colombier fillbezierop(Image *dst, Point p0, Point p1, Point p2, Point p3, int w, Image *src, Point sp, Drawop op)
210*ac57dd0bSDavid du Colombier {
2117dd7cddfSDavid du Colombier 	Plist l;
2127dd7cddfSDavid du Colombier 
2137dd7cddfSDavid du Colombier 	l.np = 0;
2147dd7cddfSDavid du Colombier 	bezierpts(&l, p0, p1, p2, p3);
2157dd7cddfSDavid du Colombier 	if(l.np == -1)
2167dd7cddfSDavid du Colombier 		return 0;
2177dd7cddfSDavid du Colombier 	if(l.np != 0){
218*ac57dd0bSDavid du Colombier 		fillpolyop(dst, l.p, l.np, w, src, addpt(subpt(sp, p0), l.p[0]), op);
2197dd7cddfSDavid du Colombier 		free(l.p);
2207dd7cddfSDavid du Colombier 	}
2217dd7cddfSDavid du Colombier 	return 1;
2227dd7cddfSDavid du Colombier }
2237dd7cddfSDavid du Colombier 
2247dd7cddfSDavid du Colombier int
fillbezspline(Image * dst,Point * pt,int npt,int w,Image * src,Point sp)2257dd7cddfSDavid du Colombier fillbezspline(Image *dst, Point *pt, int npt, int w, Image *src, Point sp)
2267dd7cddfSDavid du Colombier {
227*ac57dd0bSDavid du Colombier 	return fillbezsplineop(dst, pt, npt, w, src, sp, SoverD);
228*ac57dd0bSDavid du Colombier }
229*ac57dd0bSDavid du Colombier 
230*ac57dd0bSDavid du Colombier int
fillbezsplineop(Image * dst,Point * pt,int npt,int w,Image * src,Point sp,Drawop op)231*ac57dd0bSDavid du Colombier fillbezsplineop(Image *dst, Point *pt, int npt, int w, Image *src, Point sp, Drawop op)
232*ac57dd0bSDavid du Colombier {
2337dd7cddfSDavid du Colombier 	Plist l;
2347dd7cddfSDavid du Colombier 
2357dd7cddfSDavid du Colombier 	l.np = 0;
236*ac57dd0bSDavid du Colombier 	_bezsplinepts(&l, pt, npt);
2377dd7cddfSDavid du Colombier 	if(l.np == -1)
2387dd7cddfSDavid du Colombier 		return 0;
2397dd7cddfSDavid du Colombier 	if(l.np > 0){
240*ac57dd0bSDavid du Colombier 		fillpolyop(dst, l.p, l.np, w, src, addpt(subpt(sp, pt[0]), l.p[0]), op);
2417dd7cddfSDavid du Colombier 		free(l.p);
2427dd7cddfSDavid du Colombier 	}
2437dd7cddfSDavid du Colombier 	return 1;
2447dd7cddfSDavid du Colombier }
245