nim_duilib/examples/contour/bspline.cpp
2025-03-16 16:42:44 +08:00

144 lines
3.2 KiB
C++

#include "bspline.hpp"
namespace cvpr
{
BSpline::BSpline() : m_isValid(false)
{
}
BSpline::BSpline(const std::vector<std::vector<double>>& points, int degree)
{
createImpl(points, degree);
}
void BSpline::create(const std::vector<std::vector<double>>& points, int degree)
{
createImpl(points, degree);
}
void BSpline::createImpl(const std::vector<std::vector<double>>& points, int degree)
{
m_isValid = true;
m_knots.clear();
m_weights.clear();
m_v.clear();
m_degree = degree;
if (points.empty()) {
m_isValid = false;
return;
}
int n = points.size();
if (m_degree < 1) {
m_isValid = false;
return;
}
if (m_degree > (n - 1)) {
m_isValid = false;
return;
}
int step = m_degree + 1;
int count = (m_degree + 1 + points.size());
for (int i = 0; i < count; ++i) {
if (i < step) {
m_knots.emplace_back(0);
}
else if (i >= count - step) {
m_knots.emplace_back(count - 2 * step + 1);
}
else {
m_knots.emplace_back(i - step + 1);
}
}
m_dim = points[0].size();
if (m_weights.empty()) {
for (int i = 0; i < n; i++) {
m_weights.emplace_back(1);
}
}
if (m_knots.empty()) {
for (int i = 0; i < n + m_degree + 1; i++) {
m_knots.emplace_back(i);
}
}
else if (m_knots.size() != (n + m_degree + 1)) {
m_isValid = false;
return;
}
m_domain = { m_degree, m_knots.size() - 1 - m_degree };
for (int i = 0; i < n; i++) {
m_v.emplace_back(std::vector<double>());
for (int j = 0; j < m_dim; j++) {
m_v[i].emplace_back(points[i][j] * m_weights[i]);
}
m_v[i].emplace_back(m_weights[i]);
}
}
bool BSpline::isValid()
{
return m_isValid;
}
std::vector<double> BSpline::eval(double t)
{
if (!isValid()) {
return {};
}
std::vector<double> result;
double low = m_knots[m_domain.first];
double high = m_knots[m_domain.second];
t = t * (high - low) + low;
if (t < low || t > high) {
return result;
}
int s = 0;
for (s = m_domain.first; s < m_domain.second; s++) {
if (t >= m_knots[s] && t <= m_knots[s + 1]) {
break;
}
}
std::vector<std::vector<double>> v = m_v;
double alpha = 0.0;
for (int l = 1; l <= m_degree + 1; l++) {
for (int i = s; i > s - m_degree - 1 + l; i--) {
alpha = (t - m_knots[i]) / (m_knots[i + m_degree + 1 - l] - m_knots[i]);
for (int j = 0; j < m_dim + 1; j++) {
v[i][j] = (1 - alpha) * v[i - 1][j] + alpha * v[i][j];
}
}
}
for (int i = 0; i < m_dim; i++) {
result.emplace_back(v[s][i] / v[s][m_dim]);
}
return result;
}
}