2 Routine to convert cubic splines into quadratic ones.
4 Part of the swftools package.
6 Copyright (c) 2001,2002,2003 Matthias Kramm <kramm@quiss.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
27 static int solve(double a
,double b
,double c
, double*dd
)
31 if(det
<0) return 0; // we don't do imaginary. not today.
32 if(det
==0) { // unlikely, but we have to deal with it.
34 return (dd
[0]>0 && dd
[0]<1);
37 dd
[pos
]=(-b
+sqrt(det
))/(2*a
);
38 if(dd
[pos
]>0 && dd
[pos
]<1)
40 dd
[pos
]=(-b
-sqrt(det
))/(2*a
);
41 if(dd
[pos
]>0 && dd
[pos
]<1)
46 struct plotxy
splinepos(struct plotxy p0
, struct plotxy p1
, struct plotxy p2
, struct plotxy p3
, double d
) {
48 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
));
49 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
));
53 inline double plotxy_dist(struct plotxy a
, struct plotxy b
)
55 double dx
= a
.x
- b
.x
;
56 double dy
= a
.y
- b
.y
;
57 return sqrt(dx
*dx
+dy
*dy
);
61 int wp(double p0
,double p1
,double p2
,double p3
,double*dd
)
63 double div
= (6*p0
-18*p1
+18*p2
-6*p3
);
65 dd
[0] = -(6*p1
-12*p2
+6*p3
)/div
;
66 return (dd
[0]>0 && dd
[0]<1);
69 int approximate(struct plotxy p0
, struct plotxy p1
, struct plotxy p2
, struct plotxy p3
, struct qspline
*q
)
74 struct plotxy myxy
[12];
76 // the parameters for the solve function are the 1st deviation of a cubic spline
78 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
]);
79 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
]);
80 pos
+= wp(p0
.x
,p1
.x
,p2
.x
,p3
.x
,&roots
[pos
]);
81 pos
+= wp(p0
.x
,p1
.x
,p2
.x
,p3
.x
,&roots
[pos
]);
84 // bubblesort - fast enough for 4-6 parameters
94 myxy
[t
] = splinepos(p0
,p1
,p2
,p3
,roots
[t
]);
100 double dist
=plotxy_dist(myxy
[t
],last
);
103 if(dist
>0.01 || t
==pos
-1)
111 // try 1:curve through 3 points, using the middle of the cubic spline.
112 for(t
=0;t
<pos
-1;t
++) {
113 // circle(myxy[t].x,myxy[t].y,5);
114 struct plotxy control
;
115 struct plotxy midpoint
= splinepos(p0
,p1
,p2
,p3
,(roots
[t
]+roots
[t
+1])/2);
116 control
.x
= midpoint
.x
+ (midpoint
.x
-(myxy
[t
].x
+myxy
[t
+1].x
)/2);
117 control
.y
= midpoint
.y
+ (midpoint
.y
-(myxy
[t
].y
+myxy
[t
+1].y
)/2);
118 //qspline(myxy[t],control,myxy[t+1]);
120 q
[t
].control
=control
;
125 for(t=0;t<pos-1;t++) {
127 vga.setcolor(0xffffff);
128 circle(myxy[t].x,myxy[t].y,5);
130 //double lenmain = distance(p3,p0);
131 //double lenq = distance(myxy[0],myxy[1]);
132 //control.x = myxy[0].x + (p2.x-p3.x);// /lenmain*lenq;
133 //control.y = myxy[0].y + (p2.y-p3.y);// /lenmain*lenq;
134 plotxy midpoint = splinepos(p0,p1,p2,p3,(roots[t]+roots[t+1])/2);
135 control.x = midpoint.x + (midpoint.x-(myxy[t].x+myxy[t+1].x)/2);
136 control.y = midpoint.y + (midpoint.y-(myxy[t].y+myxy[t+1].y)/2);
137 qspline(myxy[0], control, myxy[1]);
139 control.x = 2*myxy[t].x - last.x;
140 control.y = 2*myxy[t].y - last.y;
141 qspline(myxy[t], control, myxy[t+1]);
148 /* move the control point so that the spline runs through the original
150 void fixcp(qspline
*s
)
153 mid
.x
= (s
->end
.x
+ s
->start
.x
)/2;
154 mid
.y
= (s
->end
.y
+ s
->start
.y
)/2;
155 dir
.x
= s
->control
.x
- mid
.x
;
156 dir
.y
= s
->control
.y
- mid
.y
;
157 s
->control
.x
= mid
.x
+ 2*dir
.x
;
158 s
->control
.y
= mid
.y
+ 2*dir
.y
;
161 int approximate2(struct cspline
*s
, struct qspline
*q
, double quality
, double start
, double end
);
163 void check(struct cspline
*s
, struct qspline
*q
, int num
)
168 plotxy p2
= q
[t
].start
;
169 if(plotxy_dist(p
,p2
) > 0.005) {
175 if(plotxy_dist(p
, s
->end
) > 0.005) {
181 int cspline_approximate(struct cspline
*s
, struct qspline
*q
, double quality
, approximate_method method
)
184 return approximate(s
->start
, s
->control1
, s
->control2
, s
->end
, q
);
186 return approximate2(s
, q
, quality
, 0.0, 1.0);
189 inline plotxy
cspline_getpoint(cspline
*s
, double t
)
192 p
.x
= s
->end
.x
*t
*t
*t
+ 3*s
->control2
.x
*t
*t
*(1-t
)
193 + 3*s
->control1
.x
*t
*(1-t
)*(1-t
) + s
->start
.x
*(1-t
)*(1-t
)*(1-t
);
194 p
.y
= s
->end
.y
*t
*t
*t
+ 3*s
->control2
.y
*t
*t
*(1-t
)
195 + 3*s
->control1
.y
*t
*(1-t
)*(1-t
) + s
->start
.y
*(1-t
)*(1-t
)*(1-t
);
198 plotxy
cspline_getderivative(cspline
*s
, double t
)
201 d
.x
= s
->end
.x
*(3*t
*t
) + 3*s
->control2
.x
*(2*t
-3*t
*t
) +
202 3*s
->control1
.x
*(1-4*t
+3*t
*t
) + s
->start
.x
*(-3+6*t
-3*t
*t
);
203 d
.y
= s
->end
.y
*(3*t
*t
) + 3*s
->control2
.y
*(2*t
-3*t
*t
) +
204 3*s
->control1
.y
*(1-4*t
+3*t
*t
) + s
->start
.y
*(-3+6*t
-3*t
*t
);
207 plotxy
cspline_getderivative2(cspline
*s
, double t
)
210 d
.x
= s
->end
.x
*(6*t
) + 3*s
->control2
.x
*(2-6*t
) +
211 3*s
->control1
.x
*(-4+6*t
) + s
->start
.x
*(6-6*t
);
212 d
.y
= s
->end
.y
*(6*t
) + 3*s
->control2
.y
*(2-6*t
) +
213 3*s
->control1
.y
*(-4+6*t
) + s
->start
.y
*(6-6*t
);
216 plotxy
cspline_getderivative3(cspline
*s
, double t
)
219 d
.x
= 6*s
->end
.x
- 18*s
->control2
.x
+ 18*s
->control1
.x
- 6*s
->start
.x
;
220 d
.y
= 6*s
->end
.y
- 18*s
->control2
.y
+ 18*s
->control1
.y
- 6*s
->start
.y
;
223 void cspline_getequalspacedpoints(cspline
*s
, float**p
, int*num
, double dist
)
229 float*positions
= (float*)malloc(1048576);
237 plotxy d
= cspline_getderivative(s
, t
);
238 plotxy d2
= cspline_getderivative2(s
, t
);
240 double dl
= sqrt(d
.x
*d
.x
+d
.y
*d
.y
);
241 double dl2
= sqrt(d2
.x
*d2
.x
+d2
.y
*d2
.y
);
243 double rdl
= dist
/dl
;
248 plotxy p
= cspline_getpoint(s
, t
);
249 while(plotxy_dist(cspline_getpoint(s
, t
+rdl
), p
) > dist
) {
250 /* we were ask to divide the spline into dist long fragments,
251 but for the value we estimated even the geometric distance
252 is bigger than 'dist'. Approximate a better value.
266 plotxy
qspline_getpoint(qspline
*s
, double t
)
269 p
.x
= s
->end
.x
*t
*t
+ 2*s
->control
.x
*t
*(1-t
) + s
->start
.x
*(1-t
)*(1-t
);
270 p
.y
= s
->end
.y
*t
*t
+ 2*s
->control
.y
*t
*(1-t
) + s
->start
.y
*(1-t
)*(1-t
);
273 plotxy
qspline_getderivative(qspline
*s
, double t
)
276 p
.x
= s
->end
.x
*2*t
+ 2*s
->control
.x
*(1-2*t
) + s
->start
.x
*(-2+2*t
);
277 p
.y
= s
->end
.y
*2*t
+ 2*s
->control
.y
*(1-2*t
) + s
->start
.y
*(-2+2*t
);
280 plotxy
qspline_getderivative2(qspline
*s
, double t
)
283 p
.x
= s
->end
.x
*2 + 2*s
->control
.x
*(-2) + s
->start
.x
*(2);
284 p
.y
= s
->end
.y
*2 + 2*s
->control
.y
*(-2) + s
->start
.y
*(2);
287 double qspline_getlength(qspline
*s
)
292 plotxy last
= qspline_getpoint(s
, 0.0);
298 plotxy d2
= qspline_getderivative2(s
, t
);
299 double dl2
= sqrt(d2
.x
*d2
.x
+d2
.y
*d2
.y
);
300 double rdl
= 1.0/dl2
;
304 plotxy here
= qspline_getpoint(s
, t
);
305 len
+= plotxy_dist(last
, here
);
311 void qsplines_getequalspacedpoints(qspline
**s
, int num
, float**p
, int*pnum
, double acc
)
321 void qsplines_getdrawpoints(qspline
*s
, int num
, float**p
, int*pnum
, double acc
)
327 float*positions
= (float*)malloc(1048576);
335 plotxy d
= qspline_getderivative(s
, t
);
336 double dl
= sqrt(d
.x
*d
.x
+d
.y
*d
.y
);
354 int approximate2(struct cspline
*s
, struct qspline
*q
, double quality
, double start
, double end
)
357 plotxy qr1
,qr2
,cr1
,cr2
;
363 test
.start
= cspline_getpoint(s
, start
);
364 test
.control
= cspline_getpoint(s
, (start
+end
)/2);
365 test
.end
= cspline_getpoint(s
, end
);
370 test
.control
= cspline_getderivative(s
, start
);
371 test
.control
.x
*= (end
-start
)/2;
372 test
.control
.y
*= (end
-start
)/2;
373 test
.control
.x
+= test
.start
.x
;
374 test
.control
.y
+= test
.start
.y
;
376 test
.control
= cspline_getderivative(s
, end
);
377 test
.control
.x
*= -(end
-start
)/2;
378 test
.control
.y
*= -(end
-start
)/2;
379 test
.control
.x
+= test
.end
.x
;
380 test
.control
.y
+= test
.end
.y
;
384 for(t
=0;t
<probes
;t
++) {
385 double pos
= 0.5/(probes
*2)*(t
*2+1);
386 qr1
= qspline_getpoint(&test
, pos
);
387 cr1
= cspline_getpoint(s
, start
+pos
*(end
-start
));
388 dist1
= plotxy_dist(qr1
, cr1
);
392 qr2
= qspline_getpoint(&test
, (1-pos
));
393 cr2
= cspline_getpoint(s
, start
+(1-pos
)*(end
-start
));
394 dist2
= plotxy_dist(qr2
, cr2
);
400 if(recurse
&& (end
-start
)>1.0/120) {
401 /* quality is too bad, split it up recursively */
402 num
+= approximate2(s
, q
, quality
, start
, (start
+end
)/2);
404 num
+= approximate2(s
, q
, quality
, (start
+end
)/2, end
);