mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 12:02:42 +01:00
a1315808b4
When edges of a polygon coicide, it can form an pike, that is turned into a line after an operation. In this case a former polygon point can be an end of a single line, and that case wasn't properly handled. per-file comments: mysql-test/r/gis-precise.result bug 839327 Crash in Gcalc_operation_reducer::end_couple with ST_UNION and MULTIPOLYGONs in 5.3-gis. test result updated. mysql-test/t/gis-precise.test bug 839327 Crash in Gcalc_operation_reducer::end_couple with ST_UNION and MULTIPOLYGONs in 5.3-gis. test case added. sql/gcalc_tools.cc bug 839327 Crash in Gcalc_operation_reducer::end_couple with ST_UNION and MULTIPOLYGONs in 5.3-gis. in the scev_two_ends case check if we have single line ending on a polygon node.
1273 lines
30 KiB
C++
1273 lines
30 KiB
C++
/* Copyright (c) 2000, 2010 Oracle and/or its affiliates. All rights reserved.
|
|
|
|
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
|
|
|
|
#include "mysql_priv.h"
|
|
|
|
#ifdef HAVE_SPATIAL
|
|
|
|
#include "gcalc_tools.h"
|
|
#include "spatial.h"
|
|
|
|
#define float_to_coord(d) ((double) d)
|
|
|
|
|
|
/*
|
|
Adds new shape to the relation.
|
|
After that it can be used as an argument of an operation.
|
|
*/
|
|
|
|
gcalc_shape_info Gcalc_function::add_new_shape(uint32 shape_id,
|
|
shape_type shape_kind)
|
|
{
|
|
shapes_buffer.q_append((uint32) shape_kind);
|
|
return n_shapes++;
|
|
}
|
|
|
|
|
|
/*
|
|
Adds new operation to the constructed relation.
|
|
To construct the complex relation one has to specify operations
|
|
in prefix style.
|
|
*/
|
|
|
|
void Gcalc_function::add_operation(op_type operation, uint32 n_operands)
|
|
{
|
|
uint32 op_code= (uint32 ) operation + n_operands;
|
|
function_buffer.q_append(op_code);
|
|
}
|
|
|
|
|
|
/*
|
|
Sometimes the number of arguments is unknown at the moment the operation
|
|
is added. That allows to specify it later.
|
|
*/
|
|
|
|
void Gcalc_function::add_operands_to_op(uint32 operation_pos, uint32 n_operands)
|
|
{
|
|
uint32 op_code= uint4korr(function_buffer.ptr() + operation_pos) + n_operands;
|
|
function_buffer.write_at_position(operation_pos, op_code);
|
|
}
|
|
|
|
|
|
/*
|
|
Just like the add_operation() but the result will be the inverted
|
|
value of an operation.
|
|
*/
|
|
|
|
void Gcalc_function::add_not_operation(op_type operation, uint32 n_operands)
|
|
{
|
|
uint32 op_code= ((uint32) op_not | (uint32 ) operation) + n_operands;
|
|
function_buffer.q_append(op_code);
|
|
}
|
|
|
|
|
|
int Gcalc_function::single_shape_op(shape_type shape_kind, gcalc_shape_info *si)
|
|
{
|
|
if (reserve_shape_buffer(1) || reserve_op_buffer(1))
|
|
return 1;
|
|
*si= add_new_shape(0, shape_kind);
|
|
add_operation(op_shape, *si);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Specify how many arguments we're going to have.
|
|
*/
|
|
|
|
int Gcalc_function::reserve_shape_buffer(uint n_shapes)
|
|
{
|
|
return shapes_buffer.reserve(n_shapes * 4, 512);
|
|
}
|
|
|
|
|
|
/*
|
|
Specify how many operations we're going to have.
|
|
*/
|
|
|
|
int Gcalc_function::reserve_op_buffer(uint n_ops)
|
|
{
|
|
return function_buffer.reserve(n_ops * 4, 512);
|
|
}
|
|
|
|
|
|
int Gcalc_function::alloc_states()
|
|
{
|
|
if (function_buffer.reserve((n_shapes+1) * 2 * sizeof(int)))
|
|
return 1;
|
|
i_states= (int *) (function_buffer.ptr() + ALIGN_SIZE(function_buffer.length()));
|
|
saved_i_states= i_states + (n_shapes + 1);
|
|
return 0;
|
|
}
|
|
|
|
|
|
void Gcalc_function::save_states()
|
|
{
|
|
memcpy(saved_i_states, i_states, (n_shapes+1) * sizeof(int));
|
|
}
|
|
|
|
|
|
void Gcalc_function::restore_states()
|
|
{
|
|
memcpy(i_states, saved_i_states, (n_shapes+1) * sizeof(int));
|
|
}
|
|
|
|
|
|
int Gcalc_function::count_internal()
|
|
{
|
|
int c_op= uint4korr(cur_func);
|
|
op_type next_func= (op_type) (c_op & op_any);
|
|
int mask= (c_op & op_not) ? 1:0;
|
|
int n_ops= c_op & ~op_any;
|
|
int result;
|
|
|
|
cur_func+= 4;
|
|
if (next_func == op_shape)
|
|
return i_states[c_op & ~(op_any | op_not)] ^ mask;
|
|
if (n_ops == 0)
|
|
return mask;
|
|
|
|
result= count_internal();
|
|
|
|
while (--n_ops)
|
|
{
|
|
int next_res= count_internal();
|
|
switch (next_func)
|
|
{
|
|
case op_union:
|
|
result= result | next_res;
|
|
break;
|
|
case op_intersection:
|
|
result= result & next_res;
|
|
break;
|
|
case op_symdifference:
|
|
result= result ^ next_res;
|
|
break;
|
|
case op_difference:
|
|
result= result & !next_res;
|
|
break;
|
|
case op_backdifference:
|
|
result= !result & next_res;
|
|
break;
|
|
default:
|
|
DBUG_ASSERT(FALSE);
|
|
};
|
|
}
|
|
|
|
return result ^ mask;
|
|
}
|
|
|
|
|
|
/*
|
|
Clear the state of the object.
|
|
*/
|
|
|
|
void Gcalc_function::reset()
|
|
{
|
|
n_shapes= 0;
|
|
shapes_buffer.length(0);
|
|
function_buffer.length(0);
|
|
}
|
|
|
|
|
|
int Gcalc_function::find_function(Gcalc_scan_iterator &scan_it)
|
|
{
|
|
const Gcalc_scan_iterator::point *eq_start, *cur_eq, *events;
|
|
|
|
while (scan_it.more_points())
|
|
{
|
|
if (scan_it.step())
|
|
return -1;
|
|
events= scan_it.get_events();
|
|
|
|
/* these kinds of events don't change the function */
|
|
if (events->simple_event())
|
|
continue;
|
|
|
|
Gcalc_point_iterator pit(&scan_it);
|
|
clear_state();
|
|
/* Walk to the event, marking polygons we met */
|
|
for (; pit.point() != scan_it.get_event_position(); ++pit)
|
|
{
|
|
gcalc_shape_info si= pit.point()->get_shape();
|
|
if ((get_shape_kind(si) == Gcalc_function::shape_polygon))
|
|
invert_state(si);
|
|
}
|
|
save_states();
|
|
/* Check the status of the event point */
|
|
for (; events; events= events->get_next())
|
|
set_on_state(events->get_shape());
|
|
|
|
if (count())
|
|
return 1;
|
|
|
|
if (scan_it.get_event_position() == scan_it.get_event_end())
|
|
continue;
|
|
|
|
/* Check the status after the event */
|
|
restore_states();
|
|
eq_start= pit.point();
|
|
do
|
|
{
|
|
++pit;
|
|
if (pit.point() != scan_it.get_event_end() &&
|
|
eq_start->cmp_dx_dy(pit.point()) == 0)
|
|
continue;
|
|
save_states();
|
|
for (cur_eq= eq_start; cur_eq != pit.point();
|
|
cur_eq= cur_eq->get_next())
|
|
set_on_state(cur_eq->get_shape());
|
|
if (count())
|
|
return 1;
|
|
restore_states();
|
|
for (cur_eq= eq_start; cur_eq != pit.point(); cur_eq= cur_eq->get_next())
|
|
{
|
|
gcalc_shape_info si= cur_eq->get_shape();
|
|
if ((get_shape_kind(si) == Gcalc_function::shape_polygon))
|
|
invert_state(si);
|
|
}
|
|
if (count())
|
|
return 1;
|
|
eq_start= pit.point();
|
|
} while (pit.point() != scan_it.get_event_end());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_operation_transporter::single_point(double x, double y)
|
|
{
|
|
gcalc_shape_info si;
|
|
return m_fn->single_shape_op(Gcalc_function::shape_point, &si) ||
|
|
int_single_point(si, x, y);
|
|
}
|
|
|
|
|
|
int Gcalc_operation_transporter::start_line()
|
|
{
|
|
int_start_line();
|
|
return m_fn->single_shape_op(Gcalc_function::shape_line, &m_si);
|
|
}
|
|
|
|
|
|
int Gcalc_operation_transporter::complete_line()
|
|
{
|
|
int_complete_line();
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_operation_transporter::start_poly()
|
|
{
|
|
int_start_poly();
|
|
return m_fn->single_shape_op(Gcalc_function::shape_polygon, &m_si);
|
|
}
|
|
|
|
|
|
int Gcalc_operation_transporter::complete_poly()
|
|
{
|
|
int_complete_poly();
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_operation_transporter::start_ring()
|
|
{
|
|
int_start_ring();
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_operation_transporter::complete_ring()
|
|
{
|
|
int_complete_ring();
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_operation_transporter::add_point(double x, double y)
|
|
{
|
|
return int_add_point(m_si, x, y);
|
|
}
|
|
|
|
|
|
int Gcalc_operation_transporter::start_collection(int n_objects)
|
|
{
|
|
if (m_fn->reserve_shape_buffer(n_objects) || m_fn->reserve_op_buffer(1))
|
|
return 1;
|
|
m_fn->add_operation(Gcalc_function::op_union, n_objects);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_result_receiver::start_shape(Gcalc_function::shape_type shape)
|
|
{
|
|
if (buffer.reserve(4*2, 512))
|
|
return 1;
|
|
cur_shape= shape;
|
|
shape_pos= buffer.length();
|
|
buffer.length(shape_pos + ((shape == Gcalc_function::shape_point) ? 4:8));
|
|
n_points= 0;
|
|
shape_area= 0.0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_result_receiver::add_point(double x, double y)
|
|
{
|
|
if (n_points && x == prev_x && y == prev_y)
|
|
return 0;
|
|
|
|
if (!n_points++)
|
|
{
|
|
prev_x= first_x= x;
|
|
prev_y= first_y= y;
|
|
return 0;
|
|
}
|
|
|
|
shape_area+= prev_x*y - prev_y*x;
|
|
|
|
if (buffer.reserve(8*2, 512))
|
|
return 1;
|
|
buffer.q_append(prev_x);
|
|
buffer.q_append(prev_y);
|
|
prev_x= x;
|
|
prev_y= y;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_result_receiver::complete_shape()
|
|
{
|
|
if (n_points == 0)
|
|
{
|
|
buffer.length(shape_pos);
|
|
return 0;
|
|
}
|
|
if (n_points == 1)
|
|
{
|
|
if (cur_shape != Gcalc_function::shape_point)
|
|
{
|
|
if (cur_shape == Gcalc_function::shape_hole)
|
|
{
|
|
buffer.length(shape_pos);
|
|
return 0;
|
|
}
|
|
cur_shape= Gcalc_function::shape_point;
|
|
buffer.length(buffer.length()-4);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBUG_ASSERT(cur_shape != Gcalc_function::shape_point);
|
|
if (cur_shape == Gcalc_function::shape_hole)
|
|
{
|
|
shape_area+= prev_x*first_y - prev_y*first_x;
|
|
if (fabs(shape_area) < 1e-8)
|
|
{
|
|
buffer.length(shape_pos);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if ((cur_shape == Gcalc_function::shape_polygon ||
|
|
cur_shape == Gcalc_function::shape_hole) &&
|
|
prev_x == first_x && prev_y == first_y)
|
|
{
|
|
n_points--;
|
|
buffer.write_at_position(shape_pos+4, n_points);
|
|
goto do_complete;
|
|
}
|
|
buffer.write_at_position(shape_pos+4, n_points);
|
|
}
|
|
|
|
if (buffer.reserve(8*2, 512))
|
|
return 1;
|
|
buffer.q_append(prev_x);
|
|
buffer.q_append(prev_y);
|
|
|
|
do_complete:
|
|
buffer.write_at_position(shape_pos, (uint32) cur_shape);
|
|
|
|
if (!n_shapes++)
|
|
{
|
|
DBUG_ASSERT(cur_shape != Gcalc_function::shape_hole);
|
|
common_shapetype= cur_shape;
|
|
}
|
|
else if (cur_shape == Gcalc_function::shape_hole)
|
|
{
|
|
++n_holes;
|
|
}
|
|
else if (!collection_result && (cur_shape != common_shapetype))
|
|
{
|
|
collection_result= true;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_result_receiver::single_point(double x, double y)
|
|
{
|
|
return start_shape(Gcalc_function::shape_point) ||
|
|
add_point(x, y) ||
|
|
complete_shape();
|
|
}
|
|
|
|
|
|
int Gcalc_result_receiver::done()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
void Gcalc_result_receiver::reset()
|
|
{
|
|
buffer.length(0);
|
|
collection_result= FALSE;
|
|
n_shapes= n_holes= 0;
|
|
}
|
|
|
|
|
|
int Gcalc_result_receiver::get_result_typeid()
|
|
{
|
|
if (!n_shapes || collection_result)
|
|
return Geometry::wkb_geometrycollection;
|
|
|
|
switch (common_shapetype)
|
|
{
|
|
case Gcalc_function::shape_polygon:
|
|
return (n_shapes - n_holes == 1) ?
|
|
Geometry::wkb_polygon : Geometry::wkb_multipolygon;
|
|
case Gcalc_function::shape_point:
|
|
return (n_shapes == 1) ? Geometry::wkb_point : Geometry::wkb_multipoint;
|
|
case Gcalc_function::shape_line:
|
|
return (n_shapes == 1) ? Geometry::wkb_linestring :
|
|
Geometry::wkb_multilinestring;
|
|
default:
|
|
DBUG_ASSERT(0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_result_receiver::move_hole(uint32 dest_position, uint32 source_position,
|
|
uint32 *position_shift)
|
|
{
|
|
char *ptr;
|
|
int source_len;
|
|
|
|
*position_shift= source_len= buffer.length() - source_position;
|
|
|
|
if (dest_position == source_position)
|
|
return 0;
|
|
|
|
if (buffer.reserve(source_len, MY_ALIGN(source_len, 512)))
|
|
return 1;
|
|
|
|
ptr= (char *) buffer.ptr();
|
|
memmove(ptr + dest_position + source_len, ptr + dest_position,
|
|
buffer.length() - dest_position);
|
|
memcpy(ptr + dest_position, ptr + buffer.length(), source_len);
|
|
return 0;
|
|
}
|
|
|
|
|
|
Gcalc_operation_reducer::Gcalc_operation_reducer(size_t blk_size) :
|
|
Gcalc_dyn_list(blk_size, sizeof(res_point)),
|
|
m_res_hook((Gcalc_dyn_list::Item **)&m_result),
|
|
m_first_active_thread(NULL)
|
|
{}
|
|
|
|
|
|
void Gcalc_operation_reducer::init(Gcalc_function *fn, modes mode)
|
|
{
|
|
m_fn= fn;
|
|
m_mode= mode;
|
|
m_first_active_thread= NULL;
|
|
m_lines= NULL;
|
|
m_lines_hook= (Gcalc_dyn_list::Item **) &m_lines;
|
|
m_poly_borders= NULL;
|
|
m_poly_borders_hook= (Gcalc_dyn_list::Item **) &m_poly_borders;
|
|
}
|
|
|
|
|
|
Gcalc_operation_reducer::
|
|
Gcalc_operation_reducer(Gcalc_function *fn, modes mode, size_t blk_size) :
|
|
Gcalc_dyn_list(blk_size, sizeof(res_point)),
|
|
m_res_hook((Gcalc_dyn_list::Item **)&m_result)
|
|
{
|
|
init(fn, mode);
|
|
}
|
|
|
|
|
|
inline int Gcalc_operation_reducer::continue_range(active_thread *t,
|
|
const Gcalc_heap::Info *p,
|
|
int horiz_dir, double dx_dy)
|
|
{
|
|
res_point *rp= add_res_point(t->rp->type);
|
|
if (!rp)
|
|
return 1;
|
|
rp->glue= NULL;
|
|
rp->down= t->rp;
|
|
t->rp->up= rp;
|
|
rp->intersection_point= false;
|
|
rp->pi= p;
|
|
t->rp= rp;
|
|
t->horiz_dir= horiz_dir;
|
|
t->dx_dy= dx_dy;
|
|
return 0;
|
|
}
|
|
|
|
|
|
inline int Gcalc_operation_reducer::continue_i_range(active_thread *t,
|
|
double x, double y,
|
|
int horiz_dir, double dx_dy)
|
|
{
|
|
res_point *rp= add_res_point(t->rp->type);
|
|
if (!rp)
|
|
return 1;
|
|
rp->glue= NULL;
|
|
rp->down= t->rp;
|
|
t->rp->up= rp;
|
|
rp->intersection_point= true;
|
|
rp->x= x;
|
|
rp->y= y;
|
|
t->rp= rp;
|
|
t->horiz_dir= horiz_dir;
|
|
t->dx_dy= dx_dy;
|
|
return 0;
|
|
}
|
|
|
|
int Gcalc_operation_reducer::end_couple(active_thread *t0, active_thread *t1,
|
|
const Gcalc_heap::Info *p)
|
|
{
|
|
res_point *rp0, *rp1;
|
|
DBUG_ASSERT(t0->rp->type == t1->rp->type);
|
|
if (!(rp0= add_res_point(t0->rp->type)) ||
|
|
!(rp1= add_res_point(t0->rp->type)))
|
|
return 1;
|
|
rp0->down= t0->rp;
|
|
rp1->down= t1->rp;
|
|
rp1->glue= rp0;
|
|
rp0->glue= rp1;
|
|
rp0->up= rp1->up= NULL;
|
|
t0->rp->up= rp0;
|
|
t1->rp->up= rp1;
|
|
rp0->intersection_point= rp1->intersection_point= false;
|
|
rp0->pi= rp1->pi= p;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_operation_reducer::count_slice(Gcalc_scan_iterator *si)
|
|
{
|
|
Gcalc_point_iterator pi(si);
|
|
const Gcalc_heap::Info *event_point= NULL;
|
|
int prev_state= 0;
|
|
int sav_prev_state;
|
|
active_thread *prev_range= NULL;
|
|
const Gcalc_scan_iterator::point *events;
|
|
const Gcalc_scan_iterator::point *eq_start;
|
|
active_thread **cur_t_hook= &m_first_active_thread;
|
|
active_thread **starting_t_hook;
|
|
active_thread *bottom_threads= NULL;
|
|
active_thread *eq_thread, *point_thread;;
|
|
|
|
m_fn->clear_state();
|
|
/* Walk to the event, remembering what is needed. */
|
|
for (; pi.point() != si->get_event_position();
|
|
++pi, cur_t_hook= (active_thread **) &(*cur_t_hook)->next)
|
|
{
|
|
active_thread *cur_t= *cur_t_hook;
|
|
if (cur_t->enabled() &&
|
|
cur_t->rp->type == Gcalc_function::shape_polygon)
|
|
{
|
|
prev_state^= 1;
|
|
prev_range= prev_state ? cur_t : 0;
|
|
}
|
|
if (m_fn->get_shape_kind(pi.get_shape()) == Gcalc_function::shape_polygon)
|
|
m_fn->invert_state(pi.get_shape());
|
|
}
|
|
|
|
events= si->get_events();
|
|
if (events->simple_event())
|
|
{
|
|
active_thread *cur_t= *cur_t_hook;
|
|
switch (events->event)
|
|
{
|
|
case scev_point:
|
|
{
|
|
if (cur_t->enabled() &&
|
|
continue_range(cur_t, events->pi, events->horiz_dir, events->dx_dy))
|
|
return 1;
|
|
break;
|
|
}
|
|
case scev_end:
|
|
{
|
|
if (cur_t->enabled() && end_line(cur_t, events->pi, si))
|
|
return 1;
|
|
*cur_t_hook= cur_t->get_next();
|
|
free_item(cur_t);
|
|
break;
|
|
}
|
|
case scev_two_ends:
|
|
{
|
|
if (cur_t->enabled() && cur_t->get_next()->enabled())
|
|
{
|
|
/* When two threads are ended here */
|
|
if (end_couple(cur_t, cur_t->get_next(), events->pi))
|
|
return 1;
|
|
}
|
|
else if (cur_t->enabled() || cur_t->get_next()->enabled())
|
|
{
|
|
/* Rare case when edges of a polygon coincide */
|
|
if (end_line(cur_t->enabled() ? cur_t : cur_t->get_next(),
|
|
events->pi, si))
|
|
return 1;
|
|
}
|
|
*cur_t_hook= cur_t->get_next()->get_next();
|
|
free_item(cur_t->next);
|
|
free_item(cur_t);
|
|
break;
|
|
}
|
|
default:
|
|
DBUG_ASSERT(0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
starting_t_hook= cur_t_hook;
|
|
sav_prev_state= prev_state;
|
|
|
|
/* Walk through the event, collecting all the 'incoming' threads */
|
|
for (; events; events= events->get_next())
|
|
{
|
|
active_thread *cur_t= *cur_t_hook;
|
|
|
|
if (!event_point && events->event != scev_intersection)
|
|
event_point= events->pi;
|
|
if (events->event == scev_single_point)
|
|
continue;
|
|
|
|
if (events->event == scev_thread ||
|
|
events->event == scev_two_threads)
|
|
{
|
|
active_thread *new_t= new_active_thread();
|
|
if (!new_t)
|
|
return 1;
|
|
new_t->rp= NULL;
|
|
/* Insert into the main thread list before the current */
|
|
new_t->next= cur_t;
|
|
*cur_t_hook= new_t;
|
|
cur_t_hook= (active_thread **) &new_t->next;
|
|
}
|
|
else
|
|
{
|
|
if (events->is_bottom())
|
|
{
|
|
/* Move thread from the main list to the bottom_threads. */
|
|
*cur_t_hook= cur_t->get_next();
|
|
cur_t->next= bottom_threads;
|
|
bottom_threads= cur_t;
|
|
}
|
|
if (cur_t->enabled())
|
|
{
|
|
if (cur_t->rp->type == Gcalc_function::shape_line)
|
|
{
|
|
DBUG_ASSERT(!prev_state);
|
|
add_line(1, cur_t, events);
|
|
}
|
|
else
|
|
{
|
|
add_poly_border(1, cur_t, prev_state, events);
|
|
prev_state^= 1;
|
|
}
|
|
if (!events->is_bottom())
|
|
{
|
|
active_thread *new_t= new_active_thread();
|
|
if (!new_t)
|
|
return 1;
|
|
new_t->rp= NULL;
|
|
/* Replace the current thread with the new. */
|
|
new_t->next= cur_t->next;
|
|
*cur_t_hook= new_t;
|
|
cur_t_hook= (active_thread **) &new_t->next;
|
|
/* And move old to the bottom list */
|
|
cur_t->next= bottom_threads;
|
|
bottom_threads= cur_t;
|
|
}
|
|
}
|
|
else if (!events->is_bottom())
|
|
cur_t_hook= (active_thread **) &cur_t->next;
|
|
}
|
|
}
|
|
prev_state= sav_prev_state;
|
|
cur_t_hook= starting_t_hook;
|
|
|
|
eq_start= pi.point();
|
|
eq_thread= point_thread= *starting_t_hook;
|
|
while (eq_start != si->get_event_end())
|
|
{
|
|
const Gcalc_scan_iterator::point *cur_eq;
|
|
int in_state, after_state;
|
|
|
|
++pi;
|
|
point_thread= point_thread->get_next();
|
|
|
|
if (pi.point() != si->get_event_end() &&
|
|
eq_start->cmp_dx_dy(pi.point()) == 0)
|
|
continue;
|
|
|
|
m_fn->save_states();
|
|
for (cur_eq= eq_start; cur_eq != pi.point(); cur_eq= cur_eq->get_next())
|
|
m_fn->set_on_state(cur_eq->get_shape());
|
|
in_state= m_fn->count();
|
|
|
|
m_fn->restore_states();
|
|
for (cur_eq= eq_start; cur_eq != pi.point(); cur_eq= cur_eq->get_next())
|
|
{
|
|
gcalc_shape_info si= cur_eq->get_shape();
|
|
if ((m_fn->get_shape_kind(si) == Gcalc_function::shape_polygon))
|
|
m_fn->invert_state(si);
|
|
}
|
|
after_state= m_fn->count();
|
|
if (prev_state != after_state)
|
|
{
|
|
if (add_poly_border(0, eq_thread, prev_state, eq_start))
|
|
return 1;
|
|
}
|
|
else if (!prev_state /* &&!after_state */ && in_state)
|
|
{
|
|
if (add_line(0, eq_thread, eq_start))
|
|
return 1;
|
|
}
|
|
|
|
prev_state= after_state;
|
|
eq_start= pi.point();
|
|
eq_thread= point_thread;
|
|
}
|
|
|
|
if (!sav_prev_state && !m_poly_borders && !m_lines)
|
|
{
|
|
/* Check if we need to add the event point itself */
|
|
m_fn->clear_state();
|
|
for (pi.restart(si); pi.point() != si->get_event_position(); ++pi)
|
|
{
|
|
if (m_fn->get_shape_kind(pi.get_shape()) == Gcalc_function::shape_polygon)
|
|
m_fn->invert_state(pi.get_shape());
|
|
}
|
|
for (events= si->get_events(); events; events= events->get_next())
|
|
m_fn->set_on_state(events->get_shape());
|
|
|
|
return m_fn->count() ? add_single_point(event_point, si) : 0;
|
|
}
|
|
|
|
if (m_poly_borders)
|
|
{
|
|
*m_poly_borders_hook= NULL;
|
|
while (m_poly_borders)
|
|
{
|
|
poly_border *pb1, *pb2;
|
|
pb1= m_poly_borders;
|
|
DBUG_ASSERT(m_poly_borders->next);
|
|
|
|
pb2= get_pair_border(pb1);
|
|
/* Remove pb1 from the list. The pb2 already removed in get_pair_border. */
|
|
m_poly_borders= pb1->get_next();
|
|
if (connect_threads(pb1->incoming, pb2->incoming,
|
|
pb1->t, pb2->t, pb1->p, pb2->p,
|
|
prev_range, event_point, si,
|
|
Gcalc_function::shape_polygon))
|
|
return 1;
|
|
|
|
free_item(pb1);
|
|
free_item(pb2);
|
|
}
|
|
m_poly_borders_hook= (Gcalc_dyn_list::Item **) &m_poly_borders;
|
|
m_poly_borders= NULL;
|
|
}
|
|
|
|
if (m_lines)
|
|
{
|
|
*m_lines_hook= NULL;
|
|
if (m_lines->get_next() &&
|
|
!m_lines->get_next()->get_next())
|
|
{
|
|
if (connect_threads(m_lines->incoming, m_lines->get_next()->incoming,
|
|
m_lines->t, m_lines->get_next()->t,
|
|
m_lines->p, m_lines->get_next()->p, NULL,
|
|
event_point, si, Gcalc_function::shape_line))
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
for (line *cur_line= m_lines; cur_line; cur_line= cur_line->get_next())
|
|
{
|
|
if (cur_line->incoming)
|
|
{
|
|
if (end_line(cur_line->t, event_point, si))
|
|
return 1;
|
|
}
|
|
else
|
|
start_line(cur_line->t, cur_line->p, event_point, si);
|
|
}
|
|
}
|
|
free_list(m_lines);
|
|
m_lines= NULL;
|
|
m_lines_hook= (Gcalc_dyn_list::Item **) &m_lines;
|
|
}
|
|
|
|
if (bottom_threads)
|
|
free_list(bottom_threads);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_operation_reducer::add_single_point(const Gcalc_heap::Info *p,
|
|
const Gcalc_scan_iterator *si)
|
|
{
|
|
res_point *rp= add_res_point(Gcalc_function::shape_point);
|
|
if (!rp)
|
|
return 1;
|
|
rp->glue= rp->up= rp->down= NULL;
|
|
if (p)
|
|
{
|
|
rp->intersection_point= false;
|
|
rp->pi= p;
|
|
}
|
|
else
|
|
{
|
|
rp->intersection_point= true;
|
|
rp->x= si->get_y();
|
|
rp->y= si->get_events()->x;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
Gcalc_operation_reducer::poly_border
|
|
*Gcalc_operation_reducer::get_pair_border(poly_border *b1)
|
|
{
|
|
poly_border *prev_b= b1;
|
|
poly_border *result= b1->get_next();
|
|
if (b1->prev_state)
|
|
{
|
|
if (b1->incoming)
|
|
{
|
|
/* Find the first outgoing, otherwise the last one. */
|
|
while (result->incoming && result->get_next())
|
|
{
|
|
prev_b= result;
|
|
result= result->get_next();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Get the last one */
|
|
while (result->get_next())
|
|
{
|
|
prev_b= result;
|
|
result= result->get_next();
|
|
}
|
|
}
|
|
}
|
|
else /* !b1->prev_state */
|
|
{
|
|
if (b1->incoming)
|
|
{
|
|
/* Get the next incoming, otherwise the last one. */
|
|
while (!result->incoming && result->get_next())
|
|
{
|
|
prev_b= result;
|
|
result= result->get_next();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Just pick the next one */
|
|
}
|
|
}
|
|
/* Delete the result from the list. */
|
|
prev_b->next= result->next;
|
|
return result;
|
|
}
|
|
|
|
|
|
int Gcalc_operation_reducer::connect_threads(
|
|
int incoming_a, int incoming_b,
|
|
active_thread *ta, active_thread *tb,
|
|
const Gcalc_scan_iterator::point *pa, const Gcalc_scan_iterator::point *pb,
|
|
active_thread *prev_range, const Gcalc_heap::Info *ev_p,
|
|
const Gcalc_scan_iterator *si, Gcalc_function::shape_type s_t)
|
|
{
|
|
if (incoming_a && incoming_b)
|
|
{
|
|
res_point *rpa, *rpb;
|
|
DBUG_ASSERT(ta->rp->type == tb->rp->type);
|
|
if (!(rpa= add_res_point(ta->rp->type)) ||
|
|
!(rpb= add_res_point(ta->rp->type)))
|
|
return 1;
|
|
rpa->down= ta->rp;
|
|
rpb->down= tb->rp;
|
|
rpb->glue= rpa;
|
|
rpa->glue= rpb;
|
|
rpa->up= rpb->up= NULL;
|
|
ta->rp->up= rpa;
|
|
tb->rp->up= rpb;
|
|
if (ev_p)
|
|
{
|
|
rpa->intersection_point= rpb->intersection_point= false;
|
|
rpa->pi= rpb->pi= ev_p;
|
|
}
|
|
else
|
|
{
|
|
rpa->intersection_point= rpb->intersection_point= true;
|
|
rpa->x= rpb->x= si->get_events()->x;
|
|
rpa->y= rpb->y= si->get_y();
|
|
}
|
|
|
|
ta->rp= tb->rp= NULL;
|
|
return 0;
|
|
}
|
|
if (!incoming_a)
|
|
{
|
|
DBUG_ASSERT(!incoming_b);
|
|
|
|
res_point *rp0, *rp1;
|
|
if (!(rp0= add_res_point(s_t)) || !(rp1= add_res_point(s_t)))
|
|
return 1;
|
|
rp0->glue= rp1;
|
|
rp1->glue= rp0;
|
|
if (ev_p)
|
|
{
|
|
rp0->intersection_point= rp1->intersection_point= false;
|
|
rp0->pi= rp1->pi= ev_p;
|
|
}
|
|
else
|
|
{
|
|
rp0->intersection_point= rp1->intersection_point= true;
|
|
rp0->x= rp1->x= si->get_events()->x;
|
|
rp0->y= rp1->y= si->get_y();
|
|
}
|
|
rp0->down= rp1->down= NULL;
|
|
ta->rp= rp0;
|
|
tb->rp= rp1;
|
|
ta->horiz_dir= pa->horiz_dir;
|
|
ta->dx_dy= pa->dx_dy;
|
|
|
|
tb->horiz_dir= pb->horiz_dir;
|
|
tb->dx_dy= pb->dx_dy;
|
|
|
|
if (prev_range)
|
|
{
|
|
rp0->outer_poly= prev_range->thread_start;
|
|
tb->thread_start= prev_range->thread_start;
|
|
}
|
|
else
|
|
{
|
|
rp0->outer_poly= 0;
|
|
ta->thread_start= rp0;
|
|
}
|
|
return 0;
|
|
}
|
|
/* else, if only ta is incoming */
|
|
|
|
DBUG_ASSERT(tb != ta);
|
|
tb->rp= ta->rp;
|
|
tb->thread_start= ta->thread_start;
|
|
if (Gcalc_scan_iterator::point::
|
|
compare_dx_dy(ta->horiz_dir, ta->dx_dy,
|
|
pb->horiz_dir, pb->dx_dy) != 0)
|
|
{
|
|
if (ev_p ? continue_range(tb, ev_p, pb->horiz_dir, pb->dx_dy):
|
|
continue_i_range(tb,
|
|
si->get_events()->x, si->get_y(),
|
|
pb->horiz_dir, pb->dx_dy))
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
tb->horiz_dir= pb->horiz_dir;
|
|
tb->dx_dy= pb->dx_dy;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_operation_reducer::start_line(active_thread *t,
|
|
const Gcalc_scan_iterator::point *p,
|
|
const Gcalc_heap::Info *ev_p,
|
|
const Gcalc_scan_iterator *si)
|
|
{
|
|
res_point *rp= add_res_point(Gcalc_function::shape_line);
|
|
if (!rp)
|
|
return 1;
|
|
rp->glue= rp->down= NULL;
|
|
if (ev_p)
|
|
{
|
|
rp->intersection_point= false;
|
|
rp->pi= ev_p;
|
|
}
|
|
else
|
|
{
|
|
rp->intersection_point= true;
|
|
rp->x= si->get_events()->x;
|
|
rp->y= si->get_y();
|
|
}
|
|
t->rp= rp;
|
|
t->horiz_dir= p->horiz_dir;
|
|
t->dx_dy= p->dx_dy;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_operation_reducer::end_line(active_thread *t,
|
|
const Gcalc_heap::Info *ev_p,
|
|
const Gcalc_scan_iterator *si)
|
|
{
|
|
DBUG_ASSERT(t->rp->type == Gcalc_function::shape_line);
|
|
res_point *rp= add_res_point(Gcalc_function::shape_line);
|
|
if (!rp)
|
|
return 1;
|
|
rp->glue= rp->up= NULL;
|
|
rp->down= t->rp;
|
|
if (ev_p)
|
|
{
|
|
rp->intersection_point= false;
|
|
rp->pi= ev_p;
|
|
}
|
|
else
|
|
{
|
|
rp->intersection_point= true;
|
|
rp->x= si->get_events()->x;
|
|
rp->y= si->get_y();
|
|
}
|
|
t->rp->up= rp;
|
|
t->rp= NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_operation_reducer::count_all(Gcalc_heap *hp)
|
|
{
|
|
Gcalc_scan_iterator si;
|
|
si.init(hp);
|
|
while (si.more_points())
|
|
{
|
|
if (si.step())
|
|
return 1;
|
|
if (count_slice(&si))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
inline void Gcalc_operation_reducer::free_result(res_point *res)
|
|
{
|
|
if ((*res->prev_hook= res->next))
|
|
{
|
|
res->get_next()->prev_hook= res->prev_hook;
|
|
}
|
|
free_item(res);
|
|
}
|
|
|
|
|
|
inline int Gcalc_operation_reducer::get_single_result(res_point *res,
|
|
Gcalc_result_receiver *storage)
|
|
{
|
|
if (res->intersection_point)
|
|
{
|
|
if (storage->single_point(float_to_coord(res->x),
|
|
float_to_coord(res->y)))
|
|
return 1;
|
|
}
|
|
else
|
|
if (storage->single_point(res->pi->x, res->pi->y))
|
|
return 1;
|
|
free_result(res);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_operation_reducer::get_result_thread(res_point *cur,
|
|
Gcalc_result_receiver *storage,
|
|
int move_upward,
|
|
res_point *first_poly_node)
|
|
{
|
|
res_point *next;
|
|
bool glue_step= false;
|
|
double x, y;
|
|
while (cur)
|
|
{
|
|
if (!glue_step)
|
|
{
|
|
if (cur->intersection_point)
|
|
{
|
|
x= float_to_coord(cur->x);
|
|
y= float_to_coord(cur->y);
|
|
}
|
|
else
|
|
{
|
|
x= cur->pi->x;
|
|
y= cur->pi->y;
|
|
}
|
|
if (storage->add_point(x, y))
|
|
return 1;
|
|
}
|
|
|
|
next= move_upward ? cur->up : cur->down;
|
|
if (!next && !glue_step)
|
|
{
|
|
next= cur->glue;
|
|
move_upward^= 1;
|
|
glue_step= true;
|
|
if (next)
|
|
next->glue= NULL;
|
|
}
|
|
else
|
|
glue_step= false;
|
|
|
|
cur->first_poly_node= first_poly_node;
|
|
free_result(cur);
|
|
cur= next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Gcalc_operation_reducer::get_polygon_result(res_point *cur,
|
|
Gcalc_result_receiver *storage,
|
|
res_point *first_poly_node)
|
|
{
|
|
res_point *glue= cur->glue;
|
|
glue->up->down= NULL;
|
|
free_result(glue);
|
|
return get_result_thread(cur, storage, 1, first_poly_node) ||
|
|
storage->complete_shape();
|
|
}
|
|
|
|
|
|
int Gcalc_operation_reducer::get_line_result(res_point *cur,
|
|
Gcalc_result_receiver *storage)
|
|
{
|
|
res_point *next;
|
|
res_point *cur_orig= cur;
|
|
int move_upward= 1;
|
|
if (cur->glue)
|
|
{
|
|
/* Here we have to find the beginning of the line */
|
|
next= cur->up;
|
|
move_upward= 1;
|
|
while (next)
|
|
{
|
|
cur= next;
|
|
next= move_upward ? next->up : next->down;
|
|
if (!next)
|
|
{
|
|
next= cur->glue;
|
|
if (next == cur_orig)
|
|
{
|
|
/* It's the line loop */
|
|
cur= cur_orig;
|
|
cur->glue->glue= NULL;
|
|
move_upward= 1;
|
|
break;
|
|
}
|
|
move_upward^= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return get_result_thread(cur, storage, move_upward, 0) ||
|
|
storage->complete_shape();
|
|
}
|
|
|
|
|
|
int Gcalc_operation_reducer::get_result(Gcalc_result_receiver *storage)
|
|
{
|
|
poly_instance *polygons= NULL;
|
|
|
|
*m_res_hook= NULL;
|
|
|
|
while (m_result)
|
|
{
|
|
Gcalc_function::shape_type shape= m_result->type;
|
|
if (shape == Gcalc_function::shape_point)
|
|
{
|
|
if (get_single_result(m_result, storage))
|
|
return 1;
|
|
continue;
|
|
}
|
|
if (shape == Gcalc_function::shape_polygon)
|
|
{
|
|
if (m_result->outer_poly)
|
|
{
|
|
uint32 insert_position, hole_position, position_shift;
|
|
poly_instance *cur_poly;
|
|
insert_position= m_result->outer_poly->first_poly_node->poly_position;
|
|
DBUG_ASSERT(insert_position);
|
|
hole_position= storage->position();
|
|
storage->start_shape(Gcalc_function::shape_hole);
|
|
if (get_polygon_result(m_result, storage,
|
|
m_result->outer_poly->first_poly_node) ||
|
|
storage->move_hole(insert_position, hole_position,
|
|
&position_shift))
|
|
return 1;
|
|
for (cur_poly= polygons;
|
|
cur_poly && *cur_poly->after_poly_position >= insert_position;
|
|
cur_poly= cur_poly->get_next())
|
|
*cur_poly->after_poly_position+= position_shift;
|
|
}
|
|
else
|
|
{
|
|
uint32 *poly_position= &m_result->poly_position;
|
|
poly_instance *p= new_poly();
|
|
p->after_poly_position= poly_position;
|
|
p->next= polygons;
|
|
polygons= p;
|
|
storage->start_shape(Gcalc_function::shape_polygon);
|
|
if (get_polygon_result(m_result, storage, m_result))
|
|
return 1;
|
|
*poly_position= storage->position();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
storage->start_shape(shape);
|
|
if (get_line_result(m_result, storage))
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
m_res_hook= (Gcalc_dyn_list::Item **)&m_result;
|
|
storage->done();
|
|
return 0;
|
|
}
|
|
|
|
|
|
void Gcalc_operation_reducer::reset()
|
|
{
|
|
free_list(m_result, m_res_hook);
|
|
m_res_hook= (Gcalc_dyn_list::Item **)&m_result;
|
|
free_list(m_first_active_thread);
|
|
}
|
|
|
|
#endif /*HAVE_SPATIAL*/
|
|
|