mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2026-01-03 05:20:29 -07:00
Small area flow compensator improvements (#11716)
* Replace spline with PchipInterpolatorHelper in flow compensator Swapped out the tk::spline implementation for PchipInterpolatorHelper in SmallAreaInfillFlowCompensator. Updated member types and method calls to use the new interpolator for improved flow compensation modeling. * Enforce strictly increasing flow compensation factors Added a check to ensure that flow compensation factors in SmallAreaInfillFlowCompensator strictly increase with extrusion length, throwing an exception if this condition is not met. This improves input validation and prevents invalid compensation models. * Add context to Small Area Flow Compensation errors Prefixed error messages in SmallAreaInfillFlowCompensator with 'Small Area Flow Compensation' for improved clarity and debugging. Also rethrows exceptions after logging to ensure proper error propagation. * Remove spline library from dependencies Eliminated the spline header-only library from the project by deleting its CMake configuration and header file, and updating documentation and build scripts to remove references to spline. This streamlines the dependencies and build process.
This commit is contained in:
parent
2877c6032d
commit
81dd153798
6 changed files with 21 additions and 1018 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 <semver.h>
|
||||
|
||||
// For spline
|
||||
#include <spline.h>
|
||||
|
||||
// For stb_dxt
|
||||
#include <stb_dxt.h>
|
||||
|
||||
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
)
|
||||
|
||||
# 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
|
||||
)
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef TK_SPLINE_H
|
||||
#define TK_SPLINE_H
|
||||
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#ifdef HAVE_SSTREAM
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#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<double> 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<double> 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<double>& X, const std::vector<double>& 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<double>& x,
|
||||
const std::vector<double>& 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<double> solve(double y, bool ignore_extrapolation=true) const;
|
||||
|
||||
// returns the input data points
|
||||
std::vector<double> get_x() const { return m_x; }
|
||||
std::vector<double> 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<double> > m_upper; // upper band
|
||||
std::vector< std::vector<double> > 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<double> r_solve(const std::vector<double>& b) const;
|
||||
std::vector<double> l_solve(const std::vector<double>& b) const;
|
||||
std::vector<double> lu_solve(const std::vector<double>& b,
|
||||
bool is_lu_decomposed=false);
|
||||
|
||||
};
|
||||
|
||||
double get_eps();
|
||||
|
||||
std::vector<double> 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<n-1; i++) {
|
||||
const double h = m_x[i+1]-m_x[i];
|
||||
// from continuity and differentiability condition
|
||||
m_c[i] = ( 3.0*(m_y[i+1]-m_y[i])/h - (2.0*m_b[i]+m_b[i+1]) ) / h;
|
||||
// from differentiability condition
|
||||
m_d[i] = ( (m_b[i+1]-m_b[i])/(3.0*h) - 2.0/3.0*m_c[i] ) / h;
|
||||
}
|
||||
|
||||
// for left extrapolation coefficients
|
||||
m_c0 = (m_left==first_deriv) ? 0.0 : m_c[0];
|
||||
}
|
||||
|
||||
void spline::set_points(const std::vector<double>& x,
|
||||
const std::vector<double>& 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<n-1; i++) {
|
||||
assert(m_x[i]<m_x[i+1]);
|
||||
}
|
||||
|
||||
|
||||
if(type==linear) {
|
||||
// linear interpolation
|
||||
m_d.resize(n);
|
||||
m_c.resize(n);
|
||||
m_b.resize(n);
|
||||
for(int i=0; i<n-1; i++) {
|
||||
m_d[i]=0.0;
|
||||
m_c[i]=0.0;
|
||||
m_b[i]=(m_y[i+1]-m_y[i])/(m_x[i+1]-m_x[i]);
|
||||
}
|
||||
// ignore boundary conditions, set slope equal to the last segment
|
||||
m_b[n-1]=m_b[n-2];
|
||||
m_c[n-1]=0.0;
|
||||
m_d[n-1]=0.0;
|
||||
} else if(type==cspline) {
|
||||
// classical cubic splines which are C^2 (twice cont differentiable)
|
||||
// this requires solving an equation system
|
||||
|
||||
// setting up the matrix and right hand side of the equation system
|
||||
// for the parameters b[]
|
||||
int n_upper = (m_left == spline::not_a_knot) ? 2 : 1;
|
||||
int n_lower = (m_right == spline::not_a_knot) ? 2 : 1;
|
||||
internal::band_matrix A(n,n_upper,n_lower);
|
||||
std::vector<double> rhs(n);
|
||||
for(int i=1; i<n-1; i++) {
|
||||
A(i,i-1)=1.0/3.0*(x[i]-x[i-1]);
|
||||
A(i,i)=2.0/3.0*(x[i+1]-x[i-1]);
|
||||
A(i,i+1)=1.0/3.0*(x[i+1]-x[i]);
|
||||
rhs[i]=(y[i+1]-y[i])/(x[i+1]-x[i]) - (y[i]-y[i-1])/(x[i]-x[i-1]);
|
||||
}
|
||||
// boundary conditions
|
||||
if(m_left == spline::second_deriv) {
|
||||
// 2*c[0] = f''
|
||||
A(0,0)=2.0;
|
||||
A(0,1)=0.0;
|
||||
rhs[0]=m_left_value;
|
||||
} else if(m_left == spline::first_deriv) {
|
||||
// b[0] = f', needs to be re-expressed in terms of c:
|
||||
// (2c[0]+c[1])(x[1]-x[0]) = 3 ((y[1]-y[0])/(x[1]-x[0]) - f')
|
||||
A(0,0)=2.0*(x[1]-x[0]);
|
||||
A(0,1)=1.0*(x[1]-x[0]);
|
||||
rhs[0]=3.0*((y[1]-y[0])/(x[1]-x[0])-m_left_value);
|
||||
} else if(m_left == spline::not_a_knot) {
|
||||
// f'''(x[1]) exists, i.e. d[0]=d[1], or re-expressed in c:
|
||||
// -h1*c[0] + (h0+h1)*c[1] - h0*c[2] = 0
|
||||
A(0,0) = -(x[2]-x[1]);
|
||||
A(0,1) = x[2]-x[0];
|
||||
A(0,2) = -(x[1]-x[0]);
|
||||
rhs[0] = 0.0;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
if(m_right == spline::second_deriv) {
|
||||
// 2*c[n-1] = f''
|
||||
A(n-1,n-1)=2.0;
|
||||
A(n-1,n-2)=0.0;
|
||||
rhs[n-1]=m_right_value;
|
||||
} else if(m_right == spline::first_deriv) {
|
||||
// b[n-1] = f', needs to be re-expressed in terms of c:
|
||||
// (c[n-2]+2c[n-1])(x[n-1]-x[n-2])
|
||||
// = 3 (f' - (y[n-1]-y[n-2])/(x[n-1]-x[n-2]))
|
||||
A(n-1,n-1)=2.0*(x[n-1]-x[n-2]);
|
||||
A(n-1,n-2)=1.0*(x[n-1]-x[n-2]);
|
||||
rhs[n-1]=3.0*(m_right_value-(y[n-1]-y[n-2])/(x[n-1]-x[n-2]));
|
||||
} else if(m_right == spline::not_a_knot) {
|
||||
// f'''(x[n-2]) exists, i.e. d[n-3]=d[n-2], or re-expressed in c:
|
||||
// -h_{n-2}*c[n-3] + (h_{n-3}+h_{n-2})*c[n-2] - h_{n-3}*c[n-1] = 0
|
||||
A(n-1,n-3) = -(x[n-1]-x[n-2]);
|
||||
A(n-1,n-2) = x[n-1]-x[n-3];
|
||||
A(n-1,n-1) = -(x[n-2]-x[n-3]);
|
||||
rhs[0] = 0.0;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// solve the equation system to obtain the parameters c[]
|
||||
m_c=A.lu_solve(rhs);
|
||||
|
||||
// calculate parameters b[] and d[] based on c[]
|
||||
m_d.resize(n);
|
||||
m_b.resize(n);
|
||||
for(int i=0; i<n-1; i++) {
|
||||
m_d[i]=1.0/3.0*(m_c[i+1]-m_c[i])/(x[i+1]-x[i]);
|
||||
m_b[i]=(y[i+1]-y[i])/(x[i+1]-x[i])
|
||||
- 1.0/3.0*(2.0*m_c[i]+m_c[i+1])*(x[i+1]-x[i]);
|
||||
}
|
||||
// for the right extrapolation coefficients (zero cubic term)
|
||||
// f_{n-1}(x) = y_{n-1} + b*(x-x_{n-1}) + c*(x-x_{n-1})^2
|
||||
double h=x[n-1]-x[n-2];
|
||||
// m_c[n-1] is determined by the boundary condition
|
||||
m_d[n-1]=0.0;
|
||||
m_b[n-1]=3.0*m_d[n-2]*h*h+2.0*m_c[n-2]*h+m_b[n-2]; // = f'_{n-2}(x_{n-1})
|
||||
if(m_right==first_deriv)
|
||||
m_c[n-1]=0.0; // force linear extrapolation
|
||||
|
||||
} else if(type==cspline_hermite) {
|
||||
// hermite cubic splines which are C^1 (cont. differentiable)
|
||||
// and derivatives are specified on each grid point
|
||||
// (here we use 3-point finite differences)
|
||||
m_b.resize(n);
|
||||
m_c.resize(n);
|
||||
m_d.resize(n);
|
||||
// set b to match 1st order derivative finite difference
|
||||
for(int i=1; i<n-1; i++) {
|
||||
const double h = m_x[i+1]-m_x[i];
|
||||
const double hl = m_x[i]-m_x[i-1];
|
||||
m_b[i] = -h/(hl*(hl+h))*m_y[i-1] + (h-hl)/(hl*h)*m_y[i]
|
||||
+ hl/(h*(hl+h))*m_y[i+1];
|
||||
}
|
||||
// boundary conditions determine b[0] and b[n-1]
|
||||
if(m_left==first_deriv) {
|
||||
m_b[0]=m_left_value;
|
||||
} else if(m_left==second_deriv) {
|
||||
const double h = m_x[1]-m_x[0];
|
||||
m_b[0]=0.5*(-m_b[1]-0.5*m_left_value*h+3.0*(m_y[1]-m_y[0])/h);
|
||||
} else if(m_left == not_a_knot) {
|
||||
// f''' continuous at x[1]
|
||||
const double h0 = m_x[1]-m_x[0];
|
||||
const double h1 = m_x[2]-m_x[1];
|
||||
m_b[0]= -m_b[1] + 2.0*(m_y[1]-m_y[0])/h0
|
||||
+ h0*h0/(h1*h1)*(m_b[1]+m_b[2]-2.0*(m_y[2]-m_y[1])/h1);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
if(m_right==first_deriv) {
|
||||
m_b[n-1]=m_right_value;
|
||||
m_c[n-1]=0.0;
|
||||
} else if(m_right==second_deriv) {
|
||||
const double h = m_x[n-1]-m_x[n-2];
|
||||
m_b[n-1]=0.5*(-m_b[n-2]+0.5*m_right_value*h+3.0*(m_y[n-1]-m_y[n-2])/h);
|
||||
m_c[n-1]=0.5*m_right_value;
|
||||
} else if(m_right == not_a_knot) {
|
||||
// f''' continuous at x[n-2]
|
||||
const double h0 = m_x[n-2]-m_x[n-3];
|
||||
const double h1 = m_x[n-1]-m_x[n-2];
|
||||
m_b[n-1]= -m_b[n-2] + 2.0*(m_y[n-1]-m_y[n-2])/h1 + h1*h1/(h0*h0)
|
||||
*(m_b[n-3]+m_b[n-2]-2.0*(m_y[n-2]-m_y[n-3])/h0);
|
||||
// f'' continuous at x[n-1]: c[n-1] = 3*d[n-2]*h[n-2] + c[n-1]
|
||||
m_c[n-1]=(m_b[n-2]+2.0*m_b[n-1])/h1-3.0*(m_y[n-1]-m_y[n-2])/(h1*h1);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
m_d[n-1]=0.0;
|
||||
|
||||
// parameters c and d are determined by continuity and differentiability
|
||||
set_coeffs_from_b();
|
||||
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// for left extrapolation coefficients
|
||||
m_c0 = (m_left==first_deriv) ? 0.0 : m_c[0];
|
||||
}
|
||||
|
||||
bool spline::make_monotonic()
|
||||
{
|
||||
assert(m_x.size()==m_y.size());
|
||||
assert(m_x.size()==m_b.size());
|
||||
assert(m_x.size()>2);
|
||||
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<n; i++) {
|
||||
int im1 = std::max(i-1, 0);
|
||||
int ip1 = std::min(i+1, n-1);
|
||||
if( ((m_y[im1]<=m_y[i]) && (m_y[i]<=m_y[ip1]) && m_b[i]<0.0) ||
|
||||
((m_y[im1]>=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<n-1; i++) {
|
||||
double h = m_x[i+1]-m_x[i];
|
||||
double avg = (m_y[i+1]-m_y[i])/h;
|
||||
if( avg==0.0 && (m_b[i]!=0.0 || m_b[i+1]!=0.0) ) {
|
||||
modified=true;
|
||||
m_b[i]=0.0;
|
||||
m_b[i+1]=0.0;
|
||||
} else if( (m_b[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<m_x[0])
|
||||
size_t spline::find_closest(double x) const
|
||||
{
|
||||
std::vector<double>::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(x<m_x[0]) {
|
||||
// extrapolation to the left
|
||||
interpol=(m_c0*h + m_b[0])*h + m_y[0];
|
||||
} else if(x>m_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(x<m_x[0]) {
|
||||
// extrapolation to the left
|
||||
switch(order) {
|
||||
case 1:
|
||||
interpol=2.0*m_c0*h + m_b[0];
|
||||
break;
|
||||
case 2:
|
||||
interpol=2.0*m_c0;
|
||||
break;
|
||||
default:
|
||||
interpol=0.0;
|
||||
break;
|
||||
}
|
||||
} else if(x>m_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<double> spline::solve(double y, bool ignore_extrapolation) const
|
||||
{
|
||||
std::vector<double> x; // roots for the entire spline
|
||||
std::vector<double> 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; j<root.size(); j++) {
|
||||
if(root[j]<0.0) {
|
||||
x.push_back(m_x[0]+root[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// brute force check if piecewise cubic has roots in their resp. segment
|
||||
// TODO: make more efficient
|
||||
for(size_t i=0; i<n-1; i++) {
|
||||
root = internal::solve_cubic(m_y[i]-y,m_b[i],m_c[i],m_d[i],1);
|
||||
for(size_t j=0; j<root.size(); j++) {
|
||||
double h = (i>0) ? (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]<m_x[i+1]-m_x[i]) ) {
|
||||
double new_root = m_x[i]+root[j];
|
||||
if(x.size()>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; j<root.size(); j++) {
|
||||
if(0.0<=root[j]) {
|
||||
x.push_back(m_x[n-1]+root[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return x;
|
||||
};
|
||||
|
||||
|
||||
#ifdef HAVE_SSTREAM
|
||||
std::string spline::info() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "type " << m_type << ", left boundary deriv " << m_left << " = ";
|
||||
ss << m_left_value << ", right boundary deriv " << m_right << " = ";
|
||||
ss << m_right_value << std::endl;
|
||||
if(m_made_monotonic) {
|
||||
ss << "(spline has been adjusted for piece-wise monotonicity)";
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
#endif // HAVE_SSTREAM
|
||||
|
||||
|
||||
namespace internal
|
||||
{
|
||||
|
||||
// band_matrix implementation
|
||||
// -------------------------
|
||||
|
||||
band_matrix::band_matrix(int dim, int n_u, int n_l)
|
||||
{
|
||||
resize(dim, n_u, n_l);
|
||||
}
|
||||
void band_matrix::resize(int dim, int n_u, int n_l)
|
||||
{
|
||||
assert(dim>0);
|
||||
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; i<m_upper.size(); i++) {
|
||||
m_upper[i].resize(dim);
|
||||
}
|
||||
for(size_t i=0; i<m_lower.size(); i++) {
|
||||
m_lower[i].resize(dim);
|
||||
}
|
||||
}
|
||||
int band_matrix::dim() const
|
||||
{
|
||||
if(m_upper.size()>0) {
|
||||
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<dim()) && (j>=0) && (j<dim()) );
|
||||
assert( (-num_lower()<=k) && (k<=num_upper()) );
|
||||
// k=0 -> 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<dim()) && (j>=0) && (j<dim()) );
|
||||
assert( (-num_lower()<=k) && (k<=num_upper()) );
|
||||
// k=0 -> 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<dim()) );
|
||||
return m_lower[0][i];
|
||||
}
|
||||
double & band_matrix::saved_diag(int i)
|
||||
{
|
||||
assert( (i>=0) && (i<dim()) );
|
||||
return m_lower[0][i];
|
||||
}
|
||||
|
||||
// LR-Decomposition of a band matrix
|
||||
void band_matrix::lu_decompose()
|
||||
{
|
||||
int i_max,j_max;
|
||||
int j_min;
|
||||
double x;
|
||||
|
||||
// preconditioning
|
||||
// normalize column i so that a_ii=1
|
||||
for(int i=0; i<this->dim(); 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; k<this->dim(); 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<double> band_matrix::l_solve(const std::vector<double>& b) const
|
||||
{
|
||||
assert( this->dim()==(int)b.size() );
|
||||
std::vector<double> x(this->dim());
|
||||
int j_start;
|
||||
double sum;
|
||||
for(int i=0; i<this->dim(); i++) {
|
||||
sum=0;
|
||||
j_start=std::max(0,i-this->num_lower());
|
||||
for(int j=j_start; j<i; j++) sum += this->operator()(i,j)*x[j];
|
||||
x[i]=(b[i]*this->saved_diag(i)) - sum;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
// solves Rx=y
|
||||
std::vector<double> band_matrix::r_solve(const std::vector<double>& b) const
|
||||
{
|
||||
assert( this->dim()==(int)b.size() );
|
||||
std::vector<double> 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<double> band_matrix::lu_solve(const std::vector<double>& b,
|
||||
bool is_lu_decomposed)
|
||||
{
|
||||
assert( this->dim()==(int)b.size() );
|
||||
std::vector<double> 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<double>::epsilon(); // __DBL_EPSILON__
|
||||
return 2.2204460492503131e-16; // 2^-52
|
||||
}
|
||||
|
||||
// solutions for a + b*x = 0
|
||||
std::vector<double> solve_linear(double a, double b)
|
||||
{
|
||||
std::vector<double> 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<double> 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<double> 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; i<x.size(); i++) {
|
||||
for(int k=0; k<newton_iter; k++) {
|
||||
double f = (c*x[i] + b)*x[i] + a;
|
||||
double f1 = 2.0*c*x[i] + b;
|
||||
// only adjust if slope is large enough
|
||||
if(fabs(f1)>1e-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<double> 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<double> 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<double>
|
||||
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; i<z.size(); i++) {
|
||||
// convert depressed cubic roots to original cubic: x = z - c/3
|
||||
z[i] -= (1.0/3.0)*c;
|
||||
// improve solution via newton steps
|
||||
for(int k=0; k<newton_iter; k++) {
|
||||
double f = ((z[i] + c)*z[i] + b)*z[i] + a;
|
||||
double f1 = (3.0*z[i] + 2.0*c)*z[i] + b;
|
||||
// only adjust if slope is large enough
|
||||
if(fabs(f1)>1e-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; i<z.size(); i++) {
|
||||
if(xmin>fabs(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 */
|
||||
|
|
@ -15,7 +15,6 @@
|
|||
#include "../PrintConfig.hpp"
|
||||
|
||||
#include "SmallAreaInfillFlowCompensator.hpp"
|
||||
#include "spline/spline.h"
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
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<tk::spline>();
|
||||
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<PchipInterpolatorHelper>(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)
|
||||
|
|
|
|||
|
|
@ -4,12 +4,9 @@
|
|||
#include "../libslic3r.h"
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../ExtrusionEntity.hpp"
|
||||
#include "PchipInterpolatorHelper.hpp"
|
||||
#include <memory>
|
||||
|
||||
namespace tk {
|
||||
class spline;
|
||||
} // namespace tk
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class SmallAreaInfillFlowCompensator
|
||||
|
|
@ -26,8 +23,7 @@ private:
|
|||
std::vector<double> eLengths;
|
||||
std::vector<double> flowComps;
|
||||
|
||||
// TODO: Cubic Spline
|
||||
std::unique_ptr<tk::spline> flowModel;
|
||||
std::unique_ptr<PchipInterpolatorHelper> flowModel;
|
||||
|
||||
double flow_comp_model(const double line_length);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue