MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
gcalc_slicescan.h
1 /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
2 
3  This program is free software; you can redistribute it and/or modify
4  it under the terms of the GNU General Public License as published by
5  the Free Software Foundation; version 2 of the License.
6 
7  This program is distributed in the hope that it will be useful,
8  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  GNU General Public License for more details.
11 
12  You should have received a copy of the GNU General Public License
13  along with this program; if not, write to the Free Software
14  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 
17 #ifndef GCALC_SLICESCAN_INCLUDED
18 #define GCALC_SLICESCAN_INCLUDED
19 
20 
21 /*
22  Gcalc_dyn_list class designed to manage long lists of same-size objects
23  with the possible efficiency.
24  It allocates fixed-size blocks of memory (blk_size specified at the time
25  of creation). When new object is added to the list, it occupies part of
26  this block until it's full. Then the new block is allocated.
27  Freed objects are chained to the m_free list, and if it's not empty, the
28  newly added object is taken from this list instead the block.
29 */
30 
32 {
33 #ifndef DBUG_OFF
34  uint m_last_item_id;
35 #endif
36 public:
37  class Item
38  {
39 #ifndef DBUG_OFF
40  uint m_item_id;
41  public:
42  uint item_id() const { return m_item_id; }
43  void set_item_id(uint id) { m_item_id= id; }
44 #endif
45  public:
46  Item *next;
47  };
48 
49  Gcalc_dyn_list(size_t blk_size, size_t sizeof_item);
50  ~Gcalc_dyn_list();
51  Item *new_item()
52  {
53  Item *result;
54  if (!m_free && alloc_new_blk())
55  return NULL;
56 
57  DBUG_ASSERT(m_free);
58  result= m_free;
59  m_free= m_free->next;
60 
61 #ifndef DBUG_OFF
62  result->set_item_id(++m_last_item_id);
63 #endif
64  result->next= NULL;
65  return result;
66  }
67  inline void free_item(Item *item)
68  {
69  item->next= m_free;
70  m_free= item;
71  }
72  inline void free_list(Item *list, Item **hook)
73  {
74  *hook= m_free;
75  m_free= list;
76  }
77 
78  void free_list(Item *list)
79  {
80  Item **hook= &list;
81  while (*hook)
82  hook= &(*hook)->next;
83  free_list(list, hook);
84  }
85 
86  void reset();
87  void cleanup();
88 
89 protected:
90  size_t m_blk_size;
91  size_t m_sizeof_item;
92  unsigned int m_points_per_blk;
93  void *m_first_blk;
94  void **m_blk_hook;
95  Item *m_free;
96  Item *m_keep;
97 
98  bool alloc_new_blk();
99  void format_blk(void* block);
100  inline Item *ptr_add(Item *ptr, int n_items)
101  {
102  return (Item *)(((char*)ptr) + n_items * m_sizeof_item);
103  }
104 };
105 
106 typedef uint gcalc_shape_info;
107 
108 /*
109  Gcalc_heap represents the 'dynamic list' of Info objects, that
110  contain information about vertexes of all the shapes that take
111  part in some spatial calculation. Can become quite long.
112  After filled, the list is usually sorted and then walked through
113  in the slicescan algorithm.
114  The Gcalc_heap and the algorithm can only operate with two
115  kinds of shapes - polygon and polyline. So all the spatial
116  objects should be represented as sets of these two.
117 */
118 
120 {
121 public:
122  class Info : public Gcalc_dyn_list::Item
123  {
124  public:
125  gcalc_shape_info shape;
126  Info *left;
127  Info *right;
128  double x,y;
129 
130  inline bool is_bottom() const { return !left; }
131  inline Info *get_next() { return (Info *)next; }
132  inline const Info *get_next() const { return (const Info *)next; }
133 #ifndef DBUG_OFF
134  inline void dbug_print() const;
135 #endif
136  };
137 
138  Gcalc_heap(size_t blk_size=8192) :
139  Gcalc_dyn_list(blk_size, sizeof(Info)), m_hook(&m_first), m_n_points(0) {}
140  Info *new_point_info(double x, double y, gcalc_shape_info shape)
141  {
142  Info *result= (Info *)new_item();
143  if (!result)
144  return NULL;
145  *m_hook= result;
146  m_hook= &result->next;
147  m_n_points++;
148  result->x= x;
149  result->y= y;
150  result->shape= shape;
151  return result;
152  }
153  void prepare_operation();
154  inline bool ready() const { return m_hook == NULL; }
155  Info *get_first() { return (Info *)m_first; }
156  const Info *get_first() const { return (const Info *)m_first; }
157  Gcalc_dyn_list::Item **get_last_hook() { return m_hook; }
158  void reset();
159 private:
160  Gcalc_dyn_list::Item *m_first;
161  Gcalc_dyn_list::Item **m_hook;
162  int m_n_points;
163 };
164 
165 
172 {
173 public:
174  int m_nshapes;
175  int m_last_shape_pos;
177  {
178  m_nshapes= 0; // How many shapes have been collected
179  m_last_shape_pos= 0; // Last shape start position in function_buffer
180  }
181 };
182 
183 
184 /*
185  the spatial object has to be represented as a set of
186  simple polygones and polylines to be sent to the slicescan.
187 
188  Gcalc_shape_transporter class and his descendants are used to
189  simplify storing the information about the shape into necessary structures.
190  This base class only fills the Gcalc_heap with the information about
191  shapes and vertices.
192 
193  Normally the Gcalc_shape_transporter family object is sent as a parameter
194  to the 'get_shapes' method of an 'spatial' object so it can pass
195  the spatial information about itself. The virtual methods are
196  treating this data in a way the caller needs.
197 */
198 
200 {
201 private:
202  Gcalc_heap::Info *m_first;
203  Gcalc_heap::Info *m_prev;
204  int m_shape_started;
205  void int_complete();
206 protected:
207  Gcalc_heap *m_heap;
208  int int_single_point(gcalc_shape_info Info, double x, double y);
209  int int_add_point(gcalc_shape_info Info, double x, double y);
210  void int_start_line()
211  {
212  DBUG_ASSERT(!m_shape_started);
213  m_shape_started= 1;
214  m_first= m_prev= NULL;
215  }
216  void int_complete_line()
217  {
218  DBUG_ASSERT(m_shape_started== 1);
219  int_complete();
220  m_shape_started= 0;
221  }
222  void int_start_ring()
223  {
224  DBUG_ASSERT(m_shape_started== 2);
225  m_shape_started= 3;
226  m_first= m_prev= NULL;
227  }
228  void int_complete_ring()
229  {
230  DBUG_ASSERT(m_shape_started== 3);
231  int_complete();
232  m_shape_started= 2;
233  }
234  void int_start_poly()
235  {
236  DBUG_ASSERT(!m_shape_started);
237  m_shape_started= 2;
238  }
239  void int_complete_poly()
240  {
241  DBUG_ASSERT(m_shape_started== 2);
242  m_shape_started= 0;
243  }
244  bool line_started() { return m_shape_started == 1; };
245 public:
247  m_shape_started(0), m_heap(heap) {}
248 
249  /* Transformation event methods */
250  virtual int single_point(Gcalc_shape_status *st, double x, double y)=0;
251  virtual int start_line(Gcalc_shape_status *st)=0;
252  virtual int complete_line(Gcalc_shape_status *st)=0;
253  virtual int start_poly(Gcalc_shape_status *st)=0;
254  virtual int complete_poly(Gcalc_shape_status *st)=0;
255  virtual int start_ring(Gcalc_shape_status *st)=0;
256  virtual int complete_ring(Gcalc_shape_status *st)=0;
257  virtual int add_point(Gcalc_shape_status *st, double x, double y)=0;
258  virtual int start_collection(Gcalc_shape_status *st, int nshapes)= 0;
259  virtual int complete_collection(Gcalc_shape_status *st)= 0;
260  virtual int collection_add_item(Gcalc_shape_status *st_collection,
261  Gcalc_shape_status *st_item)= 0;
262  int start_simple_poly(Gcalc_shape_status *st)
263  {
264  return start_poly(st) || start_ring(st);
265  }
266  int complete_simple_poly(Gcalc_shape_status *st)
267  {
268  return complete_ring(st) || complete_poly(st);
269  }
270 
271  /*
272  Filter methods: in some cases we are not interested in certain
273  geometry types and can skip them during transformation instead
274  of inserting "no operation" actions.
275  For example, ST_Buffer() called with a negative distance argument
276  does not need any Points and LineStrings.
277  */
278  virtual bool skip_point() const { return false; }
279  virtual bool skip_line_string() const { return false; }
280  virtual bool skip_poly() const { return false; }
281 
282  virtual ~Gcalc_shape_transporter() {}
283 };
284 
285 
286 enum Gcalc_scan_events
287 {
288  scev_point= 1, /* Just a new point in thread */
289  scev_thread= 2, /* Start of the new thread */
290  scev_two_threads= 4, /* A couple of new threads started */
291  scev_intersection= 8, /* Intersection happened */
292  scev_end= 16, /* Single thread finished */
293  scev_two_ends= 32, /* A couple of threads finished */
294  scev_single_point= 64 /* Got single point */
295 };
296 
297 #ifndef DBUG_OFF
298 const char *Gcalc_scan_event_name(enum Gcalc_scan_events event);
299 #endif
300 
301 typedef int sc_thread_id;
302 
303 /*
304  Gcalc_scan_iterator incapsulates the slisescan algorithm.
305  It takes filled Gcalc_heap as an datasource. Then can be
306  iterated trought the vertexes and intersection points with
307  the step() method. After the 'step()' one usually observes
308  the current 'slice' to do the necessary calculations, like
309  looking for intersections, calculating the area, whatever.
310 */
311 
313 {
314 public:
316  {
317  public:
318  double x;
319  double dx_dy;
320  int horiz_dir;
321  Gcalc_heap::Info *pi;
322  Gcalc_heap::Info *next_pi;
323  sc_thread_id thread;
324  const point *precursor; /* used as a temporary field */
325 
326  inline const point *c_get_next() const
327  { return (const point *)next; }
328  inline bool is_bottom() const { return pi->is_bottom(); }
329  gcalc_shape_info get_shape() const { return pi->shape; }
330  inline point *get_next() { return (point *)next; }
331  inline const point *get_next() const { return (const point *)next; }
332 
333  /* copies all but 'next' 'x' and 'precursor' */
334  void copy_core(const point *from)
335  {
336  dx_dy= from->dx_dy;
337  horiz_dir= from->horiz_dir;
338  pi= from->pi;
339  next_pi= from->next_pi;
340  thread= from->thread;
341  }
342 #ifndef DBUG_OFF
343  inline void dbug_print_slice(double y, enum Gcalc_scan_events event) const;
344 #endif
345  };
346 
348  {
349  public:
350  sc_thread_id thread_a;
351  sc_thread_id thread_b;
352  double x;
353  double y;
354  inline intersection *get_next() { return (intersection *)next; }
355  };
356 
357 public:
358  Gcalc_scan_iterator(size_t blk_size= 8192);
359 
360  void init(Gcalc_heap *points); /* Iterator can be reused */
361  void reset();
362  int step()
363  {
364  DBUG_ASSERT(more_points());
365  return m_cur_intersection ? intersection_scan() : normal_scan();
366  }
367 
368  inline Gcalc_heap::Info *more_points() { return m_cur_pi; }
369  inline bool more_trapezoids()
370  { return m_cur_pi && m_cur_pi->next; }
371 
372  inline Gcalc_scan_events get_event() const { return m_event0; }
373  inline const point *get_event_position() const
374  { return m_event_position0; }
375  inline const point *get_b_slice() const { return m_slice0; }
376  inline const point *get_t_slice() const { return m_slice1; }
377  inline double get_h() const { return m_h; }
378  inline double get_y() const { return m_y0; }
379 
380 private:
381  Gcalc_heap::Info *m_cur_pi;
382  point *m_slice0;
383  point *m_slice1;
384  point *m_sav_slice;
385  intersection *m_intersections;
386  int m_n_intersections;
387  intersection *m_cur_intersection;
388  point **m_pre_intersection_hook;
389  double m_h;
390  double m_y0;
391  double m_y1;
392  double m_sav_y;
393  bool m_next_is_top_point;
394  unsigned int m_bottom_points_count;
395  sc_thread_id m_cur_thread;
396  Gcalc_scan_events m_event0, m_event1;
397  point *m_event_position0;
398  point *m_event_position1;
399 
400  int normal_scan();
401  int intersection_scan();
402  void sort_intersections();
403  int handle_intersections();
404  int insert_top_point();
405  int add_intersection(const point *a, const point *b,
406  int isc_kind, Gcalc_dyn_list::Item ***p_hook);
407  int find_intersections();
408  void pop_suitable_intersection();
409 
410  intersection *new_intersection()
411  {
412  return (intersection *)new_item();
413  }
414  point *new_slice_point()
415  {
416  return (point *)new_item();
417  }
418  point *new_slice(point *example);
419 };
420 
421 
422 /*
423  Gcalc_trapezoid_iterator simplifies the calculations on
424  the current slice of the Gcalc_scan_iterator.
425  One can walk through the trapezoids formed between
426  previous and current slices.
427 */
428 
430 {
431 protected:
432  const Gcalc_scan_iterator::point *sp0;
433  const Gcalc_scan_iterator::point *sp1;
434 public:
436  sp0(scan_i->get_b_slice()),
437  sp1(scan_i->get_t_slice())
438  {}
439 
440  inline bool more() const { return sp1 && sp1->next; }
441 
442  const Gcalc_scan_iterator::point *lt() const { return sp1; }
443  const Gcalc_scan_iterator::point *lb() const { return sp0; }
444  const Gcalc_scan_iterator::point *rb() const
445  {
446  const Gcalc_scan_iterator::point *result= sp0;
447  while ((result= result->c_get_next())->is_bottom())
448  {}
449  return result;
450  }
451  const Gcalc_scan_iterator::point *rt() const
452  { return sp1->c_get_next(); }
453 
454  void operator++()
455  {
456  sp0= rb();
457  sp1= rt();
458  }
459 };
460 
461 
462 /*
463  Gcalc_point_iterator simplifies the calculations on
464  the current slice of the Gcalc_scan_iterator.
465  One can walk through the points on the current slice.
466 */
467 
469 {
470 protected:
471  const Gcalc_scan_iterator::point *sp;
472 public:
474  sp(scan_i->get_b_slice())
475  {}
476 
477  inline bool more() const { return sp != NULL; }
478  inline void operator++() { sp= sp->c_get_next(); }
479  inline const Gcalc_scan_iterator::point *point() const { return sp; }
480  inline const Gcalc_heap::Info *get_pi() const { return sp->pi; }
481  inline gcalc_shape_info get_shape() const { return sp->get_shape(); }
482  inline double get_x() const { return sp->x; }
483 };
484 
485 #endif /*GCALC_SLICESCAN_INCLUDED*/
486