#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2015 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library 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 IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License #ifndef NUCLEX_GEOMETRY_LINES_GENERATORS_POLYLINE2GENERATOR_H #define NUCLEX_GEOMETRY_LINES_GENERATORS_POLYLINE2GENERATOR_H #include "Nuclex/Geometry/Config.h" #include "Nuclex/Geometry/Lines/PolyLine2.h" #include "Nuclex/Geometry/Lines/Line2.h" namespace Nuclex { namespace Geometry { namespace Lines { namespace Generators { // ------------------------------------------------------------------------------------------- // /// Primitive generation methods for 2D poly lines template class PolyLine2Generator { #pragma region class DouglasPeuckerSimplifier /// Simplifies poly lines using the Douglas-Peucker algorithm private: class DouglasPeuckerSimplifier { /// Range of indices, left index and right index (inclusive) private: typedef std::pair IndexRange; /// Initializes a new Douglas-Peucker poly line simplifier /// Poly line of which a simplified version will be built public: DouglasPeuckerSimplifier(const PolyLine2 &polyLine) : polyLine(polyLine) {} /// /// Builds a simplified poly line out of the poly line the simplifier was constructed with /// /// /// Distance under which a vertex will be collapsed into the line segment it lies on /// /// The simplified poly line public: PolyLine2 GetSimplified(TScalar maximumError) const { std::size_t vertexCount = this->polyLine.Vertices.size(); if(vertexCount < 3) { return this->polyLine.Subset(0, vertexCount); } PolyLine2 result; result.Vertices.reserve(vertexCount / 4); result.Vertices.push_back(this->polyLine.Vertices[0]); eliminateOrSplit( IndexRange(0, vertexCount - 1), maximumError * maximumError, result ); return result; } /// /// Eliminates all points within the span or performs a recursive split on the vertex /// with the largest error /// /// Span in which vertices will be analyzed /// /// Distance under which a vertex will be collapsed into the line segment it lies on /// /// Poly line in which the final vertices will be placed private: void eliminateOrSplit( const IndexRange &span, TScalar maximumSquaredError, PolyLine2 &output ) const { Line2 line = Line2::FromSegment( this->polyLine.Vertices[span.first], this->polyLine.Vertices[span.second] ); // This method will only be called with at least 1 interior vertex assert((span.first + 1 < span.second) && "eliminateOrSplit() has at least 3 vertices"); // Initialize the largest error using the first interior vertex std::size_t largestErrorIndex = span.first + 1; TScalar largestSquaredError = getSquaredPerpendicularDistance( line, this->polyLine.Vertices[largestErrorIndex] ); // Scan the remaining interior vertices to find the one with the largest deviation for(std::size_t index = span.first + 2; index < span.second; ++index) { TScalar currentSquaredError = getSquaredPerpendicularDistance( line, this->polyLine.Vertices[index] ); if(currentSquaredError > largestSquaredError) { largestErrorIndex = index; largestSquaredError = currentSquaredError; } } // If all vertices are below the maximum error, eliminate all of them if(largestSquaredError < maximumSquaredError) { output.Vertices.push_back(this->polyLine.Vertices[span.second]); } else { if(largestErrorIndex > span.first + 1) { // Is the segment long enough to simplify? eliminateOrSplit( IndexRange(0, largestErrorIndex), maximumSquaredError, output ); } else { output.Vertices.push_back(this->polyLine.Vertices[largestErrorIndex]); } if(span.second > largestErrorIndex + 1) { // Is the segment long enough to simplify? eliminateOrSplit( IndexRange(largestErrorIndex, span.second), maximumSquaredError, output ); } else { output.Vertices.push_back(this->polyLine.Vertices[span.second]); } } } /// Calculates the squared distance of a point to a line /// Line to which the distance will be calculated /// Point whose distance to the line will be calculated /// The squared distance of the point to the line private: static TScalar getSquaredPerpendicularDistance( const Line2 &line, const Point2 &point ) { Vector2 pointOffset = point - line.Origin; TScalar t = Vector2::Dot(pointOffset, line.Direction); return Point2::SquaredDistanceBetween( point, line.Origin + (line.Direction * t) ); } private: DouglasPeuckerSimplifier(const DouglasPeuckerSimplifier &) = delete; private: DouglasPeuckerSimplifier &operator =(const DouglasPeuckerSimplifier &) = delete; /// Poly line that is being simplified private: const PolyLine2 polyLine; }; #pragma endregion // class DouglasPeuckerSimplifier #pragma region class SpineMerger /// Merges poly lines by finding common spines private: class SpineMerger { /// Initializes a new spine merger for a set of poly lines /// Lines the spine merger will merge /// Number of poly lines in the input array public: SpineMerger(const PolyLine2 *polyLines, std::size_t count) : polyLines(polyLines, count) {} // Method // - find beginning order and indices (B, A10, /// Merges the input poly lines into a single output poly line /// /// Minimum number of poly lines that need to overlap to be covered in the output line /// /// The merged poly line public: PolyLine2 Merge(std::size_t minimumOverlapCount = 1) const { LineAndVertexIndex optimalStartingPoint; for(std::size_t index = 0; index < this->count; ++index) { //Point2 } } /// /// Addresses one vertex by the index of its poly line and the vertex itself /// private: typedef std::pair LineAndVertexIndex; /// Contains one index for each input poly line private: typedef std::vector LineIndices; /// Poly lines that will be merged private: const PolyLine2 *polyLines; /// Number of poly lines that will be merged private: std::size_t count; }; #pragma endregion // class SpineMerger /// Creates a simplified poly line using the Douglas-Peucker algorithm /// Poly line that will be simplified /// Maximum error under which vertices can be eliminated public: static PolyLine2 GetDouglasPeuckerSimplified( const PolyLine2 &polyLine, TScalar maximumError ) { return DouglasPeuckerSimplifier(polyLine).GetSimplified(maximumError); } /// Merges the overlapping sections of the specified poly lines /// Poly lines that will be averaged /// Number of poly lines that will be averaged public: static PolyLine2 GetSpineMerged( const PolyLine2 *polyLines, std::size_t count ) { return SpineMerger(polyLines, count).Merge(); } }; // ------------------------------------------------------------------------------------------- // }}}} // namespace Nuclex::Geometry::Lines::Generators #endif // NUCLEX_GEOMETRY_LINES_GENERATORS_POLYLINE2GENERATOR_H