Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#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;
}