Merge branch 'master' of https://github.com/prusa3d/Slic3r
BIN
resources/icons/move_hover.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
resources/icons/move_off.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
resources/icons/move_on.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.4 KiB |
BIN
resources/icons/scale_hover.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
resources/icons/scale_off.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
resources/icons/scale_on.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 15 KiB |
BIN
resources/localization/ko_KR/Slic3rPE.mo
Normal file
4679
resources/localization/ko_KR/Slic3rPE.po
Normal file
@ -1,10 +1,10 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <libslic3r.h>
|
#include <libslic3r/libslic3r.h>
|
||||||
#include "TriangleMesh.hpp"
|
#include <libslic3r/TriangleMesh.hpp>
|
||||||
#include "SLABasePool.hpp"
|
#include <libslic3r/SLA/SLABasePool.hpp>
|
||||||
#include "benchmark.h"
|
#include <libnest2d/tools/benchmark.h>
|
||||||
|
|
||||||
const std::string USAGE_STR = {
|
const std::string USAGE_STR = {
|
||||||
"Usage: slabasebed stlfilename.stl"
|
"Usage: slabasebed stlfilename.stl"
|
||||||
@ -28,7 +28,7 @@ int main(const int argc, const char *argv[]) {
|
|||||||
ExPolygons ground_slice;
|
ExPolygons ground_slice;
|
||||||
TriangleMesh basepool;
|
TriangleMesh basepool;
|
||||||
|
|
||||||
sla::ground_layer(model, ground_slice, 0.1f);
|
sla::base_plate(model, ground_slice, 0.1f);
|
||||||
|
|
||||||
bench.start();
|
bench.start();
|
||||||
sla::create_base_pool(ground_slice, basepool);
|
sla::create_base_pool(ground_slice, basepool);
|
||||||
|
@ -9,7 +9,9 @@
|
|||||||
#endif /* SLIC3R_GUI */
|
#endif /* SLIC3R_GUI */
|
||||||
|
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
|
#include "ClipperUtils.hpp"
|
||||||
#include "EdgeGrid.hpp"
|
#include "EdgeGrid.hpp"
|
||||||
|
#include "SVG.hpp"
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// Enable debugging and assert in this file.
|
// Enable debugging and assert in this file.
|
||||||
@ -756,8 +758,8 @@ void EdgeGrid::Grid::calculate_sdf()
|
|||||||
float search_radius = float(m_resolution<<1);
|
float search_radius = float(m_resolution<<1);
|
||||||
m_signed_distance_field.assign(nrows * ncols, search_radius);
|
m_signed_distance_field.assign(nrows * ncols, search_radius);
|
||||||
// For each cell:
|
// For each cell:
|
||||||
for (size_t r = 0; r < m_rows; ++ r) {
|
for (int r = 0; r < (int)m_rows; ++ r) {
|
||||||
for (size_t c = 0; c < m_cols; ++ c) {
|
for (int c = 0; c < (int)m_cols; ++ c) {
|
||||||
const Cell &cell = m_cells[r * m_cols + c];
|
const Cell &cell = m_cells[r * m_cols + c];
|
||||||
// For each segment in the cell:
|
// For each segment in the cell:
|
||||||
for (size_t i = cell.begin; i != cell.end; ++ i) {
|
for (size_t i = cell.begin; i != cell.end; ++ i) {
|
||||||
@ -842,6 +844,8 @@ void EdgeGrid::Grid::calculate_sdf()
|
|||||||
#if 0
|
#if 0
|
||||||
static int iRun = 0;
|
static int iRun = 0;
|
||||||
++ iRun;
|
++ iRun;
|
||||||
|
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
|
||||||
|
wxImage::AddHandler(new wxPNGHandler);
|
||||||
//#ifdef SLIC3R_GUI
|
//#ifdef SLIC3R_GUI
|
||||||
{
|
{
|
||||||
wxImage img(ncols, nrows);
|
wxImage img(ncols, nrows);
|
||||||
@ -1356,9 +1360,101 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) co
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline int segments_could_intersect(
|
||||||
|
const Slic3r::Point &ip1, const Slic3r::Point &ip2,
|
||||||
|
const Slic3r::Point &jp1, const Slic3r::Point &jp2)
|
||||||
|
{
|
||||||
|
Vec2i64 iv = (ip2 - ip1).cast<int64_t>();
|
||||||
|
Vec2i64 vij1 = (jp1 - ip1).cast<int64_t>();
|
||||||
|
Vec2i64 vij2 = (jp2 - ip1).cast<int64_t>();
|
||||||
|
int64_t tij1 = cross2(iv, vij1);
|
||||||
|
int64_t tij2 = cross2(iv, vij2);
|
||||||
|
int sij1 = (tij1 > 0) ? 1 : ((tij1 < 0) ? -1 : 0); // signum
|
||||||
|
int sij2 = (tij2 > 0) ? 1 : ((tij2 < 0) ? -1 : 0);
|
||||||
|
return sij1 * sij2;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool segments_intersect(
|
||||||
|
const Slic3r::Point &ip1, const Slic3r::Point &ip2,
|
||||||
|
const Slic3r::Point &jp1, const Slic3r::Point &jp2)
|
||||||
|
{
|
||||||
|
return segments_could_intersect(ip1, ip2, jp1, jp2) <= 0 &&
|
||||||
|
segments_could_intersect(jp1, jp2, ip1, ip2) <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> EdgeGrid::Grid::intersecting_edges() const
|
||||||
|
{
|
||||||
|
std::vector<std::pair<ContourEdge, ContourEdge>> out;
|
||||||
|
// For each cell:
|
||||||
|
for (int r = 0; r < (int)m_rows; ++ r) {
|
||||||
|
for (int c = 0; c < (int)m_cols; ++ c) {
|
||||||
|
const Cell &cell = m_cells[r * m_cols + c];
|
||||||
|
// For each pair of segments in the cell:
|
||||||
|
for (size_t i = cell.begin; i != cell.end; ++ i) {
|
||||||
|
const Slic3r::Points &ipts = *m_contours[m_cell_data[i].first];
|
||||||
|
size_t ipt = m_cell_data[i].second;
|
||||||
|
// End points of the line segment and their vector.
|
||||||
|
const Slic3r::Point &ip1 = ipts[ipt];
|
||||||
|
const Slic3r::Point &ip2 = ipts[(ipt + 1 == ipts.size()) ? 0 : ipt + 1];
|
||||||
|
for (size_t j = i + 1; j != cell.end; ++ j) {
|
||||||
|
const Slic3r::Points &jpts = *m_contours[m_cell_data[j].first];
|
||||||
|
size_t jpt = m_cell_data[j].second;
|
||||||
|
// End points of the line segment and their vector.
|
||||||
|
const Slic3r::Point &jp1 = jpts[jpt];
|
||||||
|
const Slic3r::Point &jp2 = jpts[(jpt + 1 == jpts.size()) ? 0 : jpt + 1];
|
||||||
|
if (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2))
|
||||||
|
// Segments of the same contour share a common vertex.
|
||||||
|
continue;
|
||||||
|
if (segments_intersect(ip1, ip2, jp1, jp2)) {
|
||||||
|
// The two segments intersect. Add them to the output.
|
||||||
|
int jfirst = (&jpts < &ipts) || (&jpts == &ipts && jpt < ipt);
|
||||||
|
out.emplace_back(jfirst ?
|
||||||
|
std::make_pair(std::make_pair(&ipts, ipt), std::make_pair(&jpts, jpt)) :
|
||||||
|
std::make_pair(std::make_pair(&ipts, ipt), std::make_pair(&jpts, jpt)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Slic3r::sort_remove_duplicates(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EdgeGrid::Grid::has_intersecting_edges() const
|
||||||
|
{
|
||||||
|
// For each cell:
|
||||||
|
for (int r = 0; r < (int)m_rows; ++ r) {
|
||||||
|
for (int c = 0; c < (int)m_cols; ++ c) {
|
||||||
|
const Cell &cell = m_cells[r * m_cols + c];
|
||||||
|
// For each pair of segments in the cell:
|
||||||
|
for (size_t i = cell.begin; i != cell.end; ++ i) {
|
||||||
|
const Slic3r::Points &ipts = *m_contours[m_cell_data[i].first];
|
||||||
|
size_t ipt = m_cell_data[i].second;
|
||||||
|
// End points of the line segment and their vector.
|
||||||
|
const Slic3r::Point &ip1 = ipts[ipt];
|
||||||
|
const Slic3r::Point &ip2 = ipts[(ipt + 1 == ipts.size()) ? 0 : ipt + 1];
|
||||||
|
for (size_t j = i + 1; j != cell.end; ++ j) {
|
||||||
|
const Slic3r::Points &jpts = *m_contours[m_cell_data[j].first];
|
||||||
|
size_t jpt = m_cell_data[j].second;
|
||||||
|
// End points of the line segment and their vector.
|
||||||
|
const Slic3r::Point &jp1 = jpts[jpt];
|
||||||
|
const Slic3r::Point &jp2 = jpts[(jpt + 1 == jpts.size()) ? 0 : jpt + 1];
|
||||||
|
if (! (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2)) &&
|
||||||
|
segments_intersect(ip1, ip2, jp1, jp2))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path)
|
void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path)
|
||||||
{
|
{
|
||||||
|
if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr)
|
||||||
|
wxImage::AddHandler(new wxPNGHandler);
|
||||||
|
|
||||||
unsigned int w = (bbox.max(0) - bbox.min(0) + resolution - 1) / resolution;
|
unsigned int w = (bbox.max(0) - bbox.min(0) + resolution - 1) / resolution;
|
||||||
unsigned int h = (bbox.max(1) - bbox.min(1) + resolution - 1) / resolution;
|
unsigned int h = (bbox.max(1) - bbox.min(1) + resolution - 1) / resolution;
|
||||||
wxImage img(w, h);
|
wxImage img(w, h);
|
||||||
@ -1450,4 +1546,59 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo
|
|||||||
}
|
}
|
||||||
#endif /* SLIC3R_GUI */
|
#endif /* SLIC3R_GUI */
|
||||||
|
|
||||||
|
// Find all pairs of intersectiong edges from the set of polygons.
|
||||||
|
std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> intersecting_edges(const Polygons &polygons)
|
||||||
|
{
|
||||||
|
double len = 0;
|
||||||
|
size_t cnt = 0;
|
||||||
|
BoundingBox bbox;
|
||||||
|
for (const Polygon &poly : polygons) {
|
||||||
|
if (poly.points.size() < 2)
|
||||||
|
continue;
|
||||||
|
for (size_t i = 0; i < poly.points.size(); ++ i) {
|
||||||
|
bbox.merge(poly.points[i]);
|
||||||
|
size_t j = (i == 0) ? (poly.points.size() - 1) : i - 1;
|
||||||
|
len += (poly.points[j] - poly.points[i]).cast<double>().norm();
|
||||||
|
++ cnt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
len /= double(cnt);
|
||||||
|
bbox.offset(20);
|
||||||
|
EdgeGrid::Grid grid;
|
||||||
|
grid.set_bbox(bbox);
|
||||||
|
grid.create(polygons, len);
|
||||||
|
return grid.intersecting_edges();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all pairs of intersectiong edges from the set of polygons, highlight them in an SVG.
|
||||||
|
void export_intersections_to_svg(const std::string &filename, const Polygons &polygons)
|
||||||
|
{
|
||||||
|
std::vector<std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge>> intersections = intersecting_edges(polygons);
|
||||||
|
BoundingBox bbox = get_extents(polygons);
|
||||||
|
SVG svg(filename.c_str(), bbox);
|
||||||
|
svg.draw(union_ex(polygons), "gray", 0.25f);
|
||||||
|
svg.draw_outline(polygons, "black");
|
||||||
|
std::set<const Points*> intersecting_contours;
|
||||||
|
for (const std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge> &ie : intersections) {
|
||||||
|
intersecting_contours.insert(ie.first.first);
|
||||||
|
intersecting_contours.insert(ie.second.first);
|
||||||
|
}
|
||||||
|
// Highlight the contours with intersections.
|
||||||
|
coord_t line_width = coord_t(scale_(0.01));
|
||||||
|
for (const Points *ic : intersecting_contours) {
|
||||||
|
svg.draw_outline(Polygon(*ic), "green");
|
||||||
|
svg.draw_outline(Polygon(*ic), "black", line_width);
|
||||||
|
}
|
||||||
|
// Paint the intersections.
|
||||||
|
for (const std::pair<EdgeGrid::Grid::ContourEdge, EdgeGrid::Grid::ContourEdge> &intersecting_edges : intersections) {
|
||||||
|
auto edge = [](const EdgeGrid::Grid::ContourEdge &e) {
|
||||||
|
return Line(e.first->at(e.second),
|
||||||
|
e.first->at((e.second + 1 == e.first->size()) ? 0 : e.second + 1));
|
||||||
|
};
|
||||||
|
svg.draw(edge(intersecting_edges.first), "red", line_width);
|
||||||
|
svg.draw(edge(intersecting_edges.second), "red", line_width);
|
||||||
|
}
|
||||||
|
svg.Close();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
@ -133,7 +133,7 @@ void FillGyroid::_fill_surface_single(
|
|||||||
// no rotation is supported for this infill pattern (yet)
|
// no rotation is supported for this infill pattern (yet)
|
||||||
BoundingBox bb = expolygon.contour.bounding_box();
|
BoundingBox bb = expolygon.contour.bounding_box();
|
||||||
// Density adjusted to have a good %of weight.
|
// Density adjusted to have a good %of weight.
|
||||||
double density_adjusted = std::max(0., params.density * 2.);
|
double density_adjusted = std::max(0., params.density * 2.44);
|
||||||
// Distance between the gyroid waves in scaled coordinates.
|
// Distance between the gyroid waves in scaled coordinates.
|
||||||
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
|
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
|
||||||
|
|
||||||
|
@ -545,7 +545,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
|
|||||||
m_print_brim = true;
|
m_print_brim = true;
|
||||||
|
|
||||||
// Ask our writer about how much material was consumed:
|
// Ask our writer about how much material was consumed:
|
||||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
if (m_current_tool < m_used_filament_length.size())
|
||||||
|
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||||
|
|
||||||
ToolChangeResult result;
|
ToolChangeResult result;
|
||||||
result.priming = true;
|
result.priming = true;
|
||||||
@ -698,7 +699,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
|
|||||||
m_print_brim = false; // Mark the brim as extruded
|
m_print_brim = false; // Mark the brim as extruded
|
||||||
|
|
||||||
// Ask our writer about how much material was consumed:
|
// Ask our writer about how much material was consumed:
|
||||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
if (m_current_tool < m_used_filament_length.size())
|
||||||
|
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||||
|
|
||||||
ToolChangeResult result;
|
ToolChangeResult result;
|
||||||
result.priming = false;
|
result.priming = false;
|
||||||
@ -868,7 +870,8 @@ void WipeTowerPrusaMM::toolchange_Change(
|
|||||||
material_type new_material)
|
material_type new_material)
|
||||||
{
|
{
|
||||||
// Ask the writer about how much of the old filament we consumed:
|
// Ask the writer about how much of the old filament we consumed:
|
||||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
if (m_current_tool < m_used_filament_length.size())
|
||||||
|
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||||
|
|
||||||
// Speed override for the material. Go slow for flex and soluble materials.
|
// Speed override for the material. Go slow for flex and soluble materials.
|
||||||
int speed_override;
|
int speed_override;
|
||||||
|
@ -521,10 +521,14 @@ void Model::adjust_min_z()
|
|||||||
unsigned int Model::get_auto_extruder_id(unsigned int max_extruders)
|
unsigned int Model::get_auto_extruder_id(unsigned int max_extruders)
|
||||||
{
|
{
|
||||||
unsigned int id = s_auto_extruder_id;
|
unsigned int id = s_auto_extruder_id;
|
||||||
|
if (id > max_extruders) {
|
||||||
if (++s_auto_extruder_id > max_extruders)
|
// The current counter is invalid, likely due to switching the printer profiles
|
||||||
|
// to a profile with a lower number of extruders.
|
||||||
reset_auto_extruder_id();
|
reset_auto_extruder_id();
|
||||||
|
id = s_auto_extruder_id;
|
||||||
|
} else if (++ s_auto_extruder_id > max_extruders) {
|
||||||
|
reset_auto_extruder_id();
|
||||||
|
}
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,6 +107,23 @@ extern BoundingBox get_extents(const MultiPoint &mp);
|
|||||||
extern BoundingBox get_extents_rotated(const std::vector<Point> &points, double angle);
|
extern BoundingBox get_extents_rotated(const std::vector<Point> &points, double angle);
|
||||||
extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle);
|
extern BoundingBox get_extents_rotated(const MultiPoint &mp, double angle);
|
||||||
|
|
||||||
|
inline double length(const Points &pts) {
|
||||||
|
double total = 0;
|
||||||
|
if (! pts.empty()) {
|
||||||
|
auto it = pts.begin();
|
||||||
|
for (auto it_prev = it ++; it != pts.end(); ++ it, ++ it_prev)
|
||||||
|
total += (*it - *it_prev).cast<double>().norm();
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline double area(const Points &polygon) {
|
||||||
|
double area = 0.;
|
||||||
|
for (size_t i = 0, j = polygon.size() - 1; i < polygon.size(); j = i ++)
|
||||||
|
area += double(polygon[i](0) + polygon[j](0)) * double(polygon[i](1) - polygon[j](1));
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -187,6 +187,24 @@ public:
|
|||||||
m_map.emplace(std::make_pair(Vec2crd(pt->x()>>m_grid_log2, pt->y()>>m_grid_log2), std::move(value)));
|
m_map.emplace(std::make_pair(Vec2crd(pt->x()>>m_grid_log2, pt->y()>>m_grid_log2), std::move(value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Erase a data point equal to value. (ValueType has to declare the operator==).
|
||||||
|
// Returns true if the data point equal to value was found and removed.
|
||||||
|
bool erase(const ValueType &value) {
|
||||||
|
const Point *pt = m_point_accessor(value);
|
||||||
|
if (pt != nullptr) {
|
||||||
|
// Range of fragment starts around grid_corner, close to pt.
|
||||||
|
auto range = m_map.equal_range(Point((*pt)(0)>>m_grid_log2, (*pt)(1)>>m_grid_log2));
|
||||||
|
// Remove the first item.
|
||||||
|
for (auto it = range.first; it != range.second; ++ it) {
|
||||||
|
if (it->second == value) {
|
||||||
|
m_map.erase(it);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Return a pair of <ValueType*, distance_squared>
|
// Return a pair of <ValueType*, distance_squared>
|
||||||
std::pair<const ValueType*, double> find(const Vec2crd &pt) {
|
std::pair<const ValueType*, double> find(const Vec2crd &pt) {
|
||||||
// Iterate over 4 closest grid cells around pt,
|
// Iterate over 4 closest grid cells around pt,
|
||||||
@ -214,7 +232,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (value_min != nullptr && dist_min < coordf_t(m_search_radius * m_search_radius)) ?
|
return (value_min != nullptr && dist_min < coordf_t(m_search_radius) * coordf_t(m_search_radius)) ?
|
||||||
std::make_pair(value_min, dist_min) :
|
std::make_pair(value_min, dist_min) :
|
||||||
std::make_pair(nullptr, std::numeric_limits<double>::max());
|
std::make_pair(nullptr, std::numeric_limits<double>::max());
|
||||||
}
|
}
|
||||||
|
@ -81,8 +81,8 @@ extern BoundingBox get_extents(const Polylines &polylines);
|
|||||||
|
|
||||||
inline double total_length(const Polylines &polylines) {
|
inline double total_length(const Polylines &polylines) {
|
||||||
double total = 0;
|
double total = 0;
|
||||||
for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
|
for (const Polyline &pl : polylines)
|
||||||
total += it->length();
|
total += pl.length();
|
||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -281,16 +281,17 @@ bool Print::is_step_done(PrintObjectStep step) const
|
|||||||
std::vector<unsigned int> Print::object_extruders() const
|
std::vector<unsigned int> Print::object_extruders() const
|
||||||
{
|
{
|
||||||
std::vector<unsigned int> extruders;
|
std::vector<unsigned int> extruders;
|
||||||
|
extruders.reserve(m_regions.size() * 3);
|
||||||
|
|
||||||
for (PrintRegion* region : m_regions) {
|
for (const PrintRegion *region : m_regions) {
|
||||||
// these checks reflect the same logic used in the GUI for enabling/disabling
|
// these checks reflect the same logic used in the GUI for enabling/disabling
|
||||||
// extruder selection fields
|
// extruder selection fields
|
||||||
if (region->config().perimeters.value > 0 || m_config.brim_width.value > 0)
|
if (region->config().perimeters.value > 0 || m_config.brim_width.value > 0)
|
||||||
extruders.push_back(region->config().perimeter_extruder - 1);
|
extruders.emplace_back(region->config().perimeter_extruder - 1);
|
||||||
if (region->config().fill_density.value > 0)
|
if (region->config().fill_density.value > 0)
|
||||||
extruders.push_back(region->config().infill_extruder - 1);
|
extruders.emplace_back(region->config().infill_extruder - 1);
|
||||||
if (region->config().top_solid_layers.value > 0 || region->config().bottom_solid_layers.value > 0)
|
if (region->config().top_solid_layers.value > 0 || region->config().bottom_solid_layers.value > 0)
|
||||||
extruders.push_back(region->config().solid_infill_extruder - 1);
|
extruders.emplace_back(region->config().solid_infill_extruder - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
sort_remove_duplicates(extruders);
|
sort_remove_duplicates(extruders);
|
||||||
@ -480,14 +481,6 @@ bool Print::apply_config(DynamicPrintConfig config)
|
|||||||
PrintObjectConfig new_config = this->default_object_config();
|
PrintObjectConfig new_config = this->default_object_config();
|
||||||
// we override the new config with object-specific options
|
// we override the new config with object-specific options
|
||||||
normalize_and_apply_config(new_config, object->model_object()->config);
|
normalize_and_apply_config(new_config, object->model_object()->config);
|
||||||
// Force a refresh of a variable layer height profile at the PrintObject if it is not valid.
|
|
||||||
if (! object->layer_height_profile_valid) {
|
|
||||||
// The layer_height_profile is not valid for some reason (updated by the user or invalidated due to some option change).
|
|
||||||
// Invalidate the slicing step, which in turn invalidates everything.
|
|
||||||
object->invalidate_step(posSlice);
|
|
||||||
// Trigger recalculation.
|
|
||||||
invalidated = true;
|
|
||||||
}
|
|
||||||
// check whether the new config is different from the current one
|
// check whether the new config is different from the current one
|
||||||
t_config_option_keys diff = object->config().diff(new_config);
|
t_config_option_keys diff = object->config().diff(new_config);
|
||||||
object->config_apply_only(new_config, diff, true);
|
object->config_apply_only(new_config, diff, true);
|
||||||
@ -567,8 +560,7 @@ exit_for_rearrange_regions:
|
|||||||
|
|
||||||
// Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads.
|
// Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads.
|
||||||
for (PrintObject *object : m_objects)
|
for (PrintObject *object : m_objects)
|
||||||
if (! object->layer_height_profile_valid)
|
object->update_layer_height_profile();
|
||||||
object->update_layer_height_profile();
|
|
||||||
|
|
||||||
return invalidated;
|
return invalidated;
|
||||||
}
|
}
|
||||||
@ -1141,6 +1133,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||||||
// Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads.
|
// Always make sure that the layer_height_profiles are set, as they should not be modified from the worker threads.
|
||||||
for (PrintObject *object : m_objects)
|
for (PrintObject *object : m_objects)
|
||||||
if (! object->layer_height_profile_valid)
|
if (! object->layer_height_profile_valid)
|
||||||
|
// No need to call the next line as the step should already be invalidated above.
|
||||||
|
// update_apply_status(object->invalidate_step(posSlice));
|
||||||
object->update_layer_height_profile();
|
object->update_layer_height_profile();
|
||||||
|
|
||||||
//FIXME there may be a race condition with the G-code export running at the background thread.
|
//FIXME there may be a race condition with the G-code export running at the background thread.
|
||||||
@ -1165,6 +1159,7 @@ bool Print::has_skirt() const
|
|||||||
|| this->has_infinite_skirt();
|
|| this->has_infinite_skirt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Precondition: Print::validate() requires the Print::apply() to be called its invocation.
|
||||||
std::string Print::validate() const
|
std::string Print::validate() const
|
||||||
{
|
{
|
||||||
if (m_objects.empty())
|
if (m_objects.empty())
|
||||||
@ -1253,12 +1248,10 @@ std::string Print::validate() const
|
|||||||
return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance");
|
return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance");
|
||||||
if (! equal_layering(slicing_params, slicing_params0))
|
if (! equal_layering(slicing_params, slicing_params0))
|
||||||
return L("The Wipe Tower is only supported for multiple objects if they are sliced equally.");
|
return L("The Wipe Tower is only supported for multiple objects if they are sliced equally.");
|
||||||
bool was_layer_height_profile_valid = object->layer_height_profile_valid;
|
|
||||||
object->update_layer_height_profile();
|
|
||||||
object->layer_height_profile_valid = was_layer_height_profile_valid;
|
|
||||||
|
|
||||||
if ( m_config.variable_layer_height ) { // comparing layer height profiles
|
if ( m_config.variable_layer_height ) { // comparing layer height profiles
|
||||||
bool failed = false;
|
bool failed = false;
|
||||||
|
// layer_height_profile should be set by Print::apply().
|
||||||
if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size() ) {
|
if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size() ) {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while ( i < object->layer_height_profile.size() && i < tallest_object->layer_height_profile.size()) {
|
while ( i < object->layer_height_profile.size() && i < tallest_object->layer_height_profile.size()) {
|
||||||
|
@ -78,7 +78,6 @@ private: // Prevents erroneous use by other classes.
|
|||||||
public:
|
public:
|
||||||
// vector of (vectors of volume ids), indexed by region_id
|
// vector of (vectors of volume ids), indexed by region_id
|
||||||
std::vector<std::vector<int>> region_volumes;
|
std::vector<std::vector<int>> region_volumes;
|
||||||
t_layer_height_ranges layer_height_ranges;
|
|
||||||
|
|
||||||
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
|
// Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers.
|
||||||
// The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS.
|
// The pairs of <z, layer_height> are packed into a 1D array to simplify handling by the Perl XS.
|
||||||
@ -298,7 +297,10 @@ public:
|
|||||||
|
|
||||||
// methods for handling state
|
// methods for handling state
|
||||||
bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); }
|
bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); }
|
||||||
|
// Returns true if an object step is done on all objects and there's at least one object.
|
||||||
bool is_step_done(PrintObjectStep step) const;
|
bool is_step_done(PrintObjectStep step) const;
|
||||||
|
// Returns true if the last step was finished with success.
|
||||||
|
bool finished() const override { return this->is_step_done(psGCodeExport); }
|
||||||
|
|
||||||
bool has_infinite_skirt() const;
|
bool has_infinite_skirt() const;
|
||||||
bool has_skirt() const;
|
bool has_skirt() const;
|
||||||
|
@ -285,6 +285,8 @@ public:
|
|||||||
void cancel_internal() { m_cancel_status = CANCELED_INTERNAL; }
|
void cancel_internal() { m_cancel_status = CANCELED_INTERNAL; }
|
||||||
// Cancel the running computation. Stop execution of all the background threads.
|
// Cancel the running computation. Stop execution of all the background threads.
|
||||||
void restart() { m_cancel_status = NOT_CANCELED; }
|
void restart() { m_cancel_status = NOT_CANCELED; }
|
||||||
|
// Returns true if the last step was finished with success.
|
||||||
|
virtual bool finished() const = 0;
|
||||||
|
|
||||||
const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; }
|
const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; }
|
||||||
PlaceholderParser& placeholder_parser() { return m_placeholder_parser; }
|
PlaceholderParser& placeholder_parser() { return m_placeholder_parser; }
|
||||||
|
@ -65,7 +65,6 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta
|
|||||||
this->set_copies(copies);
|
this->set_copies(copies);
|
||||||
}
|
}
|
||||||
|
|
||||||
this->layer_height_ranges = model_object->layer_height_ranges;
|
|
||||||
this->layer_height_profile = model_object->layer_height_profile;
|
this->layer_height_profile = model_object->layer_height_profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1109,7 +1108,7 @@ void PrintObject::discover_vertical_shells()
|
|||||||
#if 1
|
#if 1
|
||||||
// Intentionally inflate a bit more than how much the region has been shrunk,
|
// Intentionally inflate a bit more than how much the region has been shrunk,
|
||||||
// so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill).
|
// so there will be some overlap between this solid infill and the other infill regions (mainly the sparse infill).
|
||||||
shell = offset2(shell, - 0.5f * min_perimeter_infill_spacing, 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare);
|
shell = offset(offset_ex(union_ex(shell), - 0.5f * min_perimeter_infill_spacing), 0.8f * min_perimeter_infill_spacing, ClipperLib::jtSquare);
|
||||||
if (shell.empty())
|
if (shell.empty())
|
||||||
continue;
|
continue;
|
||||||
#else
|
#else
|
||||||
@ -1330,7 +1329,7 @@ bool PrintObject::update_layer_height_profile(std::vector<coordf_t> &layer_heigh
|
|||||||
bool updated = false;
|
bool updated = false;
|
||||||
|
|
||||||
// If the layer height profile is not set, try to use the one stored at the ModelObject.
|
// If the layer height profile is not set, try to use the one stored at the ModelObject.
|
||||||
if (layer_height_profile.empty() && layer_height_profile.data() != this->model_object()->layer_height_profile.data()) {
|
if (layer_height_profile.empty()) {
|
||||||
layer_height_profile = this->model_object()->layer_height_profile;
|
layer_height_profile = this->model_object()->layer_height_profile;
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
@ -1347,10 +1346,9 @@ bool PrintObject::update_layer_height_profile(std::vector<coordf_t> &layer_heigh
|
|||||||
if (layer_height_profile.empty()) {
|
if (layer_height_profile.empty()) {
|
||||||
if (0)
|
if (0)
|
||||||
// if (this->layer_height_profile.empty())
|
// if (this->layer_height_profile.empty())
|
||||||
layer_height_profile = layer_height_profile_adaptive(slicing_params, this->layer_height_ranges,
|
layer_height_profile = layer_height_profile_adaptive(slicing_params, this->model_object()->layer_height_ranges, this->model_object()->volumes);
|
||||||
this->model_object()->volumes);
|
|
||||||
else
|
else
|
||||||
layer_height_profile = layer_height_profile_from_ranges(slicing_params, this->layer_height_ranges);
|
layer_height_profile = layer_height_profile_from_ranges(slicing_params, this->model_object()->layer_height_ranges);
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
return updated;
|
return updated;
|
||||||
|
186
src/libslic3r/Rasterizer/bicubic.h
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
#ifndef BICUBIC_HPP
|
||||||
|
#define BICUBIC_HPP
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include <Eigen/Dense>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
namespace BicubicInternal {
|
||||||
|
// Linear kernel, to be able to test cubic methods with hat kernels.
|
||||||
|
template<typename T>
|
||||||
|
struct LinearKernel
|
||||||
|
{
|
||||||
|
typedef T FloatType;
|
||||||
|
|
||||||
|
static T a00() { return T(0.); }
|
||||||
|
static T a01() { return T(0.); }
|
||||||
|
static T a02() { return T(0.); }
|
||||||
|
static T a03() { return T(0.); }
|
||||||
|
static T a10() { return T(1.); }
|
||||||
|
static T a11() { return T(-1.); }
|
||||||
|
static T a12() { return T(0.); }
|
||||||
|
static T a13() { return T(0.); }
|
||||||
|
static T a20() { return T(0.); }
|
||||||
|
static T a21() { return T(1.); }
|
||||||
|
static T a22() { return T(0.); }
|
||||||
|
static T a23() { return T(0.); }
|
||||||
|
static T a30() { return T(0.); }
|
||||||
|
static T a31() { return T(0.); }
|
||||||
|
static T a32() { return T(0.); }
|
||||||
|
static T a33() { return T(0.); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Interpolation kernel aka Catmul-Rom aka Keyes kernel.
|
||||||
|
template<typename T>
|
||||||
|
struct CubicCatmulRomKernel
|
||||||
|
{
|
||||||
|
typedef T FloatType;
|
||||||
|
|
||||||
|
static T a00() { return 0; }
|
||||||
|
static T a01() { return (T)-0.5; }
|
||||||
|
static T a02() { return (T) 1.; }
|
||||||
|
static T a03() { return (T)-0.5; }
|
||||||
|
static T a10() { return (T) 1.; }
|
||||||
|
static T a11() { return 0; }
|
||||||
|
static T a12() { return (T)-5./2.; }
|
||||||
|
static T a13() { return (T) 3./2.; }
|
||||||
|
static T a20() { return 0; }
|
||||||
|
static T a21() { return (T) 0.5; }
|
||||||
|
static T a22() { return (T) 2.; }
|
||||||
|
static T a23() { return (T)-3./2.; }
|
||||||
|
static T a30() { return 0; }
|
||||||
|
static T a31() { return 0; }
|
||||||
|
static T a32() { return (T)-0.5; }
|
||||||
|
static T a33() { return (T) 0.5; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// B-spline kernel
|
||||||
|
template<typename T>
|
||||||
|
struct CubicBSplineKernel
|
||||||
|
{
|
||||||
|
typedef T FloatType;
|
||||||
|
|
||||||
|
static T a00() { return (T) 1./6.; }
|
||||||
|
static T a01() { return (T) -3./6.; }
|
||||||
|
static T a02() { return (T) 3./6.; }
|
||||||
|
static T a03() { return (T) -1./6.; }
|
||||||
|
static T a10() { return (T) 4./6.; }
|
||||||
|
static T a11() { return 0; }
|
||||||
|
static T a12() { return (T) -6./6.; }
|
||||||
|
static T a13() { return (T) 3./6.; }
|
||||||
|
static T a20() { return (T) 1./6.; }
|
||||||
|
static T a21() { return (T) 3./6.; }
|
||||||
|
static T a22() { return (T) 3./6.; }
|
||||||
|
static T a23() { return (T)- 3./6.; }
|
||||||
|
static T a30() { return 0; }
|
||||||
|
static T a31() { return 0; }
|
||||||
|
static T a32() { return 0; }
|
||||||
|
static T a33() { return (T) 1./6.; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
inline T clamp(T a, T lower, T upper)
|
||||||
|
{
|
||||||
|
return (a < lower) ? lower :
|
||||||
|
(a > upper) ? upper : a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename KERNEL>
|
||||||
|
struct CubicKernel
|
||||||
|
{
|
||||||
|
typedef typename KERNEL KernelInternal;
|
||||||
|
typedef typename KERNEL::FloatType FloatType;
|
||||||
|
|
||||||
|
static FloatType kernel(FloatType x)
|
||||||
|
{
|
||||||
|
x = fabs(x);
|
||||||
|
if (x >= (FloatType)2.)
|
||||||
|
return 0.0f;
|
||||||
|
if (x <= (FloatType)1.) {
|
||||||
|
FloatType x2 = x * x;
|
||||||
|
FloatType x3 = x2 * x;
|
||||||
|
return KERNEL::a10() + KERNEL::a11() * x + KERNEL::a12() * x2 + KERNEL::a13() * x3;
|
||||||
|
}
|
||||||
|
assert(x > (FloatType)1. && x < (FloatType)2.);
|
||||||
|
x -= (FloatType)1.;
|
||||||
|
FloatType x2 = x * x;
|
||||||
|
FloatType x3 = x2 * x;
|
||||||
|
return KERNEL::a00() + KERNEL::a01() * x + KERNEL::a02() * x2 + KERNEL::a03() * x3;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FloatType interpolate(FloatType f0, FloatType f1, FloatType f2, FloatType f3, FloatType x)
|
||||||
|
{
|
||||||
|
const FloatType x2 = x*x;
|
||||||
|
const FloatType x3 = x*x*x;
|
||||||
|
return f0*(KERNEL::a00() + KERNEL::a01() * x + KERNEL::a02() * x2 + KERNEL::a03() * x3) +
|
||||||
|
f1*(KERNEL::a10() + KERNEL::a11() * x + KERNEL::a12() * x2 + KERNEL::a13() * x3) +
|
||||||
|
f2*(KERNEL::a20() + KERNEL::a21() * x + KERNEL::a22() * x2 + KERNEL::a23() * x3) +
|
||||||
|
f3*(KERNEL::a30() + KERNEL::a31() * x + KERNEL::a32() * x2 + KERNEL::a33() * x3);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Linear splines
|
||||||
|
typedef CubicKernel<BicubicInternal::LinearKernel<float>> LinearKernelf;
|
||||||
|
typedef CubicKernel<BicubicInternal::LinearKernel<double>> LinearKerneld;
|
||||||
|
// Catmul-Rom splines
|
||||||
|
typedef CubicKernel<BicubicInternal::CubicCatmulRomKernel<float>> CubicCatmulRomKernelf;
|
||||||
|
typedef CubicKernel<BicubicInternal::CubicCatmulRomKernel<double>> CubicCatmulRomKerneld;
|
||||||
|
typedef CubicKernel<BicubicInternal::CubicCatmulRomKernel<float>> CubicInterpolationKernelf;
|
||||||
|
typedef CubicKernel<BicubicInternal::CubicCatmulRomKernel<double>> CubicInterpolationKerneld;
|
||||||
|
// Cubic B-splines
|
||||||
|
typedef CubicKernel<BicubicInternal::CubicBSplineKernel<float>> CubicBSplineKernelf;
|
||||||
|
typedef CubicKernel<BicubicInternal::CubicBSplineKernel<double>> CubicBSplineKerneld;
|
||||||
|
|
||||||
|
template<typename KERNEL, typename Derived>
|
||||||
|
static float cubic_interpolate(const Eigen::ArrayBase<Derived> &F, const typename KERNEL::FloatType pt, const typename KERNEL::FloatType dx)
|
||||||
|
{
|
||||||
|
typedef typename KERNEL::FloatType T;
|
||||||
|
const int w = int(F.size());
|
||||||
|
const int ix = (int)floor(pt);
|
||||||
|
const T s = pt - (T)ix;
|
||||||
|
|
||||||
|
if (ix > 1 && ix + 2 < w) {
|
||||||
|
// Inside the fully interpolated region.
|
||||||
|
return KERNEL::interpolate(F[ix - 1], F[ix], F[ix + 1], F[ix + 2], s);
|
||||||
|
}
|
||||||
|
// Transition region. Extend with a constant function.
|
||||||
|
auto f = [&F, w](x) { return F[BicubicInternal::clamp(x, 0, w - 1)]; }
|
||||||
|
return KERNEL::interpolate(f(ix - 1), f(ix), f(ix + 1), f(ix + 2), s);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename KERNEL, typename Derived>
|
||||||
|
static float bicubic_interpolate(const Eigen::MatrixBase<Derived> &F, const Eigen::Matrix<typename KERNEL::FloatType, 2, 1, Eigen::DontAlign> &pt, const typename KERNEL::FloatType dx)
|
||||||
|
{
|
||||||
|
typedef typename KERNEL::FloatType T;
|
||||||
|
const int w = F.cols();
|
||||||
|
const int h = F.rows();
|
||||||
|
const int ix = (int)floor(pt[0]);
|
||||||
|
const int iy = (int)floor(pt[1]);
|
||||||
|
const T s = pt[0] - (T)ix;
|
||||||
|
const T t = pt[1] - (T)iy;
|
||||||
|
|
||||||
|
if (ix > 1 && ix + 2 < w && iy > 1 && iy + 2 < h) {
|
||||||
|
// Inside the fully interpolated region.
|
||||||
|
return KERNEL::interpolate(
|
||||||
|
KERNEL::interpolate(F(ix-1,iy-1),F(ix ,iy-1),F(ix+1,iy-1),F(ix+2,iy-1),s),
|
||||||
|
KERNEL::interpolate(F(ix-1,iy ),F(ix ,iy ),F(ix+1,iy ),F(ix+2,iy ),s),
|
||||||
|
KERNEL::interpolate(F(ix-1,iy+1),F(ix ,iy+1),F(ix+1,iy+1),F(ix+2,iy+1),s),
|
||||||
|
KERNEL::interpolate(F(ix-1,iy+2),F(ix ,iy+2),F(ix+1,iy+2),F(ix+2,iy+2),s),t);
|
||||||
|
}
|
||||||
|
// Transition region. Extend with a constant function.
|
||||||
|
auto f = [&f, w, h](int x, int y) { return F(BicubicInternal::clamp(x,0,w-1),BicubicInternal::clamp(y,0,h-1)); }
|
||||||
|
return KERNEL::interpolate(
|
||||||
|
KERNEL::interpolate(f(ix-1,iy-1),f(ix ,iy-1),f(ix+1,iy-1),f(ix+2,iy-1),s),
|
||||||
|
KERNEL::interpolate(f(ix-1,iy ),f(ix ,iy ),f(ix+1,iy ),f(ix+2,iy ),s),
|
||||||
|
KERNEL::interpolate(f(ix-1,iy+1),f(ix ,iy+1),f(ix+1,iy+1),f(ix+2,iy+1),s),
|
||||||
|
KERNEL::interpolate(f(ix-1,iy+2),f(ix ,iy+2),f(ix+1,iy+2),f(ix+2,iy+2),s),t);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif /* BICUBIC_HPP */
|
@ -31,7 +31,9 @@ Contour3D convert(const Polygons& triangles, coord_t z, bool dir) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling,
|
Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling,
|
||||||
double floor_z_mm, double ceiling_z_mm) {
|
double floor_z_mm, double ceiling_z_mm,
|
||||||
|
ThrowOnCancel thr)
|
||||||
|
{
|
||||||
using std::transform; using std::back_inserter;
|
using std::transform; using std::back_inserter;
|
||||||
|
|
||||||
ExPolygon poly;
|
ExPolygon poly;
|
||||||
@ -61,8 +63,10 @@ Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling,
|
|||||||
};
|
};
|
||||||
|
|
||||||
std::for_each(tri.begin(), tri.end(),
|
std::for_each(tri.begin(), tri.end(),
|
||||||
[&rp, &rpi, &poly, &idx, is_upper, fz, cz](const Polygon& pp)
|
[&rp, &rpi, thr, &idx, is_upper, fz, cz](const Polygon& pp)
|
||||||
{
|
{
|
||||||
|
thr(); // may throw if cancellation was requested
|
||||||
|
|
||||||
for(auto& p : pp.points)
|
for(auto& p : pp.points)
|
||||||
if(is_upper(p))
|
if(is_upper(p))
|
||||||
rp.emplace_back(unscale(x(p), y(p), mm(cz)));
|
rp.emplace_back(unscale(x(p), y(p), mm(cz)));
|
||||||
@ -213,11 +217,12 @@ inline Contour3D roofs(const ExPolygon& poly, coord_t z_distance) {
|
|||||||
|
|
||||||
template<class ExP, class D>
|
template<class ExP, class D>
|
||||||
Contour3D round_edges(const ExPolygon& base_plate,
|
Contour3D round_edges(const ExPolygon& base_plate,
|
||||||
double radius_mm,
|
double radius_mm,
|
||||||
double degrees,
|
double degrees,
|
||||||
double ceilheight_mm,
|
double ceilheight_mm,
|
||||||
bool dir,
|
bool dir,
|
||||||
ExP&& last_offset = ExP(), D&& last_height = D())
|
ThrowOnCancel throw_on_cancel,
|
||||||
|
ExP&& last_offset = ExP(), D&& last_height = D())
|
||||||
{
|
{
|
||||||
auto ob = base_plate;
|
auto ob = base_plate;
|
||||||
auto ob_prev = ob;
|
auto ob_prev = ob;
|
||||||
@ -231,6 +236,8 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
|||||||
|
|
||||||
if(degrees >= 90) {
|
if(degrees >= 90) {
|
||||||
for(int i = 1; i <= steps; ++i) {
|
for(int i = 1; i <= steps; ++i) {
|
||||||
|
throw_on_cancel();
|
||||||
|
|
||||||
ob = base_plate;
|
ob = base_plate;
|
||||||
|
|
||||||
double r2 = radius_mm * radius_mm;
|
double r2 = radius_mm * radius_mm;
|
||||||
@ -242,7 +249,7 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
|||||||
wh = ceilheight_mm - radius_mm + stepy;
|
wh = ceilheight_mm - radius_mm + stepy;
|
||||||
|
|
||||||
Contour3D pwalls;
|
Contour3D pwalls;
|
||||||
pwalls = walls(ob, ob_prev, wh, wh_prev);
|
pwalls = walls(ob, ob_prev, wh, wh_prev, throw_on_cancel);
|
||||||
|
|
||||||
curvedwalls.merge(pwalls);
|
curvedwalls.merge(pwalls);
|
||||||
ob_prev = ob;
|
ob_prev = ob;
|
||||||
@ -254,6 +261,7 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
|||||||
int tos = int(tox / stepx);
|
int tos = int(tox / stepx);
|
||||||
|
|
||||||
for(int i = 1; i <= tos; ++i) {
|
for(int i = 1; i <= tos; ++i) {
|
||||||
|
throw_on_cancel();
|
||||||
ob = base_plate;
|
ob = base_plate;
|
||||||
|
|
||||||
double r2 = radius_mm * radius_mm;
|
double r2 = radius_mm * radius_mm;
|
||||||
@ -264,7 +272,7 @@ Contour3D round_edges(const ExPolygon& base_plate,
|
|||||||
wh = ceilheight_mm - radius_mm - stepy;
|
wh = ceilheight_mm - radius_mm - stepy;
|
||||||
|
|
||||||
Contour3D pwalls;
|
Contour3D pwalls;
|
||||||
pwalls = walls(ob_prev, ob, wh_prev, wh);
|
pwalls = walls(ob_prev, ob, wh_prev, wh, throw_on_cancel);
|
||||||
|
|
||||||
curvedwalls.merge(pwalls);
|
curvedwalls.merge(pwalls);
|
||||||
ob_prev = ob;
|
ob_prev = ob;
|
||||||
@ -348,7 +356,8 @@ inline Point centroid(const ExPolygon& poly) {
|
|||||||
/// with explicit bridges. Bridges are generated from each shape's centroid
|
/// with explicit bridges. Bridges are generated from each shape's centroid
|
||||||
/// to the center of the "scene" which is the centroid calculated from the shape
|
/// to the center of the "scene" which is the centroid calculated from the shape
|
||||||
/// centroids (a star is created...)
|
/// centroids (a star is created...)
|
||||||
ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50)
|
ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50,
|
||||||
|
ThrowOnCancel throw_on_cancel = [](){})
|
||||||
{
|
{
|
||||||
namespace bgi = boost::geometry::index;
|
namespace bgi = boost::geometry::index;
|
||||||
using SpatElement = std::pair<BoundingBox, unsigned>;
|
using SpatElement = std::pair<BoundingBox, unsigned>;
|
||||||
@ -383,9 +392,10 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50)
|
|||||||
idx = 0;
|
idx = 0;
|
||||||
std::transform(centroids.begin(), centroids.end(),
|
std::transform(centroids.begin(), centroids.end(),
|
||||||
std::back_inserter(punion),
|
std::back_inserter(punion),
|
||||||
[&punion, &boxindex, cc, max_dist_mm, &idx](const Point& c)
|
[&punion, &boxindex, cc, max_dist_mm, &idx, throw_on_cancel]
|
||||||
|
(const Point& c)
|
||||||
{
|
{
|
||||||
|
throw_on_cancel();
|
||||||
double dx = x(c) - x(cc), dy = y(c) - y(cc);
|
double dx = x(c) - x(cc), dy = y(c) - y(cc);
|
||||||
double l = std::sqrt(dx * dx + dy * dy);
|
double l = std::sqrt(dx * dx + dy * dy);
|
||||||
double nx = dx / l, ny = dy / l;
|
double nx = dx / l, ny = dy / l;
|
||||||
@ -419,7 +429,7 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
|
void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
|
||||||
float layerh)
|
float layerh, ThrowOnCancel thrfn)
|
||||||
{
|
{
|
||||||
TriangleMesh m = mesh;
|
TriangleMesh m = mesh;
|
||||||
TriangleMeshSlicer slicer(&m);
|
TriangleMeshSlicer slicer(&m);
|
||||||
@ -431,7 +441,7 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h,
|
|||||||
heights.emplace_back(hi);
|
heights.emplace_back(hi);
|
||||||
|
|
||||||
std::vector<ExPolygons> out; out.reserve(size_t(std::ceil(h/layerh)));
|
std::vector<ExPolygons> out; out.reserve(size_t(std::ceil(h/layerh)));
|
||||||
slicer.slice(heights, &out, [](){});
|
slicer.slice(heights, &out, thrfn);
|
||||||
|
|
||||||
size_t count = 0; for(auto& o : out) count += o.size();
|
size_t count = 0; for(auto& o : out) count += o.size();
|
||||||
ExPolygons tmp; tmp.reserve(count);
|
ExPolygons tmp; tmp.reserve(count);
|
||||||
@ -447,7 +457,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
|||||||
double mdist = 2*(1.8*cfg.min_wall_thickness_mm + 4*cfg.edge_radius_mm) +
|
double mdist = 2*(1.8*cfg.min_wall_thickness_mm + 4*cfg.edge_radius_mm) +
|
||||||
cfg.max_merge_distance_mm;
|
cfg.max_merge_distance_mm;
|
||||||
|
|
||||||
auto concavehs = concave_hull(ground_layer, mdist);
|
auto concavehs = concave_hull(ground_layer, mdist, cfg.throw_on_cancel);
|
||||||
for(ExPolygon& concaveh : concavehs) {
|
for(ExPolygon& concaveh : concavehs) {
|
||||||
if(concaveh.contour.points.empty()) return;
|
if(concaveh.contour.points.empty()) return;
|
||||||
concaveh.holes.clear();
|
concaveh.holes.clear();
|
||||||
@ -505,6 +515,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
|||||||
phi, // 170 degrees
|
phi, // 170 degrees
|
||||||
0, // z position of the input plane
|
0, // z position of the input plane
|
||||||
true,
|
true,
|
||||||
|
cfg.throw_on_cancel,
|
||||||
ob, wh);
|
ob, wh);
|
||||||
|
|
||||||
pool.merge(curvedwalls);
|
pool.merge(curvedwalls);
|
||||||
@ -512,7 +523,8 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
|||||||
ExPolygon ob_contr = ob;
|
ExPolygon ob_contr = ob;
|
||||||
ob_contr.holes.clear();
|
ob_contr.holes.clear();
|
||||||
|
|
||||||
auto pwalls = walls(ob_contr, inner_base, wh, -cfg.min_wall_height_mm);
|
auto pwalls = walls(ob_contr, inner_base, wh, -cfg.min_wall_height_mm,
|
||||||
|
cfg.throw_on_cancel);
|
||||||
pool.merge(pwalls);
|
pool.merge(pwalls);
|
||||||
|
|
||||||
Polygons top_triangles, bottom_triangles;
|
Polygons top_triangles, bottom_triangles;
|
||||||
@ -528,6 +540,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
|
|||||||
90, // 90 degrees
|
90, // 90 degrees
|
||||||
0, // z position of the input plane
|
0, // z position of the input plane
|
||||||
false,
|
false,
|
||||||
|
cfg.throw_on_cancel,
|
||||||
ob, wh);
|
ob, wh);
|
||||||
pool.merge(curvedwalls);
|
pool.merge(curvedwalls);
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#define SLABASEPOOL_HPP
|
#define SLABASEPOOL_HPP
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -11,12 +12,14 @@ class TriangleMesh;
|
|||||||
namespace sla {
|
namespace sla {
|
||||||
|
|
||||||
using ExPolygons = std::vector<ExPolygon>;
|
using ExPolygons = std::vector<ExPolygon>;
|
||||||
|
using ThrowOnCancel = std::function<void(void)>;
|
||||||
|
|
||||||
/// Calculate the polygon representing the silhouette from the specified height
|
/// Calculate the polygon representing the silhouette from the specified height
|
||||||
void base_plate(const TriangleMesh& mesh,
|
void base_plate(const TriangleMesh& mesh, // input mesh
|
||||||
ExPolygons& output,
|
ExPolygons& output, // Output will be merged with
|
||||||
float zlevel = 0.1f,
|
float zlevel = 0.1f, // Plate creation level
|
||||||
float layerheight = 0.05f);
|
float layerheight = 0.05f, // The sampling height
|
||||||
|
ThrowOnCancel thrfn = [](){}); // Will be called frequently
|
||||||
|
|
||||||
struct PoolConfig {
|
struct PoolConfig {
|
||||||
double min_wall_thickness_mm = 2;
|
double min_wall_thickness_mm = 2;
|
||||||
@ -24,6 +27,8 @@ struct PoolConfig {
|
|||||||
double max_merge_distance_mm = 50;
|
double max_merge_distance_mm = 50;
|
||||||
double edge_radius_mm = 1;
|
double edge_radius_mm = 1;
|
||||||
|
|
||||||
|
ThrowOnCancel throw_on_cancel = [](){};
|
||||||
|
|
||||||
inline PoolConfig() {}
|
inline PoolConfig() {}
|
||||||
inline PoolConfig(double wt, double wh, double md, double er):
|
inline PoolConfig(double wt, double wh, double md, double er):
|
||||||
min_wall_thickness_mm(wt),
|
min_wall_thickness_mm(wt),
|
||||||
@ -35,8 +40,7 @@ struct PoolConfig {
|
|||||||
/// Calculate the pool for the mesh for SLA printing
|
/// Calculate the pool for the mesh for SLA printing
|
||||||
void create_base_pool(const ExPolygons& base_plate,
|
void create_base_pool(const ExPolygons& base_plate,
|
||||||
TriangleMesh& output_mesh,
|
TriangleMesh& output_mesh,
|
||||||
const PoolConfig& = PoolConfig()
|
const PoolConfig& = PoolConfig());
|
||||||
);
|
|
||||||
|
|
||||||
/// TODO: Currently the base plate of the pool will have half the height of the
|
/// TODO: Currently the base plate of the pool will have half the height of the
|
||||||
/// whole pool. So the carved out space has also half the height. This is not
|
/// whole pool. So the carved out space has also half the height. This is not
|
||||||
|
@ -58,7 +58,7 @@ struct Contour3D {
|
|||||||
points.insert(points.end(), ctr.points.begin(), ctr.points.end());
|
points.insert(points.end(), ctr.points.begin(), ctr.points.end());
|
||||||
indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end());
|
indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end());
|
||||||
|
|
||||||
for(auto n = s; n < indices.size(); n++) {
|
for(size_t n = s; n < indices.size(); n++) {
|
||||||
auto& idx = indices[n]; x(idx) += s3; y(idx) += s3; z(idx) += s3;
|
auto& idx = indices[n]; x(idx) += s3; y(idx) += s3; z(idx) += s3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -515,8 +515,13 @@ struct Pad {
|
|||||||
zlevel(ground_level + sla::get_pad_elevation(pcfg))
|
zlevel(ground_level + sla::get_pad_elevation(pcfg))
|
||||||
{
|
{
|
||||||
ExPolygons basep;
|
ExPolygons basep;
|
||||||
|
cfg.throw_on_cancel();
|
||||||
|
|
||||||
|
// The 0.1f is the layer height with which the mesh is sampled and then
|
||||||
|
// the layers are unified into one vector of polygons.
|
||||||
base_plate(object_support_mesh, basep,
|
base_plate(object_support_mesh, basep,
|
||||||
float(cfg.min_wall_height_mm)/*,layer_height*/);
|
float(cfg.min_wall_height_mm), 0.1f, pcfg.throw_on_cancel);
|
||||||
|
|
||||||
for(auto& bp : baseplate) basep.emplace_back(bp);
|
for(auto& bp : baseplate) basep.emplace_back(bp);
|
||||||
|
|
||||||
create_base_pool(basep, tmesh, cfg);
|
create_base_pool(basep, tmesh, cfg);
|
||||||
@ -622,12 +627,19 @@ class SLASupportTree::Impl {
|
|||||||
std::vector<Junction> m_junctions;
|
std::vector<Junction> m_junctions;
|
||||||
std::vector<Bridge> m_bridges;
|
std::vector<Bridge> m_bridges;
|
||||||
std::vector<CompactBridge> m_compact_bridges;
|
std::vector<CompactBridge> m_compact_bridges;
|
||||||
|
Controller m_ctl;
|
||||||
|
|
||||||
Pad m_pad;
|
Pad m_pad;
|
||||||
mutable TriangleMesh meshcache; mutable bool meshcache_valid;
|
mutable TriangleMesh meshcache; mutable bool meshcache_valid;
|
||||||
mutable double model_height = 0; // the full height of the model
|
mutable double model_height = 0; // the full height of the model
|
||||||
public:
|
public:
|
||||||
double ground_level = 0;
|
double ground_level = 0;
|
||||||
|
|
||||||
|
Impl() = default;
|
||||||
|
inline Impl(const Controller& ctl): m_ctl(ctl) {}
|
||||||
|
|
||||||
|
const Controller& ctl() const { return m_ctl; }
|
||||||
|
|
||||||
template<class...Args> Head& add_head(Args&&... args) {
|
template<class...Args> Head& add_head(Args&&... args) {
|
||||||
m_heads.emplace_back(std::forward<Args>(args)...);
|
m_heads.emplace_back(std::forward<Args>(args)...);
|
||||||
m_heads.back().id = long(m_heads.size() - 1);
|
m_heads.back().id = long(m_heads.size() - 1);
|
||||||
@ -710,27 +722,38 @@ public:
|
|||||||
meshcache = TriangleMesh();
|
meshcache = TriangleMesh();
|
||||||
|
|
||||||
for(auto& head : heads()) {
|
for(auto& head : heads()) {
|
||||||
|
if(m_ctl.stopcondition()) break;
|
||||||
auto&& m = mesh(head.mesh);
|
auto&& m = mesh(head.mesh);
|
||||||
meshcache.merge(m);
|
meshcache.merge(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& stick : pillars()) {
|
for(auto& stick : pillars()) {
|
||||||
|
if(m_ctl.stopcondition()) break;
|
||||||
meshcache.merge(mesh(stick.mesh));
|
meshcache.merge(mesh(stick.mesh));
|
||||||
meshcache.merge(mesh(stick.base));
|
meshcache.merge(mesh(stick.base));
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& j : junctions()) {
|
for(auto& j : junctions()) {
|
||||||
|
if(m_ctl.stopcondition()) break;
|
||||||
meshcache.merge(mesh(j.mesh));
|
meshcache.merge(mesh(j.mesh));
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& cb : compact_bridges()) {
|
for(auto& cb : compact_bridges()) {
|
||||||
|
if(m_ctl.stopcondition()) break;
|
||||||
meshcache.merge(mesh(cb.mesh));
|
meshcache.merge(mesh(cb.mesh));
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& bs : bridges()) {
|
for(auto& bs : bridges()) {
|
||||||
|
if(m_ctl.stopcondition()) break;
|
||||||
meshcache.merge(mesh(bs.mesh));
|
meshcache.merge(mesh(bs.mesh));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(m_ctl.stopcondition()) {
|
||||||
|
// In case of failure we have to return an empty mesh
|
||||||
|
meshcache = TriangleMesh();
|
||||||
|
return meshcache;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Is this necessary?
|
// TODO: Is this necessary?
|
||||||
meshcache.repair();
|
meshcache.repair();
|
||||||
|
|
||||||
@ -1347,7 +1370,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||||||
add_base(cfg.base_height_mm, cfg.base_radius_mm);
|
add_base(cfg.base_height_mm, cfg.base_radius_mm);
|
||||||
|
|
||||||
// connects to ground, eligible for bridging
|
// connects to ground, eligible for bridging
|
||||||
cl_centroids.emplace_back(sidehead.id);
|
cl_centroids.emplace_back(c);
|
||||||
} else {
|
} else {
|
||||||
// Creating the bridge to the nearest pillar
|
// Creating the bridge to the nearest pillar
|
||||||
|
|
||||||
@ -1659,22 +1682,19 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
|
|||||||
fullmesh.merge(get_pad());
|
fullmesh.merge(get_pad());
|
||||||
TriangleMeshSlicer slicer(&fullmesh);
|
TriangleMeshSlicer slicer(&fullmesh);
|
||||||
SlicedSupports ret;
|
SlicedSupports ret;
|
||||||
slicer.slice(heights, &ret, m_ctl.cancelfn);
|
slicer.slice(heights, &ret, get().ctl().cancelfn);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate,
|
const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate,
|
||||||
double min_wall_thickness_mm,
|
const PoolConfig& pcfg) const
|
||||||
double min_wall_height_mm,
|
|
||||||
double max_merge_distance_mm,
|
|
||||||
double edge_radius_mm) const
|
|
||||||
{
|
{
|
||||||
PoolConfig pcfg;
|
// PoolConfig pcfg;
|
||||||
pcfg.min_wall_thickness_mm = min_wall_thickness_mm;
|
// pcfg.min_wall_thickness_mm = min_wall_thickness_mm;
|
||||||
pcfg.min_wall_height_mm = min_wall_height_mm;
|
// pcfg.min_wall_height_mm = min_wall_height_mm;
|
||||||
pcfg.max_merge_distance_mm = max_merge_distance_mm;
|
// pcfg.max_merge_distance_mm = max_merge_distance_mm;
|
||||||
pcfg.edge_radius_mm = edge_radius_mm;
|
// pcfg.edge_radius_mm = edge_radius_mm;
|
||||||
return m_impl->create_pad(merged_mesh(), baseplate, pcfg).tmesh;
|
return m_impl->create_pad(merged_mesh(), baseplate, pcfg).tmesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1687,14 +1707,14 @@ SLASupportTree::SLASupportTree(const PointSet &points,
|
|||||||
const EigenMesh3D& emesh,
|
const EigenMesh3D& emesh,
|
||||||
const SupportConfig &cfg,
|
const SupportConfig &cfg,
|
||||||
const Controller &ctl):
|
const Controller &ctl):
|
||||||
m_impl(new Impl()), m_ctl(ctl)
|
m_impl(new Impl(ctl))
|
||||||
{
|
{
|
||||||
m_impl->ground_level = emesh.ground_level - cfg.object_elevation_mm;
|
m_impl->ground_level = emesh.ground_level - cfg.object_elevation_mm;
|
||||||
generate(points, emesh, cfg, ctl);
|
generate(points, emesh, cfg, ctl);
|
||||||
}
|
}
|
||||||
|
|
||||||
SLASupportTree::SLASupportTree(const SLASupportTree &c):
|
SLASupportTree::SLASupportTree(const SLASupportTree &c):
|
||||||
m_impl(new Impl(*c.m_impl)), m_ctl(c.m_ctl) {}
|
m_impl(new Impl(*c.m_impl)) {}
|
||||||
|
|
||||||
SLASupportTree &SLASupportTree::operator=(const SLASupportTree &c)
|
SLASupportTree &SLASupportTree::operator=(const SLASupportTree &c)
|
||||||
{
|
{
|
||||||
|
@ -69,6 +69,8 @@ struct SupportConfig {
|
|||||||
double object_elevation_mm = 10;
|
double object_elevation_mm = 10;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PoolConfig;
|
||||||
|
|
||||||
/// A Control structure for the support calculation. Consists of the status
|
/// A Control structure for the support calculation. Consists of the status
|
||||||
/// indicator callback and the stop condition predicate.
|
/// indicator callback and the stop condition predicate.
|
||||||
struct Controller {
|
struct Controller {
|
||||||
@ -119,7 +121,6 @@ public:
|
|||||||
class SLASupportTree {
|
class SLASupportTree {
|
||||||
class Impl;
|
class Impl;
|
||||||
std::unique_ptr<Impl> m_impl;
|
std::unique_ptr<Impl> m_impl;
|
||||||
Controller m_ctl;
|
|
||||||
|
|
||||||
Impl& get() { return *m_impl; }
|
Impl& get() { return *m_impl; }
|
||||||
const Impl& get() const { return *m_impl; }
|
const Impl& get() const { return *m_impl; }
|
||||||
@ -158,10 +159,7 @@ public:
|
|||||||
|
|
||||||
/// Adding the "pad" (base pool) under the supports
|
/// Adding the "pad" (base pool) under the supports
|
||||||
const TriangleMesh& add_pad(const SliceLayer& baseplate,
|
const TriangleMesh& add_pad(const SliceLayer& baseplate,
|
||||||
double min_wall_thickness_mm,
|
const PoolConfig& pcfg) const;
|
||||||
double min_wall_height_mm,
|
|
||||||
double max_merge_distance_mm,
|
|
||||||
double edge_radius_mm) const;
|
|
||||||
|
|
||||||
/// Get the pad geometry
|
/// Get the pad geometry
|
||||||
const TriangleMesh& get_pad() const;
|
const TriangleMesh& get_pad() const;
|
||||||
|
@ -445,7 +445,7 @@ void SLAPrint::process()
|
|||||||
|
|
||||||
// Slicing the model object. This method is oversimplified and needs to
|
// Slicing the model object. This method is oversimplified and needs to
|
||||||
// be compared with the fff slicing algorithm for verification
|
// be compared with the fff slicing algorithm for verification
|
||||||
auto slice_model = [this, ilh, ilhd](SLAPrintObject& po) {
|
auto slice_model = [this, ilh](SLAPrintObject& po) {
|
||||||
double lh = po.m_config.layer_height.getFloat();
|
double lh = po.m_config.layer_height.getFloat();
|
||||||
|
|
||||||
TriangleMesh mesh = po.transformed_mesh();
|
TriangleMesh mesh = po.transformed_mesh();
|
||||||
@ -530,6 +530,11 @@ void SLAPrint::process()
|
|||||||
po.m_supportdata->support_tree_ptr.reset(
|
po.m_supportdata->support_tree_ptr.reset(
|
||||||
new SLASupportTree(pts, emesh, scfg, ctl));
|
new SLASupportTree(pts, emesh, scfg, ctl));
|
||||||
|
|
||||||
|
// Create the unified mesh
|
||||||
|
auto rc = SlicingStatus::RELOAD_SCENE;
|
||||||
|
set_status(-1, L("Visualizing supports"));
|
||||||
|
po.m_supportdata->support_tree_ptr->merged_mesh();
|
||||||
|
set_status(-1, L("Visualizing supports"), rc);
|
||||||
} catch(sla::SLASupportsStoppedException&) {
|
} catch(sla::SLASupportsStoppedException&) {
|
||||||
// no need to rethrow
|
// no need to rethrow
|
||||||
// throw_if_canceled();
|
// throw_if_canceled();
|
||||||
@ -560,18 +565,23 @@ void SLAPrint::process()
|
|||||||
auto&& trmesh = po.transformed_mesh();
|
auto&& trmesh = po.transformed_mesh();
|
||||||
|
|
||||||
// This call can get pretty time consuming
|
// This call can get pretty time consuming
|
||||||
if(elevation < pad_h) sla::base_plate(trmesh, bp,
|
auto thrfn = [this](){ throw_if_canceled(); };
|
||||||
float(pad_h), float(lh));
|
|
||||||
|
|
||||||
po.m_supportdata->support_tree_ptr->add_pad(bp, wt, h, md, er);
|
if(elevation < pad_h)
|
||||||
|
sla::base_plate(trmesh, bp, float(pad_h), float(lh),
|
||||||
|
thrfn);
|
||||||
|
|
||||||
|
pcfg.throw_on_cancel = thrfn;
|
||||||
|
po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the base pool (which means also the support tree) is
|
// // if the base pool (which means also the support tree) is
|
||||||
// done, do a refresh when indicating progress. Now the
|
// // done, do a refresh when indicating progress. Now the
|
||||||
// geometries for the supports and the optional base pad are
|
// // geometries for the supports and the optional base pad are
|
||||||
// ready. We can grant access for the control thread to read
|
// // ready. We can grant access for the control thread to read
|
||||||
// the geometries, but first we have to update the caches:
|
// // the geometries, but first we have to update the caches:
|
||||||
po.support_mesh(); /*po->pad_mesh();*/
|
// po.support_mesh(); /*po->pad_mesh();*/
|
||||||
|
po.throw_if_canceled();
|
||||||
auto rc = SlicingStatus::RELOAD_SCENE;
|
auto rc = SlicingStatus::RELOAD_SCENE;
|
||||||
set_status(-1, L("Visualizing supports"), rc);
|
set_status(-1, L("Visualizing supports"), rc);
|
||||||
};
|
};
|
||||||
@ -589,9 +599,9 @@ void SLAPrint::process()
|
|||||||
|
|
||||||
// We have the layer polygon collection but we need to unite them into
|
// We have the layer polygon collection but we need to unite them into
|
||||||
// an index where the key is the height level in discrete levels (clipper)
|
// an index where the key is the height level in discrete levels (clipper)
|
||||||
auto index_slices = [this, ilh, ilhd](SLAPrintObject& po) {
|
auto index_slices = [ilhd](SLAPrintObject& po) {
|
||||||
po.m_slice_index.clear();
|
po.m_slice_index.clear();
|
||||||
auto sih = LevelID(scale_(ilh));
|
auto sih = LevelID(scale_(ilhd));
|
||||||
|
|
||||||
// For all print objects, go through its initial layers and place them
|
// For all print objects, go through its initial layers and place them
|
||||||
// into the layers hash
|
// into the layers hash
|
||||||
@ -635,7 +645,7 @@ void SLAPrint::process()
|
|||||||
// shortcut for empty index into the slice vectors
|
// shortcut for empty index into the slice vectors
|
||||||
static const auto EMPTY_SLICE = SLAPrintObject::SliceRecord::NONE;
|
static const auto EMPTY_SLICE = SLAPrintObject::SliceRecord::NONE;
|
||||||
|
|
||||||
for(int i = 0; i < oslices.size(); ++i) {
|
for(size_t i = 0; i < oslices.size(); ++i) {
|
||||||
LevelID h = levelids[i];
|
LevelID h = levelids[i];
|
||||||
|
|
||||||
float fh = float(double(h) * SCALING_FACTOR);
|
float fh = float(double(h) * SCALING_FACTOR);
|
||||||
@ -652,7 +662,7 @@ void SLAPrint::process()
|
|||||||
po.m_supportdata->level_ids.clear();
|
po.m_supportdata->level_ids.clear();
|
||||||
po.m_supportdata->level_ids.reserve(sslices.size());
|
po.m_supportdata->level_ids.reserve(sslices.size());
|
||||||
|
|
||||||
for(int i = 0; i < sslices.size(); ++i) {
|
for(int i = 0; i < int(sslices.size()); ++i) {
|
||||||
int a = i == 0 ? 0 : 1;
|
int a = i == 0 ? 0 : 1;
|
||||||
int b = i == 0 ? 0 : i - 1;
|
int b = i == 0 ? 0 : i - 1;
|
||||||
LevelID h = sminZ + a * sih + b * slh;
|
LevelID h = sminZ + a * sih + b * slh;
|
||||||
@ -662,7 +672,7 @@ void SLAPrint::process()
|
|||||||
|
|
||||||
SLAPrintObject::SliceRecord& sr = po.m_slice_index[fh];
|
SLAPrintObject::SliceRecord& sr = po.m_slice_index[fh];
|
||||||
assert(sr.support_slices_idx == EMPTY_SLICE);
|
assert(sr.support_slices_idx == EMPTY_SLICE);
|
||||||
sr.support_slices_idx = i;
|
sr.support_slices_idx = SLAPrintObject::SliceRecord::Idx(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -670,7 +680,7 @@ void SLAPrint::process()
|
|||||||
auto& levels = m_printer_input;
|
auto& levels = m_printer_input;
|
||||||
|
|
||||||
// Rasterizing the model objects, and their supports
|
// Rasterizing the model objects, and their supports
|
||||||
auto rasterize = [this, ilh, ilhd, max_objstatus, &levels]() {
|
auto rasterize = [this, max_objstatus, &levels]() {
|
||||||
if(canceled()) return;
|
if(canceled()) return;
|
||||||
|
|
||||||
// clear the rasterizer input
|
// clear the rasterizer input
|
||||||
@ -688,14 +698,14 @@ void SLAPrint::process()
|
|||||||
// now merge this object's support and object slices with the rest
|
// now merge this object's support and object slices with the rest
|
||||||
// of the print object slices
|
// of the print object slices
|
||||||
|
|
||||||
for(int i = 0; i < oslices.size(); ++i) {
|
for(size_t i = 0; i < oslices.size(); ++i) {
|
||||||
auto& lyrs = levels[gndlvl + po.m_level_ids[i]];
|
auto& lyrs = levels[gndlvl + po.m_level_ids[i]];
|
||||||
lyrs.emplace_back(oslices[i], po.m_instances);
|
lyrs.emplace_back(oslices[i], po.m_instances);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!po.m_supportdata) continue;
|
if(!po.m_supportdata) continue;
|
||||||
auto& sslices = po.m_supportdata->support_slices;
|
auto& sslices = po.m_supportdata->support_slices;
|
||||||
for(int i = 0; i < sslices.size(); ++i) {
|
for(size_t i = 0; i < sslices.size(); ++i) {
|
||||||
auto& lyrs = levels[gndlvl + po.m_supportdata->level_ids[i]];
|
auto& lyrs = levels[gndlvl + po.m_supportdata->level_ids[i]];
|
||||||
lyrs.emplace_back(sslices[i], po.m_instances);
|
lyrs.emplace_back(sslices[i], po.m_instances);
|
||||||
}
|
}
|
||||||
@ -713,8 +723,8 @@ void SLAPrint::process()
|
|||||||
|
|
||||||
double w = printcfg.display_width.getFloat();
|
double w = printcfg.display_width.getFloat();
|
||||||
double h = printcfg.display_height.getFloat();
|
double h = printcfg.display_height.getFloat();
|
||||||
unsigned pw = printcfg.display_pixels_x.getInt();
|
auto pw = unsigned(printcfg.display_pixels_x.getInt());
|
||||||
unsigned ph = printcfg.display_pixels_y.getInt();
|
auto ph = unsigned(printcfg.display_pixels_y.getInt());
|
||||||
double lh = ocfg.layer_height.getFloat();
|
double lh = ocfg.layer_height.getFloat();
|
||||||
double exp_t = matcfg.exposure_time.getFloat();
|
double exp_t = matcfg.exposure_time.getFloat();
|
||||||
double iexp_t = matcfg.initial_exposure_time.getFloat();
|
double iexp_t = matcfg.initial_exposure_time.getFloat();
|
||||||
@ -926,6 +936,18 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
|
|||||||
return invalidated;
|
return invalidated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true if an object step is done on all objects and there's at least one object.
|
||||||
|
bool SLAPrint::is_step_done(SLAPrintObjectStep step) const
|
||||||
|
{
|
||||||
|
if (m_objects.empty())
|
||||||
|
return false;
|
||||||
|
tbb::mutex::scoped_lock lock(this->state_mutex());
|
||||||
|
for (const SLAPrintObject *object : m_objects)
|
||||||
|
if (! object->m_state.is_done_unguarded(step))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object):
|
SLAPrintObject::SLAPrintObject(SLAPrint *print, ModelObject *model_object):
|
||||||
Inherited(print, model_object),
|
Inherited(print, model_object),
|
||||||
m_stepmask(slaposCount, true),
|
m_stepmask(slaposCount, true),
|
||||||
@ -1098,7 +1120,9 @@ TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const
|
|||||||
const TriangleMesh& SLAPrintObject::support_mesh() const
|
const TriangleMesh& SLAPrintObject::support_mesh() const
|
||||||
{
|
{
|
||||||
if(m_config.supports_enable.getBool() && m_supportdata &&
|
if(m_config.supports_enable.getBool() && m_supportdata &&
|
||||||
m_supportdata->support_tree_ptr) return m_supportdata->support_tree_ptr->merged_mesh();
|
m_supportdata->support_tree_ptr) {
|
||||||
|
return m_supportdata->support_tree_ptr->merged_mesh();
|
||||||
|
}
|
||||||
|
|
||||||
return EMPTY_MESH;
|
return EMPTY_MESH;
|
||||||
}
|
}
|
||||||
|
@ -185,6 +185,9 @@ public:
|
|||||||
bool empty() const override { return m_objects.empty(); }
|
bool empty() const override { return m_objects.empty(); }
|
||||||
ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) override;
|
ApplyStatus apply(const Model &model, const DynamicPrintConfig &config) override;
|
||||||
void process() override;
|
void process() override;
|
||||||
|
bool is_step_done(SLAPrintObjectStep step) const;
|
||||||
|
// Returns true if the last step was finished with success.
|
||||||
|
bool finished() const override { return this->is_step_done(slaposIndexSlices); }
|
||||||
|
|
||||||
template<class Fmt> void export_raster(const std::string& fname) {
|
template<class Fmt> void export_raster(const std::string& fname) {
|
||||||
if(m_printer) m_printer->save<Fmt>(fname);
|
if(m_printer) m_printer->save<Fmt>(fname);
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#include <boost/property_tree/ini_parser.hpp>
|
#include <boost/property_tree/ini_parser.hpp>
|
||||||
#include <boost/property_tree/ptree.hpp>
|
#include <boost/property_tree/ptree.hpp>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -125,8 +126,13 @@ void AppConfig::load()
|
|||||||
|
|
||||||
void AppConfig::save()
|
void AppConfig::save()
|
||||||
{
|
{
|
||||||
|
// The config is first written to a file with a PID suffix and then moved
|
||||||
|
// to avoid race conditions with multiple instances of Slic3r
|
||||||
|
const auto path = config_path();
|
||||||
|
std::string path_pid = (boost::format("%1%.%2%") % path % get_current_pid()).str();
|
||||||
|
|
||||||
boost::nowide::ofstream c;
|
boost::nowide::ofstream c;
|
||||||
c.open(AppConfig::config_path(), std::ios::out | std::ios::trunc);
|
c.open(path_pid, std::ios::out | std::ios::trunc);
|
||||||
c << "# " << Slic3r::header_slic3r_generated() << std::endl;
|
c << "# " << Slic3r::header_slic3r_generated() << std::endl;
|
||||||
// Make sure the "no" category is written first.
|
// Make sure the "no" category is written first.
|
||||||
for (const std::pair<std::string, std::string> &kvp : m_storage[""])
|
for (const std::pair<std::string, std::string> &kvp : m_storage[""])
|
||||||
@ -155,6 +161,8 @@ void AppConfig::save()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.close();
|
c.close();
|
||||||
|
|
||||||
|
rename_file(path_pid, path);
|
||||||
m_dirty = false;
|
m_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,6 +110,11 @@ public:
|
|||||||
State state() const { return m_state; }
|
State state() const { return m_state; }
|
||||||
bool idle() const { return m_state == STATE_IDLE; }
|
bool idle() const { return m_state == STATE_IDLE; }
|
||||||
bool running() const { return m_state == STATE_STARTED || m_state == STATE_RUNNING || m_state == STATE_FINISHED || m_state == STATE_CANCELED; }
|
bool running() const { return m_state == STATE_STARTED || m_state == STATE_RUNNING || m_state == STATE_FINISHED || m_state == STATE_CANCELED; }
|
||||||
|
// Returns true if the last step of the active print was finished with success.
|
||||||
|
// The "finished" flag is reset by the apply() method, if it changes the state of the print.
|
||||||
|
// This "finished" flag does not account for the final export of the output file (.gcode or zipped PNGs),
|
||||||
|
// and it does not account for the OctoPrint scheduling.
|
||||||
|
bool finished() const { return m_print->finished(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void thread_proc();
|
void thread_proc();
|
||||||
|
@ -323,17 +323,17 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent, const int label_width) :
|
|||||||
double brim_width = config->opt_float("brim_width");
|
double brim_width = config->opt_float("brim_width");
|
||||||
if (boost::any_cast<bool>(value) == true)
|
if (boost::any_cast<bool>(value) == true)
|
||||||
{
|
{
|
||||||
new_val = m_brim_width == 0.0 ? 10 :
|
new_val = m_brim_width == 0.0 ? 5 :
|
||||||
m_brim_width < 0.0 ? m_brim_width * (-1) :
|
m_brim_width < 0.0 ? m_brim_width * (-1) :
|
||||||
m_brim_width;
|
m_brim_width;
|
||||||
}
|
}
|
||||||
else{
|
else {
|
||||||
m_brim_width = brim_width * (-1);
|
m_brim_width = brim_width * (-1);
|
||||||
new_val = 0;
|
new_val = 0;
|
||||||
}
|
}
|
||||||
new_conf.set_key_value("brim_width", new ConfigOptionFloat(new_val));
|
new_conf.set_key_value("brim_width", new ConfigOptionFloat(new_val));
|
||||||
}
|
}
|
||||||
else{ //(opt_key == "support")
|
else { //(opt_key == "support")
|
||||||
const wxString& selection = boost::any_cast<wxString>(value);
|
const wxString& selection = boost::any_cast<wxString>(value);
|
||||||
|
|
||||||
auto support_material = selection == _("None") ? false : true;
|
auto support_material = selection == _("None") ? false : true;
|
||||||
@ -2212,6 +2212,7 @@ void Plater::priv::set_current_panel(wxPanel* panel)
|
|||||||
}
|
}
|
||||||
else if (current_panel == preview)
|
else if (current_panel == preview)
|
||||||
{
|
{
|
||||||
|
this->q->reslice();
|
||||||
preview->reload_print();
|
preview->reload_print();
|
||||||
preview->set_canvas_as_dirty();
|
preview->set_canvas_as_dirty();
|
||||||
}
|
}
|
||||||
@ -3064,7 +3065,7 @@ void Plater::reslice()
|
|||||||
#else
|
#else
|
||||||
this->p->canvas3D->reload_scene(false);
|
this->p->canvas3D->reload_scene(false);
|
||||||
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
#endif // ENABLE_REMOVE_TABS_FROM_PLATER
|
||||||
if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 && !this->p->background_process.running()) {
|
if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 && !this->p->background_process.running() && !this->p->background_process.finished()) {
|
||||||
// The print is valid and it can be started.
|
// The print is valid and it can be started.
|
||||||
if (this->p->background_process.start())
|
if (this->p->background_process.start())
|
||||||
this->p->statusbar()->set_cancel_callback([this]() {
|
this->p->statusbar()->set_cancel_callback([this]() {
|
||||||
|
@ -49,8 +49,6 @@ _constant()
|
|||||||
Ref<StaticPrintConfig> config()
|
Ref<StaticPrintConfig> config()
|
||||||
%code%{ RETVAL = &THIS->config(); %};
|
%code%{ RETVAL = &THIS->config(); %};
|
||||||
Points copies();
|
Points copies();
|
||||||
t_layer_height_ranges layer_height_ranges()
|
|
||||||
%code%{ RETVAL = THIS->layer_height_ranges; %};
|
|
||||||
std::vector<double> layer_height_profile()
|
std::vector<double> layer_height_profile()
|
||||||
%code%{ RETVAL = THIS->layer_height_profile; %};
|
%code%{ RETVAL = THIS->layer_height_profile; %};
|
||||||
Clone<BoundingBox> bounding_box();
|
Clone<BoundingBox> bounding_box();
|
||||||
@ -58,9 +56,6 @@ _constant()
|
|||||||
Points _shifted_copies()
|
Points _shifted_copies()
|
||||||
%code%{ RETVAL = THIS->copies(); %};
|
%code%{ RETVAL = THIS->copies(); %};
|
||||||
|
|
||||||
void set_layer_height_ranges(t_layer_height_ranges layer_height_ranges)
|
|
||||||
%code%{ THIS->layer_height_ranges = layer_height_ranges; %};
|
|
||||||
|
|
||||||
size_t layer_count();
|
size_t layer_count();
|
||||||
Ref<Layer> get_layer(int idx);
|
Ref<Layer> get_layer(int idx);
|
||||||
|
|
||||||
|