| 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_TOOLS_INCLUDED |
| 19 | #define GCALC_TOOLS_INCLUDED |
| 20 | |
| 21 | #include "gcalc_slicescan.h" |
| 22 | #include "sql_string.h" |
| 23 | |
| 24 | |
| 25 | /* |
| 26 | The Gcalc_function class objects are used to check for a binary relation. |
| 27 | The relation can be constructed with the prefix notation using predicates as |
| 28 | op_not (as !A) |
| 29 | op_union ( A || B || C... ) |
| 30 | op_intersection ( A && B && C ... ) |
| 31 | op_symdifference ( A+B+C+... == 1 ) |
| 32 | op_difference ( A && !(B||C||..)) |
| 33 | with the calls of the add_operation(operation, n_operands) method. |
| 34 | The relation is calculated over a set of shapes, that in turn have |
| 35 | to be added with the add_new_shape() method. All the 'shapes' can |
| 36 | be set to 0 with clear_shapes() method and single value |
| 37 | can be changed with the invert_state() method. |
| 38 | Then the value of the relation can be calculated with the count() method. |
| 39 | Frequently used method is find_function(Gcalc_scan_iterator it) that |
| 40 | iterates through the 'it' until the relation becomes TRUE. |
| 41 | */ |
| 42 | |
| 43 | class Gcalc_function |
| 44 | { |
| 45 | private: |
| 46 | String shapes_buffer; |
| 47 | String function_buffer; |
| 48 | int *i_states; |
| 49 | int *b_states; |
| 50 | uint32 cur_object_id; |
| 51 | uint n_shapes; |
| 52 | int count_internal(const char *cur_func, uint set_type, |
| 53 | const char **end); |
| 54 | public: |
| 55 | enum value |
| 56 | { |
| 57 | v_empty= 0x0000000, |
| 58 | v_find_t= 0x1000000, |
| 59 | v_find_f= 0x2000000, |
| 60 | v_t_found= 0x3000000, |
| 61 | v_f_found= 0x4000000, |
| 62 | v_mask= 0x7000000 |
| 63 | }; |
| 64 | enum op_type |
| 65 | { |
| 66 | op_not= 0x80000000, |
| 67 | op_shape= 0x00000000, |
| 68 | op_union= 0x10000000, |
| 69 | op_intersection= 0x20000000, |
| 70 | op_symdifference= 0x30000000, |
| 71 | op_difference= 0x40000000, |
| 72 | op_repeat= 0x50000000, |
| 73 | op_border= 0x60000000, |
| 74 | op_internals= 0x70000000, |
| 75 | op_false= 0x08000000, |
| 76 | op_any= 0x78000000 /* The mask to get any of the operations */ |
| 77 | }; |
| 78 | enum shape_type |
| 79 | { |
| 80 | shape_point= 0, |
| 81 | shape_line= 1, |
| 82 | shape_polygon= 2, |
| 83 | shape_hole= 3 |
| 84 | }; |
| 85 | enum count_result |
| 86 | { |
| 87 | result_false= 0, |
| 88 | result_true= 1, |
| 89 | result_unknown= 2 |
| 90 | }; |
| 91 | Gcalc_function() : n_shapes(0) {} |
| 92 | gcalc_shape_info add_new_shape(uint32 shape_id, shape_type shape_kind); |
| 93 | /* |
| 94 | Adds the leaf operation that returns the shape value. |
| 95 | Also adds the shape to the list of operands. |
| 96 | */ |
| 97 | int single_shape_op(shape_type shape_kind, gcalc_shape_info *si); |
| 98 | void add_operation(uint operation, uint32 n_operands); |
| 99 | void add_not_operation(op_type operation, uint32 n_operands); |
| 100 | uint32 get_next_expression_pos() { return function_buffer.length(); } |
| 101 | void add_operands_to_op(uint32 operation_pos, uint32 n_operands); |
| 102 | int repeat_expression(uint32 exp_pos); |
| 103 | void set_cur_obj(uint32 cur_obj) { cur_object_id= cur_obj; } |
| 104 | int reserve_shape_buffer(uint n_shapes); |
| 105 | int reserve_op_buffer(uint n_ops); |
| 106 | uint get_nshapes() const { return n_shapes; } |
| 107 | shape_type get_shape_kind(gcalc_shape_info si) const |
| 108 | { |
| 109 | return (shape_type) uint4korr(shapes_buffer.ptr() + (si*4)); |
| 110 | } |
| 111 | |
| 112 | void set_states(int *shape_states) { i_states= shape_states; } |
| 113 | int alloc_states(); |
| 114 | void invert_i_state(gcalc_shape_info shape) { i_states[shape]^= 1; } |
| 115 | void set_i_state(gcalc_shape_info shape) { i_states[shape]= 1; } |
| 116 | void clear_i_state(gcalc_shape_info shape) { i_states[shape]= 0; } |
| 117 | void set_b_state(gcalc_shape_info shape) { b_states[shape]= 1; } |
| 118 | void clear_b_state(gcalc_shape_info shape) { b_states[shape]= 0; } |
| 119 | int get_state(gcalc_shape_info shape) |
| 120 | { return i_states[shape] | b_states[shape]; } |
| 121 | int get_i_state(gcalc_shape_info shape) { return i_states[shape]; } |
| 122 | int get_b_state(gcalc_shape_info shape) { return b_states[shape]; } |
| 123 | int count() |
| 124 | { return count_internal(function_buffer.ptr(), 0, 0); } |
| 125 | int count_last() |
| 126 | { return count_internal(function_buffer.ptr(), 1, 0); } |
| 127 | void clear_i_states(); |
| 128 | void clear_b_states(); |
| 129 | void reset(); |
| 130 | |
| 131 | int check_function(Gcalc_scan_iterator &scan_it); |
| 132 | }; |
| 133 | |
| 134 | |
| 135 | /* |
| 136 | Gcalc_operation_transporter class extends the Gcalc_shape_transporter. |
| 137 | In addition to the parent's functionality, it fills the Gcalc_function |
| 138 | object so it has the function that determines the proper shape. |
| 139 | For example Multipolyline will be represented as an union of polylines. |
| 140 | */ |
| 141 | |
| 142 | class Gcalc_operation_transporter : public Gcalc_shape_transporter |
| 143 | { |
| 144 | protected: |
| 145 | Gcalc_function *m_fn; |
| 146 | gcalc_shape_info m_si; |
| 147 | public: |
| 148 | Gcalc_operation_transporter(Gcalc_function *fn, Gcalc_heap *heap) : |
| 149 | Gcalc_shape_transporter(heap), m_fn(fn) {} |
| 150 | |
| 151 | int single_point(double x, double y); |
| 152 | int start_line(); |
| 153 | int complete_line(); |
| 154 | int start_poly(); |
| 155 | int complete_poly(); |
| 156 | int start_ring(); |
| 157 | int complete_ring(); |
| 158 | int add_point(double x, double y); |
| 159 | int start_collection(int n_objects); |
| 160 | int empty_shape(); |
| 161 | }; |
| 162 | |
| 163 | |
| 164 | /* |
| 165 | When we calculate the result of an spatial operation like |
| 166 | Union or Intersection, we receive vertexes of the result |
| 167 | one-by-one, and probably need to treat them in variative ways. |
| 168 | So, the Gcalc_result_receiver class designed to get these |
| 169 | vertexes and construct shapes/objects out of them. |
| 170 | and to store the result in an appropriate format |
| 171 | */ |
| 172 | |
| 173 | class Gcalc_result_receiver |
| 174 | { |
| 175 | String buffer; |
| 176 | uint32 n_points; |
| 177 | Gcalc_function::shape_type common_shapetype; |
| 178 | bool collection_result; |
| 179 | uint32 n_shapes; |
| 180 | uint32 n_holes; |
| 181 | |
| 182 | Gcalc_function::shape_type cur_shape; |
| 183 | uint32 shape_pos; |
| 184 | double first_x, first_y, prev_x, prev_y; |
| 185 | double shape_area; |
| 186 | public: |
| 187 | Gcalc_result_receiver() : collection_result(FALSE), n_shapes(0), n_holes(0) |
| 188 | {} |
| 189 | int start_shape(Gcalc_function::shape_type shape); |
| 190 | int add_point(double x, double y); |
| 191 | int complete_shape(); |
| 192 | int single_point(double x, double y); |
| 193 | int done(); |
| 194 | void reset(); |
| 195 | |
| 196 | const char *result() { return buffer.ptr(); } |
| 197 | uint length() { return buffer.length(); } |
| 198 | int get_nshapes() { return n_shapes; } |
| 199 | int get_nholes() { return n_holes; } |
| 200 | int get_result_typeid(); |
| 201 | uint32 position() { return buffer.length(); } |
| 202 | int move_hole(uint32 dest_position, uint32 source_position, |
| 203 | uint32 *position_shift); |
| 204 | }; |
| 205 | |
| 206 | |
| 207 | /* |
| 208 | Gcalc_operation_reducer class incapsulates the spatial |
| 209 | operation functionality. It analyses the slices generated by |
| 210 | the slicescan and calculates the shape of the result defined |
| 211 | by some Gcalc_function. |
| 212 | */ |
| 213 | |
| 214 | class Gcalc_operation_reducer : public Gcalc_dyn_list |
| 215 | { |
| 216 | public: |
| 217 | enum modes |
| 218 | { |
| 219 | /* Numeric values important here - careful with changing */ |
| 220 | default_mode= 0, |
| 221 | prefer_big_with_holes= 1, |
| 222 | polygon_selfintersections_allowed= 2, /* allowed in the result */ |
| 223 | line_selfintersections_allowed= 4 /* allowed in the result */ |
| 224 | }; |
| 225 | |
| 226 | Gcalc_operation_reducer(size_t blk_size=8192); |
| 227 | void init(Gcalc_function *fn, modes mode= default_mode); |
| 228 | Gcalc_operation_reducer(Gcalc_function *fn, modes mode= default_mode, |
| 229 | size_t blk_size=8192); |
| 230 | GCALC_DECL_TERMINATED_STATE(killed) |
| 231 | int count_slice(Gcalc_scan_iterator *si); |
| 232 | int count_all(Gcalc_heap *hp); |
| 233 | int get_result(Gcalc_result_receiver *storage); |
| 234 | void reset(); |
| 235 | |
| 236 | #ifndef GCALC_DBUG_OFF |
| 237 | int n_res_points; |
| 238 | #endif /*GCALC_DBUG_OFF*/ |
| 239 | class res_point : public Gcalc_dyn_list::Item |
| 240 | { |
| 241 | public: |
| 242 | int intersection_point; |
| 243 | union |
| 244 | { |
| 245 | const Gcalc_heap::Info *pi; |
| 246 | res_point *first_poly_node; |
| 247 | }; |
| 248 | union |
| 249 | { |
| 250 | res_point *outer_poly; |
| 251 | uint32 poly_position; |
| 252 | }; |
| 253 | res_point *up; |
| 254 | res_point *down; |
| 255 | res_point *glue; |
| 256 | Gcalc_function::shape_type type; |
| 257 | Gcalc_dyn_list::Item **prev_hook; |
| 258 | #ifndef GCALC_DBUG_OFF |
| 259 | int point_n; |
| 260 | #endif /*GCALC_DBUG_OFF*/ |
| 261 | void set(const Gcalc_scan_iterator *si); |
| 262 | res_point *get_next() { return (res_point *)next; } |
| 263 | }; |
| 264 | |
| 265 | class active_thread : public Gcalc_dyn_list::Item |
| 266 | { |
| 267 | public: |
| 268 | res_point *rp; |
| 269 | res_point *thread_start; |
| 270 | |
| 271 | const Gcalc_heap::Info *p1, *p2; |
| 272 | res_point *enabled() { return rp; } |
| 273 | active_thread *get_next() { return (active_thread *)next; } |
| 274 | }; |
| 275 | |
| 276 | class poly_instance : public Gcalc_dyn_list::Item |
| 277 | { |
| 278 | public: |
| 279 | uint32 *after_poly_position; |
| 280 | poly_instance *get_next() { return (poly_instance *)next; } |
| 281 | }; |
| 282 | |
| 283 | class line : public Gcalc_dyn_list::Item |
| 284 | { |
| 285 | public: |
| 286 | active_thread *t; |
| 287 | int incoming; |
| 288 | const Gcalc_scan_iterator::point *p; |
| 289 | line *get_next() { return (line *)next; } |
| 290 | }; |
| 291 | |
| 292 | class poly_border : public Gcalc_dyn_list::Item |
| 293 | { |
| 294 | public: |
| 295 | active_thread *t; |
| 296 | int incoming; |
| 297 | int prev_state; |
| 298 | const Gcalc_scan_iterator::point *p; |
| 299 | poly_border *get_next() { return (poly_border *)next; } |
| 300 | }; |
| 301 | |
| 302 | line *m_lines; |
| 303 | Gcalc_dyn_list::Item **m_lines_hook; |
| 304 | poly_border *m_poly_borders; |
| 305 | Gcalc_dyn_list::Item **m_poly_borders_hook; |
| 306 | line *new_line() { return (line *) new_item(); } |
| 307 | poly_border *new_poly_border() { return (poly_border *) new_item(); } |
| 308 | int add_line(int incoming, active_thread *t, |
| 309 | const Gcalc_scan_iterator::point *p); |
| 310 | int add_poly_border(int incoming, active_thread *t, int prev_state, |
| 311 | const Gcalc_scan_iterator::point *p); |
| 312 | |
| 313 | protected: |
| 314 | Gcalc_function *m_fn; |
| 315 | Gcalc_dyn_list::Item **m_res_hook; |
| 316 | res_point *m_result; |
| 317 | int m_mode; |
| 318 | |
| 319 | res_point *result_heap; |
| 320 | active_thread *m_first_active_thread; |
| 321 | |
| 322 | res_point *add_res_point(Gcalc_function::shape_type type); |
| 323 | active_thread *new_active_thread() { return (active_thread *)new_item(); } |
| 324 | |
| 325 | poly_instance *new_poly() { return (poly_instance *) new_item(); } |
| 326 | |
| 327 | private: |
| 328 | int start_line(active_thread *t, const Gcalc_scan_iterator::point *p, |
| 329 | const Gcalc_scan_iterator *si); |
| 330 | int end_line(active_thread *t, const Gcalc_scan_iterator *si); |
| 331 | int connect_threads(int incoming_a, int incoming_b, |
| 332 | active_thread *ta, active_thread *tb, |
| 333 | const Gcalc_scan_iterator::point *pa, |
| 334 | const Gcalc_scan_iterator::point *pb, |
| 335 | active_thread *prev_range, |
| 336 | const Gcalc_scan_iterator *si, |
| 337 | Gcalc_function::shape_type s_t); |
| 338 | int add_single_point(const Gcalc_scan_iterator *si); |
| 339 | poly_border *get_pair_border(poly_border *b1); |
| 340 | int continue_range(active_thread *t, const Gcalc_heap::Info *p, |
| 341 | const Gcalc_heap::Info *p_next); |
| 342 | int continue_i_range(active_thread *t, |
| 343 | const Gcalc_heap::Info *ii); |
| 344 | int end_couple(active_thread *t0, active_thread *t1, const Gcalc_heap::Info *p); |
| 345 | int get_single_result(res_point *res, Gcalc_result_receiver *storage); |
| 346 | int get_result_thread(res_point *cur, Gcalc_result_receiver *storage, |
| 347 | int move_upward, res_point *first_poly_node); |
| 348 | int get_polygon_result(res_point *cur, Gcalc_result_receiver *storage, |
| 349 | res_point *first_poly_node); |
| 350 | int get_line_result(res_point *cur, Gcalc_result_receiver *storage); |
| 351 | |
| 352 | void free_result(res_point *res); |
| 353 | }; |
| 354 | |
| 355 | #endif /*GCALC_TOOLS_INCLUDED*/ |
| 356 | |
| 357 | |