You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
368 lines
12 KiB
368 lines
12 KiB
//---------------------------------------------------------------------------- |
|
// 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 <assert.h> |
|
|
|
#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); |
|
} |
|
|
|
assert(m_coord_blocks); |
|
*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
|
|
|