#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_POLYLINE3GENERATOR_H #define NUCLEX_GEOMETRY_LINES_GENERATORS_POLYLINE3GENERATOR_H #include "Nuclex/Geometry/Config.h" #include "Nuclex/Geometry/Lines/PolyLine3.h" #include "Nuclex/Geometry/Lines/Line3.h" namespace Nuclex { namespace Geometry { namespace Lines { namespace Generators { // ------------------------------------------------------------------------------------------- // /// Primitive generation methods for 3D poly lines template class PolyLine3Generator { #pragma region class DouglasPeuckerSimplifier /// Simplifies poly lines using the Douglas-Peucker algorithm private: class DouglasPeuckerSimplifier { /// Initializes a new Douglas-Peucker poly line simplifier /// Poly line of which a simplified version will be built public: DouglasPeuckerSimplifier(const PolyLine3 &polyLine) : polyLine(polyLine) {} /// /// Builds the simplified poly line out of 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: PolyLine3 Simplify(TScalar maximumError) const { PolyLine3 result; eliminateOrSplit( std::make_pair(0, this->polyLine.Vertices.size() - 1), maximumError * maximumError, result ); result.Vertices.push_back(this->polyLine.Vertices.back()); 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 std::pair &span, TScalar squaredMaximumError, PolyLine3 &output ) const { const Point3 &origin = this->polyLine.Vertices[span.first]; Line3 line( origin, Vector3::Normalize(this->polyLine.Vertices[span.second] - origin) ); // If there are interior points, see whether one of the interior points can be // simplified into a straight line std::size_t firstInteriorIndex = span.first + 1; if(firstInteriorIndex < span.second) { TScalar largestSquaredError = getSquaredPerpendicularDistance( line, this->polyLine.Vertices[firstInteriorIndex] ); std::size_t largestSquaredErrorIndex = firstInteriorIndex; for(std::size_t index = firstInteriorIndex + 1; index < span.second; ++index) { TScalar squaredError = getSquaredPerpendicularDistance( line, this->polyLine.Vertices[index] ); if(squaredError > largestSquaredError) { largestSquaredError = squaredError; largestSquaredErrorIndex = index; } } // If the point with the largest error exceeds the maximum allowed error, // use it as a new output vertex and try to simplify the lines left and right of it if(largestSquaredError > squaredMaximumError) { eliminateOrSplit( std::pair(span.first, largestSquaredErrorIndex), squaredMaximumError, output ); eliminateOrSplit( std::pair(largestSquaredErrorIndex, span.second), squaredMaximumError, output ); } else { output.Vertices.push_back(this->polyLine.Vertices[span.first]); } } else { output.Vertices.push_back(this->polyLine.Vertices[span.first]); } } /// 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 Line3 &line, const Point3 &point ) { Vector3 pointOffset = point - line.Origin; TScalar dot = Vector3::Dot(pointOffset, line.Direction); return Point3::SquaredDistanceBetween( point, line.Origin + (line.Direction * dot) ); } private: DouglasPeuckerSimplifier(const DouglasPeuckerSimplifier &); private: DouglasPeuckerSimplifier &operator =(const DouglasPeuckerSimplifier &); /// Poly line that is being simplified private: const PolyLine3 polyLine; }; #pragma endregion // class DouglasPeuckerSimplifier /// 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 PolyLine3 GetDouglasPeuckerSimplified( const PolyLine3 &polyLine, TScalar maximumError ) { return DouglasPeuckerSimplifier(polyLine).Simplify(maximumError); } }; // ------------------------------------------------------------------------------------------- // }}}} // namespace Nuclex::Geometry::Lines::Generators #endif // NUCLEX_GEOMETRY_LINES_GENERATORS_POLYLINE3GENERATOR_H