2 Routine to convert cubic splines into quadratic ones.
4 Part of the swftools package.
6 Copyright (c) 2001 Matthias Kramm <kramm@quiss.org>
8 This file is distributed under the GPL, see file COPYING for details */
15 static int solve(double a,double b,double c, double*dd)
19 if(det<0) return 0; // we don't do imaginary. not today.
20 if(det==0) { // unlikely, but we have to deal with it.
22 return (dd[0]>0 && dd[0]<1);
25 dd[pos]=(-b+sqrt(det))/(2*a);
26 if(dd[pos]>0 && dd[pos]<1)
28 dd[pos]=(-b-sqrt(det))/(2*a);
29 if(dd[pos]>0 && dd[pos]<1)
34 struct plotxy splinepos(struct plotxy p0, struct plotxy p1, struct plotxy p2, struct plotxy p3, double d) {
36 p.x = (p0.x * d*d*d + p1.x * 3*(1-d)*d*d + p2.x * 3*(1-d)*(1-d)*d + p3.x * (1-d)*(1-d)*(1-d));
37 p.y = (p0.y * d*d*d + p1.y * 3*(1-d)*d*d + p2.y * 3*(1-d)*(1-d)*d + p3.y * (1-d)*(1-d)*(1-d));
41 inline double plotxy_dist(struct plotxy a, struct plotxy b)
43 double dx = a.x - b.x;
44 double dy = a.y - b.y;
45 return sqrt(dx*dx+dy*dy);
49 int wp(double p0,double p1,double p2,double p3,double*dd)
51 double div= (6*p0-18*p1+18*p2-6*p3);
53 dd[0] = -(6*p1-12*p2+6*p3)/div;
54 return (dd[0]>0 && dd[0]<1);
57 int approximate(struct plotxy p0, struct plotxy p1, struct plotxy p2, struct plotxy p3, struct qspline*q)
62 struct plotxy myxy[12];
64 // the parameters for the solve function are the 1st deviation of a cubic spline
66 pos += solve(3*p0.x-9*p1.x+9*p2.x-3*p3.x, 6*p1.x-12*p2.x+6*p3.x,3*p2.x-3*p3.x, &roots[pos]);
67 pos += solve(3*p0.y-9*p1.y+9*p2.y-3*p3.y, 6*p1.y-12*p2.y+6*p3.y,3*p2.y-3*p3.y, &roots[pos]);
68 pos += wp(p0.x,p1.x,p2.x,p3.x,&roots[pos]);
69 pos += wp(p0.x,p1.x,p2.x,p3.x,&roots[pos]);
72 // bubblesort - fast enough for 4-6 parameters
82 myxy[t] = splinepos(p0,p1,p2,p3,roots[t]);
88 double dist=plotxy_dist(myxy[t],last);
91 if(dist>0.01 || t==pos-1)
99 // try 1:curve through 3 points, using the middle of the cubic spline.
100 for(t=0;t<pos-1;t++) {
101 // circle(myxy[t].x,myxy[t].y,5);
102 struct plotxy control;
103 struct plotxy midpoint = splinepos(p0,p1,p2,p3,(roots[t]+roots[t+1])/2);
104 control.x = midpoint.x + (midpoint.x-(myxy[t].x+myxy[t+1].x)/2);
105 control.y = midpoint.y + (midpoint.y-(myxy[t].y+myxy[t+1].y)/2);
106 //qspline(myxy[t],control,myxy[t+1]);
108 q[t].control=control;
113 for(t=0;t<pos-1;t++) {
115 vga.setcolor(0xffffff);
116 circle(myxy[t].x,myxy[t].y,5);
118 //double lenmain = distance(p3,p0);
119 //double lenq = distance(myxy[0],myxy[1]);
120 //control.x = myxy[0].x + (p2.x-p3.x);// /lenmain*lenq;
121 //control.y = myxy[0].y + (p2.y-p3.y);// /lenmain*lenq;
122 plotxy midpoint = splinepos(p0,p1,p2,p3,(roots[t]+roots[t+1])/2);
123 control.x = midpoint.x + (midpoint.x-(myxy[t].x+myxy[t+1].x)/2);
124 control.y = midpoint.y + (midpoint.y-(myxy[t].y+myxy[t+1].y)/2);
125 qspline(myxy[0], control, myxy[1]);
127 control.x = 2*myxy[t].x - last.x;
128 control.y = 2*myxy[t].y - last.y;
129 qspline(myxy[t], control, myxy[t+1]);
136 /* move the control point so that the spline runs through the original
138 void fixcp(qspline*s)
141 mid.x = (s->end.x + s->start.x)/2;
142 mid.y = (s->end.y + s->start.y)/2;
143 dir.x = s->control.x - mid.x;
144 dir.y = s->control.y - mid.y;
145 s->control.x = mid.x + 2*dir.x;
146 s->control.y = mid.y + 2*dir.y;
149 int approximate2(struct cspline*s, struct qspline*q, double quality, double start, double end);
151 void check(struct cspline*s, struct qspline*q, int num)
156 plotxy p2 = q[t].start;
157 if(plotxy_dist(p,p2) > 0.005) {
163 if(plotxy_dist(p, s->end) > 0.005) {
169 int cspline_approximate(struct cspline*s, struct qspline*q, double quality, approximate_method method)
172 return approximate(s->start, s->control1, s->control2, s->end, q);
174 return approximate2(s, q, quality, 0.0, 1.0);
177 inline plotxy cspline_getpoint(cspline*s, double t)
180 p.x= s->end.x*t*t*t + 3*s->control2.x*t*t*(1-t)
181 + 3*s->control1.x*t*(1-t)*(1-t) + s->start.x*(1-t)*(1-t)*(1-t);
182 p.y= s->end.y*t*t*t + 3*s->control2.y*t*t*(1-t)
183 + 3*s->control1.y*t*(1-t)*(1-t) + s->start.y*(1-t)*(1-t)*(1-t);
186 plotxy cspline_getderivative(cspline*s, double t)
189 d.x = s->end.x*(3*t*t) + 3*s->control2.x*(2*t-3*t*t) +
190 3*s->control1.x*(1-4*t+3*t*t) + s->start.x*(-3+6*t-3*t*t);
191 d.y = s->end.y*(3*t*t) + 3*s->control2.y*(2*t-3*t*t) +
192 3*s->control1.y*(1-4*t+3*t*t) + s->start.y*(-3+6*t-3*t*t);
195 plotxy cspline_getderivative2(cspline*s, double t)
198 d.x = s->end.x*(6*t) + 3*s->control2.x*(2-6*t) +
199 3*s->control1.x*(-4+6*t) + s->start.x*(6-6*t);
200 d.y = s->end.y*(6*t) + 3*s->control2.y*(2-6*t) +
201 3*s->control1.y*(-4+6*t) + s->start.y*(6-6*t);
204 plotxy cspline_getderivative3(cspline*s, double t)
207 d.x = 6*s->end.x - 18*s->control2.x + 18*s->control1.x - 6*s->start.x;
208 d.y = 6*s->end.y - 18*s->control2.y + 18*s->control1.y - 6*s->start.y;
211 void cspline_getequalspacedpoints(cspline*s, float**p, int*num, double dist)
217 float*positions = (float*)malloc(1048576);
225 plotxy d = cspline_getderivative(s, t);
226 plotxy d2 = cspline_getderivative2(s, t);
228 double dl = sqrt(d.x*d.x+d.y*d.y);
229 double dl2 = sqrt(d2.x*d2.x+d2.y*d2.y);
231 double rdl = dist/dl;
236 plotxy p = cspline_getpoint(s, t);
237 while(plotxy_dist(cspline_getpoint(s, t+rdl), p) > dist) {
238 /* we were ask to divide the spline into dist long fragments,
239 but for the value we estimated even the geometric distance
240 is bigger than 'dist'. Approximate a better value.
254 plotxy qspline_getpoint(qspline*s, double t)
257 p.x= s->end.x*t*t + 2*s->control.x*t*(1-t) + s->start.x*(1-t)*(1-t);
258 p.y= s->end.y*t*t + 2*s->control.y*t*(1-t) + s->start.y*(1-t)*(1-t);
261 plotxy qspline_getderivative(qspline*s, double t)
264 p.x= s->end.x*2*t + 2*s->control.x*(1-2*t) + s->start.x*(-2+2*t);
265 p.y= s->end.y*2*t + 2*s->control.y*(1-2*t) + s->start.y*(-2+2*t);
268 plotxy qspline_getderivative2(qspline*s, double t)
271 p.x= s->end.x*2 + 2*s->control.x*(-2) + s->start.x*(2);
272 p.y= s->end.y*2 + 2*s->control.y*(-2) + s->start.y*(2);
275 double qspline_getlength(qspline*s)
280 plotxy last = qspline_getpoint(s, 0.0);
286 plotxy d2 = qspline_getderivative2(s, t);
287 double dl2 = sqrt(d2.x*d2.x+d2.y*d2.y);
288 double rdl = 1.0/dl2;
292 plotxy here = qspline_getpoint(s, t);
293 len += plotxy_dist(last, here);
299 void qsplines_getequalspacedpoints(qspline**s, int num, float**p, int*pnum, double acc)
309 void qsplines_getdrawpoints(qspline*s, int num, float**p, int*pnum, double acc)
315 float*positions = (float*)malloc(1048576);
323 plotxy d = qspline_getderivative(s, t);
324 double dl = sqrt(d.x*d.x+d.y*d.y);
342 int approximate2(struct cspline*s, struct qspline*q, double quality, double start, double end)
345 plotxy qr1,qr2,cr1,cr2;
351 test.start = cspline_getpoint(s, start);
352 test.control = cspline_getpoint(s, (start+end)/2);
353 test.end = cspline_getpoint(s, end);
358 test.control = cspline_getderivative(s, start);
359 test.control.x *= (end-start)/2;
360 test.control.y *= (end-start)/2;
361 test.control.x += test.start.x;
362 test.control.y += test.start.y;
364 test.control = cspline_getderivative(s, end);
365 test.control.x *= -(end-start)/2;
366 test.control.y *= -(end-start)/2;
367 test.control.x += test.end.x;
368 test.control.y += test.end.y;
372 for(t=0;t<probes;t++) {
373 double pos = 0.5/(probes*2)*(t*2+1);
374 qr1 = qspline_getpoint(&test, pos);
375 cr1 = cspline_getpoint(s, start+pos*(end-start));
376 dist1 = plotxy_dist(qr1, cr1);
380 qr2 = qspline_getpoint(&test, (1-pos));
381 cr2 = cspline_getpoint(s, start+(1-pos)*(end-start));
382 dist2 = plotxy_dist(qr2, cr2);
388 if(recurse && (end-start)>1.0/120) {
389 /* quality is too bad, split it up recursively */
390 num += approximate2(s, q, quality, start, (start+end)/2);
392 num += approximate2(s, q, quality, (start+end)/2, end);