Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The results of the tangent function are different than expected for NURBS #245

Open
abrzozowski opened this issue Jun 3, 2024 · 2 comments

Comments

@abrzozowski
Copy link

abrzozowski commented Jun 3, 2024

I need to obtain the angle of the tangent line at a point of the function (NURBS), but I am getting results that I don't understand.

Below, I am providing the code that I used for testing. In it, I created a NURBS curve defined as a quarter circle with a radius of 3 meters.

image

#include <iostream>
#include <cmath>

#include "tinysplinecxx.h"

int main(int argc, char **argv)
{
	tinyspline::BSpline nurbs(3, 3, 2, tinyspline::BSpline::Type::Clamped);
	std::vector<tinyspline::real> ctrlp(3 * 3, 1.0);
	std::vector<tinyspline::real> weights{1.0, 0.707, 1.0};
	nurbs.setControlPoints({
		0.0 * weights[0],
		0.0 * weights[0],
		weights[0],
		3.0 * weights[1],
		0.0 * weights[1],
		weights[1],
		3.0 * weights[2],
		3.0 * weights[2],
		weights[2],
	});
	nurbs.setKnots({0.0, 0.0, 0.0, 1.0, 1.0, 1.0});

	std::vector<double> parameters{0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0};

	// [1] the derive method
	const auto samples = nurbs.evalAll(parameters);
	const auto derives = nurbs.derive().evalAll(parameters);
	for (std::size_t i = 0; i < parameters.size(); ++i)
	{
		const auto theta = std::atan2(derives[i * 3 + 1] / derives[i * 3 + 2], derives[i * 3 + 0] / derives[i * 3 + 2]);

		std::cout << "p(" << samples[i * 3 + 0] / samples[i * 3 + 2] << ", " << samples[i * 3 + 1] / samples[i * 3 + 2]
				  << ", " << theta << " (" << theta / M_PI * 180.0 << " degs)"
				  << ")" << std::endl;
	}

	// [2] use frames
	const auto frames = nurbs.computeRMF(parameters);
	for (std::size_t i = 0; i < frames.size(); ++i)
	{
		const auto dx = frames.at(i).tangent().x() / frames.at(i).tangent().z();
		const auto dy = frames.at(i).tangent().y() / frames.at(i).tangent().z();
		const auto position = frames.at(i).position();

		const auto theta = std::atan2(dy, dx);

		std::cout << "p(" << position.x() / position.z() << ", " << position.y() / position.z()
				  << ", " << theta << " (" << theta / M_PI * 180.0 << " degs)"
				  << ")" << std::endl;
	}

	// [3] custom implemention of the derive
	for (std::size_t i = 0; i < parameters.size(); ++i)
	{
		const auto p1 = nurbs.eval(std::max(parameters[i] - TS_KNOT_EPSILON, nurbs.domain().min())).resultVec3();
		const auto p2 = nurbs.eval(std::min(parameters[i] + TS_KNOT_EPSILON, nurbs.domain().max())).resultVec3();
		const auto dx = p2.x() / p2.z() - p1.x() / p1.z();
		const auto dy = p2.y() / p2.z() - p1.y() / p1.z();
		const auto theta = std::atan2(dy, dx);

		std::cout << "p(" << samples[i * 3 + 0] / samples[i * 3 + 2] << ", " << samples[i * 3 + 1] / samples[i * 3 + 2]
				  << ", " << theta << " (" << theta / M_PI * 180.0 << " degs)"
				  << ")" << std::endl;
	}

	return 0;
}

The results are as follows

// [1] the derive method
p(0, 0, -3.14159 (-180 degs))
p(0.545828, 0.0500851, -2.95309 (-169.2 degs))
p(1.1042, 0.210645, -2.74886 (-157.498 degs))
p(1.64155, 0.489042, -2.54465 (-145.798 degs))
p(2.12127, 0.878735, 0.785398 (45 degs))
p(2.51096, 1.35845, 0.94862 (54.3519 degs))
p(2.78936, 1.8958, 1.08448 (62.1361 degs))
p(2.94991, 2.45417, 1.19547 (68.4956 degs))
p(3, 3, 1.28577 (73.6694 degs))

// [2] use frames
p(0, 0, -3.14159 (-180 degs))
p(0.545828, 0.0500851, -2.95309 (-169.2 degs))
p(1.1042, 0.210645, -2.74886 (-157.498 degs))
p(1.64155, 0.489042, -2.54465 (-145.798 degs))
p(2.12127, 0.878735, 0.785398 (45 degs))
p(2.51096, 1.35845, 0.94862 (54.3519 degs))
p(2.78936, 1.8958, 1.08448 (62.1361 degs))
p(2.94991, 2.45417, 1.19547 (68.4956 degs))
p(3, 3, 1.28577 (73.6694 degs))

// [3] custom implemention of the derive
p(0, 0, 7.07234e-05 (0.00405215 degs))
p(0.545828, 0.0500851, 0.183002 (10.4853 degs))
p(1.1042, 0.210645, 0.376984 (21.5996 degs))
p(1.64155, 0.489042, 0.579044 (33.1768 degs))
p(2.12127, 0.878735, 0.785398 (45 degs))
p(2.51096, 1.35845, 0.991753 (56.8232 degs))
p(2.78936, 1.8958, 1.19381 (68.4004 degs))
p(2.94991, 2.45417, 1.38779 (79.5147 degs))
p(3, 3, 1.57073 (89.9959 degs))

The greatest difference is seen at the last point p(3, 3) of the curve: 89.9959 degs vs 73.6694 degs. The result should indicate a value of 90 degs, because at this point, the NURBS "indicates due north".

Is there an obvious mistake I am making using tinyspline?

@abrzozowski abrzozowski changed the title The results of the tangent function are different than expected The results of the tangent function are different than expected for NURBS Jun 4, 2024
@msteinbeck
Copy link
Owner

hi @abrzozowski,

that's an interesting observation. I'll have a look.

@abrzozowski
Copy link
Author

@msteinbeck The solution is a bit more complicated, but it is quite well described here: https://public.vrac.iastate.edu/~oliver/courses/me625/week8.pdf
image
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants