You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
411 lines
12 KiB
411 lines
12 KiB
/**
|
|
* Functionality for checking validity and properties of NURBS curves and
|
|
* surfaces
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be found in
|
|
* the LICENSE file.
|
|
*/
|
|
|
|
#ifndef TINYNURBS_CHECK_H
|
|
#define TINYNURBS_CHECK_H
|
|
|
|
#include "curve.h"
|
|
#include "glm/glm.hpp"
|
|
#include "surface.h"
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <vector>
|
|
|
|
namespace tinynurbs
|
|
{
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
namespace internal
|
|
{
|
|
|
|
/**
|
|
* Checks if the relation between degree, number of knots, and
|
|
* number of control points is valid
|
|
* @param[in] degree Degree
|
|
* @param[in] num_knots Number of knot values
|
|
* @param[in] num_ctrl_pts Number of control points
|
|
* @return Whether the relationship is valid
|
|
*/
|
|
inline bool isValidRelation(unsigned int degree, size_t num_knots, size_t num_ctrl_pts)
|
|
{
|
|
return (num_knots - degree - 1) == num_ctrl_pts;
|
|
}
|
|
|
|
/**
|
|
* isKnotVectorMonotonic returns whether the knots are in ascending order
|
|
* @tparam Type of knot values
|
|
* @param[in] knots Knot vector
|
|
* @return Whether monotonic
|
|
*/
|
|
template <typename T> bool isKnotVectorMonotonic(const std::vector<T> &knots)
|
|
{
|
|
return std::is_sorted(knots.begin(), knots.end());
|
|
}
|
|
|
|
/**
|
|
* Returns whether the curve is valid
|
|
* @tparam T Type of control point coordinates, knot values
|
|
* @param[in] degree Degree of curve
|
|
* @param[in] knots Knot vector of curve
|
|
* @param[in] control_points Control points of curve
|
|
* @return Whether valid
|
|
*/
|
|
template <typename T>
|
|
bool curveIsValid(unsigned int degree, const std::vector<T> &knots,
|
|
const std::vector<glm::vec<3, T>> &control_points)
|
|
{
|
|
if (degree < 1 || degree > 9)
|
|
{
|
|
return false;
|
|
}
|
|
if (!isValidRelation(degree, knots.size(), control_points.size()))
|
|
{
|
|
return false;
|
|
}
|
|
if (!isKnotVectorMonotonic(knots))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns whether the curve is valid
|
|
* @tparam T Type of control point coordinates, knot values and weights
|
|
* @param[in] degree Degree of curve
|
|
* @param[in] knots Knot vector of curve
|
|
* @param[in] control_points Control points of curve
|
|
* @return Whether valid
|
|
*/
|
|
template <typename T>
|
|
bool curveIsValid(unsigned int degree, const std::vector<T> &knots,
|
|
const std::vector<glm::vec<3, T>> &control_points, const std::vector<T> &weights)
|
|
{
|
|
if (!isValidRelation(degree, knots.size(), control_points.size()))
|
|
{
|
|
return false;
|
|
}
|
|
if (weights.size() != control_points.size())
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns whether the surface is valid
|
|
* @tparam T Type of control point coordinates, knot values
|
|
* @param[in] degree_u Degree of surface along u-direction
|
|
* @param[in] degree_v Degree of surface along v-direction
|
|
* @param[in] knots_u Knot vector of surface along u-direction
|
|
* @param[in] knots_v Knot vector of surface along v-direction
|
|
* @param[in] control_points Control points grid of surface
|
|
* @return Whether valid
|
|
*/
|
|
template <typename T>
|
|
bool surfaceIsValid(unsigned int degree_u, unsigned int degree_v, const std::vector<T> &knots_u,
|
|
const std::vector<T> &knots_v, const array2<glm::vec<3, T>> &control_points)
|
|
{
|
|
if (degree_u < 1 || degree_u > 9 || degree_v < 1 || degree_v > 9)
|
|
{
|
|
return false;
|
|
}
|
|
if (!isValidRelation(degree_u, knots_u.size(), control_points.rows()) ||
|
|
!isValidRelation(degree_v, knots_v.size(), control_points.cols()))
|
|
{
|
|
return false;
|
|
}
|
|
if (!isKnotVectorMonotonic(knots_u) || !isKnotVectorMonotonic(knots_v))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns whether the rational surface is valid
|
|
* @tparam T Type of control point coordinates, knot values
|
|
* @param[in] degree_u Degree of surface along u-direction
|
|
* @param[in] degree_v Degree of surface along v-direction
|
|
* @param[in] knots_u Knot vector of surface along u-direction
|
|
* @param[in] knots_v Knot vector of surface along v-direction
|
|
* @param[in] control_points Control points grid of surface
|
|
* @param[in] weights Weights corresponding to control point grid of surface
|
|
* @return Whether valid
|
|
*/
|
|
template <typename T>
|
|
bool surfaceIsValid(unsigned int degree_u, unsigned int degree_v, const std::vector<T> &knots_u,
|
|
const std::vector<T> &knots_v, const array2<glm::vec<3, T>> &control_points,
|
|
const array2<T> &weights)
|
|
{
|
|
if (!surfaceIsValid(degree_u, degree_v, knots_u, knots_v, control_points))
|
|
{
|
|
return false;
|
|
}
|
|
if (control_points.rows() != weights.rows() || control_points.cols() != weights.cols())
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns whether the given knot vector is closed by checking the
|
|
* periodicity of knot vectors near the start and end
|
|
* @param[in] degree Degree of curve/surface
|
|
* @param[in] knots Knot vector of curve/surface
|
|
* @return Whether knot vector is closed
|
|
*/
|
|
template <typename T> bool isKnotVectorClosed(unsigned int degree, const std::vector<T> &knots)
|
|
{
|
|
for (int i = 0; i < degree - 1; ++i)
|
|
{
|
|
int j = knots.size() - degree + i;
|
|
if (std::abs((knots[i + 1] - knots[i]) - (knots[j + 1] - knots[j])) >
|
|
std::numeric_limits<T>::epsilon())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns whether the given knot vector is closed by checking the
|
|
* periodicity of knot vectors near the start and end
|
|
* @param[in] degree Degree of curve/surface
|
|
* @param[in] vec Array of any control points/weights
|
|
* @return Whether knot vector is closed
|
|
*/
|
|
template <typename T> bool isArray1Closed(unsigned int degree, const std::vector<T> &vec)
|
|
{
|
|
for (int i = 0; i < degree; ++i)
|
|
{
|
|
int j = vec.size() - degree + i;
|
|
if (glm::length(vec[i] - vec[j]) > 1e-5)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns whether the 2D array is closed along the u-direction
|
|
* i.e., along rows.
|
|
* @param[in] degree_u Degree along u-direction
|
|
* @param[in] arr 2D array of control points / weights
|
|
* @return Whether closed along u-direction
|
|
*/
|
|
template <typename T> bool isArray2ClosedU(unsigned int degree_u, const array2<T> &arr)
|
|
{
|
|
for (int i = 0; i < degree_u; ++i)
|
|
{
|
|
for (int j = 0; j < arr.cols(); ++j)
|
|
{
|
|
int k = arr.cols() - degree_u + i;
|
|
if (glm::length(arr(i, j) - arr(k, j)) > 1e-5)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns whether the 2D array is closed along the v-direction
|
|
* i.e., along columns.
|
|
* @param[in] degree_v Degree along v-direction
|
|
* @param[in] arr 2D array of control points / weights
|
|
* @return Whether closed along v-direction
|
|
*/
|
|
template <typename T> bool isArray2ClosedV(unsigned int degree_v, const array2<T> &arr)
|
|
{
|
|
for (int i = 0; i < arr.rows(); ++i)
|
|
{
|
|
for (int j = 0; j < degree_v; j++)
|
|
{
|
|
int k = arr.rows() - degree_v + i;
|
|
if (glm::length(arr(i, j) - arr(i, k)) > 1e-5)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace internal
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Returns the multiplicity of the knot at index
|
|
* @tparam Type of knot values
|
|
* @param[in] knots Knot vector
|
|
* @param[in] knot_val Knot of interest
|
|
* @return Multiplicity (>= 0)
|
|
*/
|
|
template <typename T> unsigned int knotMultiplicity(const std::vector<T> &knots, T knot_val)
|
|
{
|
|
T eps = std::numeric_limits<T>::epsilon();
|
|
unsigned int mult = 0;
|
|
for (const T knot : knots)
|
|
{
|
|
if (std::abs(knot_val - knot) < eps)
|
|
{
|
|
++mult;
|
|
}
|
|
}
|
|
return mult;
|
|
}
|
|
|
|
/**
|
|
* Returns the mulitplicity of the knot at index
|
|
* @tparam Type of knot values
|
|
* @param[in] knots Knot vector
|
|
* @param[in] index Index of knot of interest
|
|
* @return Multiplicity (>= 1)
|
|
*/
|
|
template <typename T>
|
|
[[deprecated("Use knotMultiplicity(knots, param).")]]
|
|
unsigned int knotMultiplicity(const std::vector<T> &knots, unsigned int index)
|
|
{
|
|
T curr_knot_val = knots[index];
|
|
T eps = std::numeric_limits<T>::epsilon();
|
|
unsigned int mult = 0;
|
|
for (const T knot : knots)
|
|
{
|
|
if (std::abs(curr_knot_val - knot) < eps)
|
|
{
|
|
++mult;
|
|
}
|
|
}
|
|
return mult;
|
|
}
|
|
|
|
/**
|
|
* Returns whether the curve is valid
|
|
* @tparam T Type of control point coordinates, knot values
|
|
* @param[in] crv Curve object
|
|
* @return Whether valid
|
|
*/
|
|
template <typename T> bool curveIsValid(const Curve<T> &crv)
|
|
{
|
|
return internal::curveIsValid(crv.degree, crv.knots, crv.control_points);
|
|
}
|
|
|
|
/**
|
|
* Returns whether the curve is valid
|
|
* @tparam T Type of control point coordinates, knot values
|
|
* @param[in] crv RationalCurve object
|
|
* @return Whether valid
|
|
*/
|
|
template <typename T> bool curveIsValid(const RationalCurve<T> &crv)
|
|
{
|
|
return internal::curveIsValid(crv.degree, crv.knots, crv.control_points, crv.weights);
|
|
}
|
|
|
|
/**
|
|
* Returns whether the surface is valid
|
|
* @tparam T Type of control point coordinates, knot values
|
|
* @param srf Surface object
|
|
* @return Whether valid
|
|
*/
|
|
template <typename T> bool surfaceIsValid(const Surface<T> &srf)
|
|
{
|
|
return internal::surfaceIsValid(srf.degree_u, srf.degree_v, srf.knots_u, srf.knots_v,
|
|
srf.control_points);
|
|
}
|
|
|
|
/**
|
|
* Returns whether the rational surface is valid
|
|
* @tparam T Type of control point coordinates, knot values
|
|
* @param[in] srf RationalSurface object
|
|
* @return Whether valid
|
|
*/
|
|
template <typename T> bool surfaceIsValid(const RationalSurface<T> &srf)
|
|
{
|
|
return internal::surfaceIsValid(srf.degree_u, srf.degree_v, srf.knots_u, srf.knots_v,
|
|
srf.control_points, srf.weights);
|
|
}
|
|
|
|
/**
|
|
* Checks whether the curve is closed
|
|
* @param[in] crv Curve object
|
|
* @return Whether closed
|
|
*/
|
|
template <typename T> bool curveIsClosed(const Curve<T> &crv)
|
|
{
|
|
return internal::isArray1Closed(crv.degree, crv.control_points) &&
|
|
internal::isKnotVectorClosed(crv.degree, crv.knots);
|
|
}
|
|
|
|
/**
|
|
* Checks whether the rational curve is closed
|
|
* @param[in] crv RationalCurve object
|
|
* @return Whether closed
|
|
*/
|
|
template <typename T> bool curveIsClosed(const RationalCurve<T> &crv)
|
|
{
|
|
return internal::isArray1Closed(crv.degree, crv.control_points) &&
|
|
internal::isArray1Closed(crv.degree, crv.weights) &&
|
|
internal::isKnotVectorClosed(crv.degree, crv.knots);
|
|
}
|
|
|
|
/**
|
|
* Checks whether the surface is closed along u-direction
|
|
* @param[in] srf Surface object
|
|
* @return Whether closed along u-direction
|
|
*/
|
|
template <typename T> bool surfaceIsClosedU(const Surface<T> &srf)
|
|
{
|
|
return internal::isArray2ClosedU(srf.degree_u, srf.control_points) &&
|
|
internal::isKnotVectorClosed(srf.degree_u, srf.knots_u);
|
|
}
|
|
|
|
/**
|
|
* Checks whether the surface is closed along v-direction
|
|
* @param[in] srf Surface object
|
|
* @return Whether closed along v-direction
|
|
*/
|
|
template <typename T> bool surfaceIsClosedV(const Surface<T> &srf)
|
|
{
|
|
return internal::isArray2ClosedV(srf.degree_v, srf.control_points) &&
|
|
internal::isKnotVectorClosed(srf.degree_v, srf.knots_v);
|
|
}
|
|
|
|
/**
|
|
* Checks whether the rational surface is closed along u-direction
|
|
* @param[in] srf RationalSurface object
|
|
* @return Whether closed along u-direction
|
|
*/
|
|
template <typename T> bool surfaceIsClosedU(const RationalSurface<T> &srf)
|
|
{
|
|
return internal::isArray2ClosedU(srf.degree_u, srf.control_points) &&
|
|
internal::isKnotVectorClosed(srf.degree_u, srf.knots_u) &&
|
|
internal::isArray2ClosedU(srf.degree_u, srf.weights);
|
|
}
|
|
|
|
/**
|
|
* Checks whether the rational surface is closed along v-direction
|
|
* @param[in] srf RationalSurface object
|
|
* @return Whether closed along v-direction
|
|
*/
|
|
template <typename T> bool surfaceIsClosedV(const RationalSurface<T> &srf)
|
|
{
|
|
return internal::isArray2ClosedV(srf.degree_v, srf.control_points) &&
|
|
internal::isKnotVectorClosed(srf.degree_v, srf.knots_v) &&
|
|
internal::isArray2ClosedV(srf.degree_v, srf.weights);
|
|
}
|
|
|
|
} // namespace tinynurbs
|
|
|
|
#endif // TINYNURBS_CHECK_H
|
|
|