summaryrefslogtreecommitdiff
path: root/agg/inc/agg_path_storage.h
blob: 8cdb9b9bbeb6ca31f405c9ef6cc2ce6bdd87bd85 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
//----------------------------------------------------------------------------
// Anti-Grain Geometry - Version 2.3
// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com)
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
//----------------------------------------------------------------------------
// Contact: mcseem@antigrain.com
//          mcseemagg@yahoo.com
//          http://www.antigrain.com
//----------------------------------------------------------------------------

#ifndef AGG_PATH_STORAGE_INCLUDED
#define AGG_PATH_STORAGE_INCLUDED

#include "agg_basics.h"

namespace agg
{

    //------------------------------------------------------------path_storage
    // A container to store vertices with their flags.
    // A path consists of a number of contours separated with "move_to"
    // commands. The path storage can keep and maintain more than one
    // path.
    // To navigate to the beginning of a particular path, use rewind(path_id);
    // Where path_id is what start_new_path() returns. So, when you call
    // start_new_path() you need to store its return value somewhere else
    // to navigate to the path afterwards.
    //
    // See Implementation: agg_path_storage.cpp
    // See also: vertex_source concept
    //------------------------------------------------------------------------
    class path_storage
    {
        // Allocation parameters
        enum
        {
            block_shift = 8,
            block_size  = 1 << block_shift,
            block_mask  = block_size - 1,
            block_pool  = 256
        };

    public:

        //--------------------------------------------------------------------
        class const_iterator
        {
            void vertex()
            {
                if(m_vertex_idx < m_path->total_vertices())
                {
                    m_vertex.cmd = m_path->vertex(m_vertex_idx, &m_vertex.x, &m_vertex.y);
                }
                else
                {
                    m_vertex.cmd = path_cmd_stop;
                    m_vertex.x = m_vertex.y = 0.0;
                }
            }

        public:
            const_iterator() {}
            const_iterator(unsigned cmd) { m_vertex.cmd = cmd; }
            const_iterator(const const_iterator& i) :
                m_path(i.m_path),
                m_vertex_idx(i.m_vertex_idx),
                m_vertex(i.m_vertex)
            {
            }

            const_iterator(const path_storage& p, unsigned id) :
                m_path(&p),
                m_vertex_idx(id)
            {
                vertex();
            }

            const_iterator& operator++()
            {
                ++m_vertex_idx;
                vertex();
                return *this;
            }

            const vertex_type& operator*() const { return m_vertex; }
            const vertex_type* operator->() const { return &m_vertex; }

            bool operator != (const const_iterator& i)
            {
                return m_vertex.cmd != i.m_vertex.cmd;
            }

        private:
            const path_storage* m_path;
            unsigned            m_vertex_idx;
            vertex_type         m_vertex;
        };

        ~path_storage();
        path_storage();
        path_storage(const path_storage& ps);

        void remove_all();

        unsigned last_vertex(double* x, double* y) const;
        unsigned prev_vertex(double* x, double* y) const;

        void rel_to_abs(double* x, double* y) const;

        void move_to(double x, double y);
        void move_rel(double dx, double dy);

        void line_to(double x, double y);
        void line_rel(double dx, double dy);

        void arc_to(double rx, double ry,
                    double angle,
                    bool large_arc_flag,
                    bool sweep_flag,
                    double x, double y);

        void arc_rel(double rx, double ry,
                     double angle,
                     bool large_arc_flag,
                     bool sweep_flag,
                     double dx, double dy);

        void curve3(double x_ctrl, double y_ctrl,
                    double x_to,   double y_to);

        void curve3_rel(double dx_ctrl, double dy_ctrl,
                        double dx_to,   double dy_to);

        void curve3(double x_to, double y_to);

        void curve3_rel(double dx_to, double dy_to);

        void curve4(double x_ctrl1, double y_ctrl1,
                    double x_ctrl2, double y_ctrl2,
                    double x_to,    double y_to);

        void curve4_rel(double dx_ctrl1, double dy_ctrl1,
                        double dx_ctrl2, double dy_ctrl2,
                        double dx_to,    double dy_to);

        void curve4(double x_ctrl2, double y_ctrl2,
                    double x_to,    double y_to);

        void curve4_rel(double x_ctrl2, double y_ctrl2,
                        double x_to,    double y_to);


        void end_poly(unsigned flags = path_flags_close);

        void close_polygon(unsigned flags = path_flags_none)
        {
            end_poly(path_flags_close | flags);
        }

        void add_poly(const double* vertices, unsigned num,
                      bool solid_path = false,
                      unsigned end_flags = path_flags_none);

        template<class VertexSource>
        void add_path(VertexSource& vs,
                      unsigned path_id = 0,
                      bool solid_path = true)
        {
            double x, y;
            unsigned cmd;
            vs.rewind(path_id);
            while(!is_stop(cmd = vs.vertex(&x, &y)))
            {
                if(is_move_to(cmd) && solid_path && m_total_vertices)
                {
                    cmd = path_cmd_line_to;
                }
                add_vertex(x, y, cmd);
            }
        }

        unsigned start_new_path();

        void copy_from(const path_storage& ps);
        const path_storage& operator = (const path_storage& ps)
        {
            copy_from(ps);
            return *this;
        }


        unsigned total_vertices() const { return m_total_vertices; }
        unsigned vertex(unsigned idx, double* x, double* y) const
        {
            unsigned nb = idx >> block_shift;
            const double* pv = m_coord_blocks[nb] + ((idx & block_mask) << 1);
            *x = *pv++;
            *y = *pv;
            return m_cmd_blocks[nb][idx & block_mask];
        }
        unsigned command(unsigned idx) const
        {
            return m_cmd_blocks[idx >> block_shift][idx & block_mask];
        }

        void     rewind(unsigned path_id);
        unsigned vertex(double* x, double* y);

        const_iterator begin(unsigned id) const { return const_iterator(*this, id); }
        const_iterator begin()            const { return const_iterator(*this, 0); }
        const_iterator end()              const { return const_iterator(path_cmd_stop); }

        // Arrange the orientation of all the polygons. After calling this
        // method all the polygons will have the same orientation
        // determined by the new_orientation flag, i.e.,
        // path_flags_cw or path_flags_ccw
        unsigned arrange_orientations(unsigned path_id, path_flags_e new_orientation);
        void arrange_orientations_all_paths(path_flags_e new_orientation);

        // Flip all the vertices horizontally or vertically
        void flip_x(double x1, double x2);
        void flip_y(double y1, double y2);

        // This function adds a vertex with its flags directly. Since there's no
        // checking for errors, keeping proper path integrity is the responsibility
        // of the caller. It can be said the function is "not very public".
        void add_vertex(double x, double y, unsigned cmd);

        // Allows you to modify vertex coordinates. The caller must know
        // the index of the vertex.
        void modify_vertex(unsigned idx, double x, double y)
        {
            double* pv = m_coord_blocks[idx >> block_shift] + ((idx & block_mask) << 1);
            *pv++ = x;
            *pv   = y;
        }

        // Allows you to modify vertex command. The caller must know
        // the index of the vertex.
        void modify_command(unsigned idx, unsigned cmd)
        {
            m_cmd_blocks[idx >> block_shift][idx & block_mask] = (unsigned char)cmd;
        }


    private:
        void allocate_block(unsigned nb);
        unsigned char* storage_ptrs(double** xy_ptr);
        unsigned perceive_polygon_orientation(unsigned idx,
                                              double xs, double ys,
                                              unsigned* orientation);
        void reverse_polygon(unsigned start, unsigned end);

    private:
        unsigned        m_total_vertices;
        unsigned        m_total_blocks;
        unsigned        m_max_blocks;
        double**        m_coord_blocks;
        unsigned char** m_cmd_blocks;
        unsigned        m_iterator;
    };


    //------------------------------------------------------------------------
    inline unsigned path_storage::vertex(double* x, double* y)
    {
        if(m_iterator >= m_total_vertices) return path_cmd_stop;
        return vertex(m_iterator++, x, y);
    }

    //------------------------------------------------------------------------
    inline unsigned path_storage::prev_vertex(double* x, double* y) const
    {
        if(m_total_vertices > 1)
        {
            return vertex(m_total_vertices - 2, x, y);
        }
        return path_cmd_stop;
    }

    //------------------------------------------------------------------------
    inline unsigned path_storage::last_vertex(double* x, double* y) const
    {
        if(m_total_vertices)
        {
            return vertex(m_total_vertices - 1, x, y);
        }
        return path_cmd_stop;
    }

    //------------------------------------------------------------------------
    inline void path_storage::rel_to_abs(double* x, double* y) const
    {
        if(m_total_vertices)
        {
            double x2;
            double y2;
            if(is_vertex(vertex(m_total_vertices - 1, &x2, &y2)))
            {
                *x += x2;
                *y += y2;
            }
        }
    }

    //------------------------------------------------------------------------
    inline unsigned char* path_storage::storage_ptrs(double** xy_ptr)
    {
        unsigned nb = m_total_vertices >> block_shift;
        if(nb >= m_total_blocks)
        {
            allocate_block(nb);
        }
        *xy_ptr = m_coord_blocks[nb] + ((m_total_vertices & block_mask) << 1);
        return m_cmd_blocks[nb] + (m_total_vertices & block_mask);
    }


    //------------------------------------------------------------------------
    inline void path_storage::add_vertex(double x, double y, unsigned cmd)
    {
        double* coord_ptr = 0;
        unsigned char* cmd_ptr = storage_ptrs(&coord_ptr);
        *cmd_ptr = (unsigned char)cmd;
        *coord_ptr++ = x;
        *coord_ptr   = y;
        m_total_vertices++;
    }

    //------------------------------------------------------------------------
    inline void path_storage::move_to(double x, double y)
    {
        add_vertex(x, y, path_cmd_move_to);
    }

    //------------------------------------------------------------------------
    inline void path_storage::move_rel(double dx, double dy)
    {
        rel_to_abs(&dx, &dy);
        add_vertex(dx, dy, path_cmd_move_to);
    }

    //------------------------------------------------------------------------
    inline void path_storage::line_to(double x, double y)
    {
        add_vertex(x, y, path_cmd_line_to);
    }

    //------------------------------------------------------------------------
    inline void path_storage::line_rel(double dx, double dy)
    {
        rel_to_abs(&dx, &dy);
        add_vertex(dx, dy, path_cmd_line_to);
    }
}



#endif