aboutsummaryrefslogtreecommitdiff
path: root/src/libdraw/bezier.c
diff options
context:
space:
mode:
authorrsc <devnull@localhost>2003-09-30 17:47:42 +0000
committerrsc <devnull@localhost>2003-09-30 17:47:42 +0000
commited7c8e8d02c02bdbff1e88a6d8d1419f39af48ad (patch)
treeebcd32d20b0df2584bce713fefa87620ecd1cce7 /src/libdraw/bezier.c
parentb2cfc4e2e71d0f0a5113ddfbd93c8285cc4d74e4 (diff)
downloadplan9port-ed7c8e8d02c02bdbff1e88a6d8d1419f39af48ad.tar.gz
plan9port-ed7c8e8d02c02bdbff1e88a6d8d1419f39af48ad.tar.bz2
plan9port-ed7c8e8d02c02bdbff1e88a6d8d1419f39af48ad.zip
Initial import.
Diffstat (limited to 'src/libdraw/bezier.c')
-rw-r--r--src/libdraw/bezier.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/src/libdraw/bezier.c b/src/libdraw/bezier.c
new file mode 100644
index 00000000..dca61bc0
--- /dev/null
+++ b/src/libdraw/bezier.c
@@ -0,0 +1,244 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+
+#define PINC 32 /* realloc granularity */
+
+typedef struct Plist Plist;
+struct Plist
+{
+ Point *p;
+ int np; /* -1 if malloc/realloc failed */
+};
+
+static void
+appendpt(Plist *l, Point p)
+{
+ if(l->np == -1)
+ return;
+ if(l->np == 0)
+ l->p = malloc(PINC*sizeof(Point));
+ else if(l->np%PINC == 0)
+ l->p = realloc(l->p, (l->np+PINC)*sizeof(Point));
+ if(l->p == 0){
+ l->np = -1;
+ return;
+ }
+ l->p[l->np++] = p;
+}
+
+static int
+normsq(Point p)
+{
+ return p.x*p.x+p.y*p.y;
+}
+
+static int
+psdist(Point p, Point a, Point b)
+{
+ int num, den;
+
+ p = subpt(p, a);
+ b = subpt(b, a);
+ num = p.x*b.x + p.y*b.y;
+ if(num <= 0)
+ return normsq(p);
+ den = normsq(b);
+ if(num >= den)
+ return normsq(subpt(b, p));
+ return normsq(subpt(divpt(mulpt(b, num), den), p));
+}
+
+/*
+ * Convert cubic Bezier curve control points to polyline
+ * vertices. Leaves the last vertex off, so you can continue
+ * with another curve.
+ */
+static void
+bpts1(Plist *l, Point p0, Point p1, Point p2, Point p3, int scale)
+{
+ Point p01, p12, p23, p012, p123, p0123;
+ Point tp0, tp1, tp2, tp3;
+ tp0=divpt(p0, scale);
+ tp1=divpt(p1, scale);
+ tp2=divpt(p2, scale);
+ tp3=divpt(p3, scale);
+ if(psdist(tp1, tp0, tp3)<=1 && psdist(tp2, tp0, tp3)<=1){
+ appendpt(l, tp0);
+ appendpt(l, tp1);
+ appendpt(l, tp2);
+ }
+ else{
+ /*
+ * if scale factor is getting too big for comfort,
+ * rescale now & concede the rounding error
+ */
+ if(scale>(1<<12)){
+ p0=tp0;
+ p1=tp1;
+ p2=tp2;
+ p3=tp3;
+ scale=1;
+ }
+ p01=addpt(p0, p1);
+ p12=addpt(p1, p2);
+ p23=addpt(p2, p3);
+ p012=addpt(p01, p12);
+ p123=addpt(p12, p23);
+ p0123=addpt(p012, p123);
+ bpts1(l, mulpt(p0, 8), mulpt(p01, 4), mulpt(p012, 2), p0123, scale*8);
+ bpts1(l, p0123, mulpt(p123, 2), mulpt(p23, 4), mulpt(p3, 8), scale*8);
+ }
+}
+
+static void
+bpts(Plist *l, Point p0, Point p1, Point p2, Point p3)
+{
+ bpts1(l, p0, p1, p2, p3, 1);
+}
+
+static void
+bezierpts(Plist *l, Point p0, Point p1, Point p2, Point p3)
+{
+ bpts(l, p0, p1, p2, p3);
+ appendpt(l, p3);
+}
+
+static void
+_bezsplinepts(Plist *l, Point *pt, int npt)
+{
+ Point *p, *ep;
+ Point a, b, c, d;
+ int periodic;
+
+ if(npt<3)
+ return;
+ ep = &pt[npt-3];
+ periodic = eqpt(pt[0], ep[2]);
+ if(periodic){
+ a = divpt(addpt(ep[1], pt[0]), 2);
+ b = divpt(addpt(ep[1], mulpt(pt[0], 5)), 6);
+ c = divpt(addpt(mulpt(pt[0], 5), pt[1]), 6);
+ d = divpt(addpt(pt[0], pt[1]), 2);
+ bpts(l, a, b, c, d);
+ }
+ for(p=pt; p<=ep; p++){
+ if(p==pt && !periodic){
+ a = p[0];
+ b = divpt(addpt(p[0], mulpt(p[1], 2)), 3);
+ }
+ else{
+ a = divpt(addpt(p[0], p[1]), 2);
+ b = divpt(addpt(p[0], mulpt(p[1], 5)), 6);
+ }
+ if(p==ep && !periodic){
+ c = divpt(addpt(mulpt(p[1], 2), p[2]), 3);
+ d = p[2];
+ }
+ else{
+ c = divpt(addpt(mulpt(p[1], 5), p[2]), 6);
+ d = divpt(addpt(p[1], p[2]), 2);
+ }
+ bpts(l, a, b, c, d);
+ }
+ appendpt(l, d);
+}
+
+int
+bezsplinepts(Point *pt, int npt, Point **pp)
+{
+ Plist l;
+ l.np = 0;
+ l.p = nil;
+ _bezsplinepts(&l, pt, npt);
+ *pp = l.p;
+ return l.np;
+}
+
+int
+bezier(Image *dst, Point p0, Point p1, Point p2, Point p3, int end0, int end1, int radius, Image *src, Point sp)
+{
+ return bezierop(dst, p0, p1, p2, p3, end0, end1, radius, src, sp, SoverD);
+}
+
+int
+bezierop(Image *dst, Point p0, Point p1, Point p2, Point p3, int end0, int end1, int radius, Image *src, Point sp, Drawop op)
+{
+ Plist l;
+
+ l.np = 0;
+ bezierpts(&l, p0, p1, p2, p3);
+ if(l.np == -1)
+ return 0;
+ if(l.np != 0){
+ polyop(dst, l.p, l.np, end0, end1, radius, src, addpt(subpt(sp, p0), l.p[0]), op);
+ free(l.p);
+ }
+ return 1;
+}
+
+int
+bezspline(Image *dst, Point *pt, int npt, int end0, int end1, int radius, Image *src, Point sp)
+{
+ return bezsplineop(dst, pt, npt, end0, end1, radius, src, sp, SoverD);
+}
+
+int
+bezsplineop(Image *dst, Point *pt, int npt, int end0, int end1, int radius, Image *src, Point sp, Drawop op)
+{
+ Plist l;
+
+ l.np = 0;
+ _bezsplinepts(&l, pt, npt);
+ if(l.np==-1)
+ return 0;
+ if(l.np != 0){
+ polyop(dst, l.p, l.np, end0, end1, radius, src, addpt(subpt(sp, pt[0]), l.p[0]), op);
+ free(l.p);
+ }
+ return 1;
+}
+
+int
+fillbezier(Image *dst, Point p0, Point p1, Point p2, Point p3, int w, Image *src, Point sp)
+{
+ return fillbezierop(dst, p0, p1, p2, p3, w, src, sp, SoverD);
+}
+
+int
+fillbezierop(Image *dst, Point p0, Point p1, Point p2, Point p3, int w, Image *src, Point sp, Drawop op)
+{
+ Plist l;
+
+ l.np = 0;
+ bezierpts(&l, p0, p1, p2, p3);
+ if(l.np == -1)
+ return 0;
+ if(l.np != 0){
+ fillpolyop(dst, l.p, l.np, w, src, addpt(subpt(sp, p0), l.p[0]), op);
+ free(l.p);
+ }
+ return 1;
+}
+
+int
+fillbezspline(Image *dst, Point *pt, int npt, int w, Image *src, Point sp)
+{
+ return fillbezsplineop(dst, pt, npt, w, src, sp, SoverD);
+}
+
+int
+fillbezsplineop(Image *dst, Point *pt, int npt, int w, Image *src, Point sp, Drawop op)
+{
+ Plist l;
+
+ l.np = 0;
+ _bezsplinepts(&l, pt, npt);
+ if(l.np == -1)
+ return 0;
+ if(l.np > 0){
+ fillpolyop(dst, l.p, l.np, w, src, addpt(subpt(sp, pt[0]), l.p[0]), op);
+ free(l.p);
+ }
+ return 1;
+}