1 | /* Copyright (c) 2000, 2010 Oracle and/or its affiliates. All rights reserved. |
2 | Copyright (C) 2011 Monty Program Ab. |
3 | |
4 | This program is free software; you can redistribute it and/or modify |
5 | it under the terms of the GNU General Public License as published by |
6 | the Free Software Foundation; version 2 of the License. |
7 | |
8 | This program is distributed in the hope that it will be useful, |
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 | GNU General Public License for more details. |
12 | |
13 | You should have received a copy of the GNU General Public License |
14 | along with this program; if not, write to the Free Software |
15 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ |
16 | |
17 | |
18 | #ifndef GCALC_SLICESCAN_INCLUDED |
19 | #define GCALC_SLICESCAN_INCLUDED |
20 | |
21 | #ifndef DBUG_OFF |
22 | // #define GCALC_CHECK_WITH_FLOAT |
23 | #else |
24 | #define GCALC_DBUG_OFF |
25 | #endif /*DBUG_OFF*/ |
26 | |
27 | #ifndef GCALC_DBUG_OFF |
28 | #define GCALC_DBUG_PRINT(b) DBUG_PRINT("Gcalc", b) |
29 | #define GCALC_DBUG_ENTER(a) DBUG_ENTER("Gcalc " a) |
30 | #define GCALC_DBUG_RETURN(r) DBUG_RETURN(r) |
31 | #define GCALC_DBUG_VOID_RETURN DBUG_VOID_RETURN |
32 | #define GCALC_DBUG_ASSERT(r) DBUG_ASSERT(r) |
33 | #else |
34 | #define GCALC_DBUG_PRINT(b) do {} while(0) |
35 | #define GCALC_DBUG_ENTER(a) do {} while(0) |
36 | #define GCALC_DBUG_RETURN(r) return (r) |
37 | #define GCALC_DBUG_VOID_RETURN do {} while(0) |
38 | #define GCALC_DBUG_ASSERT(r) do {} while(0) |
39 | #endif /*GCALC_DBUG_OFF*/ |
40 | |
41 | #define GCALC_TERMINATED(state_var) (state_var && (*state_var)) |
42 | #define GCALC_SET_TERMINATED(state_var, val) state_var= val |
43 | #define GCALC_DECL_TERMINATED_STATE(varname) \ |
44 | volatile int *varname; |
45 | |
46 | /* |
47 | Gcalc_dyn_list class designed to manage long lists of same-size objects |
48 | with the possible efficiency. |
49 | It allocates fixed-size blocks of memory (blk_size specified at the time |
50 | of creation). When new object is added to the list, it occupies part of |
51 | this block until it's full. Then the new block is allocated. |
52 | Freed objects are chained to the m_free list, and if it's not empty, the |
53 | newly added object is taken from this list instead the block. |
54 | */ |
55 | |
56 | class Gcalc_dyn_list |
57 | { |
58 | public: |
59 | class Item |
60 | { |
61 | public: |
62 | Item *next; |
63 | }; |
64 | |
65 | Gcalc_dyn_list(size_t blk_size, size_t sizeof_item); |
66 | ~Gcalc_dyn_list(); |
67 | Item *new_item() |
68 | { |
69 | Item *result; |
70 | if (m_free) |
71 | { |
72 | result= m_free; |
73 | m_free= m_free->next; |
74 | } |
75 | else |
76 | result= alloc_new_blk(); |
77 | |
78 | return result; |
79 | } |
80 | inline void free_item(Item *item) |
81 | { |
82 | item->next= m_free; |
83 | m_free= item; |
84 | } |
85 | inline void free_list(Item **list, Item **hook) |
86 | { |
87 | *hook= m_free; |
88 | m_free= *list; |
89 | } |
90 | |
91 | void free_list(Item *list) |
92 | { |
93 | Item **hook= &list; |
94 | while (*hook) |
95 | hook= &(*hook)->next; |
96 | free_list(&list, hook); |
97 | } |
98 | |
99 | void reset(); |
100 | void cleanup(); |
101 | |
102 | protected: |
103 | size_t m_blk_size; |
104 | size_t m_sizeof_item; |
105 | unsigned int m_points_per_blk; |
106 | void *m_first_blk; |
107 | void **m_blk_hook; |
108 | Item *m_free; |
109 | Item *m_keep; |
110 | |
111 | Item *alloc_new_blk(); |
112 | void format_blk(void* block); |
113 | inline Item *ptr_add(Item *ptr, int n_items) |
114 | { |
115 | return (Item *)(((char*)ptr) + n_items * m_sizeof_item); |
116 | } |
117 | }; |
118 | |
119 | /* Internal Gcalc coordinates to provide the precise calculations */ |
120 | |
121 | #define GCALC_DIG_BASE 1000000000 |
122 | typedef uint32 gcalc_digit_t; |
123 | typedef unsigned long long gcalc_coord2; |
124 | typedef gcalc_digit_t Gcalc_internal_coord; |
125 | #define GCALC_COORD_BASE 2 |
126 | #define GCALC_COORD_BASE2 4 |
127 | #define GCALC_COORD_BASE3 6 |
128 | #define GCALC_COORD_BASE4 8 |
129 | #define GCALC_COORD_BASE5 10 |
130 | |
131 | typedef gcalc_digit_t Gcalc_coord1[GCALC_COORD_BASE]; |
132 | typedef gcalc_digit_t Gcalc_coord2[GCALC_COORD_BASE*2]; |
133 | typedef gcalc_digit_t Gcalc_coord3[GCALC_COORD_BASE*3]; |
134 | |
135 | |
136 | void gcalc_mul_coord(Gcalc_internal_coord *result, int result_len, |
137 | const Gcalc_internal_coord *a, int a_len, |
138 | const Gcalc_internal_coord *b, int b_len); |
139 | |
140 | void gcalc_add_coord(Gcalc_internal_coord *result, int result_len, |
141 | const Gcalc_internal_coord *a, |
142 | const Gcalc_internal_coord *b); |
143 | |
144 | void gcalc_sub_coord(Gcalc_internal_coord *result, int result_len, |
145 | const Gcalc_internal_coord *a, |
146 | const Gcalc_internal_coord *b); |
147 | |
148 | int gcalc_cmp_coord(const Gcalc_internal_coord *a, |
149 | const Gcalc_internal_coord *b, int len); |
150 | |
151 | /* Internal coordinates declarations end. */ |
152 | |
153 | |
154 | typedef uint gcalc_shape_info; |
155 | |
156 | /* |
157 | Gcalc_heap represents the 'dynamic list' of Info objects, that |
158 | contain information about vertexes of all the shapes that take |
159 | part in some spatial calculation. Can become quite long. |
160 | After filled, the list is usually sorted and then walked through |
161 | in the slicescan algorithm. |
162 | The Gcalc_heap and the algorithm can only operate with two |
163 | kinds of shapes - polygon and polyline. So all the spatial |
164 | objects should be represented as sets of these two. |
165 | */ |
166 | |
167 | class Gcalc_heap : public Gcalc_dyn_list |
168 | { |
169 | public: |
170 | enum node_type |
171 | { |
172 | nt_shape_node, |
173 | nt_intersection, |
174 | nt_eq_node |
175 | }; |
176 | class Info : public Gcalc_dyn_list::Item |
177 | { |
178 | public: |
179 | node_type type; |
180 | union |
181 | { |
182 | struct |
183 | { |
184 | /* nt_shape_node */ |
185 | gcalc_shape_info shape; |
186 | Info *left; |
187 | Info *right; |
188 | double x,y; |
189 | Gcalc_coord1 ix, iy; |
190 | int top_node; |
191 | } shape; |
192 | struct |
193 | { |
194 | /* nt_intersection */ |
195 | /* Line p1-p2 supposed to intersect line p3-p4 */ |
196 | const Info *p1; |
197 | const Info *p2; |
198 | const Info *p3; |
199 | const Info *p4; |
200 | void *data; |
201 | int equal; |
202 | } intersection; |
203 | struct |
204 | { |
205 | /* nt_eq_node */ |
206 | const Info *node; |
207 | void *data; |
208 | } eq; |
209 | } node; |
210 | |
211 | bool is_bottom() const |
212 | { GCALC_DBUG_ASSERT(type == nt_shape_node); return !node.shape.left; } |
213 | bool is_top() const |
214 | { GCALC_DBUG_ASSERT(type == nt_shape_node); return node.shape.top_node; } |
215 | bool is_single_node() const |
216 | { return is_bottom() && is_top(); } |
217 | |
218 | void calc_xy(double *x, double *y) const; |
219 | int equal_pi(const Info *pi) const; |
220 | #ifdef GCALC_CHECK_WITH_FLOAT |
221 | void calc_xy_ld(long double *x, long double *y) const; |
222 | #endif /*GCALC_CHECK_WITH_FLOAT*/ |
223 | |
224 | Info *get_next() { return (Info *)next; } |
225 | const Info *get_next() const { return (const Info *)next; } |
226 | }; |
227 | |
228 | Gcalc_heap(size_t blk_size=8192) : |
229 | Gcalc_dyn_list(blk_size, sizeof(Info)), |
230 | m_hook(&m_first), m_n_points(0) |
231 | {} |
232 | void set_extent(double xmin, double xmax, double ymin, double ymax); |
233 | Info *new_point_info(double x, double y, gcalc_shape_info shape); |
234 | void free_point_info(Info *i, Gcalc_dyn_list::Item **i_hook); |
235 | Info *new_intersection(const Info *p1, const Info *p2, |
236 | const Info *p3, const Info *p4); |
237 | void prepare_operation(); |
238 | inline bool ready() const { return m_hook == NULL; } |
239 | Info *get_first() { return (Info *)m_first; } |
240 | const Info *get_first() const { return (const Info *)m_first; } |
241 | Gcalc_dyn_list::Item **get_last_hook() { return m_hook; } |
242 | void reset(); |
243 | #ifdef GCALC_CHECK_WITH_FLOAT |
244 | long double get_double(const Gcalc_internal_coord *c) const; |
245 | #endif /*GCALC_CHECK_WITH_FLOAT*/ |
246 | double coord_extent; |
247 | Gcalc_dyn_list::Item **get_cur_hook() { return m_hook; } |
248 | |
249 | private: |
250 | Gcalc_dyn_list::Item *m_first; |
251 | Gcalc_dyn_list::Item **m_hook; |
252 | int m_n_points; |
253 | }; |
254 | |
255 | |
256 | /* |
257 | the spatial object has to be represented as a set of |
258 | simple polygones and polylines to be sent to the slicescan. |
259 | |
260 | Gcalc_shape_transporter class and his descendants are used to |
261 | simplify storing the information about the shape into necessary structures. |
262 | This base class only fills the Gcalc_heap with the information about |
263 | shapes and vertices. |
264 | |
265 | Normally the Gcalc_shape_transporter family object is sent as a parameter |
266 | to the 'get_shapes' method of an 'spatial' object so it can pass |
267 | the spatial information about itself. The virtual methods are |
268 | treating this data in a way the caller needs. |
269 | */ |
270 | |
271 | class Gcalc_shape_transporter |
272 | { |
273 | private: |
274 | Gcalc_heap::Info *m_first; |
275 | Gcalc_heap::Info *m_prev; |
276 | Gcalc_dyn_list::Item **m_prev_hook; |
277 | int m_shape_started; |
278 | void int_complete(); |
279 | protected: |
280 | Gcalc_heap *m_heap; |
281 | int int_single_point(gcalc_shape_info Info, double x, double y); |
282 | int int_add_point(gcalc_shape_info Info, double x, double y); |
283 | void int_start_line() |
284 | { |
285 | DBUG_ASSERT(!m_shape_started); |
286 | m_shape_started= 1; |
287 | m_first= m_prev= NULL; |
288 | } |
289 | void int_complete_line() |
290 | { |
291 | DBUG_ASSERT(m_shape_started== 1); |
292 | int_complete(); |
293 | m_shape_started= 0; |
294 | } |
295 | void int_start_ring() |
296 | { |
297 | DBUG_ASSERT(m_shape_started== 2); |
298 | m_shape_started= 3; |
299 | m_first= m_prev= NULL; |
300 | } |
301 | void int_complete_ring() |
302 | { |
303 | DBUG_ASSERT(m_shape_started== 3); |
304 | int_complete(); |
305 | m_shape_started= 2; |
306 | } |
307 | void int_start_poly() |
308 | { |
309 | DBUG_ASSERT(!m_shape_started); |
310 | m_shape_started= 2; |
311 | } |
312 | void int_complete_poly() |
313 | { |
314 | DBUG_ASSERT(m_shape_started== 2); |
315 | m_shape_started= 0; |
316 | } |
317 | bool line_started() { return m_shape_started == 1; }; |
318 | public: |
319 | Gcalc_shape_transporter(Gcalc_heap *heap) : |
320 | m_shape_started(0), m_heap(heap) {} |
321 | |
322 | virtual int single_point(double x, double y)=0; |
323 | virtual int start_line()=0; |
324 | virtual int complete_line()=0; |
325 | virtual int start_poly()=0; |
326 | virtual int complete_poly()=0; |
327 | virtual int start_ring()=0; |
328 | virtual int complete_ring()=0; |
329 | virtual int add_point(double x, double y)=0; |
330 | virtual int start_collection(int n_objects) { return 0; } |
331 | virtual int empty_shape() { return 0; } |
332 | int start_simple_poly() |
333 | { |
334 | return start_poly() || start_ring(); |
335 | } |
336 | int complete_simple_poly() |
337 | { |
338 | return complete_ring() || complete_poly(); |
339 | } |
340 | virtual ~Gcalc_shape_transporter() {} |
341 | }; |
342 | |
343 | |
344 | enum Gcalc_scan_events |
345 | { |
346 | scev_none= 0, |
347 | scev_point= 1, /* Just a new point in thread */ |
348 | scev_thread= 2, /* Start of the new thread */ |
349 | scev_two_threads= 4, /* A couple of new threads started */ |
350 | scev_intersection= 8, /* Intersection happened */ |
351 | scev_end= 16, /* Single thread finished */ |
352 | scev_two_ends= 32, /* A couple of threads finished */ |
353 | scev_single_point= 64 /* Got single point */ |
354 | }; |
355 | |
356 | |
357 | /* |
358 | Gcalc_scan_iterator incapsulates the slisescan algorithm. |
359 | It takes filled Gcalc_heap as an datasource. Then can be |
360 | iterated trought the vertexes and intersection points with |
361 | the step() method. After the 'step()' one usually observes |
362 | the current 'slice' to do the necessary calculations, like |
363 | looking for intersections, calculating the area, whatever. |
364 | */ |
365 | |
366 | class Gcalc_scan_iterator : public Gcalc_dyn_list |
367 | { |
368 | public: |
369 | class point : public Gcalc_dyn_list::Item |
370 | { |
371 | public: |
372 | Gcalc_coord1 dx; |
373 | Gcalc_coord1 dy; |
374 | Gcalc_heap::Info *pi; |
375 | Gcalc_heap::Info *next_pi; |
376 | Gcalc_heap::Info *ev_pi; |
377 | const Gcalc_coord1 *l_border; |
378 | const Gcalc_coord1 *r_border; |
379 | point *ev_next; |
380 | |
381 | Gcalc_scan_events event; |
382 | |
383 | inline const point *c_get_next() const |
384 | { return (const point *)next; } |
385 | inline bool is_bottom() const { return !next_pi; } |
386 | gcalc_shape_info get_shape() const { return pi->node.shape.shape; } |
387 | inline point *get_next() { return (point *)next; } |
388 | inline const point *get_next() const { return (const point *)next; } |
389 | /* Compare the dx_dy parameters regarding the horiz_dir */ |
390 | /* returns -1 if less, 0 if equal, 1 if bigger */ |
391 | static int cmp_dx_dy(const Gcalc_coord1 dx_a, |
392 | const Gcalc_coord1 dy_a, |
393 | const Gcalc_coord1 dx_b, |
394 | const Gcalc_coord1 dy_b); |
395 | static int cmp_dx_dy(const Gcalc_heap::Info *p1, |
396 | const Gcalc_heap::Info *p2, |
397 | const Gcalc_heap::Info *p3, |
398 | const Gcalc_heap::Info *p4); |
399 | int cmp_dx_dy(const point *p) const; |
400 | point **next_ptr() { return (point **) &next; } |
401 | #ifndef GCALC_DBUG_OFF |
402 | unsigned int thread; |
403 | #endif /*GCALC_DBUG_OFF*/ |
404 | #ifdef GCALC_CHECK_WITH_FLOAT |
405 | void calc_x(long double *x, long double y, long double ix) const; |
406 | #endif /*GCALC_CHECK_WITH_FLOAT*/ |
407 | }; |
408 | |
409 | /* That class introduced mostly for the 'typecontrol' reason. */ |
410 | /* only difference from the point classis the get_next() function. */ |
411 | class event_point : public point |
412 | { |
413 | public: |
414 | inline const event_point *get_next() const |
415 | { return (const event_point*) ev_next; } |
416 | int simple_event() const |
417 | { |
418 | return !ev_next ? (event & (scev_point | scev_end)) : |
419 | (!ev_next->ev_next && event == scev_two_ends); |
420 | } |
421 | }; |
422 | |
423 | class intersection_info : public Gcalc_dyn_list::Item |
424 | { |
425 | public: |
426 | point *edge_a; |
427 | point *edge_b; |
428 | |
429 | Gcalc_coord2 t_a; |
430 | Gcalc_coord2 t_b; |
431 | int t_calculated; |
432 | Gcalc_coord3 x_exp; |
433 | int x_calculated; |
434 | Gcalc_coord3 y_exp; |
435 | int y_calculated; |
436 | void calc_t() |
437 | {if (!t_calculated) do_calc_t(); } |
438 | void calc_y_exp() |
439 | { if (!y_calculated) do_calc_y(); } |
440 | void calc_x_exp() |
441 | { if (!x_calculated) do_calc_x(); } |
442 | |
443 | void do_calc_t(); |
444 | void do_calc_x(); |
445 | void do_calc_y(); |
446 | }; |
447 | |
448 | |
449 | class slice_state |
450 | { |
451 | public: |
452 | point *slice; |
453 | point **event_position_hook; |
454 | point *event_end; |
455 | const Gcalc_heap::Info *pi; |
456 | }; |
457 | |
458 | public: |
459 | Gcalc_scan_iterator(size_t blk_size= 8192); |
460 | |
461 | GCALC_DECL_TERMINATED_STATE(killed) |
462 | |
463 | void init(Gcalc_heap *points); /* Iterator can be reused */ |
464 | void reset(); |
465 | int step(); |
466 | |
467 | Gcalc_heap::Info *more_points() { return m_cur_pi; } |
468 | bool more_trapezoids() |
469 | { return m_cur_pi && m_cur_pi->next; } |
470 | |
471 | const point *get_bottom_points() const |
472 | { return m_bottom_points; } |
473 | const point *get_event_position() const |
474 | { return *state.event_position_hook; } |
475 | const point *get_event_end() const |
476 | { return state.event_end; } |
477 | const event_point *get_events() const |
478 | { return (const event_point *) |
479 | (*state.event_position_hook == state.event_end ? |
480 | m_bottom_points : *state.event_position_hook); } |
481 | const point *get_b_slice() const { return state.slice; } |
482 | double get_h() const; |
483 | double get_y() const; |
484 | double get_event_x() const; |
485 | double get_sp_x(const point *sp) const; |
486 | int intersection_step() const |
487 | { return state.pi->type == Gcalc_heap::nt_intersection; } |
488 | const Gcalc_heap::Info *get_cur_pi() const |
489 | { |
490 | return state.pi; |
491 | } |
492 | |
493 | private: |
494 | Gcalc_heap *m_heap; |
495 | Gcalc_heap::Info *m_cur_pi; |
496 | slice_state state; |
497 | |
498 | #ifndef GCALC_DBUG_OFF |
499 | unsigned int m_cur_thread; |
500 | #endif /*GCALC_DBUG_OFF*/ |
501 | |
502 | point *m_bottom_points; |
503 | point **m_bottom_hook; |
504 | |
505 | int node_scan(); |
506 | void eq_scan(); |
507 | void intersection_scan(); |
508 | void remove_bottom_node(); |
509 | int insert_top_node(); |
510 | int add_intersection(point *sp_a, point *sp_b, |
511 | Gcalc_heap::Info *pi_from); |
512 | int add_eq_node(Gcalc_heap::Info *node, point *sp); |
513 | int add_events_for_node(point *sp_node); |
514 | |
515 | point *new_slice_point() |
516 | { |
517 | point *new_point= (point *)new_item(); |
518 | return new_point; |
519 | } |
520 | intersection_info *new_intersection_info(point *a, point *b) |
521 | { |
522 | intersection_info *ii= (intersection_info *)new_item(); |
523 | ii->edge_a= a; |
524 | ii->edge_b= b; |
525 | ii->t_calculated= ii->x_calculated= ii->y_calculated= 0; |
526 | return ii; |
527 | } |
528 | int arrange_event(int do_sorting, int n_intersections); |
529 | static double get_pure_double(const Gcalc_internal_coord *d, int d_len); |
530 | }; |
531 | |
532 | |
533 | /* |
534 | Gcalc_trapezoid_iterator simplifies the calculations on |
535 | the current slice of the Gcalc_scan_iterator. |
536 | One can walk through the trapezoids formed between |
537 | previous and current slices. |
538 | */ |
539 | |
540 | #ifdef TMP_BLOCK |
541 | class Gcalc_trapezoid_iterator |
542 | { |
543 | protected: |
544 | const Gcalc_scan_iterator::point *sp0; |
545 | const Gcalc_scan_iterator::point *sp1; |
546 | public: |
547 | Gcalc_trapezoid_iterator(const Gcalc_scan_iterator *scan_i) : |
548 | sp0(scan_i->get_b_slice()), |
549 | sp1(scan_i->get_t_slice()) |
550 | {} |
551 | |
552 | inline bool more() const { return sp1 && sp1->next; } |
553 | |
554 | const Gcalc_scan_iterator::point *lt() const { return sp1; } |
555 | const Gcalc_scan_iterator::point *lb() const { return sp0; } |
556 | const Gcalc_scan_iterator::point *rb() const |
557 | { |
558 | const Gcalc_scan_iterator::point *result= sp0; |
559 | while ((result= result->c_get_next())->is_bottom()) |
560 | {} |
561 | return result; |
562 | } |
563 | const Gcalc_scan_iterator::point *rt() const |
564 | { return sp1->c_get_next(); } |
565 | |
566 | void operator++() |
567 | { |
568 | sp0= rb(); |
569 | sp1= rt(); |
570 | } |
571 | }; |
572 | #endif /*TMP_BLOCK*/ |
573 | |
574 | |
575 | /* |
576 | Gcalc_point_iterator simplifies the calculations on |
577 | the current slice of the Gcalc_scan_iterator. |
578 | One can walk through the points on the current slice. |
579 | */ |
580 | |
581 | class Gcalc_point_iterator |
582 | { |
583 | protected: |
584 | const Gcalc_scan_iterator::point *sp; |
585 | public: |
586 | Gcalc_point_iterator(const Gcalc_scan_iterator *scan_i): |
587 | sp(scan_i->get_b_slice()) |
588 | {} |
589 | |
590 | inline bool more() const { return sp != NULL; } |
591 | inline void operator++() { sp= sp->c_get_next(); } |
592 | inline const Gcalc_scan_iterator::point *point() const { return sp; } |
593 | inline const Gcalc_heap::Info *get_pi() const { return sp->pi; } |
594 | inline gcalc_shape_info get_shape() const { return sp->get_shape(); } |
595 | inline void restart(const Gcalc_scan_iterator *scan_i) |
596 | { sp= scan_i->get_b_slice(); } |
597 | }; |
598 | |
599 | #endif /*GCALC_SLICESCAN_INCLUDED*/ |
600 | |
601 | |