/* Copyright (c) 2000, 2010 Oracle and/or its affiliates. All rights reserved.
Copyright ( C ) 2011 Monty Program Ab .
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; version 2 of the License .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
#ifndef GCALC_SLICESCAN_INCLUDED
#define GCALC_SLICESCAN_INCLUDED
#ifndef DBUG_OFF
// #define GCALC_CHECK_WITH_FLOAT
#else
#define GCALC_DBUG_OFF
#endif /*DBUG_OFF*/
#ifndef GCALC_DBUG_OFF
#define GCALC_DBUG_PRINT(b) DBUG_PRINT("Gcalc" , b)
#define GCALC_DBUG_ENTER(a) DBUG_ENTER("Gcalc " a)
#define GCALC_DBUG_RETURN(r) DBUG_RETURN(r)
#define GCALC_DBUG_VOID_RETURN DBUG_VOID_RETURN
#define GCALC_DBUG_ASSERT(r) DBUG_ASSERT(r)
#else
#define GCALC_DBUG_PRINT(b) do {} while (0 )
#define GCALC_DBUG_ENTER(a) do {} while (0 )
#define GCALC_DBUG_RETURN(r) return (r)
#define GCALC_DBUG_VOID_RETURN do {} while (0 )
#define GCALC_DBUG_ASSERT(r) do {} while (0 )
#endif /*GCALC_DBUG_OFF*/
#define GCALC_TERMINATED(state_var) (state_var && (*state_var))
#define GCALC_SET_TERMINATED(state_var, val) state_var= val
#define GCALC_DECL_TERMINATED_STATE(varname) \
volatile int *varname;
/*
Gcalc_dyn_list class designed to manage long lists of same - size objects
with the possible efficiency .
It allocates fixed - size blocks of memory ( blk_size specified at the time
of creation ) . When new object is added to the list , it occupies part of
this block until it ' s full . Then the new block is allocated .
Freed objects are chained to the m_free list , and if it ' s not empty , the
newly added object is taken from this list instead the block .
*/
class Gcalc_dyn_list
{
public :
class Item
{
public :
Item *next;
};
Gcalc_dyn_list(size_t blk_size, size_t sizeof_item);
Gcalc_dyn_list(const Gcalc_dyn_list &dl);
~Gcalc_dyn_list();
Item *new_item()
{
Item *result;
if (m_free)
{
result= m_free;
m_free= m_free->next;
}
else
result= alloc_new_blk();
return result;
}
inline void free_item(Item *item)
{
item->next= m_free;
m_free= item;
}
inline void free_list(Item **list, Item **hook)
{
*hook= m_free;
m_free= *list;
}
void free_list(Item *list)
{
Item **hook= &list;
while (*hook)
hook= &(*hook)->next;
free_list(&list, hook);
}
void reset();
void cleanup();
protected :
size_t m_blk_size;
size_t m_sizeof_item;
unsigned int m_points_per_blk;
void *m_first_blk;
void **m_blk_hook;
Item *m_free;
Item *m_keep;
Item *alloc_new_blk();
void format_blk(void * block);
inline Item *ptr_add(Item *ptr, int n_items)
{
return (Item *)(((char *)ptr) + n_items * m_sizeof_item);
}
};
/* Internal Gcalc coordinates to provide the precise calculations */
#define GCALC_DIG_BASE 1000000000
typedef uint32 gcalc_digit_t;
typedef unsigned long long gcalc_coord2;
typedef gcalc_digit_t Gcalc_internal_coord;
#define GCALC_COORD_BASE 2
#define GCALC_COORD_BASE2 4
#define GCALC_COORD_BASE3 6
#define GCALC_COORD_BASE4 8
#define GCALC_COORD_BASE5 10
typedef gcalc_digit_t Gcalc_coord1[GCALC_COORD_BASE];
typedef gcalc_digit_t Gcalc_coord2[GCALC_COORD_BASE*2 ];
typedef gcalc_digit_t Gcalc_coord3[GCALC_COORD_BASE*3 ];
void gcalc_mul_coord(Gcalc_internal_coord *result, int result_len,
const Gcalc_internal_coord *a, int a_len,
const Gcalc_internal_coord *b, int b_len);
void gcalc_add_coord(Gcalc_internal_coord *result, int result_len,
const Gcalc_internal_coord *a,
const Gcalc_internal_coord *b);
void gcalc_sub_coord(Gcalc_internal_coord *result, int result_len,
const Gcalc_internal_coord *a,
const Gcalc_internal_coord *b);
int gcalc_cmp_coord(const Gcalc_internal_coord *a,
const Gcalc_internal_coord *b, int len);
/* Internal coordinates declarations end. */
typedef uint gcalc_shape_info;
/*
Gcalc_heap represents the ' dynamic list ' of Info objects , that
contain information about vertexes of all the shapes that take
part in some spatial calculation . Can become quite long .
After filled , the list is usually sorted and then walked through
in the slicescan algorithm .
The Gcalc_heap and the algorithm can only operate with two
kinds of shapes - polygon and polyline . So all the spatial
objects should be represented as sets of these two .
*/
class Gcalc_heap : public Gcalc_dyn_list
{
public :
enum node_type
{
nt_shape_node,
nt_intersection,
nt_eq_node
};
class Info : public Gcalc_dyn_list::Item
{
public :
node_type type;
union
{
struct
{
/* nt_shape_node */
gcalc_shape_info shape;
Info *left;
Info *right;
double x,y;
Gcalc_coord1 ix, iy;
int top_node;
} shape;
struct
{
/* nt_intersection */
/* Line p1-p2 supposed to intersect line p3-p4 */
const Info *p1;
const Info *p2;
const Info *p3;
const Info *p4;
void *data;
int equal;
} intersection;
struct
{
/* nt_eq_node */
const Info *node;
void *data;
} eq;
} node;
bool is_bottom() const
{ GCALC_DBUG_ASSERT(type == nt_shape_node); return !node.shape.left; }
bool is_top() const
{ GCALC_DBUG_ASSERT(type == nt_shape_node); return node.shape.top_node; }
bool is_single_node() const
{ return is_bottom() && is_top(); }
void calc_intersection_xy(double *x, double *y) const ;
void get_xy(double *x, double *y) const ;
int equal_pi(const Info *pi) const ;
#ifdef GCALC_CHECK_WITH_FLOAT
void calc_xy_ld(long double *x, long double *y) const ;
#endif /*GCALC_CHECK_WITH_FLOAT*/
Info *get_next() { return (Info *)next; }
const Info *get_next() const { return (const Info *)next; }
};
Gcalc_heap(size_t blk_size=8192 ) :
Gcalc_dyn_list(blk_size, sizeof (Info)),
m_hook(&m_first), m_n_points(0 )
{}
Gcalc_heap(const Gcalc_heap &gh) :
Gcalc_dyn_list(gh),
m_hook(&m_first), m_n_points(0 )
{}
void set_extent(double xmin, double xmax, double ymin, double ymax);
Info *new_point_info(double x, double y, gcalc_shape_info shape);
void free_point_info(Info *i, Gcalc_dyn_list::Item **i_hook);
Info *new_intersection(const Info *p1, const Info *p2,
const Info *p3, const Info *p4);
void prepare_operation();
inline bool ready() const { return m_hook == NULL; }
Info *get_first() { return (Info *)m_first; }
const Info *get_first() const { return (const Info *)m_first; }
Gcalc_dyn_list::Item **get_last_hook() { return m_hook; }
void reset();
#ifdef GCALC_CHECK_WITH_FLOAT
long double get_double(const Gcalc_internal_coord *c) const ;
#endif /*GCALC_CHECK_WITH_FLOAT*/
double coord_extent;
Gcalc_dyn_list::Item **get_cur_hook() { return m_hook; }
int get_n_points() const { return m_n_points; }
private :
Gcalc_dyn_list::Item *m_first;
Gcalc_dyn_list::Item **m_hook;
int m_n_points;
};
/*
the spatial object has to be represented as a set of
simple polygones and polylines to be sent to the slicescan .
Gcalc_shape_transporter class and his descendants are used to
simplify storing the information about the shape into necessary structures .
This base class only fills the Gcalc_heap with the information about
shapes and vertices .
Normally the Gcalc_shape_transporter family object is sent as a parameter
to the ' get_shapes ' method of an ' spatial ' object so it can pass
the spatial information about itself . The virtual methods are
treating this data in a way the caller needs .
*/
class Gcalc_shape_transporter
{
private :
Gcalc_heap::Info *m_first;
Gcalc_heap::Info *m_prev;
Gcalc_dyn_list::Item **m_prev_hook;
int m_shape_started;
void int_complete();
protected :
Gcalc_heap *m_heap;
int int_single_point(gcalc_shape_info Info, double x, double y);
int int_add_point(gcalc_shape_info Info, double x, double y);
void int_start_line()
{
DBUG_ASSERT(!m_shape_started);
m_shape_started= 1 ;
m_first= m_prev= NULL;
}
void int_complete_line()
{
DBUG_ASSERT(m_shape_started== 1 );
int_complete();
m_shape_started= 0 ;
}
void int_start_ring()
{
DBUG_ASSERT(m_shape_started== 2 );
m_shape_started= 3 ;
m_first= m_prev= NULL;
}
void int_complete_ring()
{
DBUG_ASSERT(m_shape_started== 3 );
int_complete();
m_shape_started= 2 ;
}
void int_start_poly()
{
DBUG_ASSERT(!m_shape_started);
m_shape_started= 2 ;
}
void int_complete_poly()
{
DBUG_ASSERT(m_shape_started== 2 );
m_shape_started= 0 ;
}
bool line_started() { return m_shape_started == 1 ; };
public :
Gcalc_shape_transporter(Gcalc_heap *heap) :
m_shape_started(0 ), m_heap(heap) {}
virtual int single_point(double x, double y)=0 ;
virtual int start_line()=0 ;
virtual int complete_line()=0 ;
virtual int start_poly()=0 ;
virtual int complete_poly()=0 ;
virtual int start_ring()=0 ;
virtual int complete_ring()=0 ;
virtual int add_point(double x, double y)=0 ;
virtual int start_collection(int n_objects) { return 0 ; }
virtual int empty_shape() { return 0 ; }
int start_simple_poly()
{
return start_poly() || start_ring();
}
int complete_simple_poly()
{
return complete_ring() || complete_poly();
}
virtual ~Gcalc_shape_transporter() = default ;
};
enum Gcalc_scan_events
{
scev_none= 0 ,
scev_point= 1 , /* Just a new point in thread */
scev_thread= 2 , /* Start of the new thread */
scev_two_threads= 4 , /* A couple of new threads started */
scev_intersection= 8 , /* Intersection happened */
scev_end= 16 , /* Single thread finished */
scev_two_ends= 32 , /* A couple of threads finished */
scev_single_point= 64 /* Got single point */
};
/*
Gcalc_scan_iterator incapsulates the slicescan algorithm .
It takes filled Gcalc_heap as a datasource . Then can be
iterated through the vertexes and intersection points with
the step ( ) method . After the ' step ( ) ' one usually observes
the current ' slice ' to do the necessary calculations , like
looking for intersections , calculating the area , whatever .
*/
class Gcalc_scan_iterator : public Gcalc_dyn_list
{
public :
class point : public Gcalc_dyn_list::Item
{
public :
Gcalc_coord1 dx;
Gcalc_coord1 dy;
Gcalc_heap::Info *pi;
Gcalc_heap::Info *next_pi;
Gcalc_heap::Info *ev_pi;
const Gcalc_coord1 *l_border;
const Gcalc_coord1 *r_border;
point *ev_next;
Gcalc_scan_events event;
inline const point *c_get_next() const
{ return (const point *)next; }
inline bool is_bottom() const { return !next_pi; }
gcalc_shape_info get_shape() const { return pi->node.shape.shape; }
inline point *get_next() { return (point *)next; }
inline const point *get_next() const { return (const point *)next; }
/* Compare the dx_dy parameters regarding the horiz_dir */
/* returns -1 if less, 0 if equal, 1 if bigger */
static int cmp_dx_dy(const Gcalc_coord1 dx_a,
const Gcalc_coord1 dy_a,
const Gcalc_coord1 dx_b,
const Gcalc_coord1 dy_b);
static int cmp_dx_dy(const Gcalc_heap::Info *p1,
const Gcalc_heap::Info *p2,
const Gcalc_heap::Info *p3,
const Gcalc_heap::Info *p4);
int cmp_dx_dy(const point *p) const ;
point **next_ptr() { return (point **) &next; }
#ifndef GCALC_DBUG_OFF
unsigned int thread;
#endif /*GCALC_DBUG_OFF*/
#ifdef GCALC_CHECK_WITH_FLOAT
void calc_x(long double *x, long double y, long double ix) const ;
#endif /*GCALC_CHECK_WITH_FLOAT*/
};
/* That class introduced mostly for the 'typecontrol' reason. */
/* only difference from the point class is the get_next() function. */
class event_point : public point
{
public :
inline const event_point *get_next() const
{ return (const event_point*) ev_next; }
int simple_event() const
{
return !ev_next ? (event & (scev_point | scev_end)) :
(!ev_next->ev_next && event == scev_two_ends);
}
};
class intersection_info : public Gcalc_dyn_list::Item
{
public :
point *edge_a;
point *edge_b;
Gcalc_coord2 t_a;
Gcalc_coord2 t_b;
int t_calculated;
Gcalc_coord3 x_exp;
int x_calculated;
Gcalc_coord3 y_exp;
int y_calculated;
void calc_t()
{if (!t_calculated) do_calc_t(); }
void calc_y_exp()
{ if (!y_calculated) do_calc_y(); }
void calc_x_exp()
{ if (!x_calculated) do_calc_x(); }
void do_calc_t();
void do_calc_x();
void do_calc_y();
};
class slice_state
{
public :
point *slice;
point **event_position_hook;
point *event_end;
const Gcalc_heap::Info *pi;
};
public :
Gcalc_scan_iterator(size_t blk_size= 8192 );
GCALC_DECL_TERMINATED_STATE(killed)
void init(Gcalc_heap *points); /* Iterator can be reused */
void reset();
int step();
Gcalc_heap::Info *more_points() { return m_cur_pi; }
bool more_trapezoids()
{ return m_cur_pi && m_cur_pi->next; }
const point *get_bottom_points() const
{ return m_bottom_points; }
const point *get_event_position() const
{ return *state.event_position_hook; }
const point *get_event_end() const
{ return state.event_end; }
const event_point *get_events() const
{ return (const event_point *)
(*state.event_position_hook == state.event_end ?
m_bottom_points : *state.event_position_hook); }
const point *get_b_slice() const { return state.slice; }
double get_h() const ;
double get_y() const ;
double get_event_x() const ;
double get_sp_x(const point *sp) const ;
int intersection_step() const
{ return state.pi->type == Gcalc_heap::nt_intersection; }
const Gcalc_heap::Info *get_cur_pi() const
{
return state.pi;
}
private :
Gcalc_heap *m_heap;
Gcalc_heap::Info *m_cur_pi;
slice_state state;
#ifndef GCALC_DBUG_OFF
unsigned int m_cur_thread;
#endif /*GCALC_DBUG_OFF*/
point *m_bottom_points;
point **m_bottom_hook;
int node_scan();
void eq_scan();
void intersection_scan();
void remove_bottom_node();
int insert_top_node();
int add_intersection(point *sp_a, point *sp_b,
Gcalc_heap::Info *pi_from);
int add_eq_node(Gcalc_heap::Info *node, point *sp);
int add_events_for_node(point *sp_node);
point *new_slice_point()
{
point *new_point= (point *)new_item();
return new_point;
}
intersection_info *new_intersection_info(point *a, point *b)
{
intersection_info *ii= (intersection_info *)new_item();
ii->edge_a= a;
ii->edge_b= b;
ii->t_calculated= ii->x_calculated= ii->y_calculated= 0 ;
return ii;
}
int arrange_event(int do_sorting, int n_intersections);
static double get_pure_double(const Gcalc_internal_coord *d, int d_len);
};
/*
Gcalc_trapezoid_iterator simplifies the calculations on
the current slice of the Gcalc_scan_iterator .
One can walk through the trapezoids formed between
previous and current slices .
*/
#ifdef TMP_BLOCK
class Gcalc_trapezoid_iterator
{
protected :
const Gcalc_scan_iterator::point *sp0;
const Gcalc_scan_iterator::point *sp1;
public :
Gcalc_trapezoid_iterator(const Gcalc_scan_iterator *scan_i) :
sp0(scan_i->get_b_slice()),
sp1(scan_i->get_t_slice())
{}
inline bool more() const { return sp1 && sp1->next; }
const Gcalc_scan_iterator::point *lt() const { return sp1; }
const Gcalc_scan_iterator::point *lb() const { return sp0; }
const Gcalc_scan_iterator::point *rb() const
{
const Gcalc_scan_iterator::point *result= sp0;
while ((result= result->c_get_next())->is_bottom())
{}
return result;
}
const Gcalc_scan_iterator::point *rt() const
{ return sp1->c_get_next(); }
void operator ++()
{
sp0= rb();
sp1= rt();
}
};
#endif /*TMP_BLOCK*/
/*
Gcalc_point_iterator simplifies the calculations on
the current slice of the Gcalc_scan_iterator .
One can walk through the points on the current slice .
*/
class Gcalc_point_iterator
{
protected :
const Gcalc_scan_iterator::point *sp;
public :
Gcalc_point_iterator(const Gcalc_scan_iterator *scan_i):
sp(scan_i->get_b_slice())
{}
inline bool more() const { return sp != NULL; }
inline void operator ++() { sp= sp->c_get_next(); }
inline const Gcalc_scan_iterator::point *point() const { return sp; }
inline const Gcalc_heap::Info *get_pi() const { return sp->pi; }
inline gcalc_shape_info get_shape() const { return sp->get_shape(); }
inline void restart(const Gcalc_scan_iterator *scan_i)
{ sp= scan_i->get_b_slice(); }
};
#endif /*GCALC_SLICESCAN_INCLUDED*/
Messung V0.5 in Prozent C=95 H=95 G=94
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland