]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libdraw/bezier.c
devvmx: add support for SG_STICKY segments
[plan9front.git] / sys / src / libdraw / bezier.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4
5 #define PINC    32              /* realloc granularity */
6
7 typedef struct Plist Plist;
8 struct Plist
9 {
10         Point *p;
11         int np;                 /* -1 if malloc/realloc failed */
12 };
13
14 static void
15 appendpt(Plist *l, Point p)
16 {
17         if(l->np == -1)
18                 return;
19         if(l->np == 0)
20                 l->p = malloc(PINC*sizeof(Point));
21         else if(l->np%PINC == 0)
22                 l->p = realloc(l->p, (l->np+PINC)*sizeof(Point));
23         if(l->p == 0){
24                 l->np = -1;
25                 return;
26         }
27         l->p[l->np++] = p;
28 }
29
30 static int
31 normsq(Point p)
32 {
33         return p.x*p.x+p.y*p.y;
34 }
35
36 static int
37 psdist(Point p, Point a, Point b)
38 {
39         int num, den;
40
41         p = subpt(p, a);
42         b = subpt(b, a);
43         num = p.x*b.x + p.y*b.y;
44         if(num <= 0)
45                 return normsq(p);
46         den = normsq(b);
47         if(num >= den)
48                 return normsq(subpt(b, p));
49         return normsq(subpt(divpt(mulpt(b, num), den), p));
50 }
51
52 /*
53  * Convert cubic Bezier curve control points to polyline
54  * vertices.  Leaves the last vertex off, so you can continue
55  * with another curve.
56  */
57 static void
58 bpts1(Plist *l, Point p0, Point p1, Point p2, Point p3, int scale)
59 {
60         Point p01, p12, p23, p012, p123, p0123;
61         Point tp0, tp1, tp2, tp3;
62         tp0=divpt(p0, scale);
63         tp1=divpt(p1, scale);
64         tp2=divpt(p2, scale);
65         tp3=divpt(p3, scale);
66         if(psdist(tp1, tp0, tp3)<=1 && psdist(tp2, tp0, tp3)<=1){
67                 appendpt(l, tp0);
68                 appendpt(l, tp1);
69                 appendpt(l, tp2);
70         }
71         else{
72                 /*
73                  * if scale factor is getting too big for comfort,
74                  * rescale now & concede the rounding error
75                  */
76                 if(scale>(1<<12)){
77                         p0=tp0;
78                         p1=tp1;
79                         p2=tp2;
80                         p3=tp3;
81                         scale=1;
82                 }
83                 p01=addpt(p0, p1);
84                 p12=addpt(p1, p2);
85                 p23=addpt(p2, p3);
86                 p012=addpt(p01, p12);
87                 p123=addpt(p12, p23);
88                 p0123=addpt(p012, p123);
89                 bpts1(l, mulpt(p0, 8), mulpt(p01, 4), mulpt(p012, 2), p0123, scale*8);
90                 bpts1(l, p0123, mulpt(p123, 2), mulpt(p23, 4), mulpt(p3, 8), scale*8);
91         }
92 }
93
94 static void
95 bpts(Plist *l, Point p0, Point p1, Point p2, Point p3)
96 {
97         bpts1(l, p0, p1, p2, p3, 1);
98 }
99
100 static void
101 bezierpts(Plist *l, Point p0, Point p1, Point p2, Point p3)
102 {
103         bpts(l, p0, p1, p2, p3);
104         appendpt(l, p3);
105 }
106
107 static void
108 _bezsplinepts(Plist *l, Point *pt, int npt)
109 {
110         Point *p, *ep;
111         Point a, b, c, d;
112         int periodic;
113
114         if(npt<3)
115                 return;
116         ep = &pt[npt-3];
117         periodic = eqpt(pt[0], ep[2]);
118         if(periodic){
119                 a = divpt(addpt(ep[1], pt[0]), 2);
120                 b = divpt(addpt(ep[1], mulpt(pt[0], 5)), 6);
121                 c = divpt(addpt(mulpt(pt[0], 5), pt[1]), 6);
122                 d = divpt(addpt(pt[0], pt[1]), 2);
123                 bpts(l, a, b, c, d);
124         }
125         for(p=pt; p<=ep; p++){
126                 if(p==pt && !periodic){
127                         a = p[0];
128                         b = divpt(addpt(p[0], mulpt(p[1], 2)), 3);
129                 }
130                 else{
131                         a = divpt(addpt(p[0], p[1]), 2);
132                         b = divpt(addpt(p[0], mulpt(p[1], 5)), 6);
133                 }
134                 if(p==ep && !periodic){
135                         c = divpt(addpt(mulpt(p[1], 2), p[2]), 3);
136                         d = p[2];
137                 }
138                 else{
139                         c = divpt(addpt(mulpt(p[1], 5), p[2]), 6);
140                         d = divpt(addpt(p[1], p[2]), 2);
141                 }
142                 bpts(l, a, b, c, d);
143         }
144         appendpt(l, d);
145 }
146
147 int
148 bezsplinepts(Point *pt, int npt, Point **pp)
149 {
150         Plist l;
151         l.np = 0;
152         l.p = nil;
153         _bezsplinepts(&l, pt, npt);
154         *pp  = l.p;
155         return l.np;
156 }
157
158 int
159 bezier(Image *dst, Point p0, Point p1, Point p2, Point p3, int end0, int end1, int radius, Image *src, Point sp)
160 {
161         return bezierop(dst, p0, p1, p2, p3, end0, end1, radius, src, sp, SoverD);
162 }
163
164 int
165 bezierop(Image *dst, Point p0, Point p1, Point p2, Point p3, int end0, int end1, int radius, Image *src, Point sp, Drawop op)
166 {
167         Plist l;
168
169         l.np = 0;
170         bezierpts(&l, p0, p1, p2, p3);
171         if(l.np == -1)
172                 return 0;
173         if(l.np != 0){
174                 polyop(dst, l.p, l.np, end0, end1, radius, src, addpt(subpt(sp, p0), l.p[0]), op);
175                 free(l.p);
176         }
177         return 1;
178 }
179
180 int
181 bezspline(Image *dst, Point *pt, int npt, int end0, int end1, int radius, Image *src, Point sp)
182 {
183         return bezsplineop(dst, pt, npt, end0, end1, radius, src, sp, SoverD);
184 }
185
186 int
187 bezsplineop(Image *dst, Point *pt, int npt, int end0, int end1, int radius, Image *src, Point sp, Drawop op)
188 {
189         Plist l;
190
191         l.np = 0;
192         _bezsplinepts(&l, pt, npt);
193         if(l.np==-1)
194                 return 0;
195         if(l.np != 0){
196                 polyop(dst, l.p, l.np, end0, end1, radius, src, addpt(subpt(sp, pt[0]), l.p[0]), op);
197                 free(l.p);
198         }
199         return 1;
200 }
201
202 int
203 fillbezier(Image *dst, Point p0, Point p1, Point p2, Point p3, int w, Image *src, Point sp)
204 {
205         return fillbezierop(dst, p0, p1, p2, p3, w, src, sp, SoverD);
206 }
207
208 int
209 fillbezierop(Image *dst, Point p0, Point p1, Point p2, Point p3, int w, Image *src, Point sp, Drawop op)
210 {
211         Plist l;
212
213         l.np = 0;
214         bezierpts(&l, p0, p1, p2, p3);
215         if(l.np == -1)
216                 return 0;
217         if(l.np != 0){
218                 fillpolyop(dst, l.p, l.np, w, src, addpt(subpt(sp, p0), l.p[0]), op);
219                 free(l.p);
220         }
221         return 1;
222 }
223
224 int
225 fillbezspline(Image *dst, Point *pt, int npt, int w, Image *src, Point sp)
226 {
227         return fillbezsplineop(dst, pt, npt, w, src, sp, SoverD);
228 }
229
230 int
231 fillbezsplineop(Image *dst, Point *pt, int npt, int w, Image *src, Point sp, Drawop op)
232 {
233         Plist l;
234
235         l.np = 0;
236         _bezsplinepts(&l, pt, npt);
237         if(l.np == -1)
238                 return 0;
239         if(l.np > 0){
240                 fillpolyop(dst, l.p, l.np, w, src, addpt(subpt(sp, pt[0]), l.p[0]), op);
241                 free(l.p);
242         }
243         return 1;
244 }