~s-cecilio/lomse/master

« back to all changes in this revision

Viewing changes to src/agg/src/agg_bezier_arc.cpp

  • Committer: cecilios
  • Date: 2010-11-14 17:47:31 UTC
  • Revision ID: git-v1:1fa3764c8c4d338b95b1a537b1e78271170c0025
latest new code. demo_1 tested on linux and win32

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//----------------------------------------------------------------------------
 
2
// Anti-Grain Geometry - Version 2.4
 
3
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
 
4
//
 
5
// Permission to copy, use, modify, sell and distribute this software 
 
6
// is granted provided this copyright notice appears in all copies. 
 
7
// This software is provided "as is" without express or implied
 
8
// warranty, and with no claim as to its suitability for any purpose.
 
9
//
 
10
//----------------------------------------------------------------------------
 
11
// Contact: mcseem@antigrain.com
 
12
//          mcseemagg@yahoo.com
 
13
//          http://www.antigrain.com
 
14
//----------------------------------------------------------------------------
 
15
//
 
16
// Arc generator. Produces at most 4 consecutive cubic bezier curves, i.e., 
 
17
// 4, 7, 10, or 13 vertices.
 
18
//
 
19
//----------------------------------------------------------------------------
 
20
 
 
21
 
 
22
#include <math.h>
 
23
#include "agg_bezier_arc.h"
 
24
 
 
25
 
 
26
namespace agg
 
27
{
 
28
 
 
29
    // This epsilon is used to prevent us from adding degenerate curves 
 
30
    // (converging to a single point).
 
31
    // The value isn't very critical. Function arc_to_bezier() has a limit 
 
32
    // of the sweep_angle. If fabs(sweep_angle) exceeds pi/2 the curve 
 
33
    // becomes inaccurate. But slight exceeding is quite appropriate.
 
34
    //-------------------------------------------------bezier_arc_angle_epsilon
 
35
    const double bezier_arc_angle_epsilon = 0.01;
 
36
 
 
37
    //------------------------------------------------------------arc_to_bezier
 
38
    void arc_to_bezier(double cx, double cy, double rx, double ry, 
 
39
                       double start_angle, double sweep_angle,
 
40
                       double* curve)
 
41
    {
 
42
        double x0 = cos(sweep_angle / 2.0);
 
43
        double y0 = sin(sweep_angle / 2.0);
 
44
        double tx = (1.0 - x0) * 4.0 / 3.0;
 
45
        double ty = y0 - tx * x0 / y0;
 
46
        double px[4];
 
47
        double py[4];
 
48
        px[0] =  x0;
 
49
        py[0] = -y0;
 
50
        px[1] =  x0 + tx;
 
51
        py[1] = -ty;
 
52
        px[2] =  x0 + tx;
 
53
        py[2] =  ty;
 
54
        px[3] =  x0;
 
55
        py[3] =  y0;
 
56
 
 
57
        double sn = sin(start_angle + sweep_angle / 2.0);
 
58
        double cs = cos(start_angle + sweep_angle / 2.0);
 
59
 
 
60
        unsigned i;
 
61
        for(i = 0; i < 4; i++)
 
62
        {
 
63
            curve[i * 2]     = cx + rx * (px[i] * cs - py[i] * sn);
 
64
            curve[i * 2 + 1] = cy + ry * (px[i] * sn + py[i] * cs);
 
65
        }
 
66
    }
 
67
 
 
68
 
 
69
 
 
70
    //------------------------------------------------------------------------
 
71
    void bezier_arc::init(double x,  double y, 
 
72
                          double rx, double ry, 
 
73
                          double start_angle, 
 
74
                          double sweep_angle)
 
75
    {
 
76
        start_angle = fmod(start_angle, 2.0 * pi);
 
77
        if(sweep_angle >=  2.0 * pi) sweep_angle =  2.0 * pi;
 
78
        if(sweep_angle <= -2.0 * pi) sweep_angle = -2.0 * pi;
 
79
 
 
80
        if(fabs(sweep_angle) < 1e-10)
 
81
        {
 
82
            m_num_vertices = 4;
 
83
            m_cmd = path_cmd_line_to;
 
84
            m_vertices[0] = x + rx * cos(start_angle);
 
85
            m_vertices[1] = y + ry * sin(start_angle);
 
86
            m_vertices[2] = x + rx * cos(start_angle + sweep_angle);
 
87
            m_vertices[3] = y + ry * sin(start_angle + sweep_angle);
 
88
            return;
 
89
        }
 
90
 
 
91
        double total_sweep = 0.0;
 
92
        double local_sweep = 0.0;
 
93
        double prev_sweep;
 
94
        m_num_vertices = 2;
 
95
        m_cmd = path_cmd_curve4;
 
96
        bool done = false;
 
97
        do
 
98
        {
 
99
            if(sweep_angle < 0.0)
 
100
            {
 
101
                prev_sweep  = total_sweep;
 
102
                local_sweep = -pi * 0.5;
 
103
                total_sweep -= pi * 0.5;
 
104
                if(total_sweep <= sweep_angle + bezier_arc_angle_epsilon)
 
105
                {
 
106
                    local_sweep = sweep_angle - prev_sweep;
 
107
                    done = true;
 
108
                }
 
109
            }
 
110
            else
 
111
            {
 
112
                prev_sweep  = total_sweep;
 
113
                local_sweep =  pi * 0.5;
 
114
                total_sweep += pi * 0.5;
 
115
                if(total_sweep >= sweep_angle - bezier_arc_angle_epsilon)
 
116
                {
 
117
                    local_sweep = sweep_angle - prev_sweep;
 
118
                    done = true;
 
119
                }
 
120
            }
 
121
 
 
122
            arc_to_bezier(x, y, rx, ry, 
 
123
                          start_angle, 
 
124
                          local_sweep, 
 
125
                          m_vertices + m_num_vertices - 2);
 
126
 
 
127
            m_num_vertices += 6;
 
128
            start_angle += local_sweep;
 
129
        }
 
130
        while(!done && m_num_vertices < 26);
 
131
    }
 
132
 
 
133
 
 
134
 
 
135
 
 
136
    //--------------------------------------------------------------------
 
137
    void bezier_arc_svg::init(double x0, double y0, 
 
138
                              double rx, double ry, 
 
139
                              double angle,
 
140
                              bool large_arc_flag,
 
141
                              bool sweep_flag,
 
142
                              double x2, double y2)
 
143
    {
 
144
        m_radii_ok = true;
 
145
 
 
146
        if(rx < 0.0) rx = -rx;
 
147
        if(ry < 0.0) ry = -rx;
 
148
 
 
149
        // Calculate the middle point between 
 
150
        // the current and the final points
 
151
        //------------------------
 
152
        double dx2 = (x0 - x2) / 2.0;
 
153
        double dy2 = (y0 - y2) / 2.0;
 
154
 
 
155
        double cos_a = cos(angle);
 
156
        double sin_a = sin(angle);
 
157
 
 
158
        // Calculate (x1, y1)
 
159
        //------------------------
 
160
        double x1 =  cos_a * dx2 + sin_a * dy2;
 
161
        double y1 = -sin_a * dx2 + cos_a * dy2;
 
162
 
 
163
        // Ensure radii are large enough
 
164
        //------------------------
 
165
        double prx = rx * rx;
 
166
        double pry = ry * ry;
 
167
        double px1 = x1 * x1;
 
168
        double py1 = y1 * y1;
 
169
 
 
170
        // Check that radii are large enough
 
171
        //------------------------
 
172
        double radii_check = px1/prx + py1/pry;
 
173
        if(radii_check > 1.0) 
 
174
        {
 
175
            rx = sqrt(radii_check) * rx;
 
176
            ry = sqrt(radii_check) * ry;
 
177
            prx = rx * rx;
 
178
            pry = ry * ry;
 
179
            if(radii_check > 10.0) m_radii_ok = false;
 
180
        }
 
181
 
 
182
        // Calculate (cx1, cy1)
 
183
        //------------------------
 
184
        double sign = (large_arc_flag == sweep_flag) ? -1.0 : 1.0;
 
185
        double sq   = (prx*pry - prx*py1 - pry*px1) / (prx*py1 + pry*px1);
 
186
        double coef = sign * sqrt((sq < 0) ? 0 : sq);
 
187
        double cx1  = coef *  ((rx * y1) / ry);
 
188
        double cy1  = coef * -((ry * x1) / rx);
 
189
 
 
190
        //
 
191
        // Calculate (cx, cy) from (cx1, cy1)
 
192
        //------------------------
 
193
        double sx2 = (x0 + x2) / 2.0;
 
194
        double sy2 = (y0 + y2) / 2.0;
 
195
        double cx = sx2 + (cos_a * cx1 - sin_a * cy1);
 
196
        double cy = sy2 + (sin_a * cx1 + cos_a * cy1);
 
197
 
 
198
        // Calculate the start_angle (angle1) and the sweep_angle (dangle)
 
199
        //------------------------
 
200
        double ux =  (x1 - cx1) / rx;
 
201
        double uy =  (y1 - cy1) / ry;
 
202
        double vx = (-x1 - cx1) / rx;
 
203
        double vy = (-y1 - cy1) / ry;
 
204
        double p, n;
 
205
 
 
206
        // Calculate the angle start
 
207
        //------------------------
 
208
        n = sqrt(ux*ux + uy*uy);
 
209
        p = ux; // (1 * ux) + (0 * uy)
 
210
        sign = (uy < 0) ? -1.0 : 1.0;
 
211
        double v = p / n;
 
212
        if(v < -1.0) v = -1.0;
 
213
        if(v >  1.0) v =  1.0;
 
214
        double start_angle = sign * acos(v);
 
215
 
 
216
        // Calculate the sweep angle
 
217
        //------------------------
 
218
        n = sqrt((ux*ux + uy*uy) * (vx*vx + vy*vy));
 
219
        p = ux * vx + uy * vy;
 
220
        sign = (ux * vy - uy * vx < 0) ? -1.0 : 1.0;
 
221
        v = p / n;
 
222
        if(v < -1.0) v = -1.0;
 
223
        if(v >  1.0) v =  1.0;
 
224
        double sweep_angle = sign * acos(v);
 
225
        if(!sweep_flag && sweep_angle > 0) 
 
226
        {
 
227
            sweep_angle -= pi * 2.0;
 
228
        } 
 
229
        else 
 
230
        if (sweep_flag && sweep_angle < 0) 
 
231
        {
 
232
            sweep_angle += pi * 2.0;
 
233
        }
 
234
 
 
235
        // We can now build and transform the resulting arc
 
236
        //------------------------
 
237
        m_arc.init(0.0, 0.0, rx, ry, start_angle, sweep_angle);
 
238
        trans_affine mtx = trans_affine_rotation(angle);
 
239
        mtx *= trans_affine_translation(cx, cy);
 
240
        
 
241
        for(unsigned i = 2; i < m_arc.num_vertices()-2; i += 2)
 
242
        {
 
243
            mtx.transform(m_arc.vertices() + i, m_arc.vertices() + i + 1);
 
244
        }
 
245
 
 
246
        // We must make sure that the starting and ending points
 
247
        // exactly coincide with the initial (x0,y0) and (x2,y2)
 
248
        m_arc.vertices()[0] = x0;
 
249
        m_arc.vertices()[1] = y0;
 
250
        if(m_arc.num_vertices() > 2)
 
251
        {
 
252
            m_arc.vertices()[m_arc.num_vertices() - 2] = x2;
 
253
            m_arc.vertices()[m_arc.num_vertices() - 1] = y2;
 
254
        }
 
255
    }
 
256
 
 
257
 
 
258
}