diff --git a/deps_src/CMakeLists.txt b/deps_src/CMakeLists.txt index 11457c0d18..390cffad30 100644 --- a/deps_src/CMakeLists.txt +++ b/deps_src/CMakeLists.txt @@ -10,7 +10,6 @@ add_subdirectory(earcut) add_subdirectory(fast_float) add_subdirectory(nanosvg) add_subdirectory(nlohmann) -add_subdirectory(spline) # Header-only spline library add_subdirectory(stb_dxt) # Header-only STB DXT compression library # Static libraries diff --git a/deps_src/README_CMAKE_INTERFACES.md b/deps_src/README_CMAKE_INTERFACES.md index 78a806fc0b..6ab88c63af 100644 --- a/deps_src/README_CMAKE_INTERFACES.md +++ b/deps_src/README_CMAKE_INTERFACES.md @@ -24,18 +24,7 @@ target_link_libraries(your_target PRIVATE semver::semver) target_link_libraries(your_target PRIVATE hints) ``` -### 3. **spline** (Interface Library) -- **Type**: Interface library (header-only) -- **Target**: `spline` or `spline::spline` -- **Headers**: `spline.h` -- **Usage**: -```cmake -target_link_libraries(your_target PRIVATE spline) -# or -target_link_libraries(your_target PRIVATE spline::spline) -``` - -### 4. **stb_dxt** (Interface Library) +### 3. **stb_dxt** (Interface Library) - **Type**: Interface library (header-only) - **Target**: `stb_dxt` or `stb_dxt::stb_dxt` - **Headers**: `stb_dxt.h` @@ -53,10 +42,9 @@ target_link_libraries(your_target PRIVATE stb_dxt::stb_dxt) 1. **In your CMakeLists.txt**, simply link the library: ```cmake add_executable(my_app main.cpp) -target_link_libraries(my_app - PRIVATE +target_link_libraries(my_app + PRIVATE semver::semver # For version parsing - spline::spline # For spline interpolation stb_dxt::stb_dxt # For DXT texture compression hints # For hints functionality ) @@ -67,9 +55,6 @@ target_link_libraries(my_app // For semver #include -// For spline -#include - // For stb_dxt #include @@ -100,7 +85,6 @@ target_link_libraries(mycomponent PUBLIC semver::semver # Version handling is part of public API PRIVATE - spline::spline # Used internally for interpolation stb_dxt::stb_dxt # Used internally for texture compression ) diff --git a/deps_src/spline/CMakeLists.txt b/deps_src/spline/CMakeLists.txt deleted file mode 100644 index ee6d5c9cb3..0000000000 --- a/deps_src/spline/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -cmake_minimum_required(VERSION 3.13) -project(spline) - -# Create interface library for spline (header-only library) -add_library(spline INTERFACE) - -# Set include directories for the interface library -target_include_directories(spline SYSTEM - INTERFACE - $ - $ -) - -# Add compile features -target_compile_features(spline INTERFACE cxx_std_11) - -# Create an alias for consistent naming -add_library(spline::spline ALIAS spline) - -# Install headers if needed -install(FILES - spline.h - DESTINATION include/spline -) - -# Install the interface library -install(TARGETS spline - EXPORT splineTargets - INCLUDES DESTINATION include -) - -# Export the targets -install(EXPORT splineTargets - FILE splineTargets.cmake - NAMESPACE spline:: - DESTINATION lib/cmake/spline -) diff --git a/deps_src/spline/spline.h b/deps_src/spline/spline.h deleted file mode 100644 index 4b1ddd6134..0000000000 --- a/deps_src/spline/spline.h +++ /dev/null @@ -1,944 +0,0 @@ -/* - * spline.h - * - * simple cubic spline interpolation library without external - * dependencies - * - * --------------------------------------------------------------------- - * Copyright (C) 2011, 2014, 2016, 2021 Tino Kluge (ttk448 at gmail.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * --------------------------------------------------------------------- - * - */ - - -#ifndef TK_SPLINE_H -#define TK_SPLINE_H - -#include -#include -#include -#include -#include -#ifdef HAVE_SSTREAM -#include -#include -#endif // HAVE_SSTREAM - -// not ideal but disable unused-function warnings -// (we get them because we have implementations in the header file, -// and this is because we want to be able to quickly separate them -// into a cpp file if necessary) -#if !defined(_MSC_VER) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" -#endif - -namespace tk -{ - -// spline interpolation -class spline -{ -public: - // spline types - enum spline_type { - linear = 10, // linear interpolation - cspline = 30, // cubic splines (classical C^2) - cspline_hermite = 31 // cubic hermite splines (local, only C^1) - }; - - // boundary condition type for the spline end-points - enum bd_type { - first_deriv = 1, - second_deriv = 2, - not_a_knot = 3 - }; - -protected: - std::vector m_x,m_y; // x,y coordinates of points - // interpolation parameters - // f(x) = a_i + b_i*(x-x_i) + c_i*(x-x_i)^2 + d_i*(x-x_i)^3 - // where a_i = y_i, or else it won't go through grid points - std::vector m_b,m_c,m_d; // spline coefficients - double m_c0; // for left extrapolation - spline_type m_type; - bd_type m_left, m_right; - double m_left_value, m_right_value; - bool m_made_monotonic; - void set_coeffs_from_b(); // calculate c_i, d_i from b_i - size_t find_closest(double x) const; // closest idx so that m_x[idx]<=x - -public: - // default constructor: set boundary condition to be zero curvature - // at both ends, i.e. natural splines - spline(): m_type(cspline), - m_left(second_deriv), m_right(second_deriv), - m_left_value(0.0), m_right_value(0.0), m_made_monotonic(false) - { - ; - } - spline(const std::vector& X, const std::vector& Y, - spline_type type = cspline, - bool make_monotonic = false, - bd_type left = second_deriv, double left_value = 0.0, - bd_type right = second_deriv, double right_value = 0.0 - ): - m_type(type), - m_left(left), m_right(right), - m_left_value(left_value), m_right_value(right_value), - m_made_monotonic(false) // false correct here: make_monotonic() sets it - { - this->set_points(X,Y,m_type); - if(make_monotonic) { - this->make_monotonic(); - } - } - - - // modify boundary conditions: if called it must be before set_points() - void set_boundary(bd_type left, double left_value, - bd_type right, double right_value); - - // set all data points (cubic_spline=false means linear interpolation) - void set_points(const std::vector& x, - const std::vector& y, - spline_type type=cspline); - - // adjust coefficients so that the spline becomes piecewise monotonic - // where possible - // this is done by adjusting slopes at grid points by a non-negative - // factor and this will break C^2 - // this can also break boundary conditions if adjustments need to - // be made at the boundary points - // returns false if no adjustments have been made, true otherwise - bool make_monotonic(); - - // evaluates the spline at point x - double operator() (double x) const; - double deriv(int order, double x) const; - - // solves for all x so that: spline(x) = y - std::vector solve(double y, bool ignore_extrapolation=true) const; - - // returns the input data points - std::vector get_x() const { return m_x; } - std::vector get_y() const { return m_y; } - double get_x_min() const { assert(!m_x.empty()); return m_x.front(); } - double get_x_max() const { assert(!m_x.empty()); return m_x.back(); } - -#ifdef HAVE_SSTREAM - // spline info string, i.e. spline type, boundary conditions etc. - std::string info() const; -#endif // HAVE_SSTREAM - -}; - - - -namespace internal -{ - -// band matrix solver -class band_matrix -{ -private: - std::vector< std::vector > m_upper; // upper band - std::vector< std::vector > m_lower; // lower band -public: - band_matrix() {}; // constructor - band_matrix(int dim, int n_u, int n_l); // constructor - ~band_matrix() {}; // destructor - void resize(int dim, int n_u, int n_l); // init with dim,n_u,n_l - int dim() const; // matrix dimension - int num_upper() const - { - return (int)m_upper.size()-1; - } - int num_lower() const - { - return (int)m_lower.size()-1; - } - // access operator - double & operator () (int i, int j); // write - double operator () (int i, int j) const; // read - // we can store an additional diagonal (in m_lower) - double& saved_diag(int i); - double saved_diag(int i) const; - void lu_decompose(); - std::vector r_solve(const std::vector& b) const; - std::vector l_solve(const std::vector& b) const; - std::vector lu_solve(const std::vector& b, - bool is_lu_decomposed=false); - -}; - -double get_eps(); - -std::vector solve_cubic(double a, double b, double c, double d, - int newton_iter=0); - -} // namespace internal - - - - -// --------------------------------------------------------------------- -// implementation part, which could be separated into a cpp file -// --------------------------------------------------------------------- - -// spline implementation -// ----------------------- - -void spline::set_boundary(spline::bd_type left, double left_value, - spline::bd_type right, double right_value) -{ - assert(m_x.size()==0); // set_points() must not have happened yet - m_left=left; - m_right=right; - m_left_value=left_value; - m_right_value=right_value; -} - - -void spline::set_coeffs_from_b() -{ - assert(m_x.size()==m_y.size()); - assert(m_x.size()==m_b.size()); - assert(m_x.size()>2); - size_t n=m_b.size(); - if(m_c.size()!=n) - m_c.resize(n); - if(m_d.size()!=n) - m_d.resize(n); - - for(size_t i=0; i& x, - const std::vector& y, - spline_type type) -{ - assert(x.size()==y.size()); - assert(x.size()>=3); - // not-a-knot with 3 points has many solutions - if(m_left==not_a_knot || m_right==not_a_knot) - assert(x.size()>=4); - m_type=type; - m_made_monotonic=false; - m_x=x; - m_y=y; - int n = (int) x.size(); - // check strict monotonicity of input vector x - for(int i=0; i rhs(n); - for(int i=1; i2); - bool modified = false; - const int n=(int)m_x.size(); - // make sure: input data monotonic increasing --> b_i>=0 - // input data monotonic decreasing --> b_i<=0 - for(int i=0; i=m_y[i]) && (m_y[i]>=m_y[ip1]) && m_b[i]>0.0) ) { - modified=true; - m_b[i]=0.0; - } - } - // if input data is monotonic (b[i], b[i+1], avg have all the same sign) - // ensure a sufficient criteria for monotonicity is satisfied: - // sqrt(b[i]^2+b[i+1]^2) <= 3 |avg|, with avg=(y[i+1]-y[i])/h, - for(int i=0; i=0.0 && m_b[i+1]>=0.0 && avg>0.0) || - (m_b[i]<=0.0 && m_b[i+1]<=0.0 && avg<0.0) ) { - // input data is monotonic - double r = sqrt(m_b[i]*m_b[i]+m_b[i+1]*m_b[i+1])/std::fabs(avg); - if(r>3.0) { - // sufficient criteria for monotonicity: r<=3 - // adjust b[i] and b[i+1] - modified=true; - m_b[i] *= (3.0/r); - m_b[i+1] *= (3.0/r); - } - } - } - - if(modified==true) { - set_coeffs_from_b(); - m_made_monotonic=true; - } - - return modified; -} - -// return the closest idx so that m_x[idx] <= x (return 0 if x::const_iterator it; - it=std::upper_bound(m_x.begin(),m_x.end(),x); // *it > x - size_t idx = std::max( int(it-m_x.begin())-1, 0); // m_x[idx] <= x - return idx; -} - -double spline::operator() (double x) const -{ - // polynomial evaluation using Horner's scheme - // TODO: consider more numerically accurate algorithms, e.g.: - // - Clenshaw - // - Even-Odd method by A.C.R. Newbery - // - Compensated Horner Scheme - size_t n=m_x.size(); - size_t idx=find_closest(x); - - double h=x-m_x[idx]; - double interpol; - if(xm_x[n-1]) { - // extrapolation to the right - interpol=(m_c[n-1]*h + m_b[n-1])*h + m_y[n-1]; - } else { - // interpolation - interpol=((m_d[idx]*h + m_c[idx])*h + m_b[idx])*h + m_y[idx]; - } - return interpol; -} - -double spline::deriv(int order, double x) const -{ - assert(order>0); - size_t n=m_x.size(); - size_t idx = find_closest(x); - - double h=x-m_x[idx]; - double interpol; - if(xm_x[n-1]) { - // extrapolation to the right - switch(order) { - case 1: - interpol=2.0*m_c[n-1]*h + m_b[n-1]; - break; - case 2: - interpol=2.0*m_c[n-1]; - break; - default: - interpol=0.0; - break; - } - } else { - // interpolation - switch(order) { - case 1: - interpol=(3.0*m_d[idx]*h + 2.0*m_c[idx])*h + m_b[idx]; - break; - case 2: - interpol=6.0*m_d[idx]*h + 2.0*m_c[idx]; - break; - case 3: - interpol=6.0*m_d[idx]; - break; - default: - interpol=0.0; - break; - } - } - return interpol; -} - -std::vector spline::solve(double y, bool ignore_extrapolation) const -{ - std::vector x; // roots for the entire spline - std::vector root; // roots for each piecewise cubic - const size_t n=m_x.size(); - - // left extrapolation - if(ignore_extrapolation==false) { - root = internal::solve_cubic(m_y[0]-y,m_b[0],m_c0,0.0,1); - for(size_t j=0; j0) ? (m_x[i]-m_x[i-1]) : 0.0; - double eps = internal::get_eps()*512.0*std::min(h,1.0); - if( (-eps<=root[j]) && (root[j]0 && x.back()+eps > new_root) { - x.back()=new_root; // avoid spurious duplicate roots - } else { - x.push_back(new_root); - } - } - } - } - - // right extrapolation - if(ignore_extrapolation==false) { - root = internal::solve_cubic(m_y[n-1]-y,m_b[n-1],m_c[n-1],0.0,1); - for(size_t j=0; j0); - assert(n_u>=0); - assert(n_l>=0); - m_upper.resize(n_u+1); - m_lower.resize(n_l+1); - for(size_t i=0; i0) { - return (int)m_upper[0].size(); - } else { - return 0; - } -} - - -// defines the new operator (), so that we can access the elements -// by A(i,j), index going from i=0,...,dim()-1 -double & band_matrix::operator () (int i, int j) -{ - int k=j-i; // what band is the entry - assert( (i>=0) && (i=0) && (j diagonal, k<0 lower left part, k>0 upper right part - if(k>=0) return m_upper[k][i]; - else return m_lower[-k][i]; -} -double band_matrix::operator () (int i, int j) const -{ - int k=j-i; // what band is the entry - assert( (i>=0) && (i=0) && (j diagonal, k<0 lower left part, k>0 upper right part - if(k>=0) return m_upper[k][i]; - else return m_lower[-k][i]; -} -// second diag (used in LU decomposition), saved in m_lower -double band_matrix::saved_diag(int i) const -{ - assert( (i>=0) && (i=0) && (idim(); i++) { - assert(this->operator()(i,i)!=0.0); - this->saved_diag(i)=1.0/this->operator()(i,i); - j_min=std::max(0,i-this->num_lower()); - j_max=std::min(this->dim()-1,i+this->num_upper()); - for(int j=j_min; j<=j_max; j++) { - this->operator()(i,j) *= this->saved_diag(i); - } - this->operator()(i,i)=1.0; // prevents rounding errors - } - - // Gauss LR-Decomposition - for(int k=0; kdim(); k++) { - i_max=std::min(this->dim()-1,k+this->num_lower()); // num_lower not a mistake! - for(int i=k+1; i<=i_max; i++) { - assert(this->operator()(k,k)!=0.0); - x=-this->operator()(i,k)/this->operator()(k,k); - this->operator()(i,k)=-x; // assembly part of L - j_max=std::min(this->dim()-1,k+this->num_upper()); - for(int j=k+1; j<=j_max; j++) { - // assembly part of R - this->operator()(i,j)=this->operator()(i,j)+x*this->operator()(k,j); - } - } - } -} -// solves Ly=b -std::vector band_matrix::l_solve(const std::vector& b) const -{ - assert( this->dim()==(int)b.size() ); - std::vector x(this->dim()); - int j_start; - double sum; - for(int i=0; idim(); i++) { - sum=0; - j_start=std::max(0,i-this->num_lower()); - for(int j=j_start; joperator()(i,j)*x[j]; - x[i]=(b[i]*this->saved_diag(i)) - sum; - } - return x; -} -// solves Rx=y -std::vector band_matrix::r_solve(const std::vector& b) const -{ - assert( this->dim()==(int)b.size() ); - std::vector x(this->dim()); - int j_stop; - double sum; - for(int i=this->dim()-1; i>=0; i--) { - sum=0; - j_stop=std::min(this->dim()-1,i+this->num_upper()); - for(int j=i+1; j<=j_stop; j++) sum += this->operator()(i,j)*x[j]; - x[i]=( b[i] - sum ) / this->operator()(i,i); - } - return x; -} - -std::vector band_matrix::lu_solve(const std::vector& b, - bool is_lu_decomposed) -{ - assert( this->dim()==(int)b.size() ); - std::vector x,y; - if(is_lu_decomposed==false) { - this->lu_decompose(); - } - y=this->l_solve(b); - x=this->r_solve(y); - return x; -} - -// machine precision of a double, i.e. the successor of 1 is 1+eps -double get_eps() -{ - //return std::numeric_limits::epsilon(); // __DBL_EPSILON__ - return 2.2204460492503131e-16; // 2^-52 -} - -// solutions for a + b*x = 0 -std::vector solve_linear(double a, double b) -{ - std::vector x; // roots - if(b==0.0) { - if(a==0.0) { - // 0*x = 0 - x.resize(1); - x[0] = 0.0; // any x solves it but we need to pick one - return x; - } else { - // 0*x + ... = 0, no solution - return x; - } - } else { - x.resize(1); - x[0] = -a/b; - return x; - } -} - -// solutions for a + b*x + c*x^2 = 0 -std::vector solve_quadratic(double a, double b, double c, - int newton_iter=0) -{ - if(c==0.0) { - return solve_linear(a,b); - } - // rescale so that we solve x^2 + 2p x + q = (x+p)^2 + q - p^2 = 0 - double p=0.5*b/c; - double q=a/c; - double discr = p*p-q; - const double eps=0.5*internal::get_eps(); - double discr_err = (6.0*(p*p)+3.0*fabs(q)+fabs(discr))*eps; - - std::vector x; // roots - if(fabs(discr)<=discr_err) { - // discriminant is zero --> one root - x.resize(1); - x[0] = -p; - } else if(discr<0) { - // no root - } else { - // two roots - x.resize(2); - x[0] = -p - sqrt(discr); - x[1] = -p + sqrt(discr); - } - - // improve solution via newton steps - for(size_t i=0; i1e-8) { - x[i] -= f/f1; - } - } - } - - return x; -} - -// solutions for the cubic equation: a + b*x +c*x^2 + d*x^3 = 0 -// this is a naive implementation of the analytic solution without -// optimisation for speed or numerical accuracy -// newton_iter: number of newton iterations to improve analytical solution -// see also -// gsl: gsl_poly_solve_cubic() in solve_cubic.c -// octave: roots.m - via eigenvalues of the Frobenius companion matrix -std::vector solve_cubic(double a, double b, double c, double d, - int newton_iter) -{ - if(d==0.0) { - return solve_quadratic(a,b,c,newton_iter); - } - - // convert to normalised form: a + bx + cx^2 + x^3 = 0 - if(d!=1.0) { - a/=d; - b/=d; - c/=d; - } - - // convert to depressed cubic: z^3 - 3pz - 2q = 0 - // via substitution: z = x + c/3 - std::vector z; // roots of the depressed cubic - double p = -(1.0/3.0)*b + (1.0/9.0)*(c*c); - double r = 2.0*(c*c)-9.0*b; - double q = -0.5*a - (1.0/54.0)*(c*r); - double discr=p*p*p-q*q; // discriminant - // calculating numerical round-off errors with assumptions: - // - each operation is precise but each intermediate result x - // when stored has max error of x*eps - // - only multiplication with a power of 2 introduces no new error - // - a,b,c,d and some fractions (e.g. 1/3) have rounding errors eps - // - p_err << |p|, q_err << |q|, ... (this is violated in rare cases) - // would be more elegant to use boost::numeric::interval - const double eps = internal::get_eps(); - double p_err = eps*((3.0/3.0)*fabs(b)+(4.0/9.0)*(c*c)+fabs(p)); - double r_err = eps*(6.0*(c*c)+18.0*fabs(b)+fabs(r)); - double q_err = 0.5*fabs(a)*eps + (1.0/54.0)*fabs(c)*(r_err+fabs(r)*3.0*eps) - + fabs(q)*eps; - double discr_err = (p*p) * (3.0*p_err + fabs(p)*2.0*eps) - + fabs(q) * (2.0*q_err + fabs(q)*eps) + fabs(discr)*eps; - - // depending on the discriminant we get different solutions - if(fabs(discr)<=discr_err) { - // discriminant zero: one or two real roots - if(fabs(p)<=p_err) { - // p and q are zero: single root - z.resize(1); - z[0] = 0.0; // triple root - } else { - z.resize(2); - z[0] = 2.0*q/p; // single root - z[1] = -0.5*z[0]; // double root - } - } else if(discr>0) { - // three real roots: via trigonometric solution - z.resize(3); - double ac = (1.0/3.0) * acos( q/(p*sqrt(p)) ); - double sq = 2.0*sqrt(p); - z[0] = sq * cos(ac); - z[1] = sq * cos(ac-2.0*M_PI/3.0); - z[2] = sq * cos(ac-4.0*M_PI/3.0); - } else if (discr<0.0) { - // single real root: via Cardano's fromula - z.resize(1); - double sgnq = (q >= 0 ? 1 : -1); - double basis = fabs(q) + sqrt(-discr); - double C = sgnq * pow(basis, 1.0/3.0); // c++11 has std::cbrt() - z[0] = C + p/C; - } - for(size_t i=0; i1e-8) { - z[i] -= f/f1; - } - } - } - // ensure if a=0 we get exactly x=0 as root - // TODO: remove this fudge - if(a==0.0) { - assert(z.size()>0); // cubic should always have at least one root - double xmin=fabs(z[0]); - size_t imin=0; - for(size_t i=1; ifabs(z[i])) { - xmin=fabs(z[i]); - imin=i; - } - } - z[imin]=0.0; // replace the smallest absolute value with 0 - } - std::sort(z.begin(), z.end()); - return z; -} - - -} // namespace internal - - -} // namespace tk - - -#if !defined(_MSC_VER) -#pragma GCC diagnostic pop -#endif - -#endif /* TK_SPLINE_H */ diff --git a/src/libslic3r/GCode/SmallAreaInfillFlowCompensator.cpp b/src/libslic3r/GCode/SmallAreaInfillFlowCompensator.cpp index e472b20794..20377cfe8d 100644 --- a/src/libslic3r/GCode/SmallAreaInfillFlowCompensator.cpp +++ b/src/libslic3r/GCode/SmallAreaInfillFlowCompensator.cpp @@ -15,7 +15,6 @@ #include "../PrintConfig.hpp" #include "SmallAreaInfillFlowCompensator.hpp" -#include "spline/spline.h" #include namespace Slic3r { @@ -47,37 +46,43 @@ SmallAreaInfillFlowCompensator::SmallAreaInfillFlowCompensator(const Slic3r::GCo } } catch (...) { std::stringstream ss; - ss << "Error parsing data point in small area infill compensation model:" << line << std::endl; + ss << "Small Area Flow Compensation: Error parsing data point in small area infill compensation model:" << line << std::endl; throw Slic3r::InvalidArgument(ss.str()); } } } - for (int i = 0; i < eLengths.size(); i++) { + for (size_t i = 0; i < eLengths.size(); i++) { if (i == 0) { if (!nearly_equal(eLengths[i], 0.0)) { - throw Slic3r::InvalidArgument("First extrusion length for small area infill compensation model must be 0"); + throw Slic3r::InvalidArgument("Small Area Flow Compensation: First extrusion length for small area infill compensation model must be 0"); } } else { if (nearly_equal(eLengths[i], 0.0)) { - throw Slic3r::InvalidArgument("Only the first extrusion length for small area infill compensation model can be 0"); + throw Slic3r::InvalidArgument("Small Area Flow Compensation: Only the first extrusion length for small area infill compensation model can be 0"); } if (eLengths[i] <= eLengths[i - 1]) { - throw Slic3r::InvalidArgument("Extrusion lengths for subsequent points must be increasing"); + throw Slic3r::InvalidArgument("Small Area Flow Compensation: Extrusion lengths for subsequent points must be increasing"); } } } - if (!flowComps.empty() && !nearly_equal(flowComps.back(), 1.0)) { - throw Slic3r::InvalidArgument("Final compensation factor for small area infill flow compensation model must be 1.0"); + for (size_t i = 1; i < flowComps.size(); ++i) { + if (flowComps[i] <= flowComps[i - 1]) { + throw Slic3r::InvalidArgument("Small Area Flow Compensation: Flow compensation factors must strictly increase with extrusion length"); + } } - flowModel = std::make_unique(); - flowModel->set_points(eLengths, flowComps); + if (!flowComps.empty() && !nearly_equal(flowComps.back(), 1.0)) { + throw Slic3r::InvalidArgument("Small Area Flow Compensation: Final compensation factor for small area infill flow compensation model must be 1.0"); + } + + flowModel = std::make_unique(eLengths, flowComps); } catch (std::exception& e) { BOOST_LOG_TRIVIAL(error) << "Error parsing small area infill compensation model: " << e.what(); + throw; } } @@ -92,7 +97,7 @@ double SmallAreaInfillFlowCompensator::flow_comp_model(const double line_length) return 1.0; } - return (*flowModel)(line_length); + return flowModel->interpolate(line_length); } double SmallAreaInfillFlowCompensator::modify_flow(const double line_length, const double dE, const ExtrusionRole role) diff --git a/src/libslic3r/GCode/SmallAreaInfillFlowCompensator.hpp b/src/libslic3r/GCode/SmallAreaInfillFlowCompensator.hpp index 1bfa5149f7..de1df98170 100644 --- a/src/libslic3r/GCode/SmallAreaInfillFlowCompensator.hpp +++ b/src/libslic3r/GCode/SmallAreaInfillFlowCompensator.hpp @@ -4,12 +4,9 @@ #include "../libslic3r.h" #include "../PrintConfig.hpp" #include "../ExtrusionEntity.hpp" +#include "PchipInterpolatorHelper.hpp" #include -namespace tk { -class spline; -} // namespace tk - namespace Slic3r { class SmallAreaInfillFlowCompensator @@ -26,8 +23,7 @@ private: std::vector eLengths; std::vector flowComps; - // TODO: Cubic Spline - std::unique_ptr flowModel; + std::unique_ptr flowModel; double flow_comp_model(const double line_length);