433 lines
12 KiB
C
433 lines
12 KiB
C
|
//----------------------------------------------------------------------------
|
||
|
// Anti-Grain Geometry - Version 2.4
|
||
|
// 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
|
||
|
//----------------------------------------------------------------------------
|
||
|
//
|
||
|
// General Polygon Clipper based on the GPC library by Alan Murta
|
||
|
// Union, Intersection, XOR, A-B, B-A
|
||
|
// Contact the author if you intend to use it in commercial applications!
|
||
|
// http://www.cs.man.ac.uk/aig/staff/alan/software/
|
||
|
// Alan Murta (email: gpc@cs.man.ac.uk)
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
#ifndef AGG_CONV_GPC_INCLUDED
|
||
|
#define AGG_CONV_GPC_INCLUDED
|
||
|
|
||
|
#include <math.h>
|
||
|
#include "agg_basics.h"
|
||
|
#include "agg_array.h"
|
||
|
|
||
|
extern "C"
|
||
|
{
|
||
|
#include "gpc.h"
|
||
|
}
|
||
|
|
||
|
namespace agg
|
||
|
{
|
||
|
enum gpc_op_e
|
||
|
{
|
||
|
gpc_or,
|
||
|
gpc_and,
|
||
|
gpc_xor,
|
||
|
gpc_a_minus_b,
|
||
|
gpc_b_minus_a
|
||
|
};
|
||
|
|
||
|
|
||
|
//================================================================conv_gpc
|
||
|
template<class VSA, class VSB> class conv_gpc
|
||
|
{
|
||
|
enum status
|
||
|
{
|
||
|
status_move_to,
|
||
|
status_line_to,
|
||
|
status_stop
|
||
|
};
|
||
|
|
||
|
struct contour_header_type
|
||
|
{
|
||
|
int num_vertices;
|
||
|
int hole_flag;
|
||
|
gpc_vertex* vertices;
|
||
|
};
|
||
|
|
||
|
typedef pod_bvector<gpc_vertex, 8> vertex_array_type;
|
||
|
typedef pod_bvector<contour_header_type, 6> contour_header_array_type;
|
||
|
|
||
|
|
||
|
public:
|
||
|
typedef VSA source_a_type;
|
||
|
typedef VSB source_b_type;
|
||
|
typedef conv_gpc<source_a_type, source_b_type> self_type;
|
||
|
|
||
|
~conv_gpc()
|
||
|
{
|
||
|
free_gpc_data();
|
||
|
}
|
||
|
|
||
|
conv_gpc(source_a_type& a, source_b_type& b, gpc_op_e op = gpc_or) :
|
||
|
m_src_a(&a),
|
||
|
m_src_b(&b),
|
||
|
m_status(status_move_to),
|
||
|
m_vertex(-1),
|
||
|
m_contour(-1),
|
||
|
m_operation(op)
|
||
|
{
|
||
|
memset(&m_poly_a, 0, sizeof(m_poly_a));
|
||
|
memset(&m_poly_b, 0, sizeof(m_poly_b));
|
||
|
memset(&m_result, 0, sizeof(m_result));
|
||
|
}
|
||
|
|
||
|
void attach1(VSA& source) { m_src_a = &source; }
|
||
|
void attach2(VSB& source) { m_src_b = &source; }
|
||
|
|
||
|
void operation(gpc_op_e v) { m_operation = v; }
|
||
|
|
||
|
// Vertex Source Interface
|
||
|
void rewind(unsigned path_id);
|
||
|
unsigned vertex(double* x, double* y);
|
||
|
|
||
|
private:
|
||
|
conv_gpc(const conv_gpc<VSA, VSB>&);
|
||
|
const conv_gpc<VSA, VSB>& operator = (const conv_gpc<VSA, VSB>&);
|
||
|
|
||
|
//--------------------------------------------------------------------
|
||
|
void free_polygon(gpc_polygon& p);
|
||
|
void free_result();
|
||
|
void free_gpc_data();
|
||
|
void start_contour();
|
||
|
void add_vertex(double x, double y);
|
||
|
void end_contour(unsigned orientation);
|
||
|
void make_polygon(gpc_polygon& p);
|
||
|
void start_extracting();
|
||
|
bool next_contour();
|
||
|
bool next_vertex(double* x, double* y);
|
||
|
|
||
|
|
||
|
//--------------------------------------------------------------------
|
||
|
template<class VS> void add(VS& src, gpc_polygon& p)
|
||
|
{
|
||
|
unsigned cmd;
|
||
|
double x, y;
|
||
|
double start_x = 0.0;
|
||
|
double start_y = 0.0;
|
||
|
bool line_to = false;
|
||
|
unsigned orientation = 0;
|
||
|
|
||
|
m_contour_accumulator.remove_all();
|
||
|
|
||
|
while(!is_stop(cmd = src.vertex(&x, &y)))
|
||
|
{
|
||
|
if(is_vertex(cmd))
|
||
|
{
|
||
|
if(is_move_to(cmd))
|
||
|
{
|
||
|
if(line_to)
|
||
|
{
|
||
|
end_contour(orientation);
|
||
|
orientation = 0;
|
||
|
}
|
||
|
start_contour();
|
||
|
start_x = x;
|
||
|
start_y = y;
|
||
|
}
|
||
|
add_vertex(x, y);
|
||
|
line_to = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(is_end_poly(cmd))
|
||
|
{
|
||
|
orientation = get_orientation(cmd);
|
||
|
if(line_to && is_closed(cmd))
|
||
|
{
|
||
|
add_vertex(start_x, start_y);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if(line_to)
|
||
|
{
|
||
|
end_contour(orientation);
|
||
|
}
|
||
|
make_polygon(p);
|
||
|
}
|
||
|
|
||
|
|
||
|
private:
|
||
|
//--------------------------------------------------------------------
|
||
|
source_a_type* m_src_a;
|
||
|
source_b_type* m_src_b;
|
||
|
status m_status;
|
||
|
int m_vertex;
|
||
|
int m_contour;
|
||
|
gpc_op_e m_operation;
|
||
|
vertex_array_type m_vertex_accumulator;
|
||
|
contour_header_array_type m_contour_accumulator;
|
||
|
gpc_polygon m_poly_a;
|
||
|
gpc_polygon m_poly_b;
|
||
|
gpc_polygon m_result;
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
template<class VSA, class VSB>
|
||
|
void conv_gpc<VSA, VSB>::free_polygon(gpc_polygon& p)
|
||
|
{
|
||
|
int i;
|
||
|
for(i = 0; i < p.num_contours; i++)
|
||
|
{
|
||
|
pod_allocator<gpc_vertex>::deallocate(p.contour[i].vertex,
|
||
|
p.contour[i].num_vertices);
|
||
|
}
|
||
|
pod_allocator<gpc_vertex_list>::deallocate(p.contour, p.num_contours);
|
||
|
memset(&p, 0, sizeof(gpc_polygon));
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
template<class VSA, class VSB>
|
||
|
void conv_gpc<VSA, VSB>::free_result()
|
||
|
{
|
||
|
if(m_result.contour)
|
||
|
{
|
||
|
gpc_free_polygon(&m_result);
|
||
|
}
|
||
|
memset(&m_result, 0, sizeof(m_result));
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
template<class VSA, class VSB>
|
||
|
void conv_gpc<VSA, VSB>::free_gpc_data()
|
||
|
{
|
||
|
free_polygon(m_poly_a);
|
||
|
free_polygon(m_poly_b);
|
||
|
free_result();
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
template<class VSA, class VSB>
|
||
|
void conv_gpc<VSA, VSB>::start_contour()
|
||
|
{
|
||
|
contour_header_type h;
|
||
|
memset(&h, 0, sizeof(h));
|
||
|
m_contour_accumulator.add(h);
|
||
|
m_vertex_accumulator.remove_all();
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
template<class VSA, class VSB>
|
||
|
inline void conv_gpc<VSA, VSB>::add_vertex(double x, double y)
|
||
|
{
|
||
|
gpc_vertex v;
|
||
|
v.x = x;
|
||
|
v.y = y;
|
||
|
m_vertex_accumulator.add(v);
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
template<class VSA, class VSB>
|
||
|
void conv_gpc<VSA, VSB>::end_contour(unsigned orientation)
|
||
|
{
|
||
|
if(m_contour_accumulator.size())
|
||
|
{
|
||
|
if(m_vertex_accumulator.size() > 2)
|
||
|
{
|
||
|
contour_header_type& h =
|
||
|
m_contour_accumulator[m_contour_accumulator.size() - 1];
|
||
|
|
||
|
h.num_vertices = m_vertex_accumulator.size();
|
||
|
h.hole_flag = 0;
|
||
|
|
||
|
// TO DO: Clarify the "holes"
|
||
|
//if(is_cw(orientation)) h.hole_flag = 1;
|
||
|
|
||
|
h.vertices = pod_allocator<gpc_vertex>::allocate(h.num_vertices);
|
||
|
gpc_vertex* d = h.vertices;
|
||
|
int i;
|
||
|
for(i = 0; i < h.num_vertices; i++)
|
||
|
{
|
||
|
const gpc_vertex& s = m_vertex_accumulator[i];
|
||
|
d->x = s.x;
|
||
|
d->y = s.y;
|
||
|
++d;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_vertex_accumulator.remove_last();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
template<class VSA, class VSB>
|
||
|
void conv_gpc<VSA, VSB>::make_polygon(gpc_polygon& p)
|
||
|
{
|
||
|
free_polygon(p);
|
||
|
if(m_contour_accumulator.size())
|
||
|
{
|
||
|
p.num_contours = m_contour_accumulator.size();
|
||
|
|
||
|
p.hole = 0;
|
||
|
p.contour = pod_allocator<gpc_vertex_list>::allocate(p.num_contours);
|
||
|
|
||
|
int i;
|
||
|
gpc_vertex_list* pv = p.contour;
|
||
|
for(i = 0; i < p.num_contours; i++)
|
||
|
{
|
||
|
const contour_header_type& h = m_contour_accumulator[i];
|
||
|
pv->num_vertices = h.num_vertices;
|
||
|
pv->vertex = h.vertices;
|
||
|
++pv;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
template<class VSA, class VSB>
|
||
|
void conv_gpc<VSA, VSB>::start_extracting()
|
||
|
{
|
||
|
m_status = status_move_to;
|
||
|
m_contour = -1;
|
||
|
m_vertex = -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
template<class VSA, class VSB>
|
||
|
bool conv_gpc<VSA, VSB>::next_contour()
|
||
|
{
|
||
|
if(++m_contour < m_result.num_contours)
|
||
|
{
|
||
|
m_vertex = -1;
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
template<class VSA, class VSB>
|
||
|
inline bool conv_gpc<VSA, VSB>::next_vertex(double* x, double* y)
|
||
|
{
|
||
|
const gpc_vertex_list& vlist = m_result.contour[m_contour];
|
||
|
if(++m_vertex < vlist.num_vertices)
|
||
|
{
|
||
|
const gpc_vertex& v = vlist.vertex[m_vertex];
|
||
|
*x = v.x;
|
||
|
*y = v.y;
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
template<class VSA, class VSB>
|
||
|
void conv_gpc<VSA, VSB>::rewind(unsigned path_id)
|
||
|
{
|
||
|
free_result();
|
||
|
m_src_a->rewind(path_id);
|
||
|
m_src_b->rewind(path_id);
|
||
|
add(*m_src_a, m_poly_a);
|
||
|
add(*m_src_b, m_poly_b);
|
||
|
switch(m_operation)
|
||
|
{
|
||
|
case gpc_or:
|
||
|
gpc_polygon_clip(GPC_UNION,
|
||
|
&m_poly_a,
|
||
|
&m_poly_b,
|
||
|
&m_result);
|
||
|
break;
|
||
|
|
||
|
case gpc_and:
|
||
|
gpc_polygon_clip(GPC_INT,
|
||
|
&m_poly_a,
|
||
|
&m_poly_b,
|
||
|
&m_result);
|
||
|
break;
|
||
|
|
||
|
case gpc_xor:
|
||
|
gpc_polygon_clip(GPC_XOR,
|
||
|
&m_poly_a,
|
||
|
&m_poly_b,
|
||
|
&m_result);
|
||
|
break;
|
||
|
|
||
|
case gpc_a_minus_b:
|
||
|
gpc_polygon_clip(GPC_DIFF,
|
||
|
&m_poly_a,
|
||
|
&m_poly_b,
|
||
|
&m_result);
|
||
|
break;
|
||
|
|
||
|
case gpc_b_minus_a:
|
||
|
gpc_polygon_clip(GPC_DIFF,
|
||
|
&m_poly_b,
|
||
|
&m_poly_a,
|
||
|
&m_result);
|
||
|
break;
|
||
|
}
|
||
|
start_extracting();
|
||
|
}
|
||
|
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
template<class VSA, class VSB>
|
||
|
unsigned conv_gpc<VSA, VSB>::vertex(double* x, double* y)
|
||
|
{
|
||
|
if(m_status == status_move_to)
|
||
|
{
|
||
|
if(next_contour())
|
||
|
{
|
||
|
if(next_vertex(x, y))
|
||
|
{
|
||
|
m_status = status_line_to;
|
||
|
return path_cmd_move_to;
|
||
|
}
|
||
|
m_status = status_stop;
|
||
|
return path_cmd_end_poly | path_flags_close;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(next_vertex(x, y))
|
||
|
{
|
||
|
return path_cmd_line_to;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_status = status_move_to;
|
||
|
}
|
||
|
return path_cmd_end_poly | path_flags_close;
|
||
|
}
|
||
|
return path_cmd_stop;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
#endif
|