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// Stroke generator
17//
18//----------------------------------------------------------------------------
19#include <math.h>
20#include "agg_vcgen_stroke.h"
21#include "agg_shorten_path.h"
22
23namespace agg
24{
25
26    //------------------------------------------------------------------------
27    vcgen_stroke::vcgen_stroke() :
28        m_stroker(),
29        m_src_vertices(),
30        m_out_vertices(),
31        m_shorten(0.0),
32        m_closed(0),
33        m_status(initial),
34        m_src_vertex(0),
35        m_out_vertex(0)
36    {
37    }
38
39    //------------------------------------------------------------------------
40    void vcgen_stroke::remove_all()
41    {
42        m_src_vertices.remove_all();
43        m_closed = 0;
44        m_status = initial;
45    }
46
47
48    //------------------------------------------------------------------------
49    void vcgen_stroke::add_vertex(double x, double y, unsigned cmd)
50    {
51        m_status = initial;
52        if(is_move_to(cmd))
53        {
54            m_src_vertices.modify_last(vertex_dist(x, y));
55        }
56        else
57        {
58            if(is_vertex(cmd))
59            {
60                m_src_vertices.add(vertex_dist(x, y));
61            }
62            else
63            {
64                m_closed = get_close_flag(cmd);
65            }
66        }
67    }
68
69    //------------------------------------------------------------------------
70    void vcgen_stroke::rewind(unsigned)
71    {
72        if(m_status == initial)
73        {
74            m_src_vertices.close(m_closed != 0);
75            shorten_path(m_src_vertices, m_shorten, m_closed);
76            if(m_src_vertices.size() < 3) m_closed = 0;
77        }
78        m_status = ready;
79        m_src_vertex = 0;
80        m_out_vertex = 0;
81    }
82
83
84    //------------------------------------------------------------------------
85    unsigned vcgen_stroke::vertex(double* x, double* y)
86    {
87        unsigned cmd = path_cmd_line_to;
88        while(!is_stop(cmd))
89        {
90            switch(m_status)
91            {
92            case initial:
93                rewind(0);
94
95            case ready:
96                if(m_src_vertices.size() < 2 + unsigned(m_closed != 0))
97                {
98                    cmd = path_cmd_stop;
99                    break;
100                }
101                m_status = m_closed ? outline1 : cap1;
102                cmd = path_cmd_move_to;
103                m_src_vertex = 0;
104                m_out_vertex = 0;
105                break;
106
107            case cap1:
108                m_stroker.calc_cap(m_out_vertices,
109                                   m_src_vertices[0],
110                                   m_src_vertices[1],
111                                   m_src_vertices[0].dist);
112                m_src_vertex = 1;
113                m_prev_status = outline1;
114                m_status = out_vertices;
115                m_out_vertex = 0;
116                break;
117
118            case cap2:
119                m_stroker.calc_cap(m_out_vertices,
120                                   m_src_vertices[m_src_vertices.size() - 1],
121                                   m_src_vertices[m_src_vertices.size() - 2],
122                                   m_src_vertices[m_src_vertices.size() - 2].dist);
123                m_prev_status = outline2;
124                m_status = out_vertices;
125                m_out_vertex = 0;
126                break;
127
128            case outline1:
129                if(m_closed)
130                {
131                    if(m_src_vertex >= m_src_vertices.size())
132                    {
133                        m_prev_status = close_first;
134                        m_status = end_poly1;
135                        break;
136                    }
137                }
138                else
139                {
140                    if(m_src_vertex >= m_src_vertices.size() - 1)
141                    {
142                        m_status = cap2;
143                        break;
144                    }
145                }
146                m_stroker.calc_join(m_out_vertices,
147                                    m_src_vertices.prev(m_src_vertex),
148                                    m_src_vertices.curr(m_src_vertex),
149                                    m_src_vertices.next(m_src_vertex),
150                                    m_src_vertices.prev(m_src_vertex).dist,
151                                    m_src_vertices.curr(m_src_vertex).dist);
152                ++m_src_vertex;
153                m_prev_status = m_status;
154                m_status = out_vertices;
155                m_out_vertex = 0;
156                break;
157
158            case close_first:
159                m_status = outline2;
160                cmd = path_cmd_move_to;
161
162            case outline2:
163                if(m_src_vertex <= unsigned(m_closed == 0))
164                {
165                    m_status = end_poly2;
166                    m_prev_status = stop;
167                    break;
168                }
169
170                --m_src_vertex;
171                m_stroker.calc_join(m_out_vertices,
172                                    m_src_vertices.next(m_src_vertex),
173                                    m_src_vertices.curr(m_src_vertex),
174                                    m_src_vertices.prev(m_src_vertex),
175                                    m_src_vertices.curr(m_src_vertex).dist,
176                                    m_src_vertices.prev(m_src_vertex).dist);
177
178                m_prev_status = m_status;
179                m_status = out_vertices;
180                m_out_vertex = 0;
181                break;
182
183            case out_vertices:
184                if(m_out_vertex >= m_out_vertices.size())
185                {
186                    m_status = m_prev_status;
187                }
188                else
189                {
190                    const point_d& c = m_out_vertices[m_out_vertex++];
191                    *x = c.x;
192                    *y = c.y;
193                    return cmd;
194                }
195                break;
196
197            case end_poly1:
198                m_status = m_prev_status;
199                return path_cmd_end_poly | path_flags_close | path_flags_ccw;
200
201            case end_poly2:
202                m_status = m_prev_status;
203                return path_cmd_end_poly | path_flags_close | path_flags_cw;
204
205            case stop:
206                cmd = path_cmd_stop;
207                break;
208            }
209        }
210        return cmd;
211    }
212
213}
214