Rev 193 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
259 | daniel-mar | 1 | /* |
2 | This file is part of Curvaceous, a Bˇzier drawing demo |
||
3 | Copyright (C) 1992-2006 Toby Thain, toby@telegraphics.com.au |
||
4 | |||
5 | This program is free software; you can redistribute it and/or modify |
||
6 | it under the terms of the GNU General Public License as published by |
||
7 | the Free Software Foundation; either version 2 of the License, or |
||
8 | (at your option) any later version. |
||
9 | |||
10 | This program is distributed in the hope that it will be useful, |
||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
13 | GNU General Public License for more details. |
||
14 | |||
15 | You should have received a copy of the GNU General Public License |
||
16 | along with this program; if not, write to the Free Software |
||
17 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
||
18 | */ |
||
19 | |||
20 | /* copyright (C) Toby Thain 1992-94 */ |
||
21 | |||
22 | #include <fixmath.h> // X2Fix |
||
23 | #include <fp.h> // sqrt,scalb |
||
24 | #include <quickdraw.h> |
||
25 | #include <toolutils.h> // FixMul |
||
26 | |||
27 | #include "curveto.h" |
||
28 | #include "misc.h" // SGN |
||
29 | |||
30 | pt fpt(int x,int y){ pt p; |
||
31 | p.x = x<<16; |
||
32 | p.y = y<<16; |
||
33 | return p; |
||
34 | } |
||
35 | |||
36 | void mid(pt *a,pt *b,pt *c){ |
||
37 | a->x = (b->x + c->x)/2; |
||
38 | a->y = (b->y + c->y)/2; |
||
39 | } |
||
40 | |||
41 | void |
||
42 | curve_bounds_fix(pt c[],Fixed *l,Fixed *t,Fixed *r,Fixed *b){ int i; Fixed j; |
||
43 | *r = *l = c->x; |
||
44 | *b = *t = c->y; |
||
45 | for(i=3;++c,i--;){ |
||
46 | if((j = c->x) < *l) |
||
47 | *l = j; |
||
48 | else if(j > *r) |
||
49 | *r = j; |
||
50 | if((j = c->y) < *t) |
||
51 | *t = j; |
||
52 | else if(j > *b) |
||
53 | *b = j; |
||
54 | } |
||
55 | } |
||
56 | |||
57 | void |
||
58 | curve_bounds(pt c[],Rect *rr){ Fixed l,t,r,b; |
||
59 | curve_bounds_fix(c,&l,&t,&r,&b); |
||
60 | rr->right = I(r); |
||
61 | rr->left = I(l); |
||
62 | rr->bottom = I(b); |
||
63 | rr->top = I(t); |
||
64 | } |
||
65 | |||
66 | #define D(p) (dp = FixMul(dx,(p).y - c->y) + FixMul(dy,c->x - (p).x), FixMul(dp,dp) < dd) |
||
67 | |||
68 | void divide_curve(pt c[],pt z[]){ |
||
69 | /* divide the curve into two; calculate the control points for each half */ |
||
70 | pt f; |
||
71 | |||
72 | z[0] = c[0]; |
||
73 | z[6] = c[3]; |
||
74 | mid(z+1,c,c+1); |
||
75 | mid(&f,c+1,c+2); |
||
76 | mid(z+5,c+2,c+3); |
||
77 | mid(z+2,z+1,&f); |
||
78 | mid(z+4,&f,z+5); |
||
79 | mid(z+3,z+2,z+4); |
||
80 | } |
||
81 | |||
82 | #define DOT(A,B) (FixMul(A.x,B.x)+FixMul(A.y,B.y)) |
||
83 | #define FLT(x) scalb((x),-16) |
||
84 | |||
85 | int sgn(int x){ |
||
86 | return SGN(x); |
||
87 | } |
||
88 | |||
89 | double sqr(double_t x){ |
||
90 | return x*x; |
||
91 | } |
||
92 | |||
93 | int curve_step(pt c[],pt z[]){ |
||
94 | pt perp,c1,c2,midpt; |
||
95 | |||
96 | perp.x = c[0].y - c[3].y; perp.y = c[3].x - c[0].x; // a vector perpendicular to c[0]->c[3] |
||
97 | c1.x = c[1].x - c[0].x; c1.y = c[1].y - c[0].y; // c[0]->1st control point |
||
98 | c2.x = c[2].x - c[3].x; c2.y = c[2].y - c[3].y; // c[3]->2nd control point |
||
99 | |||
100 | divide_curve(c,z); |
||
101 | mid(&midpt,&c[0],&c[3]); |
||
102 | |||
103 | midpt.x -= z[3].x; midpt.y -= z[3].y; |
||
104 | |||
105 | if( |
||
106 | sgn(DOT(perp,c1))==sgn(DOT(perp,c2)) && // control points on same side |
||
107 | (sqr(FLT(midpt.x))+sqr(FLT(midpt.y)))<1. // doing this calc in fixed point IS PRONE TO overflow! |
||
108 | ){ |
||
109 | /* MoveTo(P(c[0])); Line(P(perp)); |
||
110 | MoveTo(P(c[0])); Line(P(c1)); |
||
111 | MoveTo(P(c[3])); Line(P(c2)); |
||
112 | MoveTo(P(c[0])); */ |
||
113 | |||
114 | LineTo(P(c[3])); |
||
115 | return false; |
||
116 | }else |
||
117 | return true; |
||
118 | } |
||
119 | |||
120 | void curveto(pt c[]){ pt z[7];// draw the entire curve described by c[0..3] |
||
121 | if(curve_step(c,z)){ /* the curve was not flat enough - divide into two */ |
||
122 | curveto(z); |
||
123 | curveto(z+3); |
||
124 | } |
||
125 | } |
||
126 | |||
127 | int curve_step2(pt c[],pt z[],int d){ |
||
128 | if( ( abs(I(c[0].x)-I(c[3].x))<2 && abs(I(c[0].y)-I(c[3].y))<2 ) /*|| !d*/ ){ |
||
129 | MoveTo(P(c[0])); |
||
130 | Line(0,0); |
||
131 | return false; |
||
132 | }else{ |
||
133 | divide_curve(c,z); |
||
134 | return true; |
||
135 | } |
||
136 | } |
||
137 | |||
138 | void curveto2(pt c[],int d){ pt z[7];// draw the entire curve described by c[0..3] |
||
139 | if(curve_step2(c,z,d)){ /* the curve was not flat enough - divide into two */ |
||
140 | curveto2(z,d-1); |
||
141 | curveto2(z+3,d-1); |
||
142 | } |
||
143 | } |
||
144 | |||
145 | void |
||
146 | curveto_rgn(pt c[],RgnHandle rgn){ // optimised to draw only parts of the curve inside rgn |
||
147 | Rect r; |
||
148 | pt z[7]; |
||
149 | RgnHandle rgn2; |
||
150 | |||
151 | curve_bounds(c,&r); |
||
152 | // should add some slop to the above rectangle |
||
153 | if(RectInRgn(&r,rgn)){ /* any part of the curve is within "rgn" */ |
||
154 | MoveTo(P(*c)); |
||
155 | RectRgn(rgn2 = NewRgn(),&r); |
||
156 | DiffRgn(rgn2,rgn,rgn2); |
||
157 | if(EmptyRgn(rgn2)) /* the curve is entirely inside "rgn" */ |
||
158 | curveto(c); /* so we can draw the whole curve without checking further */ |
||
159 | else /* only part of the curve is inside "rgn" */ |
||
160 | if(curve_step(c,z)){ /* subdivide further */ |
||
161 | curveto_rgn(z,rgn); /* do each half */ |
||
162 | curveto_rgn(z+3,rgn); |
||
163 | } |
||
164 | DisposeRgn(rgn2); |
||
165 | } |
||
166 | } |
||
167 | |||
168 | Boolean curve_pick_step(pt c[],pt *p,Fixed tol,Fixed *param,Fixed p0,Fixed p1){ |
||
169 | // note: tolerance parameter is pixels squared |
||
170 | Fixed l,t,r,b,ax,ay,dx,dy,dd,dp,half,tt; |
||
171 | |||
172 | curve_bounds_fix(c,&l,&t,&r,&b); |
||
173 | if(p->x > (l-tol) && p->x < (r+tol) && p->y > (t-tol) && p->y < (b+tol)){ |
||
174 | dx = c[3].x - c->x; |
||
175 | dy = c[3].y - c->y; |
||
176 | dd = FixMul(dx,dx) + FixMul(dy,dy); |
||
177 | if(D(c[1]) && D(c[2])){ /* D(p) is the "flatness" criterion - test both control points */ |
||
178 | // the curve is "flat" enough to test the point against the line c[0]--c[3] |
||
179 | |||
180 | // a -> vector from c[0] to p = (ax,ay) |
||
181 | ax = p->x - c->x; |
||
182 | ay = p->y - c->y; |
||
183 | // b -> vector from c[0] to c[3] = (dx,dy) |
||
184 | |||
185 | // distance of a's projection along vector perpendicular to b (call it B) |
||
186 | // = |a| x cos theta = a.B / |B| |
||
187 | // squared distance = (a.B)^2 / |B|^2 |
||
188 | // |B|^2 is known due to Pythagoras' theorem ( = dd = dx^2 + dy^2) |
||
189 | // is this distance less than the tolerance? |
||
190 | |||
191 | dp = FixMul(ax,-dy) + FixMul(ay,dx); // == a.B |
||
192 | if( (FixMul(ax,ax) + FixMul(ay,ay)) < FixMul(tol,tol) |
||
193 | || ( FixMul(dp,dp) < FixMul(FixMul(tol,tol),dd) |
||
194 | && (dp = FixMul(ax,dx) + FixMul(ay,dy)) > 0 |
||
195 | && (FixMul(p->x - c[3].x,dx) + FixMul(p->y - c[3].y,dy)) < 0 ) ){ // dp == a.b |
||
196 | |||
197 | tt = FixDiv(dp,dd); |
||
198 | *param = p0 + FixMul(tt,p1 - p0); |
||
199 | |||
200 | // update target point with actual closest point |
||
201 | p->x = c->x + FixMul(tt,dx); |
||
202 | p->y = c->y + FixMul(tt,dy); |
||
203 | return true; |
||
204 | }else |
||
205 | return false; |
||
206 | }else{ pt z[7]; |
||
207 | divide_curve(c,z); |
||
208 | half = (p0+p1)/2; |
||
209 | return curve_pick_step(z,p,tol,param,p0,half) || curve_pick_step(z+3,p,tol,param,half,p1); |
||
210 | } |
||
211 | }else |
||
212 | return false; |
||
213 | } |
||
214 | |||
215 | Boolean curve_pick(pt c[],pt *p,Fixed tol,Fixed *t){ |
||
216 | return curve_pick_step(c,p,tol,t,F(0),F(1)); |
||
217 | } |
||
218 | |||
219 | void mark(pt *p,Boolean f){ |
||
220 | Rect r; |
||
221 | r.bottom = (r.top = I(p->y) - 2) + 4; |
||
222 | r.right = (r.left = I(p->x) - 2) + 4; |
||
223 | f ? PaintRect(&r) : FrameRect(&r); |
||
224 | } |