From 91b8659b4d5568b8398eb86eba64af4e9d65693d Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sat, 23 May 2020 02:14:14 +0300 Subject: [PATCH] Avoid data movement --- src/Functions/pointInPolygon.cpp | 121 ++++++++++++++++++++++++++----- 1 file changed, 104 insertions(+), 17 deletions(-) diff --git a/src/Functions/pointInPolygon.cpp b/src/Functions/pointInPolygon.cpp index dcaabda4073..d0b4105ce3c 100644 --- a/src/Functions/pointInPolygon.cpp +++ b/src/Functions/pointInPolygon.cpp @@ -50,7 +50,7 @@ using Polygon = boost::geometry::model::polygon; using Box = boost::geometry::model::box; -template +template class FunctionPointInPolygon : public IFunction { public: @@ -60,7 +60,7 @@ public: static FunctionPtr create(const Context & context) { - return std::make_shared>( + return std::make_shared>( context.getSettingsRef().validate_polygons); } @@ -210,8 +210,6 @@ public: auto res_column = ColumnVector::create(input_rows_count); auto & data = res_column->getData(); - Polygon polygon; - if (isTwoDimensionalArray(*block.getByPosition(arguments[1]).type)) { ColumnPtr polygon_column_float64 = castColumn( @@ -224,12 +222,12 @@ public: for (size_t i = 0; i < input_rows_count; ++i) { - polygon.clear(); - parsePolygonFromSingleColumn2D(*polygon_column_float64, i, polygon); - - PointInNonConstPolygonImpl impl(polygon); size_t point_index = point_is_const ? 0 : i; - data[i] = impl.contains(tuple_columns[0]->getFloat64(point_index), tuple_columns[1]->getFloat64(point_index)); + data[i] = isInsidePolygonFromSingleColumn2D( + tuple_columns[0]->getFloat64(point_index), + tuple_columns[1]->getFloat64(point_index), + *polygon_column_float64, + i); } } else @@ -243,12 +241,12 @@ public: for (size_t i = 0; i < input_rows_count; ++i) { - polygon.clear(); - parsePolygonFromSingleColumn1D(*polygon_column_float64, i, polygon); - - PointInNonConstPolygonImpl impl(polygon); size_t point_index = point_is_const ? 0 : i; - data[i] = impl.contains(tuple_columns[0]->getFloat64(point_index), tuple_columns[1]->getFloat64(point_index)); + data[i] = isInsidePolygonFromSingleColumn1D( + tuple_columns[0]->getFloat64(point_index), + tuple_columns[1]->getFloat64(point_index), + *polygon_column_float64, + i); } } @@ -295,6 +293,40 @@ private: out_container.emplace_back(x_data[i], y_data[i]); } + bool isInsidePolygonPart( + Float64 point_x, + Float64 point_y, + const Float64 * ring_x_data, + const Float64 * ring_y_data, + size_t ring_begin, + size_t ring_end) const + { + size_t size = ring_end - ring_begin; + + if (size < 2) + return false; + + /// https://wrf.ecse.rpi.edu//Research/Short_Notes/pnpoly.html + + size_t vertex1_idx = ring_begin; + size_t vertex2_idx = ring_end - 1; + bool res = false; + + while (vertex1_idx < ring_end) + { + if (((ring_y_data[vertex1_idx] > point_y) != (ring_y_data[vertex2_idx] > point_y)) + && (point_x < (ring_x_data[vertex2_idx] - ring_x_data[vertex1_idx]) + * (point_y - ring_y_data[vertex1_idx]) / (ring_y_data[vertex2_idx] - ring_y_data[vertex1_idx]) + + ring_x_data[vertex1_idx])) + res = !res; + + vertex2_idx = vertex1_idx; + ++vertex1_idx; + } + + return res; + } + void parsePolygonFromSingleColumn1D(const IColumn & column, size_t i, Polygon & out_polygon) const { const auto & array_col = static_cast(column); @@ -308,6 +340,28 @@ private: parsePolygonPart(x_data, y_data, begin, end, out_polygon.outer()); } + bool isInsidePolygonFromSingleColumn1D( + Float64 point_x, + Float64 point_y, + const IColumn & polygon_column, + size_t i) const + { + const auto & array_col = static_cast(polygon_column); + + size_t begin = array_col.getOffsets()[i - 1]; + size_t end = array_col.getOffsets()[i]; + size_t size = end - begin; + + if (size < 2) + return false; + + const auto & tuple_columns = static_cast(array_col.getData()).getColumns(); + const auto * x_data = static_cast(*tuple_columns[0]).getData().data(); + const auto * y_data = static_cast(*tuple_columns[1]).getData().data(); + + return isInsidePolygonPart(point_x, point_y, x_data, y_data, begin, end); + } + void parsePolygonFromSingleColumn2D(const IColumn & column, size_t i, Polygon & out_polygon) const { const auto & array_col = static_cast(column); @@ -336,6 +390,41 @@ private: } } + bool isInsidePolygonFromSingleColumn2D( + Float64 point_x, + Float64 point_y, + const IColumn & polygon_column, + size_t i) const + { + const auto & array_col = static_cast(polygon_column); + size_t rings_begin = array_col.getOffsets()[i - 1]; + size_t rings_end = array_col.getOffsets()[i]; + + const auto & nested_array_col = static_cast(array_col.getData()); + const auto & tuple_columns = static_cast(nested_array_col.getData()).getColumns(); + const auto * x_data = static_cast(*tuple_columns[0]).getData().data(); + const auto * y_data = static_cast(*tuple_columns[1]).getData().data(); + + for (size_t j = rings_begin; j < rings_end; ++j) + { + size_t begin = nested_array_col.getOffsets()[j - 1]; + size_t end = nested_array_col.getOffsets()[j]; + + if (j == rings_begin) + { + if (!isInsidePolygonPart(point_x, point_y, x_data, y_data, begin, end)) + return false; + } + else + { + if (isInsidePolygonPart(point_x, point_y, x_data, y_data, begin, end)) + return false; + } + } + + return true; + } + void parseConstPolygonFromMultipleColumns(Block & block, const ColumnNumbers & arguments, Polygon & out_polygon) const { for (size_t i = 1; i < arguments.size(); ++i) @@ -404,9 +493,7 @@ private: void registerFunctionPointInPolygon(FunctionFactory & factory) { - factory.registerFunction, - PointInPolygon, Float64>>>(); + factory.registerFunction>>(); } }