#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