7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2024-11-24 13:05:01 +00:00
kicad/thirdparty/tinyspline_lib/tinysplinecxx.cxx
Marek Roszko 24ca892afb Update tinysplice to latest
A few years worth of bug fixes
2022-12-06 22:28:23 -05:00

1708 lines
36 KiB
C++

#define TINYSPLINE_EXPORT
#include "tinysplinecxx.h"
#include <stdlib.h>
#include <cstring>
#include <stdexcept>
#include <cstdio>
#include <sstream>
/* Suppress some useless MSVC warnings. */
#ifdef _MSC_VER
#pragma warning(push)
/* address of dllimport */
#pragma warning(disable:4232)
/* binding rvalues to non-const references */
#pragma warning(disable:4350)
/* unreferenced inline function */
#pragma warning(disable:4514)
/* function not inlined */
#pragma warning(disable:4710)
/* byte padding */
#pragma warning(disable:4820)
/* meaningless deprecation */
#pragma warning(disable:4996)
/* Spectre mitigation */
#pragma warning(disable:5045)
#endif
/*! @name Swig Type Mapping
*
* See tinysplinecxx.h for more details.
*
* @{
*/
#ifdef SWIG
#define std_real_vector_init(var) \
std_real_vector_out var = new std::vector<tinyspline::real>
#define std_real_vector_read(var) var->
#else
#define std_real_vector_init(var) \
std_real_vector_out var
#define std_real_vector_read(var) var.
#endif
/*! @} */
/*! @name Vec2
*
* @{
*/
tinyspline::Vec2::Vec2()
{
const real v = (real) 0.0;
ts_vec2_init(m_vals, v, v);
}
tinyspline::Vec2::Vec2(real x,
real y)
{
ts_vec2_init(m_vals, x, y);
}
tinyspline::Vec2
tinyspline::Vec2::operator+(const Vec2 &other)
{
return add(other);
}
tinyspline::Vec2
tinyspline::Vec2::operator-(const Vec2 &other)
{
return subtract(other);
}
tinyspline::Vec2
tinyspline::Vec2::operator*(real scalar)
{
return multiply(scalar);
}
tinyspline::real
tinyspline::Vec2::x() const
{
return m_vals[0];
}
void
tinyspline::Vec2::setX(real val)
{
m_vals[0] = val;
}
tinyspline::real
tinyspline::Vec2::y() const
{
return m_vals[1];
}
void
tinyspline::Vec2::setY(real val)
{
m_vals[1] = val;
}
std::vector<tinyspline::real>
tinyspline::Vec2::values() const
{
return std::vector<real>({ x(), y() });
}
tinyspline::Vec2
tinyspline::Vec2::add(const Vec2 &other) const
{
Vec2 vec;
ts_vec_add(m_vals, other.m_vals, 2, vec.m_vals);
return vec;
}
tinyspline::Vec2
tinyspline::Vec2::subtract(const Vec2 &other) const
{
Vec2 vec;
ts_vec_sub(m_vals, other.m_vals, 2, vec.m_vals);
return vec;
}
tinyspline::Vec2
tinyspline::Vec2::multiply(real scalar) const
{
Vec2 vec;
ts_vec_mul(m_vals, 2, scalar, vec.m_vals);
return vec;
}
tinyspline::Vec2
tinyspline::Vec2::normalize() const
{
Vec2 vec;
ts_vec_norm(m_vals, 2, vec.m_vals);
return vec;
}
tinyspline::Vec2
tinyspline::Vec2::norm() const
{
return normalize();
}
tinyspline::real
tinyspline::Vec2::magnitude() const
{
return ts_vec_mag(m_vals, 2);
}
tinyspline::real
tinyspline::Vec2::dot(const Vec2 &other) const
{
return ts_vec_dot(m_vals, other.m_vals, 2);
}
tinyspline::real
tinyspline::Vec2::angle(const Vec2 &other) const
{
real buf[4];
return ts_vec_angle(m_vals, other.m_vals, buf, 2);
}
tinyspline::real
tinyspline::Vec2::distance(const Vec2 &other) const
{
return ts_distance(m_vals, other.m_vals, 2);
}
std::string
tinyspline::Vec2::toString() const
{
std::ostringstream oss;
oss << "Vec2{"
<< "x: " << x()
<< ", y: " << y()
<< "}";
return oss.str();
}
/*! @} */
/*! @name Vec3
*
* @{
*/
tinyspline::Vec3::Vec3()
{
const real v = (real) 0.0;
ts_vec3_init(m_vals, v, v, v);
}
tinyspline::Vec3::Vec3(real x,
real y,
real z)
{
ts_vec3_init(m_vals, x, y, z);
}
tinyspline::Vec3
tinyspline::Vec3::operator+(const Vec3 &other)
{
return add(other);
}
tinyspline::Vec3
tinyspline::Vec3::operator-(const Vec3 &other)
{
return subtract(other);
}
tinyspline::Vec3
tinyspline::Vec3::operator*(real scalar)
{
return multiply(scalar);
}
tinyspline::real
tinyspline::Vec3::x() const
{
return m_vals[0];
}
void
tinyspline::Vec3::setX(real val)
{
m_vals[0] = val;
}
tinyspline::real
tinyspline::Vec3::y() const
{
return m_vals[1];
}
void
tinyspline::Vec3::setY(real val)
{
m_vals[1] = val;
}
tinyspline::real
tinyspline::Vec3::z() const
{
return m_vals[2];
}
void
tinyspline::Vec3::setZ(real val)
{
m_vals[2] = val;
}
std::vector<tinyspline::real>
tinyspline::Vec3::values() const
{
return std::vector<real>({ x(), y(), z() });
}
tinyspline::Vec3
tinyspline::Vec3::add(const Vec3 &other) const
{
Vec3 vec;
ts_vec_add(m_vals, other.m_vals, 3, vec.m_vals);
return vec;
}
tinyspline::Vec3
tinyspline::Vec3::subtract(const Vec3 &other) const
{
Vec3 vec;
ts_vec_sub(m_vals, other.m_vals, 3, vec.m_vals);
return vec;
}
tinyspline::Vec3
tinyspline::Vec3::multiply(real scalar) const
{
Vec3 vec;
ts_vec_mul(m_vals, 3, scalar, vec.m_vals);
return vec;
}
tinyspline::Vec3
tinyspline::Vec3::cross(const Vec3 &other) const
{
Vec3 vec;
ts_vec3_cross(m_vals, other.m_vals, vec.m_vals);
return vec;
}
tinyspline::Vec3
tinyspline::Vec3::normalize() const
{
Vec3 vec;
ts_vec_norm(m_vals, 3, vec.m_vals);
return vec;
}
tinyspline::Vec3
tinyspline::Vec3::norm() const
{
return normalize();
}
tinyspline::real
tinyspline::Vec3::magnitude() const
{
return ts_vec_mag(m_vals, 3);
}
tinyspline::real
tinyspline::Vec3::dot(const Vec3 &other) const
{
return ts_vec_dot(m_vals, other.m_vals, 3);
}
tinyspline::real
tinyspline::Vec3::angle(const Vec3 &other) const
{
real buf[6];
return ts_vec_angle(m_vals, other.m_vals, buf, 3);
}
tinyspline::real
tinyspline::Vec3::distance(const Vec3 &other) const
{
return ts_distance(m_vals, other.m_vals, 3);
}
std::string
tinyspline::Vec3::toString() const
{
std::ostringstream oss;
oss << "Vec3{"
<< "x: " << x()
<< ", y: " << y()
<< ", z: " << z()
<< "}";
return oss.str();
}
/*! @} */
/*! @name Vec4
*
* @{
*/
tinyspline::Vec4::Vec4()
{
const real v = (real) 0.0;
ts_vec4_init(m_vals, v, v, v, v);
}
tinyspline::Vec4::Vec4(real x,
real y,
real z,
real w)
{
ts_vec4_init(m_vals, x, y, z, w);
}
tinyspline::Vec4
tinyspline::Vec4::operator+(const Vec4 &other)
{
return add(other);
}
tinyspline::Vec4
tinyspline::Vec4::operator-(const Vec4 &other)
{
return subtract(other);
}
tinyspline::Vec4
tinyspline::Vec4::operator*(real scalar)
{
return multiply(scalar);
}
tinyspline::real
tinyspline::Vec4::x() const
{
return m_vals[0];
}
void
tinyspline::Vec4::setX(real val)
{
m_vals[0] = val;
}
tinyspline::real
tinyspline::Vec4::y() const
{
return m_vals[1];
}
void
tinyspline::Vec4::setY(real val)
{
m_vals[1] = val;
}
tinyspline::real
tinyspline::Vec4::z() const
{
return m_vals[2];
}
void
tinyspline::Vec4::setZ(real val)
{
m_vals[2] = val;
}
tinyspline::real
tinyspline::Vec4::w() const
{
return m_vals[3];
}
void
tinyspline::Vec4::setW(real val)
{
m_vals[3] = val;
}
std::vector<tinyspline::real>
tinyspline::Vec4::values() const
{
return std::vector<real>({ x(), y(), z(), w() });
}
tinyspline::Vec4
tinyspline::Vec4::add(const Vec4 &other) const
{
Vec4 vec;
ts_vec_add(m_vals, other.m_vals, 4, vec.m_vals);
return vec;
}
tinyspline::Vec4
tinyspline::Vec4::subtract(const Vec4 &other) const
{
Vec4 vec;
ts_vec_sub(m_vals, other.m_vals, 4, vec.m_vals);
return vec;
}
tinyspline::Vec4
tinyspline::Vec4::multiply(real scalar) const
{
Vec4 vec;
ts_vec_mul(m_vals, 4, scalar, vec.m_vals);
return vec;
}
tinyspline::Vec4
tinyspline::Vec4::normalize() const
{
Vec4 vec;
ts_vec_norm(m_vals, 4, vec.m_vals);
return vec;
}
tinyspline::Vec4
tinyspline::Vec4::norm() const
{
return normalize();
}
tinyspline::real
tinyspline::Vec4::magnitude() const
{
return ts_vec_mag(m_vals, 4);
}
tinyspline::real
tinyspline::Vec4::dot(const Vec4 &other) const
{
return ts_vec_dot(m_vals, other.m_vals, 4);
}
tinyspline::real
tinyspline::Vec4::angle(const Vec4 &other) const
{
real buf[8];
return ts_vec_angle(m_vals, other.m_vals, buf, 4);
}
tinyspline::real
tinyspline::Vec4::distance(const Vec4 &other) const
{
return ts_distance(m_vals, other.m_vals, 4);
}
std::string
tinyspline::Vec4::toString() const
{
std::ostringstream oss;
oss << "Vec4{"
<< "x: " << x()
<< ", y: " << y()
<< ", z: " << z()
<< ", w: " << w()
<< "}";
return oss.str();
}
/*! @} */
/*! @name Frame
*
* @{
*/
tinyspline::Frame::Frame(Vec3 &position,
Vec3 &tangent,
Vec3 &normal,
Vec3 &binormal)
: m_position(position),
m_tangent(tangent),
m_normal(normal),
m_binormal(binormal)
{}
tinyspline::Vec3
tinyspline::Frame::position() const
{
return m_position;
}
tinyspline::Vec3
tinyspline::Frame::tangent() const
{
return m_tangent;
}
tinyspline::Vec3
tinyspline::Frame::normal() const
{
return m_normal;
}
tinyspline::Vec3
tinyspline::Frame::binormal() const
{
return m_binormal;
}
std::string
tinyspline::Frame::toString() const
{
std::ostringstream oss;
oss << "Frame{"
<< "position: " << position().toString()
<< ", tangent: " << tangent().toString()
<< ", normal: " << normal().toString()
<< ", binormal: " << binormal().toString()
<< "}";
return oss.str();
}
/*! @} */
/*! @name FrameSeq
*
* @{
*/
tinyspline::FrameSeq::FrameSeq()
: m_frames(nullptr), m_size(0)
{}
tinyspline::FrameSeq::FrameSeq(tsFrame *frames,
size_t len)
: m_frames(frames), m_size(len)
{}
tinyspline::FrameSeq::FrameSeq(const FrameSeq &other)
: m_frames(nullptr), m_size(other.m_size)
{
m_frames = new tsFrame[m_size];
std::copy(other.m_frames,
other.m_frames + m_size,
m_frames);
}
tinyspline::FrameSeq::FrameSeq(FrameSeq &&other)
: m_frames(nullptr), m_size(other.m_size)
{
m_frames = other.m_frames;
other.m_frames = nullptr;
other.m_size = 0;
}
tinyspline::FrameSeq::~FrameSeq()
{
delete [] m_frames;
m_size = 0;
}
tinyspline::FrameSeq &
tinyspline::FrameSeq::operator=(const FrameSeq &other)
{
if (&other != this) {
tsFrame *data = new tsFrame[other.m_size];
std::copy(other.m_frames,
other.m_frames + other.m_size,
data);
delete [] m_frames;
m_frames = data;
m_size = other.m_size;
}
return *this;
}
tinyspline::FrameSeq &
tinyspline::FrameSeq::operator=(FrameSeq &&other)
{
if (&other != this) {
delete [] m_frames;
m_frames = other.m_frames;
m_size = other.m_size;
other.m_frames = nullptr;
other.m_size = 0;
}
return *this;
}
size_t
tinyspline::FrameSeq::size() const
{
return m_size;
}
tinyspline::Frame
tinyspline::FrameSeq::at(size_t idx) const
{
if (idx >= m_size)
throw std::out_of_range( "idx >= size");
tsFrame frame = m_frames[idx];
Vec3 position = Vec3(frame.position[0],
frame.position[1],
frame.position[2]);
Vec3 tangent = Vec3(frame.tangent[0],
frame.tangent[1],
frame.tangent[2]);
Vec3 normal = Vec3(frame.normal[0],
frame.normal[1],
frame.normal[2]);
Vec3 binormal = Vec3(frame.binormal[0],
frame.binormal[1],
frame.binormal[2]);
return Frame(position, tangent, normal, binormal);
}
std::string
tinyspline::FrameSeq::toString() const
{
std::ostringstream oss;
oss << "FrameSeq{"
<< "frames: " << size()
<< "}";
return oss.str();
}
/*! @} */
/*! @name Domain
*
* @{
*/
tinyspline::Domain::Domain(real min,
real max)
: m_min(min), m_max(max)
{}
tinyspline::real
tinyspline::Domain::min() const
{
return m_min;
}
tinyspline::real
tinyspline::Domain::max() const
{
return m_max;
}
std::string
tinyspline::Domain::toString() const
{
std::ostringstream oss;
oss << "Domain{"
<< "min: " << min()
<< ", max: " << max()
<< "}";
return oss.str();
}
/*! @} */
/*! @name DeBoorNet
*
* @{
*/
tinyspline::DeBoorNet::DeBoorNet(tsDeBoorNet &data)
: net(ts_deboornet_init())
{
ts_deboornet_move(&data, &net);
}
tinyspline::DeBoorNet::DeBoorNet(const DeBoorNet &other)
: net(ts_deboornet_init())
{
tsStatus status;
if (ts_deboornet_copy(&other.net, &net, &status))
throw std::runtime_error(status.message);
}
tinyspline::DeBoorNet::DeBoorNet(DeBoorNet &&other)
: net(ts_deboornet_init())
{
ts_deboornet_move(&other.net, &net);
}
tinyspline::DeBoorNet::~DeBoorNet()
{
ts_deboornet_free(&net);
}
tinyspline::DeBoorNet &
tinyspline::DeBoorNet::operator=(const DeBoorNet &other)
{
if (&other != this) {
tsDeBoorNet data = ts_deboornet_init();
tsStatus status;
if (ts_deboornet_copy(&other.net, &data, &status))
throw std::runtime_error(status.message);
ts_deboornet_free(&net);
ts_deboornet_move(&data, &net);
}
return *this;
}
tinyspline::DeBoorNet &
tinyspline::DeBoorNet::operator=(DeBoorNet && other)
{
if (&other != this) {
ts_deboornet_free(&net);
ts_deboornet_move(&other.net, &net);
}
return *this;
}
tinyspline::real
tinyspline::DeBoorNet::knot() const
{
return ts_deboornet_knot(&net);
}
size_t
tinyspline::DeBoorNet::index() const
{
return ts_deboornet_index(&net);
}
size_t
tinyspline::DeBoorNet::multiplicity() const
{
return ts_deboornet_multiplicity(&net);
}
size_t
tinyspline::DeBoorNet::numInsertions() const
{
return ts_deboornet_num_insertions(&net);
}
size_t
tinyspline::DeBoorNet::dimension() const
{
return ts_deboornet_dimension(&net);
}
std::vector<tinyspline::real>
tinyspline::DeBoorNet::points() const
{
const real *points = ts_deboornet_points_ptr(&net);
size_t len = ts_deboornet_len_points(&net);
return std::vector<real>(points, points + len);
}
std::vector<tinyspline::real>
tinyspline::DeBoorNet::result() const
{
const real *result = ts_deboornet_result_ptr(&net);
size_t len = ts_deboornet_len_result(&net);
return std::vector<real>(result, result + len);
}
tinyspline::Vec2
tinyspline::DeBoorNet::resultVec2(size_t idx) const
{
Vec4 vec4 = resultVec4(idx);
return Vec2(vec4.x(), vec4.y());
}
tinyspline::Vec3
tinyspline::DeBoorNet::resultVec3(size_t idx) const
{
Vec4 vec4 = resultVec4(idx);
return Vec3(vec4.x(), vec4.y(), vec4.z());
}
tinyspline::Vec4
tinyspline::DeBoorNet::resultVec4(size_t idx) const
{
if (idx >= ts_deboornet_num_result(&net))
throw std::out_of_range( "idx >= num(result)");
const real *result = ts_deboornet_result_ptr(&net);
real vals[4];
ts_vec4_set(vals, result + idx * dimension(), dimension());
return Vec4(vals[0], vals[1], vals[2], vals[3]);
}
std::string
tinyspline::DeBoorNet::toString() const
{
std::ostringstream oss;
oss << "DeBoorNet{"
<< "knot: " << knot()
<< ", index: " << index()
<< ", multiplicity: " << multiplicity()
<< ", insertions: " << numInsertions()
<< ", dimension: " << dimension()
<< ", points: " << ts_deboornet_num_points(&net)
<< "}";
return oss.str();
}
/*! @} */
/******************************************************************************
* *
* BSpline *
* *
******************************************************************************/
tinyspline::BSpline::BSpline(tsBSpline &data)
: spline(ts_bspline_init())
{
ts_bspline_move(&data, &spline);
}
tinyspline::BSpline::BSpline()
: spline(ts_bspline_init())
{
tsStatus status;
if (ts_bspline_new_with_control_points(1,
3,
0,
TS_CLAMPED,
&spline,
&status,
0.0, 0.0, 0.0))
throw std::runtime_error(status.message);
}
tinyspline::BSpline::BSpline(const tinyspline::BSpline &other)
: spline(ts_bspline_init())
{
tsStatus status;
if (ts_bspline_copy(&other.spline, &spline, &status))
throw std::runtime_error(status.message);
}
tinyspline::BSpline::BSpline(BSpline &&other)
: spline(ts_bspline_init())
{
ts_bspline_move(&other.spline, &spline);
}
tinyspline::BSpline::BSpline(size_t numControlPoints,
size_t dimension,
size_t degree,
Type type)
: spline(ts_bspline_init())
{
tsBSplineType c_type = TS_CLAMPED;
switch (type) {
case Opened:
c_type = TS_OPENED;
break;
case Clamped:
c_type = TS_CLAMPED;
break;
case Beziers:
c_type = TS_BEZIERS;
break;
default:
throw std::runtime_error("unknown type");
}
tsStatus status;
if (ts_bspline_new(numControlPoints,
dimension,
degree,
c_type,
&spline,
&status))
throw std::runtime_error(status.message);
}
tinyspline::BSpline::~BSpline()
{
ts_bspline_free(&spline);
}
tinyspline::BSpline tinyspline::BSpline::interpolateCubicNatural(
std_real_vector_in points, size_t dimension)
{
if (dimension == 0)
throw std::runtime_error("unsupported dimension: 0");
if (std_real_vector_read(points)size() % dimension != 0)
throw std::runtime_error("#points % dimension != 0");
tsBSpline data = ts_bspline_init();
tsStatus status;
if (ts_bspline_interpolate_cubic_natural(
std_real_vector_read(points)data(),
std_real_vector_read(points)size()/dimension,
dimension, &data, &status))
throw std::runtime_error(status.message);
return BSpline(data);
}
tinyspline::BSpline
tinyspline::BSpline::interpolateCatmullRom(std_real_vector_in points,
size_t dimension,
real alpha,
std::vector<real> *first,
std::vector<real> *last,
real epsilon)
{
if (dimension == 0)
throw std::runtime_error("unsupported dimension: 0");
if (std_real_vector_read(points)size() % dimension != 0)
throw std::runtime_error("#points % dimension != 0");
real *fst = nullptr;
if (first && first->size() >= dimension)
fst = first->data();
real *lst = nullptr;
if (last && last->size() >= dimension)
lst = last->data();
tsBSpline data = ts_bspline_init();
tsStatus status;
if (ts_bspline_interpolate_catmull_rom(
std_real_vector_read(points)data(),
std_real_vector_read(points)size()/dimension,
dimension, alpha, fst, lst, epsilon, &data,
&status))
throw std::runtime_error(status.message);
return BSpline(data);
}
tinyspline::BSpline tinyspline::BSpline::parseJson(std::string json)
{
tsBSpline data = ts_bspline_init();
tsStatus status;
if (ts_bspline_parse_json(json.c_str(), &data, &status))
throw std::runtime_error(status.message);
return BSpline(data);
}
tinyspline::BSpline tinyspline::BSpline::load(std::string path)
{
tsBSpline data = ts_bspline_init();
tsStatus status;
if (ts_bspline_load(path.c_str(), &data, &status))
throw std::runtime_error(status.message);
return BSpline(data);
}
bool
tinyspline::BSpline::knotsEqual(real x, real y)
{
return ts_knots_equal(x, y);
}
tinyspline::BSpline &
tinyspline::BSpline::operator=(const BSpline &other)
{
if (&other != this) {
tsBSpline data = ts_bspline_init();
tsStatus status;
if (ts_bspline_copy(&other.spline, &data, &status))
throw std::runtime_error(status.message);
ts_bspline_free(&spline);
ts_bspline_move(&data, &spline);
}
return *this;
}
tinyspline::BSpline &
tinyspline::BSpline::operator=(BSpline &&other)
{
if (&other != this) {
ts_bspline_free(&spline);
ts_bspline_move(&other.spline, &spline);
}
return *this;
}
tinyspline::DeBoorNet tinyspline::BSpline::operator()(tinyspline::real u) const
{
return eval(u);
}
size_t tinyspline::BSpline::degree() const
{
return ts_bspline_degree(&spline);
}
size_t tinyspline::BSpline::order() const
{
return ts_bspline_order(&spline);
}
size_t tinyspline::BSpline::dimension() const
{
return ts_bspline_dimension(&spline);
}
std::vector<tinyspline::real>
tinyspline::BSpline::controlPoints() const
{
const real *ctrlps = ts_bspline_control_points_ptr(&spline);
const size_t len = ts_bspline_len_control_points(&spline);
return std::vector<real>(ctrlps, ctrlps + len);
}
tinyspline::Vec2
tinyspline::BSpline::controlPointVec2At(size_t idx) const
{
const Vec4 vec4 = controlPointVec4At(idx);
return Vec2(vec4.x(), vec4.y());
}
tinyspline::Vec3
tinyspline::BSpline::controlPointVec3At(size_t idx) const
{
const Vec4 vec4 = controlPointVec4At(idx);
return Vec3(vec4.x(), vec4.y(), vec4.z());
}
tinyspline::Vec4
tinyspline::BSpline::controlPointVec4At(size_t idx) const
{
const real *ctrlp;
tsStatus status;
if (ts_bspline_control_point_at_ptr(&spline,
idx,
&ctrlp,
&status))
throw std::runtime_error(status.message);
real vals[4];
ts_vec4_set(vals, ctrlp, dimension());
return Vec4(vals[0], vals[1], vals[2], vals[3]);
}
std::vector<tinyspline::real>
tinyspline::BSpline::knots() const
{
const real *knots = ts_bspline_knots_ptr(&spline);
size_t num = ts_bspline_num_knots(&spline);
return std::vector<real>(knots, knots + num);
}
tinyspline::real tinyspline::BSpline::knotAt(size_t index) const
{
real knot;
tsStatus status;
if (ts_bspline_knot_at(&spline, index, &knot, &status))
throw std::runtime_error(status.message);
return knot;
}
size_t tinyspline::BSpline::numControlPoints() const
{
return ts_bspline_num_control_points(&spline);
}
tinyspline::DeBoorNet tinyspline::BSpline::eval(tinyspline::real u) const
{
tsDeBoorNet net = ts_deboornet_init();
tsStatus status;
if (ts_bspline_eval(&spline, u, &net, &status))
throw std::runtime_error(status.message);
return tinyspline::DeBoorNet(net);
}
tinyspline::std_real_vector_out
tinyspline::BSpline::evalAll(std_real_vector_in knots) const
{
const size_t num_knots = std_real_vector_read(knots)size();
const real *knots_ptr = std_real_vector_read(knots)data();
tinyspline::real *points;
tsStatus status;
if (ts_bspline_eval_all(&spline,
knots_ptr,
num_knots,
&points,
&status)) {
throw std::runtime_error(status.message);
}
real *first = points;
real *last = first + num_knots * dimension();
std_real_vector_init(vec)(first, last);
std::free(points);
return vec;
}
tinyspline::std_real_vector_out
tinyspline::BSpline::sample(size_t num) const
{
tinyspline::real *points;
size_t actualNum;
tsStatus status;
if (ts_bspline_sample(&spline,
num,
&points,
&actualNum,
&status)) {
throw std::runtime_error(status.message);
}
real *first = points;
real *last = first + actualNum * dimension();
std_real_vector_init(vec)(first, last);
std::free(points);
return vec;
}
tinyspline::DeBoorNet tinyspline::BSpline::bisect(real value,
real epsilon,
bool persnickety,
size_t index,
bool ascending,
size_t maxIter) const
{
tsDeBoorNet net = ts_deboornet_init();
tsStatus status;
if (ts_bspline_bisect(&spline,
value,
epsilon,
persnickety,
index,
ascending,
maxIter,
&net,
&status))
throw std::runtime_error(status.message);
return DeBoorNet(net);
}
tinyspline::Domain tinyspline::BSpline::domain() const
{
real min, max;
ts_bspline_domain(&spline, &min, &max);
return Domain(min, max);
}
bool tinyspline::BSpline::isClosed(tinyspline::real epsilon) const
{
int closed = 0;
tsStatus status;
if (ts_bspline_is_closed(&spline, epsilon, &closed, &status))
throw std::runtime_error(status.message);
return closed == 1;
}
tinyspline::FrameSeq
tinyspline::BSpline::computeRMF(std_real_vector_in knots,
tinyspline::Vec3 *firstNormal) const
{
tsStatus status;
size_t num = std_real_vector_read(knots)size();
const real *knots_ptr = std_real_vector_read(knots)data();
tsFrame *frames = new tsFrame[num];
if (firstNormal && num > 0) {
ts_vec3_init(frames[0].normal,
firstNormal->x(),
firstNormal->y(),
firstNormal->z());
}
if (ts_bspline_compute_rmf(&spline,
knots_ptr,
num,
firstNormal != nullptr,
frames,
&status))
throw std::runtime_error(status.message);
FrameSeq seq = FrameSeq(frames, num);
return seq;
}
tinyspline::BSpline
tinyspline::BSpline::subSpline(real knot0, real knot1) const
{
tsBSpline data = ts_bspline_init();
tsStatus status;
if (ts_bspline_sub_spline(&spline,
knot0,
knot1,
&data,
&status))
throw std::runtime_error(status.message);
return BSpline(data);
}
tinyspline::std_real_vector_out
tinyspline::BSpline::uniformKnotSeq(size_t num) const
{
std_real_vector_init(knots)(num);
real *knots_ptr = std_real_vector_read(knots)data();
ts_bspline_uniform_knot_seq(&spline, num, knots_ptr);
return knots;
}
tinyspline::std_real_vector_out
tinyspline::BSpline::equidistantKnotSeq(size_t num,
size_t numSamples) const
{
tsStatus status;
std_real_vector_init(knots)(num);
real *knots_ptr = std_real_vector_read(knots)data();
if (ts_bspline_equidistant_knot_seq(&spline,
num,
knots_ptr,
numSamples,
&status))
throw std::runtime_error(status.message);
return knots;
}
tinyspline::ChordLengths
tinyspline::BSpline::chordLengths(std_real_vector_in knots) const
{
tsStatus status;
size_t num = std_real_vector_read(knots)size();
real *knotsArr = new real[num];
real *lengths = new real[num];
std::copy(std_real_vector_read(knots)begin(),
std_real_vector_read(knots)end(),
knotsArr);
if (ts_bspline_chord_lengths(&spline,
knotsArr,
num,
lengths,
&status))
throw std::runtime_error(status.message);
return ChordLengths(*this, knotsArr, lengths, num);
}
tinyspline::ChordLengths
tinyspline::BSpline::chordLengths(size_t numSamples) const
{
return chordLengths(uniformKnotSeq(numSamples));
}
std::string tinyspline::BSpline::toJson() const
{
char *json;
tsStatus status;
if (ts_bspline_to_json(&spline, &json, &status))
throw std::runtime_error(status.message);
std::string string(json);
std::free(json);
return string;
}
void tinyspline::BSpline::save(std::string path) const
{
tsStatus status;
if (ts_bspline_save(&spline, path.c_str(), &status))
throw std::runtime_error(status.message);
}
void tinyspline::BSpline::setControlPoints(
const std::vector<tinyspline::real> &ctrlp)
{
size_t expected = ts_bspline_len_control_points(&spline);
size_t actual = ctrlp.size();
if (expected != actual) {
std::ostringstream oss;
oss << "Expected size: " << expected
<< ", Actual size: " << actual;
throw std::runtime_error(oss.str());
}
tsStatus status;
if (ts_bspline_set_control_points(&spline, ctrlp.data(), &status))
throw std::runtime_error(status.message);
}
void
tinyspline::BSpline::setControlPointVec2At(size_t idx, Vec2 &cp)
{
Vec4 vec4(cp.x(), cp.y(), (real) 0.0, (real) 0.0);
setControlPointVec4At(idx, vec4);
}
void
tinyspline::BSpline::setControlPointVec3At(size_t idx, Vec3 &cp)
{
Vec4 vec4(cp.x(), cp.y(), cp.z(), (real) 0.0);
setControlPointVec4At(idx, vec4);
}
void
tinyspline::BSpline::setControlPointVec4At(size_t idx, Vec4 &cp)
{
std::vector<real> vals(dimension());
for (size_t i = 0; i < vals.size(); i++)
vals[i] = (real) 0.0;
if (vals.size() >= 4) vals[3] = cp.w();
if (vals.size() >= 3) vals[2] = cp.z();
if (vals.size() >= 2) vals[1] = cp.y();
if (vals.size() >= 1) vals[0] = cp.x();
tsStatus status;
if (ts_bspline_set_control_point_at(&spline,
idx,
vals.data(),
&status))
throw std::runtime_error(status.message);
}
void
tinyspline::BSpline::setKnots(const std::vector<real> &knots)
{
size_t expected = ts_bspline_num_knots(&spline);
size_t actual = knots.size();
if (expected != actual) {
std::ostringstream oss;
oss << "Expected size: " << expected
<< ", Actual size: " << actual;
throw std::runtime_error(oss.str());
}
tsStatus status;
if (ts_bspline_set_knots(&spline,
knots.data(),
&status))
throw std::runtime_error(status.message);
}
void tinyspline::BSpline::setKnotAt(size_t index, tinyspline::real knot)
{
tsStatus status;
if (ts_bspline_set_knot_at(&spline, index, knot, &status))
throw std::runtime_error(status.message);
}
tinyspline::BSpline tinyspline::BSpline::insertKnot(tinyspline::real u,
size_t n) const
{
tsBSpline data = ts_bspline_init();
size_t k;
tsStatus status;
if (ts_bspline_insert_knot(&spline, u, n, &data, &k, &status))
throw std::runtime_error(status.message);
return BSpline(data);
}
tinyspline::BSpline tinyspline::BSpline::split(tinyspline::real u) const
{
tsBSpline data = ts_bspline_init();
size_t k;
tsStatus status;
if (ts_bspline_split(&spline, u, &data, &k, &status))
throw std::runtime_error(status.message);
return BSpline(data);
}
tinyspline::BSpline tinyspline::BSpline::tension(
tinyspline::real tension) const
{
tsBSpline data = ts_bspline_init();
tsStatus status;
if (ts_bspline_tension(&spline, tension, &data, &status))
throw std::runtime_error(status.message);
return BSpline(data);
}
tinyspline::BSpline tinyspline::BSpline::toBeziers() const
{
tsBSpline data = ts_bspline_init();
tsStatus status;
if (ts_bspline_to_beziers(&spline, &data, &status))
throw std::runtime_error(status.message);
return BSpline(data);
}
tinyspline::BSpline tinyspline::BSpline::derive(size_t n, real epsilon) const
{
tsBSpline data = ts_bspline_init();
tsStatus status;
if (ts_bspline_derive(&spline, n, epsilon, &data, &status))
throw std::runtime_error(status.message);
return BSpline(data);
}
tinyspline::BSpline tinyspline::BSpline::elevateDegree(
size_t amount, real epsilon) const
{
tsBSpline data = ts_bspline_init();
tsStatus status;
if (ts_bspline_elevate_degree(&spline, amount, epsilon,
&data, &status)) {
throw std::runtime_error(status.message);
}
return BSpline(data);
}
tinyspline::BSpline tinyspline::BSpline::alignWith(
const BSpline &other, BSpline &otherAligned, real epsilon) const
{
tsBSpline data = ts_bspline_init();
tsBSpline deleteIf_Other_And_OtherAligned_AreDifferent =
otherAligned.spline;
tsStatus status;
if (ts_bspline_align(&spline, &other.spline, epsilon, &data,
&otherAligned.spline, &status)) {
throw std::runtime_error(status.message);
}
if (&other != &otherAligned)
ts_bspline_free(&deleteIf_Other_And_OtherAligned_AreDifferent);
return BSpline(data);
}
tinyspline::Morphism tinyspline::BSpline::morphTo(
const BSpline &other, real epsilon) const
{
return Morphism(*this, other, epsilon);
}
std::string tinyspline::BSpline::toString() const
{
Domain d = domain();
std::ostringstream oss;
oss << "BSpline{"
<< "dimension: " << dimension()
<< ", degree: " << degree()
<< ", domain: [" << d.min() << ", " << d.max() << "]"
<< ", control points: " << numControlPoints()
<< ", knots: " << ts_bspline_num_knots(&spline)
<< "}";
return oss.str();
}
/*! @name Morphism
*
* @{
*/
tinyspline::Morphism::Morphism(const BSpline &origin,
const BSpline &target,
real epsilon)
: m_origin(origin), m_target(target), m_epsilon(epsilon)
{
m_originAligned = origin.alignWith(target, m_targetAligned, epsilon);
// Make buffer compatible by copying one of the aligned splines.
m_buffer = m_originAligned;
}
tinyspline::BSpline
tinyspline::Morphism::eval(real t)
{
tsStatus status;
if (t <= 0) return m_origin;
if (t >= 1) return m_target;
if (ts_bspline_morph(&m_originAligned.spline,
&m_targetAligned.spline,
t, m_epsilon,
&m_buffer.spline, &status)) {
throw std::runtime_error(status.message);
}
return m_buffer;
}
tinyspline::BSpline
tinyspline::Morphism::origin() const
{
return m_origin;
}
tinyspline::BSpline
tinyspline::Morphism::target() const
{
return m_target;
}
tinyspline::real
tinyspline::Morphism::epsilon() const
{
return m_epsilon;
}
tinyspline::BSpline
tinyspline::Morphism::operator()(real t)
{
return eval(t);
}
std::string tinyspline::Morphism::toString() const
{
std::ostringstream oss;
oss << "Morphism{"
<< "buffer: " << m_buffer.toString()
<< ", epsilon: " << epsilon()
<< "}";
return oss.str();
}
/*! @} */
/*! @name ChordLenghts
* @{
*/
tinyspline::ChordLengths::ChordLengths()
: m_spline(),
m_knots(nullptr),
m_chordLengths(nullptr),
m_size(0)
{}
tinyspline::ChordLengths::ChordLengths(const BSpline &spline,
real *knots,
real *chordLengths,
size_t size)
: m_spline(spline),
m_knots(knots),
m_chordLengths(chordLengths),
m_size(size)
{}
tinyspline::ChordLengths::ChordLengths(const ChordLengths &other)
: m_spline(other.m_spline),
m_knots(nullptr),
m_chordLengths(nullptr),
m_size(other.m_size)
{
m_knots = new real[m_size];
std::copy(other.m_knots,
other.m_knots + m_size,
m_knots);
m_chordLengths = new real[m_size];
std::copy(other.m_chordLengths,
other.m_chordLengths + m_size,
m_chordLengths);
}
tinyspline::ChordLengths::ChordLengths(ChordLengths &&other)
: m_spline(),
m_knots(nullptr),
m_chordLengths(nullptr),
m_size(0)
{
*this = std::move(other);
}
tinyspline::ChordLengths::~ChordLengths()
{
delete [] m_knots;
delete [] m_chordLengths;
m_size = 0;
}
tinyspline::ChordLengths &
tinyspline::ChordLengths::operator=(const ChordLengths &other)
{
if (&other != this) {
real *knots = new real[other.m_size];
std::copy(other.m_knots,
other.m_knots + other.m_size,
knots);
real *chordLengths = new real[other.m_size];
std::copy(other.m_chordLengths,
other.m_chordLengths + other.m_size,
chordLengths);
delete [] m_knots;
delete [] m_chordLengths;
m_spline = other.m_spline;
m_knots = knots;
m_chordLengths = chordLengths;
m_size = other.m_size;
}
return *this;
}
tinyspline::ChordLengths &
tinyspline::ChordLengths::operator=(ChordLengths &&other)
{
if (&other != this) {
delete [] m_knots;
delete [] m_chordLengths;
m_spline = other.m_spline;
m_knots = other.m_knots;
m_chordLengths = other.m_chordLengths;
m_size = other.m_size;
other.m_spline = BSpline();
other.m_knots = nullptr;
other.m_chordLengths = nullptr;
other.m_size = 0;
}
return *this;
}
tinyspline::BSpline
tinyspline::ChordLengths::spline() const
{
return m_spline;
}
std::vector<tinyspline::real>
tinyspline::ChordLengths::knots() const
{
return std::vector<real>(m_knots,
m_knots + m_size);
}
std::vector<tinyspline::real>
tinyspline::ChordLengths::values() const
{
return std::vector<real>(m_chordLengths,
m_chordLengths + m_size);
}
size_t
tinyspline::ChordLengths::size() const
{
return m_size;
}
tinyspline::real
tinyspline::ChordLengths::arcLength() const
{
return m_size == 0 ? 0 : m_chordLengths[m_size - 1];
}
tinyspline::real
tinyspline::ChordLengths::lengthToKnot(real len) const
{
tsStatus status;
real knot;
if (ts_chord_lengths_length_to_knot(m_knots,
m_chordLengths,
m_size,
len,
&knot,
&status))
throw std::runtime_error(status.message);
return knot;
}
tinyspline::real
tinyspline::ChordLengths::tToKnot(real t) const
{
tsStatus status;
real knot;
if (ts_chord_lengths_t_to_knot(m_knots,
m_chordLengths,
m_size,
t,
&knot,
&status))
throw std::runtime_error(status.message);
return knot;
}
std::string
tinyspline::ChordLengths::toString() const
{
std::ostringstream oss;
oss << "ChordLengths{"
<< "spline: " << m_spline.toString()
<< ", values: " << m_size
<< "}";
return oss.str();
}
/*! @} */
#ifdef _MSC_VER
#pragma warning(pop)
#endif