Subversion Repositories filter_foundry

Rev

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
}