#define TINYSPLINE_EXPORT #include "tinysplinecxx.h" #include #include #include #include #include /* 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 #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::Vec2::values() const { return std::vector({ 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::Vec3::values() const { return std::vector({ 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::Vec4::values() const { return std::vector({ 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::DeBoorNet::points() const { const real *points = ts_deboornet_points_ptr(&net); size_t len = ts_deboornet_len_points(&net); return std::vector(points, points + len); } std::vector tinyspline::DeBoorNet::result() const { const real *result = ts_deboornet_result_ptr(&net); size_t len = ts_deboornet_len_result(&net); return std::vector(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 *first, std::vector *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::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(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::BSpline::knots() const { const real *knots = ts_bspline_knots_ptr(&spline); size_t num = ts_bspline_num_knots(&spline); return std::vector(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 &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 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 &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::ChordLengths::knots() const { return std::vector(m_knots, m_knots + m_size); } std::vector tinyspline::ChordLengths::values() const { return std::vector(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