-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathBSpline2d.cc
More file actions
102 lines (87 loc) · 3.38 KB
/
BSpline2d.cc
File metadata and controls
102 lines (87 loc) · 3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include <cmath>
#include <iostream>
#include "splineInterpolation.h"
namespace SplineInterp {
//draft provided by Claude Sonnet 4 from prompt:
// Given a 2d point along a bspline, specified in cartesian coordinates,
// how do I determine its parametric coordinate on the spline?
/**
* Find parametric location of a given point using Newton-Raphson method
* @param targetPt (in) The cartesian point to find parameter for
* @param initialGuess (in) initial guess of parametric location
* @param tolerance (in) Convergence tolerance
* @param maxIterations (in) Maximum number of iterations
* @param tMin (in) Minimum valid parameter value
* @param tMax (in) Maximum valid parameter value
* @return parametric location, or NaN if failed to converge
*/
double BSpline2d::newtonRaphson(const Point2d& targetPt,
const double initialGuess,
const double tolerance,
const int maxIterations,
const double tMin,
const double tMax) const {
double t = initialGuess;
for (int i = 0; i < maxIterations; ++i) {
// Evaluate spline and derivative at current t
Point2d currentPoint = {x.eval(t), y.eval(t)};
Point2d derivative = {x.evalFirstDeriv(t), y.evalFirstDeriv(t)};
// Calculate error vector
Point2d error = currentPoint - targetPt;
// Check convergence
if (error.norm() < tolerance) {
return t;
}
// Check if derivative is too small (avoid division by zero)
double derivNormSq = derivative.dot(derivative);
if (derivNormSq < tolerance) {
break; // Derivative too small, cannot continue
}
// Newton-Raphson update: dt = -(error · derivative) / (derivative · derivative)
double dt = -error.dot(derivative) / derivNormSq;
t += dt;
// Clamp t to valid range
t = std::max(tMin, std::min(tMax, t));
}
return std::numeric_limits<double>::quiet_NaN(); // Failed to converge
}
double BSpline2d::invEval(const Point2d& targetPt, double guess, bool debug) const {
if(debug) {
std::cerr << "targetPt " << targetPt.x << ", " << targetPt.y << " guess " << guess << "\n";
}
// Find best initial guess by sampling
const int numSamples = x.getNumKnots()*2;
const double tolerance = 1e-10;
const int maxIterations = 50;
const double tMin = 0.0;
const double tMax = 1.0;
double minDistance = std::numeric_limits<double>::max();
std::vector<double> guesses;
guesses.reserve(numSamples);
for (int i = 0; i <= numSamples; ++i) {
double t = tMin + (tMax - tMin) * i / numSamples;
Point2d point = {x.eval(t), y.eval(t)};
double distance = (point - targetPt).norm();
if (distance < minDistance) {
minDistance = distance;
guesses.push_back(t);
}
}
if(guess != -1) {
guesses.push_back(guess);
}
for(auto rit = guesses.rbegin(); rit != guesses.rend(); ++rit) {
const auto guess = *rit;
if(debug) {
std::cerr << " guess " << guess << "\n";
}
const auto res = newtonRaphson(targetPt, guess, tolerance, maxIterations, tMin, tMax);
if(! std::isnan(res) ) {
return res;
}
}
std::cerr << "ERROR: " << __func__ << " could not find parametric coordinate for input point "
<< targetPt.x << ", " << targetPt.y << '\n';
return std::numeric_limits<double>::quiet_NaN(); // Failed to converge
}
} //end namespace