mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-28 19:21:20 -06:00
Merge branch 'master' into tm_colldetection_upgr
This commit is contained in:
commit
3999ffe713
131 changed files with 71551 additions and 40902 deletions
|
|
@ -32,6 +32,7 @@ public:
|
|||
}
|
||||
this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1));
|
||||
}
|
||||
void reset() { this->defined = false; this->min = PointClass::Zero(); this->max = PointClass::Zero(); }
|
||||
void merge(const PointClass &point);
|
||||
void merge(const std::vector<PointClass> &points);
|
||||
void merge(const BoundingBoxBase<PointClass> &bb);
|
||||
|
|
|
|||
|
|
@ -310,16 +310,15 @@ ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines)
|
|||
polylines->insert(polylines->end(), tp.begin(), tp.end());
|
||||
}
|
||||
|
||||
void
|
||||
ExPolygon::get_trapezoids(Polygons* polygons) const
|
||||
/*
|
||||
void ExPolygon::get_trapezoids(Polygons* polygons) const
|
||||
{
|
||||
ExPolygons expp;
|
||||
expp.push_back(*this);
|
||||
boost::polygon::get_trapezoids(*polygons, expp);
|
||||
}
|
||||
|
||||
void
|
||||
ExPolygon::get_trapezoids(Polygons* polygons, double angle) const
|
||||
void ExPolygon::get_trapezoids(Polygons* polygons, double angle) const
|
||||
{
|
||||
ExPolygon clone = *this;
|
||||
clone.rotate(PI/2 - angle, Point(0,0));
|
||||
|
|
@ -327,12 +326,12 @@ ExPolygon::get_trapezoids(Polygons* polygons, double angle) const
|
|||
for (Polygons::iterator polygon = polygons->begin(); polygon != polygons->end(); ++polygon)
|
||||
polygon->rotate(-(PI/2 - angle), Point(0,0));
|
||||
}
|
||||
*/
|
||||
|
||||
// This algorithm may return more trapezoids than necessary
|
||||
// (i.e. it may break a single trapezoid in several because
|
||||
// other parts of the object have x coordinates in the middle)
|
||||
void
|
||||
ExPolygon::get_trapezoids2(Polygons* polygons) const
|
||||
void ExPolygon::get_trapezoids2(Polygons* polygons) const
|
||||
{
|
||||
// get all points of this ExPolygon
|
||||
Points pp = *this;
|
||||
|
|
@ -370,8 +369,7 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
ExPolygon::get_trapezoids2(Polygons* polygons, double angle) const
|
||||
void ExPolygon::get_trapezoids2(Polygons* polygons, double angle) const
|
||||
{
|
||||
ExPolygon clone = *this;
|
||||
clone.rotate(PI/2 - angle, Point(0,0));
|
||||
|
|
@ -382,8 +380,7 @@ ExPolygon::get_trapezoids2(Polygons* polygons, double angle) const
|
|||
|
||||
// While this triangulates successfully, it's NOT a constrained triangulation
|
||||
// as it will create more vertices on the boundaries than the ones supplied.
|
||||
void
|
||||
ExPolygon::triangulate(Polygons* polygons) const
|
||||
void ExPolygon::triangulate(Polygons* polygons) const
|
||||
{
|
||||
// first make trapezoids
|
||||
Polygons trapezoids;
|
||||
|
|
@ -394,8 +391,8 @@ ExPolygon::triangulate(Polygons* polygons) const
|
|||
polygon->triangulate_convex(polygons);
|
||||
}
|
||||
|
||||
void
|
||||
ExPolygon::triangulate_pp(Polygons* polygons) const
|
||||
/*
|
||||
void ExPolygon::triangulate_pp(Polygons* polygons) const
|
||||
{
|
||||
// convert polygons
|
||||
std::list<TPPLPoly> input;
|
||||
|
|
@ -452,9 +449,113 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
|
|||
polygons->push_back(p);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void
|
||||
ExPolygon::triangulate_p2t(Polygons* polygons) const
|
||||
std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygon &ex)
|
||||
{
|
||||
std::list<TPPLPoly> input;
|
||||
// contour
|
||||
{
|
||||
input.emplace_back();
|
||||
TPPLPoly &p = input.back();
|
||||
p.Init(int(ex.contour.points.size()));
|
||||
for (const Point &point : ex.contour.points) {
|
||||
size_t i = &point - &ex.contour.points.front();
|
||||
p[i].x = point(0);
|
||||
p[i].y = point(1);
|
||||
}
|
||||
p.SetHole(false);
|
||||
}
|
||||
// holes
|
||||
for (const Polygon &hole : ex.holes) {
|
||||
input.emplace_back();
|
||||
TPPLPoly &p = input.back();
|
||||
p.Init(hole.points.size());
|
||||
for (const Point &point : hole.points) {
|
||||
size_t i = &point - &hole.points.front();
|
||||
p[i].x = point(0);
|
||||
p[i].y = point(1);
|
||||
}
|
||||
p.SetHole(true);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygons &expps)
|
||||
{
|
||||
std::list<TPPLPoly> input;
|
||||
for (const ExPolygon &ex : expps) {
|
||||
// contour
|
||||
{
|
||||
input.emplace_back();
|
||||
TPPLPoly &p = input.back();
|
||||
p.Init(int(ex.contour.points.size()));
|
||||
for (const Point &point : ex.contour.points) {
|
||||
size_t i = &point - &ex.contour.points.front();
|
||||
p[i].x = point(0);
|
||||
p[i].y = point(1);
|
||||
}
|
||||
p.SetHole(false);
|
||||
}
|
||||
// holes
|
||||
for (const Polygon &hole : ex.holes) {
|
||||
input.emplace_back();
|
||||
TPPLPoly &p = input.back();
|
||||
p.Init(hole.points.size());
|
||||
for (const Point &point : hole.points) {
|
||||
size_t i = &point - &hole.points.front();
|
||||
p[i].x = point(0);
|
||||
p[i].y = point(1);
|
||||
}
|
||||
p.SetHole(true);
|
||||
}
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
std::vector<Point> polypartition_output_to_triangles(const std::list<TPPLPoly> &output)
|
||||
{
|
||||
size_t num_triangles = 0;
|
||||
for (const TPPLPoly &poly : output)
|
||||
if (poly.GetNumPoints() >= 3)
|
||||
num_triangles += (size_t)poly.GetNumPoints() - 2;
|
||||
std::vector<Point> triangles;
|
||||
triangles.reserve(triangles.size() + num_triangles * 3);
|
||||
for (const TPPLPoly &poly : output) {
|
||||
long num_points = poly.GetNumPoints();
|
||||
if (num_points >= 3) {
|
||||
const TPPLPoint *pt0 = &poly[0];
|
||||
const TPPLPoint *pt1 = nullptr;
|
||||
const TPPLPoint *pt2 = &poly[1];
|
||||
for (long i = 2; i < num_points; ++ i) {
|
||||
pt1 = pt2;
|
||||
pt2 = &poly[i];
|
||||
triangles.emplace_back(coord_t(pt0->x), coord_t(pt0->y));
|
||||
triangles.emplace_back(coord_t(pt1->x), coord_t(pt1->y));
|
||||
triangles.emplace_back(coord_t(pt2->x), coord_t(pt2->y));
|
||||
}
|
||||
}
|
||||
}
|
||||
return triangles;
|
||||
}
|
||||
|
||||
void ExPolygon::triangulate_pp(Points *triangles) const
|
||||
{
|
||||
ExPolygons expp = union_ex(simplify_polygons(to_polygons(*this), true));
|
||||
std::list<TPPLPoly> input = expoly_to_polypartition_input(expp);
|
||||
// perform triangulation
|
||||
std::list<TPPLPoly> output;
|
||||
int res = TPPLPartition().Triangulate_MONO(&input, &output);
|
||||
// int TPPLPartition::Triangulate_EC(TPPLPolyList *inpolys, TPPLPolyList *triangles) {
|
||||
if (res != 1)
|
||||
throw std::runtime_error("Triangulation failed");
|
||||
*triangles = polypartition_output_to_triangles(output);
|
||||
}
|
||||
|
||||
// Uses the Poly2tri library maintained by Jan Niklas Hasse @jhasse // https://github.com/jhasse/poly2tri
|
||||
// See https://github.com/jhasse/poly2tri/blob/master/README.md for the limitations of the library!
|
||||
// No duplicate points are allowed, no very close points, holes must not touch outer contour etc.
|
||||
void ExPolygon::triangulate_p2t(Polygons* polygons) const
|
||||
{
|
||||
ExPolygons expp = simplify_polygons_ex(*this, true);
|
||||
|
||||
|
|
@ -478,16 +579,21 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const
|
|||
}
|
||||
|
||||
// perform triangulation
|
||||
cdt.Triangulate();
|
||||
std::vector<p2t::Triangle*> triangles = cdt.GetTriangles();
|
||||
|
||||
for (std::vector<p2t::Triangle*>::const_iterator triangle = triangles.begin(); triangle != triangles.end(); ++triangle) {
|
||||
Polygon p;
|
||||
for (int i = 0; i <= 2; ++i) {
|
||||
p2t::Point* point = (*triangle)->GetPoint(i);
|
||||
p.points.push_back(Point(point->x, point->y));
|
||||
try {
|
||||
cdt.Triangulate();
|
||||
std::vector<p2t::Triangle*> triangles = cdt.GetTriangles();
|
||||
|
||||
for (std::vector<p2t::Triangle*>::const_iterator triangle = triangles.begin(); triangle != triangles.end(); ++triangle) {
|
||||
Polygon p;
|
||||
for (int i = 0; i <= 2; ++i) {
|
||||
p2t::Point* point = (*triangle)->GetPoint(i);
|
||||
p.points.push_back(Point(point->x, point->y));
|
||||
}
|
||||
polygons->push_back(p);
|
||||
}
|
||||
polygons->push_back(p);
|
||||
} catch (const std::runtime_error & /* err */) {
|
||||
assert(false);
|
||||
// just ignore, don't triangulate
|
||||
}
|
||||
|
||||
for (p2t::Point *ptr : ContourPoints)
|
||||
|
|
@ -495,8 +601,7 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const
|
|||
}
|
||||
}
|
||||
|
||||
Lines
|
||||
ExPolygon::lines() const
|
||||
Lines ExPolygon::lines() const
|
||||
{
|
||||
Lines lines = this->contour.lines();
|
||||
for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@
|
|||
#include "Polyline.hpp"
|
||||
#include <vector>
|
||||
|
||||
// polygon class of the polypartition library
|
||||
class TPPLPoly;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class ExPolygon;
|
||||
|
|
@ -55,12 +58,13 @@ public:
|
|||
void simplify(double tolerance, ExPolygons* expolygons) const;
|
||||
void medial_axis(double max_width, double min_width, ThickPolylines* polylines) const;
|
||||
void medial_axis(double max_width, double min_width, Polylines* polylines) const;
|
||||
void get_trapezoids(Polygons* polygons) const;
|
||||
void get_trapezoids(Polygons* polygons, double angle) const;
|
||||
// void get_trapezoids(Polygons* polygons) const;
|
||||
// void get_trapezoids(Polygons* polygons, double angle) const;
|
||||
void get_trapezoids2(Polygons* polygons) const;
|
||||
void get_trapezoids2(Polygons* polygons, double angle) const;
|
||||
void triangulate(Polygons* polygons) const;
|
||||
void triangulate_pp(Polygons* polygons) const;
|
||||
// Triangulate into triples of points.
|
||||
void triangulate_pp(Points *triangles) const;
|
||||
void triangulate_p2t(Polygons* polygons) const;
|
||||
Lines lines() const;
|
||||
};
|
||||
|
|
@ -297,6 +301,10 @@ extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons);
|
|||
|
||||
extern bool remove_sticks(ExPolygon &poly);
|
||||
|
||||
extern std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygons &expp);
|
||||
extern std::list<TPPLPoly> expoly_to_polypartition_input(const ExPolygon &ex);
|
||||
extern std::vector<Point> polypartition_output_to_triangles(const std::list<TPPLPoly> &output);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
// start Boost
|
||||
|
|
|
|||
|
|
@ -6,9 +6,12 @@
|
|||
#include <float.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../BoundingBox.hpp"
|
||||
#include "../PrintConfig.hpp"
|
||||
#include "../Utils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
@ -38,6 +41,7 @@ struct FillParams
|
|||
// in this case we don't try to make more continuous paths
|
||||
bool complete;
|
||||
};
|
||||
static_assert(IsTriviallyCopyable<FillParams>::value, "FillParams class is not POD (and it should be - see constructor).");
|
||||
|
||||
class Fill
|
||||
{
|
||||
|
|
|
|||
|
|
@ -582,10 +582,7 @@ namespace Slic3r {
|
|||
|
||||
IdToLayerHeightsProfileMap::iterator obj_layer_heights_profile = m_layer_heights_profiles.find(object.first);
|
||||
if (obj_layer_heights_profile != m_layer_heights_profiles.end())
|
||||
{
|
||||
object.second->layer_height_profile = obj_layer_heights_profile->second;
|
||||
object.second->layer_height_profile_valid = true;
|
||||
}
|
||||
|
||||
IdToSlaSupportPointsMap::iterator obj_sla_support_points = m_sla_support_points.find(object.first);
|
||||
if (obj_sla_support_points != m_sla_support_points.end() && !obj_sla_support_points->second.empty())
|
||||
|
|
@ -1926,7 +1923,7 @@ namespace Slic3r {
|
|||
for (const ModelObject* object : model.objects)
|
||||
{
|
||||
++count;
|
||||
std::vector<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
|
||||
const std::vector<double> &layer_height_profile = object->layer_height_profile;
|
||||
if ((layer_height_profile.size() >= 4) && ((layer_height_profile.size() % 2) == 0))
|
||||
{
|
||||
sprintf(buffer, "object_id=%d|", count);
|
||||
|
|
|
|||
|
|
@ -279,8 +279,8 @@ void AMFParserContext::startElement(const char *name, const char **atts)
|
|||
node_type_new = NODE_TYPE_VERTICES;
|
||||
else if (strcmp(name, "volume") == 0) {
|
||||
assert(! m_volume);
|
||||
m_volume = m_object->add_volume(TriangleMesh());
|
||||
node_type_new = NODE_TYPE_VOLUME;
|
||||
m_volume = m_object->add_volume(TriangleMesh());
|
||||
node_type_new = NODE_TYPE_VOLUME;
|
||||
}
|
||||
} else if (m_path[2] == NODE_TYPE_INSTANCE) {
|
||||
assert(m_instance);
|
||||
|
|
@ -528,6 +528,7 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
}
|
||||
stl_get_size(&stl);
|
||||
m_volume->mesh.repair();
|
||||
m_volume->center_geometry();
|
||||
m_volume->calculate_convex_hull();
|
||||
m_volume_facets.clear();
|
||||
m_volume = nullptr;
|
||||
|
|
@ -578,7 +579,6 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
break;
|
||||
p = end + 1;
|
||||
}
|
||||
m_object->layer_height_profile_valid = true;
|
||||
}
|
||||
else if (m_path.size() == 3 && m_path[1] == NODE_TYPE_OBJECT && m_object && strcmp(opt_key, "sla_support_points") == 0) {
|
||||
// Parse object's layer height profile, a semicolon separated list of floats.
|
||||
|
|
@ -642,13 +642,17 @@ void AMFParserContext::endDocument()
|
|||
continue;
|
||||
}
|
||||
for (const Instance &instance : object.second.instances)
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
{
|
||||
#else
|
||||
if (instance.deltax_set && instance.deltay_set) {
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
ModelInstance *mi = m_model.objects[object.second.idx]->add_instance();
|
||||
mi->set_offset(Vec3d(instance.deltax_set ? (double)instance.deltax : 0.0, instance.deltay_set ? (double)instance.deltay : 0.0, instance.deltaz_set ? (double)instance.deltaz : 0.0));
|
||||
mi->set_rotation(Vec3d(instance.rx_set ? (double)instance.rx : 0.0, instance.ry_set ? (double)instance.ry : 0.0, instance.rz_set ? (double)instance.rz : 0.0));
|
||||
mi->set_scaling_factor(Vec3d(instance.scalex_set ? (double)instance.scalex : 1.0, instance.scaley_set ? (double)instance.scaley : 1.0, instance.scalez_set ? (double)instance.scalez : 1.0));
|
||||
mi->set_mirror(Vec3d(instance.mirrorx_set ? (double)instance.mirrorx : 1.0, instance.mirrory_set ? (double)instance.mirrory : 1.0, instance.mirrorz_set ? (double)instance.mirrorz : 1.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -885,7 +889,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
|
|||
stream << " <metadata type=\"slic3r." << key << "\">" << object->config.serialize(key) << "</metadata>\n";
|
||||
if (!object->name.empty())
|
||||
stream << " <metadata type=\"name\">" << xml_escape(object->name) << "</metadata>\n";
|
||||
std::vector<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
|
||||
const std::vector<double> &layer_height_profile = object->layer_height_profile;
|
||||
if (layer_height_profile.size() >= 4 && (layer_height_profile.size() % 2) == 0) {
|
||||
// Store the layer height profile as a single semicolon separated list.
|
||||
stream << " <metadata type=\"slic3r.layer_height_profile\">";
|
||||
|
|
@ -919,12 +923,14 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
|
|||
auto &stl = volume->mesh.stl;
|
||||
if (stl.v_shared == nullptr)
|
||||
stl_generate_shared_vertices(&stl);
|
||||
for (size_t i = 0; i < stl.stats.shared_vertices; ++ i) {
|
||||
const Transform3d& matrix = volume->get_matrix();
|
||||
for (size_t i = 0; i < stl.stats.shared_vertices; ++i) {
|
||||
stream << " <vertex>\n";
|
||||
stream << " <coordinates>\n";
|
||||
stream << " <x>" << stl.v_shared[i](0) << "</x>\n";
|
||||
stream << " <y>" << stl.v_shared[i](1) << "</y>\n";
|
||||
stream << " <z>" << stl.v_shared[i](2) << "</z>\n";
|
||||
Vec3d v = matrix * stl.v_shared[i].cast<double>();
|
||||
stream << " <x>" << v(0) << "</x>\n";
|
||||
stream << " <y>" << v(1) << "</y>\n";
|
||||
stream << " <z>" << v(2) << "</z>\n";
|
||||
stream << " </coordinates>\n";
|
||||
stream << " </vertex>\n";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1589,9 +1589,11 @@ void GCode::process_layer(
|
|||
auto objects_by_extruder_it = by_extruder.find(extruder_id);
|
||||
if (objects_by_extruder_it == by_extruder.end())
|
||||
continue;
|
||||
|
||||
// We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature):
|
||||
for (int print_wipe_extrusions=const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden(); print_wipe_extrusions>=0; --print_wipe_extrusions) {
|
||||
if (print_wipe_extrusions == 0)
|
||||
bool is_anything_overridden = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden();
|
||||
for (int print_wipe_extrusions = is_anything_overridden; print_wipe_extrusions>=0; --print_wipe_extrusions) {
|
||||
if (is_anything_overridden && print_wipe_extrusions == 0)
|
||||
gcode+="; PURGING FINISHED\n";
|
||||
|
||||
for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
|
||||
|
|
@ -1628,7 +1630,7 @@ void GCode::process_layer(
|
|||
m_layer = layers[layer_id].layer();
|
||||
}
|
||||
for (ObjectByExtruder::Island &island : object_by_extruder.islands) {
|
||||
const auto& by_region_specific = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden() ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region;
|
||||
const auto& by_region_specific = is_anything_overridden ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region;
|
||||
|
||||
if (print.config().infill_first) {
|
||||
gcode += this->extrude_infill(print, by_region_specific);
|
||||
|
|
|
|||
|
|
@ -1,24 +1,140 @@
|
|||
#include "PostProcessor.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
namespace Slic3r {
|
||||
// The standard Windows includes.
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
|
||||
//FIXME Ignore until we include boost::process
|
||||
void run_post_process_scripts(const std::string &path, const PrintConfig &config)
|
||||
// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
|
||||
// This routine appends the given argument to a command line such that CommandLineToArgvW will return the argument string unchanged.
|
||||
// Arguments in a command line should be separated by spaces; this function does not add these spaces.
|
||||
// Argument - Supplies the argument to encode.
|
||||
// CommandLine - Supplies the command line to which we append the encoded argument string.
|
||||
static void quote_argv_winapi(const std::wstring &argument, std::wstring &commmand_line_out)
|
||||
{
|
||||
// Don't quote unless we actually need to do so --- hopefully avoid problems if programs won't parse quotes properly.
|
||||
if (argument.empty() == false && argument.find_first_of(L" \t\n\v\"") == argument.npos)
|
||||
commmand_line_out.append(argument);
|
||||
else {
|
||||
commmand_line_out.push_back(L'"');
|
||||
for (auto it = argument.begin(); ; ++ it) {
|
||||
unsigned number_backslashes = 0;
|
||||
while (it != argument.end() && *it == L'\\') {
|
||||
++ it;
|
||||
++ number_backslashes;
|
||||
}
|
||||
if (it == argument.end()) {
|
||||
// Escape all backslashes, but let the terminating double quotation mark we add below be interpreted as a metacharacter.
|
||||
commmand_line_out.append(number_backslashes * 2, L'\\');
|
||||
break;
|
||||
} else if (*it == L'"') {
|
||||
// Escape all backslashes and the following double quotation mark.
|
||||
commmand_line_out.append(number_backslashes * 2 + 1, L'\\');
|
||||
commmand_line_out.push_back(*it);
|
||||
} else {
|
||||
// Backslashes aren't special here.
|
||||
commmand_line_out.append(number_backslashes, L'\\');
|
||||
commmand_line_out.push_back(*it);
|
||||
}
|
||||
}
|
||||
commmand_line_out.push_back(L'"');
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
static DWORD execute_process_winapi(const std::wstring &command_line)
|
||||
{
|
||||
// Extract the current environment to be passed to the child process.
|
||||
std::wstring envstr;
|
||||
{
|
||||
wchar_t *env = GetEnvironmentStrings();
|
||||
assert(env != nullptr);
|
||||
const wchar_t* var = env;
|
||||
size_t totallen = 0;
|
||||
size_t len;
|
||||
while ((len = wcslen(var)) > 0) {
|
||||
totallen += len + 1;
|
||||
var += len + 1;
|
||||
}
|
||||
envstr = std::wstring(env, totallen);
|
||||
FreeEnvironmentStrings(env);
|
||||
}
|
||||
|
||||
STARTUPINFOW startup_info;
|
||||
memset(&startup_info, 0, sizeof(startup_info));
|
||||
startup_info.cb = sizeof(STARTUPINFO);
|
||||
#if 0
|
||||
startup_info.dwFlags = STARTF_USESHOWWINDOW;
|
||||
startup_info.wShowWindow = SW_HIDE;
|
||||
#endif
|
||||
PROCESS_INFORMATION process_info;
|
||||
if (! ::CreateProcessW(
|
||||
nullptr /* lpApplicationName */, (LPWSTR)command_line.c_str(), nullptr /* lpProcessAttributes */, nullptr /* lpThreadAttributes */, false /* bInheritHandles */,
|
||||
CREATE_UNICODE_ENVIRONMENT /* | CREATE_NEW_CONSOLE */ /* dwCreationFlags */, (LPVOID)envstr.c_str(), nullptr /* lpCurrentDirectory */, &startup_info, &process_info))
|
||||
throw std::runtime_error(std::string("Failed starting the script ") + boost::nowide::narrow(command_line) + ", Win32 error: " + std::to_string(int(::GetLastError())));
|
||||
::WaitForSingleObject(process_info.hProcess, INFINITE);
|
||||
ULONG rc = 0;
|
||||
::GetExitCodeProcess(process_info.hProcess, &rc);
|
||||
::CloseHandle(process_info.hThread);
|
||||
::CloseHandle(process_info.hProcess);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Run the script. If it is a perl script, run it through the bundled perl interpreter.
|
||||
// If it is a batch file, run it through the cmd.exe.
|
||||
// Otherwise run it directly.
|
||||
static int run_script_win32(const std::string &script, const std::string &gcode)
|
||||
{
|
||||
// Unpack the argument list provided by the user.
|
||||
int nArgs;
|
||||
LPWSTR *szArglist = CommandLineToArgvW(boost::nowide::widen(script).c_str(), &nArgs);
|
||||
if (szArglist == nullptr || nArgs <= 0) {
|
||||
// CommandLineToArgvW failed. Maybe the command line escapment is invalid?
|
||||
throw std::runtime_error(std::string("Post processing script ") + script + " on file " + gcode + " failed. CommandLineToArgvW() refused to parse the command line path.");
|
||||
}
|
||||
|
||||
std::wstring command_line;
|
||||
std::wstring command = szArglist[0];
|
||||
if (! boost::filesystem::exists(boost::filesystem::path(command)))
|
||||
throw std::runtime_error(std::string("The configured post-processing script does not exist: ") + boost::nowide::narrow(command));
|
||||
if (boost::iends_with(command, L".pl")) {
|
||||
// This is a perl script. Run it through the perl interpreter.
|
||||
// The current process may be slic3r.exe or slic3r-console.exe.
|
||||
// Find the path of the process:
|
||||
wchar_t wpath_exe[_MAX_PATH + 1];
|
||||
::GetModuleFileNameW(nullptr, wpath_exe, _MAX_PATH);
|
||||
boost::filesystem::path path_exe(wpath_exe);
|
||||
boost::filesystem::path path_perl = path_exe.parent_path() / "perl" / "perl.exe";
|
||||
if (! boost::filesystem::exists(path_perl)) {
|
||||
LocalFree(szArglist);
|
||||
throw std::runtime_error(std::string("Perl interpreter ") + path_perl.string() + " does not exist.");
|
||||
}
|
||||
// Replace it with the current perl interpreter.
|
||||
quote_argv_winapi(boost::nowide::widen(path_perl.string()), command_line);
|
||||
command_line += L" ";
|
||||
} else if (boost::iends_with(command, ".bat")) {
|
||||
// Run a batch file through the command line interpreter.
|
||||
command_line = L"cmd.exe /C ";
|
||||
}
|
||||
|
||||
for (int i = 0; i < nArgs; ++ i) {
|
||||
quote_argv_winapi(szArglist[i], command_line);
|
||||
command_line += L" ";
|
||||
}
|
||||
LocalFree(szArglist);
|
||||
quote_argv_winapi(boost::nowide::widen(gcode), command_line);
|
||||
return (int)execute_process_winapi(command_line);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <boost/process/system.hpp>
|
||||
#ifndef WIN32
|
||||
#include <sys/stat.h> //for getting filesystem UID/GID
|
||||
#include <unistd.h> //for getting current UID/GID
|
||||
#include <boost/process.hpp>
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -33,44 +149,38 @@ void run_post_process_scripts(const std::string &path, const PrintConfig &config
|
|||
if (! boost::filesystem::exists(gcode_file))
|
||||
throw std::runtime_error(std::string("Post-processor can't find exported gcode file"));
|
||||
|
||||
for (std::string script: config.post_process.values) {
|
||||
// Ignore empty post processing script lines.
|
||||
boost::trim(script);
|
||||
if (script.empty())
|
||||
continue;
|
||||
BOOST_LOG_TRIVIAL(info) << "Executing script " << script << " on file " << path;
|
||||
if (! boost::filesystem::exists(boost::filesystem::path(script)))
|
||||
throw std::runtime_error(std::string("The configured post-processing script does not exist: ") + script);
|
||||
#ifndef WIN32
|
||||
struct stat info;
|
||||
if (stat(script.c_str(), &info))
|
||||
throw std::runtime_error(std::string("Cannot read information for post-processing script: ") + script);
|
||||
boost::filesystem::perms script_perms = boost::filesystem::status(script).permissions();
|
||||
//if UID matches, check UID perm. else if GID matches, check GID perm. Otherwise check other perm.
|
||||
if (!(script_perms & ((info.st_uid == geteuid()) ? boost::filesystem::perms::owner_exe
|
||||
: ((info.st_gid == getegid()) ? boost::filesystem::perms::group_exe
|
||||
: boost::filesystem::perms::others_exe))))
|
||||
throw std::runtime_error(std::string("The configured post-processing script is not executable: check permissions. ") + script);
|
||||
#endif
|
||||
int result = 0;
|
||||
for (const std::string &scripts : config.post_process.values) {
|
||||
std::vector<std::string> lines;
|
||||
boost::split(lines, scripts, boost::is_any_of("\r\n"));
|
||||
for (std::string script : lines) {
|
||||
// Ignore empty post processing script lines.
|
||||
boost::trim(script);
|
||||
if (script.empty())
|
||||
continue;
|
||||
BOOST_LOG_TRIVIAL(info) << "Executing script " << script << " on file " << path;
|
||||
#ifdef WIN32
|
||||
if (boost::iends_with(file, ".gcode")) {
|
||||
// The current process may be slic3r.exe or slic3r-console.exe.
|
||||
// Find the path of the process:
|
||||
wchar_t wpath_exe[_MAX_PATH + 1];
|
||||
::GetModuleFileNameW(nullptr, wpath_exe, _MAX_PATH);
|
||||
boost::filesystem::path path_exe(wpath_exe);
|
||||
// Replace it with the current perl interpreter.
|
||||
result = boost::process::system((path_exe.parent_path() / "perl5.24.0.exe").string(), script, gcode_file);
|
||||
} else
|
||||
int result = run_script_win32(script, gcode_file.string());
|
||||
#else
|
||||
result = boost::process::system(script, gcode_file);
|
||||
//FIXME testing existence of a script is risky, as the script line may contain the script and some additional command line parameters.
|
||||
// We would have to process the script line into parameters before testing for the existence of the command, the command may be looked up
|
||||
// in the PATH etc.
|
||||
if (! boost::filesystem::exists(boost::filesystem::path(script)))
|
||||
throw std::runtime_error(std::string("The configured post-processing script does not exist: ") + script);
|
||||
struct stat info;
|
||||
if (stat(script.c_str(), &info))
|
||||
throw std::runtime_error(std::string("Cannot read information for post-processing script: ") + script);
|
||||
boost::filesystem::perms script_perms = boost::filesystem::status(script).permissions();
|
||||
//if UID matches, check UID perm. else if GID matches, check GID perm. Otherwise check other perm.
|
||||
if (!(script_perms & ((info.st_uid == geteuid()) ? boost::filesystem::perms::owner_exe
|
||||
: ((info.st_gid == getegid()) ? boost::filesystem::perms::group_exe
|
||||
: boost::filesystem::perms::others_exe))))
|
||||
throw std::runtime_error(std::string("The configured post-processing script is not executable: check permissions. ") + script);
|
||||
int result = boost::process::system(script, gcode_file);
|
||||
if (result < 0)
|
||||
BOOST_LOG_TRIVIAL(error) << "Script " << script << " on file " << path << " failed. Negative error code returned.";
|
||||
#endif
|
||||
if (result < 0)
|
||||
BOOST_LOG_TRIVIAL(error) << "Script " << script << " on file " << path << " failed. Negative error code returned.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -207,8 +207,7 @@ static bool sort_pointfs(const Vec3d& a, const Vec3d& b)
|
|||
}
|
||||
|
||||
// This implementation is based on Andrew's monotone chain 2D convex hull algorithm
|
||||
Polygon
|
||||
convex_hull(Points points)
|
||||
Polygon convex_hull(Points points)
|
||||
{
|
||||
assert(points.size() >= 3);
|
||||
// sort input points
|
||||
|
|
@ -1182,59 +1181,47 @@ Transform3d assemble_transform(const Vec3d& translation, const Vec3d& rotation,
|
|||
Vec3d extract_euler_angles(const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>& rotation_matrix)
|
||||
{
|
||||
#if ENABLE_NEW_EULER_ANGLES
|
||||
// reference: http://www.gregslabaugh.net/publications/euler.pdf
|
||||
auto is_approx = [](double value, double test_value) -> bool { return std::abs(value - test_value) < EPSILON; };
|
||||
|
||||
bool x_only = is_approx(rotation_matrix(0, 0), 1.0) && is_approx(rotation_matrix(0, 1), 0.0) && is_approx(rotation_matrix(0, 2), 0.0) && is_approx(rotation_matrix(1, 0), 0.0) && is_approx(rotation_matrix(2, 0), 0.0);
|
||||
bool y_only = is_approx(rotation_matrix(0, 1), 0.0) && is_approx(rotation_matrix(1, 0), 0.0) && is_approx(rotation_matrix(1, 1), 1.0) && is_approx(rotation_matrix(1, 2), 0.0) && is_approx(rotation_matrix(2, 1), 0.0);
|
||||
bool z_only = is_approx(rotation_matrix(0, 2), 0.0) && is_approx(rotation_matrix(1, 2), 0.0) && is_approx(rotation_matrix(2, 0), 0.0) && is_approx(rotation_matrix(2, 1), 0.0) && is_approx(rotation_matrix(2, 2), 1.0);
|
||||
// bool xy_only = is_approx(rotation_matrix(0, 1), 0.0); // Rx * Ry
|
||||
bool yx_only = is_approx(rotation_matrix(1, 0), 0.0); // Ry * Rx
|
||||
// bool xz_only = is_approx(rotation_matrix(0, 2), 0.0); // Rx * Rz
|
||||
// bool zx_only = is_approx(rotation_matrix(2, 0), 0.0); // Rz * Rx
|
||||
// bool yz_only = is_approx(rotation_matrix(1, 2), 0.0); // Ry * Rz
|
||||
// bool zy_only = is_approx(rotation_matrix(2, 1), 0.0); // Rz * Ry
|
||||
|
||||
Vec3d angles = Vec3d::Zero();
|
||||
if (x_only || y_only || z_only)
|
||||
Vec3d angles1 = Vec3d::Zero();
|
||||
Vec3d angles2 = Vec3d::Zero();
|
||||
if (is_approx(std::abs(rotation_matrix(2, 0)), 1.0))
|
||||
{
|
||||
angles = rotation_matrix.eulerAngles(0, 1, 2);
|
||||
if (x_only && (std::abs(angles(1)) == (double)PI) && (std::abs(angles(2)) == (double)PI))
|
||||
angles1(2) = 0.0;
|
||||
if (rotation_matrix(2, 0) < 0.0) // == -1.0
|
||||
{
|
||||
angles(0) -= (double)PI;
|
||||
angles(1) = 0.0;
|
||||
angles(2) = 0.0;
|
||||
angles1(1) = 0.5 * (double)PI;
|
||||
angles1(0) = angles1(2) + ::atan2(rotation_matrix(0, 1), rotation_matrix(0, 2));
|
||||
}
|
||||
else // == 1.0
|
||||
{
|
||||
angles1(1) = - 0.5 * (double)PI;
|
||||
angles1(0) = - angles1(2) + ::atan2(- rotation_matrix(0, 1), - rotation_matrix(0, 2));
|
||||
}
|
||||
angles2 = angles1;
|
||||
}
|
||||
else
|
||||
{
|
||||
double cy_abs = ::sqrt(sqr(rotation_matrix(0, 0)) + sqr(rotation_matrix(1, 0)));
|
||||
angles(0) = ::atan2(rotation_matrix(2, 1), rotation_matrix(2, 2));
|
||||
angles(1) = ::atan2(-rotation_matrix(2, 0), cy_abs);
|
||||
angles(2) = ::atan2(rotation_matrix(1, 0), rotation_matrix(0, 0));
|
||||
if (yx_only && (angles(2) == (double)PI))
|
||||
{
|
||||
angles(0) -= (double)PI;
|
||||
angles(1) = (double)PI - angles(1);
|
||||
angles(2) = 0.0;
|
||||
}
|
||||
angles1(1) = -::asin(rotation_matrix(2, 0));
|
||||
double inv_cos1 = 1.0 / ::cos(angles1(1));
|
||||
angles1(0) = ::atan2(rotation_matrix(2, 1) * inv_cos1, rotation_matrix(2, 2) * inv_cos1);
|
||||
angles1(2) = ::atan2(rotation_matrix(1, 0) * inv_cos1, rotation_matrix(0, 0) * inv_cos1);
|
||||
|
||||
angles2(1) = (double)PI - angles1(1);
|
||||
double inv_cos2 = 1.0 / ::cos(angles2(1));
|
||||
angles2(0) = ::atan2(rotation_matrix(2, 1) * inv_cos2, rotation_matrix(2, 2) * inv_cos2);
|
||||
angles2(2) = ::atan2(rotation_matrix(1, 0) * inv_cos2, rotation_matrix(0, 0) * inv_cos2);
|
||||
}
|
||||
|
||||
// // debug check
|
||||
// Geometry::Transformation t;
|
||||
// t.set_rotation(angles);
|
||||
// if (!t.get_matrix().matrix().block(0, 0, 3, 3).isApprox(rotation_matrix))
|
||||
// {
|
||||
// std::cout << "something went wrong in extracting euler angles from matrix" << std::endl;
|
||||
//
|
||||
//// Eigen::Matrix<double, 3, 3, Eigen::DontAlign> m = t.get_matrix().matrix().block(0, 0, 3, 3);
|
||||
//// for (int r = 0; r < 3; ++r)
|
||||
//// {
|
||||
//// for (int c = 0; c < 3; ++c)
|
||||
//// {
|
||||
//// std::cout << r << ", " << c << ": " << m(r, c) << " - " << rotation_matrix(r, c) << std::endl;
|
||||
//// }
|
||||
//// }
|
||||
// }
|
||||
// The following euristic is the best found up to now (in the sense that it works fine with the greatest number of edge use-cases)
|
||||
// but there are other use-cases were it does not
|
||||
// We need to improve it
|
||||
double min_1 = angles1.cwiseAbs().minCoeff();
|
||||
double min_2 = angles2.cwiseAbs().minCoeff();
|
||||
bool use_1 = (min_1 < min_2) || (is_approx(min_1, min_2) && (angles1.norm() <= angles2.norm()));
|
||||
|
||||
Vec3d angles = use_1 ? angles1 : angles2;
|
||||
#else
|
||||
auto y_only = [](const Eigen::Matrix<double, 3, 3, Eigen::DontAlign>& matrix) -> bool {
|
||||
return (matrix(0, 1) == 0.0) && (matrix(1, 0) == 0.0) && (matrix(1, 1) == 1.0) && (matrix(1, 2) == 0.0) && (matrix(2, 1) == 0.0);
|
||||
|
|
@ -1303,13 +1290,18 @@ void Transformation::Flags::set(bool dont_translate, bool dont_rotate, bool dont
|
|||
}
|
||||
|
||||
Transformation::Transformation()
|
||||
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
: m_offset(Vec3d::Zero())
|
||||
, m_rotation(Vec3d::Zero())
|
||||
, m_scaling_factor(Vec3d::Ones())
|
||||
, m_mirror(Vec3d::Ones())
|
||||
, m_matrix(Transform3d::Identity())
|
||||
, m_dirty(false)
|
||||
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
{
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
reset();
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
}
|
||||
|
||||
Transformation::Transformation(const Transform3d& transform)
|
||||
|
|
@ -1428,6 +1420,18 @@ void Transformation::set_from_transform(const Transform3d& transform)
|
|||
// std::cout << "something went wrong in extracting data from matrix" << std::endl;
|
||||
}
|
||||
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
void Transformation::reset()
|
||||
{
|
||||
m_offset = Vec3d::Zero();
|
||||
m_rotation = Vec3d::Zero();
|
||||
m_scaling_factor = Vec3d::Ones();
|
||||
m_mirror = Vec3d::Ones();
|
||||
m_matrix = Transform3d::Identity();
|
||||
m_dirty = false;
|
||||
}
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
const Transform3d& Transformation::get_matrix(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const
|
||||
{
|
||||
if (m_dirty || m_flags.needs_update(dont_translate, dont_rotate, dont_scale, dont_mirror))
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ public:
|
|||
void set_rotation(const Vec3d& rotation);
|
||||
void set_rotation(Axis axis, double rotation);
|
||||
|
||||
Vec3d get_scaling_factor() const { return m_scaling_factor; }
|
||||
const Vec3d& get_scaling_factor() const { return m_scaling_factor; }
|
||||
double get_scaling_factor(Axis axis) const { return m_scaling_factor(axis); }
|
||||
|
||||
void set_scaling_factor(const Vec3d& scaling_factor);
|
||||
|
|
@ -251,6 +251,10 @@ public:
|
|||
|
||||
void set_from_transform(const Transform3d& transform);
|
||||
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
void reset();
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const;
|
||||
|
||||
Transformation operator * (const Transformation& other) const;
|
||||
|
|
|
|||
|
|
@ -570,10 +570,11 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs)
|
|||
this->sla_support_points = rhs.sla_support_points;
|
||||
this->layer_height_ranges = rhs.layer_height_ranges;
|
||||
this->layer_height_profile = rhs.layer_height_profile;
|
||||
this->layer_height_profile_valid = rhs.layer_height_profile_valid;
|
||||
this->origin_translation = rhs.origin_translation;
|
||||
m_bounding_box = rhs.m_bounding_box;
|
||||
m_bounding_box_valid = rhs.m_bounding_box_valid;
|
||||
m_raw_mesh_bounding_box = rhs.m_raw_mesh_bounding_box;
|
||||
m_raw_mesh_bounding_box_valid = rhs.m_raw_mesh_bounding_box_valid;
|
||||
|
||||
this->clear_volumes();
|
||||
this->volumes.reserve(rhs.volumes.size());
|
||||
|
|
@ -602,10 +603,11 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
|
|||
this->sla_support_points = std::move(rhs.sla_support_points);
|
||||
this->layer_height_ranges = std::move(rhs.layer_height_ranges);
|
||||
this->layer_height_profile = std::move(rhs.layer_height_profile);
|
||||
this->layer_height_profile_valid = std::move(rhs.layer_height_profile_valid);
|
||||
this->origin_translation = std::move(rhs.origin_translation);
|
||||
m_bounding_box = std::move(rhs.m_bounding_box);
|
||||
m_bounding_box_valid = std::move(rhs.m_bounding_box_valid);
|
||||
m_raw_mesh_bounding_box = rhs.m_raw_mesh_bounding_box;
|
||||
m_raw_mesh_bounding_box_valid = rhs.m_raw_mesh_bounding_box_valid;
|
||||
|
||||
this->clear_volumes();
|
||||
this->volumes = std::move(rhs.volumes);
|
||||
|
|
@ -641,6 +643,9 @@ ModelVolume* ModelObject::add_volume(const TriangleMesh &mesh)
|
|||
{
|
||||
ModelVolume* v = new ModelVolume(this, mesh);
|
||||
this->volumes.push_back(v);
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
v->center_geometry();
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
this->invalidate_bounding_box();
|
||||
return v;
|
||||
}
|
||||
|
|
@ -649,6 +654,9 @@ ModelVolume* ModelObject::add_volume(TriangleMesh &&mesh)
|
|||
{
|
||||
ModelVolume* v = new ModelVolume(this, std::move(mesh));
|
||||
this->volumes.push_back(v);
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
v->center_geometry();
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
this->invalidate_bounding_box();
|
||||
return v;
|
||||
}
|
||||
|
|
@ -657,6 +665,9 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other)
|
|||
{
|
||||
ModelVolume* v = new ModelVolume(this, other);
|
||||
this->volumes.push_back(v);
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
v->center_geometry();
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
this->invalidate_bounding_box();
|
||||
return v;
|
||||
}
|
||||
|
|
@ -665,6 +676,9 @@ ModelVolume* ModelObject::add_volume(const ModelVolume &other, TriangleMesh &&me
|
|||
{
|
||||
ModelVolume* v = new ModelVolume(this, other, std::move(mesh));
|
||||
this->volumes.push_back(v);
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
v->center_geometry();
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
this->invalidate_bounding_box();
|
||||
return v;
|
||||
}
|
||||
|
|
@ -675,6 +689,23 @@ void ModelObject::delete_volume(size_t idx)
|
|||
delete *i;
|
||||
this->volumes.erase(i);
|
||||
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
if (this->volumes.size() == 1)
|
||||
{
|
||||
// only one volume left
|
||||
// we need to collapse the volume transform into the instances transforms because now when selecting this volume
|
||||
// it will be seen as a single full instance ans so its volume transform may be ignored
|
||||
ModelVolume* v = this->volumes.front();
|
||||
Transform3d v_t = v->get_transformation().get_matrix();
|
||||
for (ModelInstance* inst : this->instances)
|
||||
{
|
||||
inst->set_transformation(Geometry::Transformation(inst->get_transformation().get_matrix() * v_t));
|
||||
}
|
||||
Geometry::Transformation t;
|
||||
v->set_transformation(t);
|
||||
v->set_new_unique_id();
|
||||
}
|
||||
#else
|
||||
if (this->volumes.size() == 1)
|
||||
{
|
||||
// only one volume left
|
||||
|
|
@ -691,6 +722,7 @@ void ModelObject::delete_volume(size_t idx)
|
|||
v->set_offset(Vec3d::Zero());
|
||||
v->set_new_unique_id();
|
||||
}
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
this->invalidate_bounding_box();
|
||||
}
|
||||
|
|
@ -755,19 +787,11 @@ void ModelObject::clear_instances()
|
|||
const BoundingBoxf3& ModelObject::bounding_box() const
|
||||
{
|
||||
if (! m_bounding_box_valid) {
|
||||
BoundingBoxf3 raw_bbox;
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part())
|
||||
{
|
||||
TriangleMesh m = v->mesh;
|
||||
m.transform(v->get_matrix());
|
||||
raw_bbox.merge(m.bounding_box());
|
||||
}
|
||||
BoundingBoxf3 bb;
|
||||
for (const ModelInstance *i : this->instances)
|
||||
bb.merge(i->transform_bounding_box(raw_bbox));
|
||||
m_bounding_box = bb;
|
||||
m_bounding_box_valid = true;
|
||||
BoundingBoxf3 raw_bbox = this->raw_mesh_bounding_box();
|
||||
m_bounding_box.reset();
|
||||
for (const ModelInstance *i : this->instances)
|
||||
m_bounding_box.merge(i->transform_bounding_box(raw_bbox));
|
||||
}
|
||||
return m_bounding_box;
|
||||
}
|
||||
|
|
@ -814,6 +838,26 @@ TriangleMesh ModelObject::full_raw_mesh() const
|
|||
return mesh;
|
||||
}
|
||||
|
||||
BoundingBoxf3 ModelObject::raw_mesh_bounding_box() const
|
||||
{
|
||||
if (! m_raw_mesh_bounding_box_valid) {
|
||||
m_raw_mesh_bounding_box_valid = true;
|
||||
m_raw_mesh_bounding_box.reset();
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part())
|
||||
m_raw_mesh_bounding_box.merge(v->mesh.transformed_bounding_box(v->get_matrix()));
|
||||
}
|
||||
return m_raw_mesh_bounding_box;
|
||||
}
|
||||
|
||||
BoundingBoxf3 ModelObject::full_raw_mesh_bounding_box() const
|
||||
{
|
||||
BoundingBoxf3 bb;
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
bb.merge(v->mesh.transformed_bounding_box(v->get_matrix()));
|
||||
return bb;
|
||||
}
|
||||
|
||||
// A transformed snug bounding box around the non-modifier object volumes, without the translation applied.
|
||||
// This bounding box is only used for the actual slicing.
|
||||
BoundingBoxf3 ModelObject::raw_bounding_box() const
|
||||
|
|
@ -868,15 +912,73 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
|
|||
return bb;
|
||||
}
|
||||
|
||||
// Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane.
|
||||
// This method is cheap in that it does not make any unnecessary copy of the volume meshes.
|
||||
// This method is used by the auto arrange function.
|
||||
Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance)
|
||||
{
|
||||
Points pts;
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part()) {
|
||||
const stl_file &stl = v->mesh.stl;
|
||||
Transform3d trafo = trafo_instance * v->get_matrix();
|
||||
if (stl.v_shared == nullptr) {
|
||||
// Using the STL faces.
|
||||
for (unsigned int i = 0; i < stl.stats.number_of_facets; ++ i) {
|
||||
const stl_facet &facet = stl.facet_start[i];
|
||||
for (size_t j = 0; j < 3; ++ j) {
|
||||
Vec3d p = trafo * facet.vertex[j].cast<double>();
|
||||
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Using the shared vertices should be a bit quicker than using the STL faces.
|
||||
for (int i = 0; i < stl.stats.shared_vertices; ++ i) {
|
||||
Vec3d p = trafo * stl.v_shared[i].cast<double>();
|
||||
pts.emplace_back(coord_t(scale_(p.x())), coord_t(scale_(p.y())));
|
||||
}
|
||||
}
|
||||
}
|
||||
std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) < b(0) || (a(0) == b(0) && a(1) < b(1)); });
|
||||
pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) == b(0) && a(1) == b(1); }), pts.end());
|
||||
|
||||
Polygon hull;
|
||||
int n = (int)pts.size();
|
||||
if (n >= 3) {
|
||||
int k = 0;
|
||||
hull.points.resize(2 * n);
|
||||
// Build lower hull
|
||||
for (int i = 0; i < n; ++ i) {
|
||||
while (k >= 2 && pts[i].ccw(hull[k-2], hull[k-1]) <= 0)
|
||||
-- k;
|
||||
hull[k ++] = pts[i];
|
||||
}
|
||||
// Build upper hull
|
||||
for (int i = n-2, t = k+1; i >= 0; i--) {
|
||||
while (k >= t && pts[i].ccw(hull[k-2], hull[k-1]) <= 0)
|
||||
-- k;
|
||||
hull[k ++] = pts[i];
|
||||
}
|
||||
hull.points.resize(k);
|
||||
assert(hull.points.front() == hull.points.back());
|
||||
hull.points.pop_back();
|
||||
}
|
||||
return hull;
|
||||
}
|
||||
|
||||
void ModelObject::center_around_origin()
|
||||
{
|
||||
// calculate the displacements needed to
|
||||
// center this object around the origin
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
BoundingBoxf3 bb = full_raw_mesh_bounding_box();
|
||||
#else
|
||||
BoundingBoxf3 bb;
|
||||
for (ModelVolume *v : this->volumes)
|
||||
if (v->is_model_part())
|
||||
bb.merge(v->mesh.bounding_box());
|
||||
|
||||
bb.merge(v->mesh.bounding_box());
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
// Shift is the vector from the center of the bounding box to the origin
|
||||
Vec3d shift = -bb.center();
|
||||
|
||||
|
|
@ -1042,7 +1144,8 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
|
|||
|
||||
if (keep_upper) { upper->add_volume(*volume); }
|
||||
if (keep_lower) { lower->add_volume(*volume); }
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
TriangleMesh upper_mesh, lower_mesh;
|
||||
|
||||
// Transform the mesh by the combined transformation matrix
|
||||
|
|
@ -1050,7 +1153,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
|
|||
|
||||
// Perform cut
|
||||
TriangleMeshSlicer tms(&volume->mesh);
|
||||
tms.cut(z, &upper_mesh, &lower_mesh);
|
||||
tms.cut(float(z), &upper_mesh, &lower_mesh);
|
||||
|
||||
// Reset volume transformation except for offset
|
||||
const Vec3d offset = volume->get_offset();
|
||||
|
|
@ -1067,14 +1170,14 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
|
|||
}
|
||||
|
||||
if (keep_upper && upper_mesh.facets_count() > 0) {
|
||||
ModelVolume* vol = upper->add_volume(upper_mesh);
|
||||
vol->name = volume->name;
|
||||
ModelVolume* vol = upper->add_volume(upper_mesh);
|
||||
vol->name = volume->name;
|
||||
vol->config = volume->config;
|
||||
vol->set_material(volume->material_id(), *volume->material());
|
||||
}
|
||||
if (keep_lower && lower_mesh.facets_count() > 0) {
|
||||
ModelVolume* vol = lower->add_volume(lower_mesh);
|
||||
vol->name = volume->name;
|
||||
ModelVolume* vol = lower->add_volume(lower_mesh);
|
||||
vol->name = volume->name;
|
||||
vol->config = volume->config;
|
||||
vol->set_material(volume->material_id(), *volume->material());
|
||||
|
||||
|
|
@ -1153,7 +1256,9 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
|
|||
for (const ModelInstance *model_instance : this->instances)
|
||||
new_object->add_instance(*model_instance);
|
||||
ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh));
|
||||
#if !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
new_vol->center_geometry();
|
||||
#endif // !ENABLE_VOLUMES_CENTERING_FIXES
|
||||
|
||||
for (ModelInstance* model_instance : new_object->instances)
|
||||
{
|
||||
|
|
@ -1321,10 +1426,20 @@ int ModelVolume::extruder_id() const
|
|||
|
||||
void ModelVolume::center_geometry()
|
||||
{
|
||||
#if ENABLE_VOLUMES_CENTERING_FIXES
|
||||
Vec3d shift = mesh.bounding_box().center();
|
||||
if (!shift.isApprox(Vec3d::Zero()))
|
||||
{
|
||||
mesh.translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
|
||||
m_convex_hull.translate(-(float)shift(0), -(float)shift(1), -(float)shift(2));
|
||||
translate(shift);
|
||||
}
|
||||
#else
|
||||
Vec3d shift = -mesh.bounding_box().center();
|
||||
mesh.translate((float)shift(0), (float)shift(1), (float)shift(2));
|
||||
m_convex_hull.translate((float)shift(0), (float)shift(1), (float)shift(2));
|
||||
translate(-shift);
|
||||
#endif // ENABLE_VOLUMES_CENTERING_FIXES
|
||||
}
|
||||
|
||||
void ModelVolume::calculate_convex_hull()
|
||||
|
|
|
|||
|
|
@ -170,12 +170,8 @@ public:
|
|||
// Variation of a layer thickness for spans of Z coordinates.
|
||||
t_layer_height_ranges layer_height_ranges;
|
||||
// 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.
|
||||
std::vector<coordf_t> layer_height_profile;
|
||||
// layer_height_profile is initialized when the layer editing mode is entered.
|
||||
// Only if the user really modified the layer height, layer_height_profile_valid is set
|
||||
// and used subsequently by the PrintObject.
|
||||
bool layer_height_profile_valid;
|
||||
|
||||
// This vector holds position of selected support points for SLA. The data are
|
||||
// saved in mesh coordinates to allow using them for several instances.
|
||||
|
|
@ -209,7 +205,7 @@ public:
|
|||
// This bounding box is approximate and not snug.
|
||||
// This bounding box is being cached.
|
||||
const BoundingBoxf3& bounding_box() const;
|
||||
void invalidate_bounding_box() { m_bounding_box_valid = false; }
|
||||
void invalidate_bounding_box() { m_bounding_box_valid = false; m_raw_mesh_bounding_box_valid = false; }
|
||||
|
||||
// A mesh containing all transformed instances of this object.
|
||||
TriangleMesh mesh() const;
|
||||
|
|
@ -223,6 +219,16 @@ public:
|
|||
BoundingBoxf3 raw_bounding_box() const;
|
||||
// A snug bounding box around the transformed non-modifier object volumes.
|
||||
BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const;
|
||||
// A snug bounding box of non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes.
|
||||
BoundingBoxf3 raw_mesh_bounding_box() const;
|
||||
// A snug bounding box of non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
|
||||
BoundingBoxf3 full_raw_mesh_bounding_box() const;
|
||||
|
||||
// Calculate 2D convex hull of of a projection of the transformed printable volumes into the XY plane.
|
||||
// This method is cheap in that it does not make any unnecessary copy of the volume meshes.
|
||||
// This method is used by the auto arrange function.
|
||||
Polygon convex_hull_2d(const Transform3d &trafo_instance);
|
||||
|
||||
void center_around_origin();
|
||||
void ensure_on_bed();
|
||||
void translate_instances(const Vec3d& vector);
|
||||
|
|
@ -261,7 +267,8 @@ protected:
|
|||
void set_model(Model *model) { m_model = model; }
|
||||
|
||||
private:
|
||||
ModelObject(Model *model) : layer_height_profile_valid(false), m_model(model), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false) {}
|
||||
ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()),
|
||||
m_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {}
|
||||
~ModelObject();
|
||||
|
||||
/* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */
|
||||
|
|
@ -280,6 +287,8 @@ private:
|
|||
// Bounding box, cached.
|
||||
mutable BoundingBoxf3 m_bounding_box;
|
||||
mutable bool m_bounding_box_valid;
|
||||
mutable BoundingBoxf3 m_raw_mesh_bounding_box;
|
||||
mutable bool m_raw_mesh_bounding_box_valid;
|
||||
};
|
||||
|
||||
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
|
||||
|
|
@ -319,6 +328,9 @@ public:
|
|||
// Extruder ID is only valid for FFF. Returns -1 for SLA or if the extruder ID is not applicable (support volumes).
|
||||
int extruder_id() const;
|
||||
|
||||
void set_splittable(const int val) { m_is_splittable = val; }
|
||||
int is_splittable() const { return m_is_splittable; }
|
||||
|
||||
// Split this volume, append the result to the object owning this volume.
|
||||
// Return the number of volumes created from this one.
|
||||
// This is useful to assign different materials to different volumes of an object.
|
||||
|
|
@ -391,6 +403,12 @@ private:
|
|||
TriangleMesh m_convex_hull;
|
||||
Geometry::Transformation m_transformation;
|
||||
|
||||
// flag to optimize the checking if the volume is splittable
|
||||
// -1 -> is unknown value (before first cheking)
|
||||
// 0 -> is not splittable
|
||||
// 1 -> is splittable
|
||||
int m_is_splittable {-1};
|
||||
|
||||
ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object)
|
||||
{
|
||||
if (mesh.stl.stats.number_of_facets > 1)
|
||||
|
|
@ -455,7 +473,7 @@ public:
|
|||
void set_rotation(const Vec3d& rotation) { m_transformation.set_rotation(rotation); }
|
||||
void set_rotation(Axis axis, double rotation) { m_transformation.set_rotation(axis, rotation); }
|
||||
|
||||
Vec3d get_scaling_factor() const { return m_transformation.get_scaling_factor(); }
|
||||
const Vec3d& get_scaling_factor() const { return m_transformation.get_scaling_factor(); }
|
||||
double get_scaling_factor(Axis axis) const { return m_transformation.get_scaling_factor(axis); }
|
||||
|
||||
void set_scaling_factor(const Vec3d& scaling_factor) { m_transformation.set_scaling_factor(scaling_factor); }
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ namespace arr {
|
|||
|
||||
using namespace libnest2d;
|
||||
|
||||
// Only for debugging. Prints the model object vertices on stdout.
|
||||
std::string toString(const Model& model, bool holes = true) {
|
||||
std::stringstream ss;
|
||||
|
||||
|
|
@ -78,6 +79,7 @@ std::string toString(const Model& model, bool holes = true) {
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
// Debugging: Save model to svg file.
|
||||
void toSVG(SVG& svg, const Model& model) {
|
||||
for(auto objptr : model.objects) {
|
||||
if(!objptr) continue;
|
||||
|
|
@ -121,6 +123,10 @@ Box boundingBox(const Box& pilebb, const Box& ibb ) {
|
|||
return Box(minc, maxc);
|
||||
}
|
||||
|
||||
// This is "the" object function which is evaluated many times for each vertex
|
||||
// (decimated with the accuracy parameter) of each object. Therefore it is
|
||||
// upmost crucial for this function to be as efficient as it possibly can be but
|
||||
// at the same time, it has to provide reasonable results.
|
||||
std::tuple<double /*score*/, Box /*farthest point from bin center*/>
|
||||
objfunc(const PointImpl& bincenter,
|
||||
const shapelike::Shapes<PolygonImpl>& merged_pile,
|
||||
|
|
@ -253,6 +259,8 @@ objfunc(const PointImpl& bincenter,
|
|||
return std::make_tuple(score, fullbb);
|
||||
}
|
||||
|
||||
// Fill in the placer algorithm configuration with values carefully chosen for
|
||||
// Slic3r.
|
||||
template<class PConf>
|
||||
void fillConfig(PConf& pcfg) {
|
||||
|
||||
|
|
@ -274,13 +282,19 @@ void fillConfig(PConf& pcfg) {
|
|||
pcfg.parallel = true;
|
||||
}
|
||||
|
||||
// Type trait for an arranger class for different bin types (box, circle,
|
||||
// polygon, etc...)
|
||||
template<class TBin>
|
||||
class AutoArranger {};
|
||||
|
||||
|
||||
// A class encapsulating the libnest2d Nester class and extending it with other
|
||||
// management and spatial index structures for acceleration.
|
||||
template<class TBin>
|
||||
class _ArrBase {
|
||||
protected:
|
||||
|
||||
// Useful type shortcuts...
|
||||
using Placer = TPacker<TBin>;
|
||||
using Selector = FirstFitSelection;
|
||||
using Packer = Nester<Placer, Selector>;
|
||||
|
|
@ -289,15 +303,15 @@ protected:
|
|||
using Pile = sl::Shapes<PolygonImpl>;
|
||||
|
||||
Packer m_pck;
|
||||
PConfig m_pconf; // Placement configuration
|
||||
PConfig m_pconf; // Placement configuration
|
||||
double m_bin_area;
|
||||
SpatIndex m_rtree;
|
||||
SpatIndex m_smallsrtree;
|
||||
double m_norm;
|
||||
Pile m_merged_pile;
|
||||
Box m_pilebb;
|
||||
ItemGroup m_remaining;
|
||||
ItemGroup m_items;
|
||||
SpatIndex m_rtree; // spatial index for the normal (bigger) objects
|
||||
SpatIndex m_smallsrtree; // spatial index for only the smaller items
|
||||
double m_norm; // A coefficient to scale distances
|
||||
Pile m_merged_pile; // The already merged pile (vector of items)
|
||||
Box m_pilebb; // The bounding box of the merged pile.
|
||||
ItemGroup m_remaining; // Remaining items (m_items at the beginning)
|
||||
ItemGroup m_items; // The items to be packed
|
||||
public:
|
||||
|
||||
_ArrBase(const TBin& bin, Distance dist,
|
||||
|
|
@ -308,6 +322,8 @@ public:
|
|||
{
|
||||
fillConfig(m_pconf);
|
||||
|
||||
// Set up a callback that is called just before arranging starts
|
||||
// This functionality is provided by the Nester class (m_pack).
|
||||
m_pconf.before_packing =
|
||||
[this](const Pile& merged_pile, // merged pile
|
||||
const ItemGroup& items, // packed items
|
||||
|
|
@ -342,18 +358,44 @@ public:
|
|||
m_rtree.clear();
|
||||
return m_pck.executeIndexed(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
inline void preload(const PackGroup& pg) {
|
||||
m_pconf.alignment = PConfig::Alignment::DONT_ALIGN;
|
||||
m_pconf.object_function = nullptr; // drop the special objectfunction
|
||||
m_pck.preload(pg);
|
||||
|
||||
// Build the rtree for queries to work
|
||||
for(const ItemGroup& grp : pg)
|
||||
for(unsigned idx = 0; idx < grp.size(); ++idx) {
|
||||
Item& itm = grp[idx];
|
||||
m_rtree.insert({itm.boundingBox(), idx});
|
||||
}
|
||||
|
||||
m_pck.configure(m_pconf);
|
||||
}
|
||||
|
||||
bool is_colliding(const Item& item) {
|
||||
if(m_rtree.empty()) return false;
|
||||
std::vector<SpatElement> result;
|
||||
m_rtree.query(bgi::intersects(item.boundingBox()),
|
||||
std::back_inserter(result));
|
||||
return !result.empty();
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class AutoArranger<Box>: public _ArrBase<Box> {
|
||||
// Arranger specialization for a Box shaped bin.
|
||||
template<> class AutoArranger<Box>: public _ArrBase<Box> {
|
||||
public:
|
||||
|
||||
AutoArranger(const Box& bin, Distance dist,
|
||||
std::function<void(unsigned)> progressind,
|
||||
std::function<bool(void)> stopcond):
|
||||
std::function<void(unsigned)> progressind = [](unsigned){},
|
||||
std::function<bool(void)> stopcond = [](){return false;}):
|
||||
_ArrBase<Box>(bin, dist, progressind, stopcond)
|
||||
{
|
||||
|
||||
// Here we set up the actual object function that calls the common
|
||||
// object function for all bin shapes than does an additional inside
|
||||
// check for the arranged pile.
|
||||
m_pconf.object_function = [this, bin] (const Item &item) {
|
||||
|
||||
auto result = objfunc(bin.center(),
|
||||
|
|
@ -387,15 +429,16 @@ inline lnCircle to_lnCircle(const Circle& circ) {
|
|||
return lnCircle({circ.center()(0), circ.center()(1)}, circ.radius());
|
||||
}
|
||||
|
||||
template<>
|
||||
class AutoArranger<lnCircle>: public _ArrBase<lnCircle> {
|
||||
// Arranger specialization for circle shaped bin.
|
||||
template<> class AutoArranger<lnCircle>: public _ArrBase<lnCircle> {
|
||||
public:
|
||||
|
||||
AutoArranger(const lnCircle& bin, Distance dist,
|
||||
std::function<void(unsigned)> progressind,
|
||||
std::function<bool(void)> stopcond):
|
||||
std::function<void(unsigned)> progressind = [](unsigned){},
|
||||
std::function<bool(void)> stopcond = [](){return false;}):
|
||||
_ArrBase<lnCircle>(bin, dist, progressind, stopcond) {
|
||||
|
||||
// As with the box, only the inside check is different.
|
||||
m_pconf.object_function = [this, &bin] (const Item &item) {
|
||||
|
||||
auto result = objfunc(bin.center(),
|
||||
|
|
@ -431,12 +474,13 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class AutoArranger<PolygonImpl>: public _ArrBase<PolygonImpl> {
|
||||
// Arranger specialization for a generalized polygon.
|
||||
// Warning: this is unfinished business. It may or may not work.
|
||||
template<> class AutoArranger<PolygonImpl>: public _ArrBase<PolygonImpl> {
|
||||
public:
|
||||
AutoArranger(const PolygonImpl& bin, Distance dist,
|
||||
std::function<void(unsigned)> progressind,
|
||||
std::function<bool(void)> stopcond):
|
||||
std::function<void(unsigned)> progressind = [](unsigned){},
|
||||
std::function<bool(void)> stopcond = [](){return false;}):
|
||||
_ArrBase<PolygonImpl>(bin, dist, progressind, stopcond)
|
||||
{
|
||||
m_pconf.object_function = [this, &bin] (const Item &item) {
|
||||
|
|
@ -461,8 +505,10 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template<> // Specialization with no bin
|
||||
class AutoArranger<bool>: public _ArrBase<Box> {
|
||||
// Specialization with no bin. In this case the arranger should just arrange
|
||||
// all objects into a minimum sized pile but it is not limited by a bin. A
|
||||
// consequence is that only one pile should be created.
|
||||
template<> class AutoArranger<bool>: public _ArrBase<Box> {
|
||||
public:
|
||||
|
||||
AutoArranger(Distance dist, std::function<void(unsigned)> progressind,
|
||||
|
|
@ -490,14 +536,15 @@ public:
|
|||
|
||||
// A container which stores a pointer to the 3D object and its projected
|
||||
// 2D shape from top view.
|
||||
using ShapeData2D =
|
||||
std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
|
||||
using ShapeData2D = std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
|
||||
|
||||
ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
||||
ShapeData2D ret;
|
||||
|
||||
auto s = std::accumulate(model.objects.begin(), model.objects.end(), size_t(0),
|
||||
[](size_t s, ModelObject* o){
|
||||
// Count all the items on the bin (all the object's instances)
|
||||
auto s = std::accumulate(model.objects.begin(), model.objects.end(),
|
||||
size_t(0), [](size_t s, ModelObject* o)
|
||||
{
|
||||
return s + o->instances.size();
|
||||
});
|
||||
|
||||
|
|
@ -506,23 +553,47 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
|||
for(ModelObject* objptr : model.objects) {
|
||||
if(objptr) {
|
||||
|
||||
TriangleMesh rmesh = objptr->raw_mesh();
|
||||
// TODO export the exact 2D projection. Cannot do it as libnest2d
|
||||
// does not support concave shapes (yet).
|
||||
ClipperLib::Path clpath;
|
||||
//WIP Vojtech's optimization of the calculation of the convex hull is not working correctly yet.
|
||||
#if 1
|
||||
{
|
||||
TriangleMesh rmesh = objptr->raw_mesh();
|
||||
|
||||
ModelInstance * finst = objptr->instances.front();
|
||||
ModelInstance * finst = objptr->instances.front();
|
||||
|
||||
// Object instances should carry the same scaling and
|
||||
// x, y rotation that is why we use the first instance.
|
||||
// The next line will apply only the full mirroring and scaling
|
||||
rmesh.transform(finst->get_matrix(true, true, false, false));
|
||||
rmesh.rotate_x(float(finst->get_rotation()(X)));
|
||||
rmesh.rotate_y(float(finst->get_rotation()(Y)));
|
||||
|
||||
// TODO export the exact 2D projection. Cannot do it as libnest2d
|
||||
// does not support concave shapes (yet).
|
||||
auto p = rmesh.convex_hull();
|
||||
|
||||
p.make_clockwise();
|
||||
p.append(p.first_point());
|
||||
clpath = Slic3rMultiPoint_to_ClipperPath(p);
|
||||
}
|
||||
#else
|
||||
// Object instances should carry the same scaling and
|
||||
// x, y rotation that is why we use the first instance.
|
||||
// The next line will apply only the full mirroring and scaling
|
||||
rmesh.transform(finst->get_matrix(true, true, false, false));
|
||||
rmesh.rotate_x(float(finst->get_rotation()(X)));
|
||||
rmesh.rotate_y(float(finst->get_rotation()(Y)));
|
||||
|
||||
// TODO export the exact 2D projection
|
||||
auto p = rmesh.convex_hull();
|
||||
|
||||
p.make_clockwise();
|
||||
p.append(p.first_point());
|
||||
auto clpath = Slic3rMultiPoint_to_ClipperPath(p);
|
||||
{
|
||||
ModelInstance *finst = objptr->instances.front();
|
||||
Vec3d rotation = finst->get_rotation();
|
||||
rotation.z() = 0.;
|
||||
Transform3d trafo_instance = Geometry::assemble_transform(Vec3d::Zero(), rotation, finst->get_scaling_factor(), finst->get_mirror());
|
||||
Polygon p = objptr->convex_hull_2d(trafo_instance);
|
||||
assert(! p.points.empty());
|
||||
p.reverse();
|
||||
assert(! p.is_counter_clockwise());
|
||||
p.append(p.first_point());
|
||||
clpath = Slic3rMultiPoint_to_ClipperPath(p);
|
||||
}
|
||||
#endif
|
||||
|
||||
for(ModelInstance* objinst : objptr->instances) {
|
||||
if(objinst) {
|
||||
|
|
@ -549,6 +620,8 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
// Apply the calculated translations and rotations (currently disabled) to the
|
||||
// Model object instances.
|
||||
void applyResult(
|
||||
IndexedPackGroup::value_type& group,
|
||||
Coord batch_offset,
|
||||
|
|
@ -576,6 +649,7 @@ void applyResult(
|
|||
}
|
||||
}
|
||||
|
||||
// Get the type of bed geometry from a simple vector of points.
|
||||
BedShapeHint bedShape(const Polyline &bed) {
|
||||
BedShapeHint ret;
|
||||
|
||||
|
|
@ -654,11 +728,15 @@ BedShapeHint bedShape(const Polyline &bed) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool arrange(Model &model,
|
||||
coord_t min_obj_distance,
|
||||
const Polyline &bed,
|
||||
BedShapeHint bedhint,
|
||||
bool first_bin_only,
|
||||
// The final client function to arrange the Model. A progress indicator and
|
||||
// a stop predicate can be also be passed to control the process.
|
||||
bool arrange(Model &model, // The model with the geometries
|
||||
coord_t min_obj_distance, // Has to be in scaled (clipper) measure
|
||||
const Polyline &bed, // The bed geometry.
|
||||
BedShapeHint bedhint, // Hint about the bed geometry type.
|
||||
bool first_bin_only, // What to do is not all items fit.
|
||||
|
||||
// Controlling callbacks.
|
||||
std::function<void (unsigned)> progressind,
|
||||
std::function<bool ()> stopcondition)
|
||||
{
|
||||
|
|
@ -759,5 +837,174 @@ bool arrange(Model &model,
|
|||
return ret && result.size() == 1;
|
||||
}
|
||||
|
||||
void find_new_position(const Model &model,
|
||||
ModelInstancePtrs toadd,
|
||||
coord_t min_obj_distance,
|
||||
const Polyline &bed)
|
||||
{
|
||||
// Get the 2D projected shapes with their 3D model instance pointers
|
||||
auto shapemap = arr::projectModelFromTop(model);
|
||||
|
||||
// Copy the references for the shapes only as the arranger expects a
|
||||
// sequence of objects convertible to Item or ClipperPolygon
|
||||
PackGroup preshapes; preshapes.emplace_back();
|
||||
ItemGroup shapes;
|
||||
preshapes.front().reserve(shapemap.size());
|
||||
|
||||
std::vector<ModelInstance*> shapes_ptr; shapes_ptr.reserve(toadd.size());
|
||||
IndexedPackGroup result;
|
||||
|
||||
// If there is no hint about the shape, we will try to guess
|
||||
BedShapeHint bedhint = bedShape(bed);
|
||||
|
||||
BoundingBox bbb(bed);
|
||||
|
||||
auto binbb = Box({
|
||||
static_cast<libnest2d::Coord>(bbb.min(0)),
|
||||
static_cast<libnest2d::Coord>(bbb.min(1))
|
||||
},
|
||||
{
|
||||
static_cast<libnest2d::Coord>(bbb.max(0)),
|
||||
static_cast<libnest2d::Coord>(bbb.max(1))
|
||||
});
|
||||
|
||||
for(auto it = shapemap.begin(); it != shapemap.end(); ++it) {
|
||||
if(std::find(toadd.begin(), toadd.end(), it->first) == toadd.end()) {
|
||||
if(it->second.isInside(binbb)) // just ignore items which are outside
|
||||
preshapes.front().emplace_back(std::ref(it->second));
|
||||
}
|
||||
else {
|
||||
shapes_ptr.emplace_back(it->first);
|
||||
shapes.emplace_back(std::ref(it->second));
|
||||
}
|
||||
}
|
||||
|
||||
auto try_first_to_center = [&shapes, &shapes_ptr, &binbb]
|
||||
(std::function<bool(const Item&)> is_colliding,
|
||||
std::function<void(Item&)> preload)
|
||||
{
|
||||
// Try to put the first item to the center, as the arranger will not
|
||||
// do this for us.
|
||||
auto shptrit = shapes_ptr.begin();
|
||||
for(auto shit = shapes.begin(); shit != shapes.end(); ++shit, ++shptrit)
|
||||
{
|
||||
// Try to place items to the center
|
||||
Item& itm = *shit;
|
||||
auto ibb = itm.boundingBox();
|
||||
auto d = binbb.center() - ibb.center();
|
||||
itm.translate(d);
|
||||
if(!is_colliding(itm)) {
|
||||
preload(itm);
|
||||
|
||||
auto offset = itm.translation();
|
||||
Radians rot = itm.rotation();
|
||||
ModelInstance *minst = *shptrit;
|
||||
Vec3d foffset(offset.X*SCALING_FACTOR,
|
||||
offset.Y*SCALING_FACTOR,
|
||||
minst->get_offset()(Z));
|
||||
|
||||
// write the transformation data into the model instance
|
||||
minst->set_rotation(Z, rot);
|
||||
minst->set_offset(foffset);
|
||||
|
||||
shit = shapes.erase(shit);
|
||||
shptrit = shapes_ptr.erase(shptrit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
switch(bedhint.type) {
|
||||
case BedShapeType::BOX: {
|
||||
|
||||
// Create the arranger for the box shaped bed
|
||||
AutoArranger<Box> arrange(binbb, min_obj_distance);
|
||||
|
||||
if(!preshapes.front().empty()) { // If there is something on the plate
|
||||
arrange.preload(preshapes);
|
||||
try_first_to_center(
|
||||
[&arrange](const Item& itm) {return arrange.is_colliding(itm);},
|
||||
[&arrange](Item& itm) { arrange.preload({{itm}}); }
|
||||
);
|
||||
}
|
||||
|
||||
// Arrange and return the items with their respective indices within the
|
||||
// input sequence.
|
||||
result = arrange(shapes.begin(), shapes.end());
|
||||
break;
|
||||
}
|
||||
case BedShapeType::CIRCLE: {
|
||||
|
||||
auto c = bedhint.shape.circ;
|
||||
auto cc = to_lnCircle(c);
|
||||
|
||||
// Create the arranger for the box shaped bed
|
||||
AutoArranger<lnCircle> arrange(cc, min_obj_distance);
|
||||
|
||||
if(!preshapes.front().empty()) { // If there is something on the plate
|
||||
arrange.preload(preshapes);
|
||||
try_first_to_center(
|
||||
[&arrange](const Item& itm) {return arrange.is_colliding(itm);},
|
||||
[&arrange](Item& itm) { arrange.preload({{itm}}); }
|
||||
);
|
||||
}
|
||||
|
||||
// Arrange and return the items with their respective indices within the
|
||||
// input sequence.
|
||||
result = arrange(shapes.begin(), shapes.end());
|
||||
break;
|
||||
}
|
||||
case BedShapeType::IRREGULAR:
|
||||
case BedShapeType::WHO_KNOWS: {
|
||||
using P = libnest2d::PolygonImpl;
|
||||
|
||||
auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
|
||||
P irrbed = sl::create<PolygonImpl>(std::move(ctour));
|
||||
|
||||
AutoArranger<P> arrange(irrbed, min_obj_distance);
|
||||
|
||||
if(!preshapes.front().empty()) { // If there is something on the plate
|
||||
arrange.preload(preshapes);
|
||||
try_first_to_center(
|
||||
[&arrange](const Item& itm) {return arrange.is_colliding(itm);},
|
||||
[&arrange](Item& itm) { arrange.preload({{itm}}); }
|
||||
);
|
||||
}
|
||||
|
||||
// Arrange and return the items with their respective indices within the
|
||||
// input sequence.
|
||||
result = arrange(shapes.begin(), shapes.end());
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Now we go through the result which will contain the fixed and the moving
|
||||
// polygons as well. We will have to search for our item.
|
||||
|
||||
const auto STRIDE_PADDING = 1.2;
|
||||
Coord stride = Coord(STRIDE_PADDING*binbb.width()*SCALING_FACTOR);
|
||||
Coord batch_offset = 0;
|
||||
|
||||
for(auto& group : result) {
|
||||
for(auto& r : group) if(r.first < shapes.size()) {
|
||||
Item& resultitem = r.second;
|
||||
unsigned idx = r.first;
|
||||
auto offset = resultitem.translation();
|
||||
Radians rot = resultitem.rotation();
|
||||
ModelInstance *minst = shapes_ptr[idx];
|
||||
Vec3d foffset(offset.X*SCALING_FACTOR + batch_offset,
|
||||
offset.Y*SCALING_FACTOR,
|
||||
minst->get_offset()(Z));
|
||||
|
||||
// write the transformation data into the model instance
|
||||
minst->set_rotation(Z, rot);
|
||||
minst->set_offset(foffset);
|
||||
}
|
||||
batch_offset += stride;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,13 @@ bool arrange(Model &model, coord_t min_obj_distance,
|
|||
std::function<void(unsigned)> progressind,
|
||||
std::function<bool(void)> stopcondition);
|
||||
|
||||
}
|
||||
/// This will find a suitable position for a new object instance and leave the
|
||||
/// old items untouched.
|
||||
void find_new_position(const Model& model,
|
||||
ModelInstancePtrs instances_to_add,
|
||||
coord_t min_obj_distance,
|
||||
const Slic3r::Polyline& bed);
|
||||
|
||||
}
|
||||
} // arr
|
||||
} // Slic3r
|
||||
#endif // MODELARRANGE_HPP
|
||||
|
|
|
|||
|
|
@ -270,9 +270,22 @@ namespace client
|
|||
{
|
||||
std::string out;
|
||||
switch (type) {
|
||||
case TYPE_BOOL: out = boost::lexical_cast<std::string>(data.b); break;
|
||||
case TYPE_INT: out = boost::lexical_cast<std::string>(data.i); break;
|
||||
case TYPE_DOUBLE: out = boost::lexical_cast<std::string>(data.d); break;
|
||||
case TYPE_BOOL: out = data.b ? "true" : "false"; break;
|
||||
case TYPE_INT: out = std::to_string(data.i); break;
|
||||
case TYPE_DOUBLE:
|
||||
#if 0
|
||||
// The default converter produces trailing zeros after the decimal point.
|
||||
out = std::to_string(data.d);
|
||||
#else
|
||||
// ostringstream default converter produces no trailing zeros after the decimal point.
|
||||
// It seems to be doing what the old boost::to_string() did.
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << data.d;
|
||||
out = ss.str();
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case TYPE_STRING: out = *data.s; break;
|
||||
default: break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -287,18 +287,8 @@ std::vector<unsigned int> Print::object_extruders() const
|
|||
{
|
||||
std::vector<unsigned int> extruders;
|
||||
extruders.reserve(m_regions.size() * 3);
|
||||
|
||||
for (const PrintRegion *region : m_regions) {
|
||||
// these checks reflect the same logic used in the GUI for enabling/disabling
|
||||
// extruder selection fields
|
||||
if (region->config().perimeters.value > 0 || m_config.brim_width.value > 0)
|
||||
extruders.emplace_back(region->config().perimeter_extruder - 1);
|
||||
if (region->config().fill_density.value > 0)
|
||||
extruders.emplace_back(region->config().infill_extruder - 1);
|
||||
if (region->config().top_solid_layers.value > 0 || region->config().bottom_solid_layers.value > 0)
|
||||
extruders.emplace_back(region->config().solid_infill_extruder - 1);
|
||||
}
|
||||
|
||||
for (const PrintRegion *region : m_regions)
|
||||
region->collect_object_printing_extruders(extruders);
|
||||
sort_remove_duplicates(extruders);
|
||||
return extruders;
|
||||
}
|
||||
|
|
@ -366,37 +356,6 @@ double Print::max_allowed_layer_height() const
|
|||
return nozzle_diameter_max;
|
||||
}
|
||||
|
||||
static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_extruders)
|
||||
{
|
||||
if (opt.value > (int)num_extruders)
|
||||
// assign the default extruder
|
||||
opt.value = 1;
|
||||
}
|
||||
|
||||
static PrintObjectConfig object_config_from_model(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders)
|
||||
{
|
||||
PrintObjectConfig config = default_object_config;
|
||||
normalize_and_apply_config(config, object.config);
|
||||
// Clamp invalid extruders to the default extruder (with index 1).
|
||||
clamp_exturder_to_default(config.support_material_extruder, num_extruders);
|
||||
clamp_exturder_to_default(config.support_material_interface_extruder, num_extruders);
|
||||
return config;
|
||||
}
|
||||
|
||||
static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders)
|
||||
{
|
||||
PrintRegionConfig config = default_region_config;
|
||||
normalize_and_apply_config(config, volume.get_object()->config);
|
||||
normalize_and_apply_config(config, volume.config);
|
||||
if (! volume.material_id().empty())
|
||||
normalize_and_apply_config(config, volume.material()->config);
|
||||
// Clamp invalid extruders to the default extruder (with index 1).
|
||||
clamp_exturder_to_default(config.infill_extruder, num_extruders);
|
||||
clamp_exturder_to_default(config.perimeter_extruder, num_extruders);
|
||||
clamp_exturder_to_default(config.solid_infill_extruder, num_extruders);
|
||||
return config;
|
||||
}
|
||||
|
||||
// Caller is responsible for supplying models whose objects don't collide
|
||||
// and have explicit instance positions.
|
||||
void Print::add_model_object(ModelObject* model_object, int idx)
|
||||
|
|
@ -433,7 +392,7 @@ void Print::add_model_object(ModelObject* model_object, int idx)
|
|||
if (! volume->is_model_part() && ! volume->is_modifier())
|
||||
continue;
|
||||
// Get the config applied to this volume.
|
||||
PrintRegionConfig config = region_config_from_model_volume(m_default_region_config, *volume, 99999);
|
||||
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, *volume, 99999);
|
||||
// Find an existing print region with the same config.
|
||||
size_t region_id = size_t(-1);
|
||||
for (size_t i = 0; i < m_regions.size(); ++ i)
|
||||
|
|
@ -514,12 +473,12 @@ bool Print::apply_config(DynamicPrintConfig config)
|
|||
// If the new config for this volume differs from the other
|
||||
// volume configs currently associated to this region, it means
|
||||
// the region subdivision does not make sense anymore.
|
||||
if (! this_region_config.equals(region_config_from_model_volume(m_default_region_config, volume, 99999))) {
|
||||
if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, volume, 99999))) {
|
||||
rearrange_regions = true;
|
||||
goto exit_for_rearrange_regions;
|
||||
}
|
||||
} else {
|
||||
this_region_config = region_config_from_model_volume(m_default_region_config, volume, 99999);
|
||||
this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, volume, 99999);
|
||||
this_region_config_set = true;
|
||||
}
|
||||
for (const PrintRegionConfig &cfg : other_region_configs) {
|
||||
|
|
@ -563,10 +522,6 @@ exit_for_rearrange_regions:
|
|||
invalidated = true;
|
||||
}
|
||||
|
||||
// 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)
|
||||
object->update_layer_height_profile();
|
||||
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
|
|
@ -888,8 +843,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
if (model_parts_differ || modifiers_differ ||
|
||||
model_object.origin_translation != model_object_new.origin_translation ||
|
||||
model_object.layer_height_ranges != model_object_new.layer_height_ranges ||
|
||||
model_object.layer_height_profile != model_object_new.layer_height_profile ||
|
||||
model_object.layer_height_profile_valid != model_object_new.layer_height_profile_valid) {
|
||||
model_object.layer_height_profile != model_object_new.layer_height_profile) {
|
||||
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
|
||||
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
|
||||
for (auto it = range.first; it != range.second; ++ it) {
|
||||
|
|
@ -915,7 +869,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
if (object_config_changed)
|
||||
model_object.config = model_object_new.config;
|
||||
if (! object_diff.empty() || object_config_changed) {
|
||||
PrintObjectConfig new_config = object_config_from_model(m_default_object_config, model_object, num_extruders);
|
||||
PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders);
|
||||
auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id()));
|
||||
for (auto it = range.first; it != range.second; ++ it) {
|
||||
t_config_option_keys diff = it->print_object->config().diff(new_config);
|
||||
|
|
@ -957,7 +911,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
old.emplace_back(&(*it));
|
||||
}
|
||||
// Generate a list of trafos and XY offsets for instances of a ModelObject
|
||||
PrintObjectConfig config = object_config_from_model(m_default_object_config, *model_object, num_extruders);
|
||||
PrintObjectConfig config = PrintObject::object_config_from_model_object(m_default_object_config, *model_object, num_extruders);
|
||||
std::vector<PrintInstances> new_print_instances = print_objects_from_model_object(*model_object);
|
||||
if (old.empty()) {
|
||||
// Simple case, just generate new instances.
|
||||
|
|
@ -1048,11 +1002,11 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
// If the new config for this volume differs from the other
|
||||
// volume configs currently associated to this region, it means
|
||||
// the region subdivision does not make sense anymore.
|
||||
if (! this_region_config.equals(region_config_from_model_volume(m_default_region_config, volume, num_extruders)))
|
||||
if (! this_region_config.equals(PrintObject::region_config_from_model_volume(m_default_region_config, volume, num_extruders)))
|
||||
// Regions were split. Reset this print_object.
|
||||
goto print_object_end;
|
||||
} else {
|
||||
this_region_config = region_config_from_model_volume(m_default_region_config, volume, num_extruders);
|
||||
this_region_config = PrintObject::region_config_from_model_volume(m_default_region_config, volume, num_extruders);
|
||||
for (size_t i = 0; i < region_id; ++i) {
|
||||
const PrintRegion ®ion_other = *m_regions[i];
|
||||
if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config))
|
||||
|
|
@ -1103,7 +1057,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co
|
|||
int region_id = -1;
|
||||
if (&print_object == &print_object0) {
|
||||
// Get the config applied to this volume.
|
||||
PrintRegionConfig config = region_config_from_model_volume(m_default_region_config, *volume, num_extruders);
|
||||
PrintRegionConfig config = PrintObject::region_config_from_model_volume(m_default_region_config, *volume, num_extruders);
|
||||
// Find an existing print region with the same config.
|
||||
int idx_empty_slot = -1;
|
||||
for (int i = 0; i < (int)m_regions.size(); ++ i) {
|
||||
|
|
@ -1139,13 +1093,6 @@ 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.
|
||||
for (PrintObject *object : m_objects)
|
||||
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();
|
||||
|
||||
//FIXME there may be a race condition with the G-code export running at the background thread.
|
||||
this->update_object_placeholders();
|
||||
|
||||
|
|
@ -1178,7 +1125,7 @@ std::string Print::validate() const
|
|||
// Check horizontal clearance.
|
||||
{
|
||||
Polygons convex_hulls_other;
|
||||
for (PrintObject *object : m_objects) {
|
||||
for (const PrintObject *object : m_objects) {
|
||||
// Get convex hull of all meshes assigned to this print object.
|
||||
Polygon convex_hull;
|
||||
{
|
||||
|
|
@ -1239,46 +1186,63 @@ std::string Print::validate() const
|
|||
return L("The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter and Repetier G-code flavors.");
|
||||
if (! m_config.use_relative_e_distances)
|
||||
return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).");
|
||||
SlicingParameters slicing_params0 = m_objects.front()->slicing_parameters();
|
||||
|
||||
const PrintObject* tallest_object = m_objects.front(); // let's find the tallest object
|
||||
for (const auto* object : m_objects)
|
||||
if (*(object->layer_height_profile.end()-2) > *(tallest_object->layer_height_profile.end()-2) )
|
||||
tallest_object = object;
|
||||
|
||||
for (PrintObject *object : m_objects) {
|
||||
SlicingParameters slicing_params = object->slicing_parameters();
|
||||
if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
|
||||
std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON)
|
||||
return L("The Wipe Tower is only supported for multiple objects if they have equal layer heigths");
|
||||
if (slicing_params.raft_layers() != slicing_params0.raft_layers())
|
||||
return L("The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers");
|
||||
if (object->config().support_material_contact_distance != m_objects.front()->config().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))
|
||||
return L("The Wipe Tower is only supported for multiple objects if they are sliced equally.");
|
||||
|
||||
if (m_config.variable_layer_height) { // comparing layer height profiles
|
||||
bool failed = false;
|
||||
// layer_height_profile should be set by Print::apply().
|
||||
if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size()) {
|
||||
int i = 0;
|
||||
while (i < object->layer_height_profile.size() && i < tallest_object->layer_height_profile.size()) {
|
||||
if (std::abs(tallest_object->layer_height_profile[i] - object->layer_height_profile[i])) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
++i;
|
||||
if (i == object->layer_height_profile.size()-2) // this element contains this objects max z
|
||||
if (tallest_object->layer_height_profile[i] > object->layer_height_profile[i]) // the difference does not matter in this case
|
||||
++i;
|
||||
}
|
||||
if (m_objects.size() > 1) {
|
||||
bool has_custom_layering = false;
|
||||
std::vector<std::vector<coordf_t>> layer_height_profiles;
|
||||
for (const PrintObject *object : m_objects) {
|
||||
has_custom_layering = ! object->model_object()->layer_height_ranges.empty() || ! object->model_object()->layer_height_profile.empty();
|
||||
if (has_custom_layering) {
|
||||
layer_height_profiles.assign(m_objects.size(), std::vector<coordf_t>());
|
||||
break;
|
||||
}
|
||||
else
|
||||
failed = true;
|
||||
}
|
||||
SlicingParameters slicing_params0 = m_objects.front()->slicing_parameters();
|
||||
size_t tallest_object_idx = 0;
|
||||
if (has_custom_layering)
|
||||
PrintObject::update_layer_height_profile(*m_objects.front()->model_object(), slicing_params0, layer_height_profiles.front());
|
||||
for (size_t i = 1; i < m_objects.size(); ++ i) {
|
||||
const PrintObject *object = m_objects[i];
|
||||
const SlicingParameters slicing_params = object->slicing_parameters();
|
||||
if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
|
||||
std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON)
|
||||
return L("The Wipe Tower is only supported for multiple objects if they have equal layer heigths");
|
||||
if (slicing_params.raft_layers() != slicing_params0.raft_layers())
|
||||
return L("The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers");
|
||||
if (object->config().support_material_contact_distance != m_objects.front()->config().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))
|
||||
return L("The Wipe Tower is only supported for multiple objects if they are sliced equally.");
|
||||
if (has_custom_layering) {
|
||||
PrintObject::update_layer_height_profile(*object->model_object(), slicing_params, layer_height_profiles[i]);
|
||||
if (*(layer_height_profiles[i].end()-2) > *(layer_height_profiles[tallest_object_idx].end()-2))
|
||||
tallest_object_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (failed)
|
||||
return L("The Wipe tower is only supported if all objects have the same layer height profile");
|
||||
if (has_custom_layering) {
|
||||
const std::vector<coordf_t> &layer_height_profile_tallest = layer_height_profiles[tallest_object_idx];
|
||||
for (size_t idx_object = 0; idx_object < m_objects.size(); ++ idx_object) {
|
||||
const PrintObject *object = m_objects[idx_object];
|
||||
const std::vector<coordf_t> &layer_height_profile = layer_height_profiles[idx_object];
|
||||
bool failed = false;
|
||||
if (layer_height_profile_tallest.size() >= layer_height_profile.size()) {
|
||||
int i = 0;
|
||||
while (i < layer_height_profile.size() && i < layer_height_profile_tallest.size()) {
|
||||
if (std::abs(layer_height_profile_tallest[i] - layer_height_profile[i])) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
++ i;
|
||||
if (i == layer_height_profile.size() - 2) // this element contains this objects max z
|
||||
if (layer_height_profile_tallest[i] > layer_height_profile[i]) // the difference does not matter in this case
|
||||
++ i;
|
||||
}
|
||||
} else
|
||||
failed = true;
|
||||
if (failed)
|
||||
return L("The Wipe tower is only supported if all objects have the same layer height profile");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,10 @@ public:
|
|||
// Average diameter of nozzles participating on extruding this region.
|
||||
coordf_t bridging_height_avg(const PrintConfig &print_config) const;
|
||||
|
||||
// Collect extruder indices used to print this region's object.
|
||||
void collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const;
|
||||
static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig ®ion_config, std::vector<unsigned int> &object_extruders);
|
||||
|
||||
// Methods modifying the PrintRegion's state:
|
||||
public:
|
||||
Print* print() { return m_print; }
|
||||
|
|
@ -79,16 +83,6 @@ public:
|
|||
// vector of (vectors of volume ids), indexed by region_id
|
||||
std::vector<std::vector<int>> region_volumes;
|
||||
|
||||
// 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.
|
||||
// layer_height_profile must not be set by the background thread.
|
||||
std::vector<coordf_t> layer_height_profile;
|
||||
// There is a layer_height_profile at both PrintObject and ModelObject. The layer_height_profile at the ModelObject
|
||||
// is used for interactive editing and for loading / storing into a project file (AMF file as of today).
|
||||
// This flag indicates that the layer_height_profile at the UI has been updated, therefore the backend needs to get it.
|
||||
// This flag is necessary as we cannot safely clear the layer_height_profile if the background calculation is running.
|
||||
bool layer_height_profile_valid;
|
||||
|
||||
// this is set to true when LayerRegion->slices is split in top/internal/bottom
|
||||
// so that next call to make_perimeters() performs a union() before computing loops
|
||||
bool typed_slices;
|
||||
|
|
@ -129,23 +123,19 @@ public:
|
|||
SupportLayerPtrs::const_iterator insert_support_layer(SupportLayerPtrs::const_iterator pos, int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
|
||||
void delete_support_layer(int idx);
|
||||
|
||||
// To be used over the layer_height_profile of both the PrintObject and ModelObject
|
||||
// to initialize the height profile with the height ranges.
|
||||
bool update_layer_height_profile(std::vector<coordf_t> &layer_height_profile) const;
|
||||
|
||||
// Process layer_height_ranges, the raft layers and first layer thickness into layer_height_profile.
|
||||
// The layer_height_profile may be later modified interactively by the user to refine layers at sloping surfaces.
|
||||
bool update_layer_height_profile();
|
||||
|
||||
void reset_layer_height_profile();
|
||||
|
||||
void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action);
|
||||
// Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters.
|
||||
// Returns true, if the layer_height_profile was changed.
|
||||
static bool update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector<coordf_t> &layer_height_profile);
|
||||
|
||||
// Collect the slicing parameters, to be used by variable layer thickness algorithm,
|
||||
// by the interactive layer height editor and by the printing process itself.
|
||||
// The slicing parameters are dependent on various configuration values
|
||||
// (layer height, first layer height, raft settings, print nozzle diameter etc).
|
||||
SlicingParameters slicing_parameters() const;
|
||||
SlicingParameters slicing_parameters() const;
|
||||
static SlicingParameters slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object);
|
||||
|
||||
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
|
||||
std::vector<unsigned int> object_extruders() const;
|
||||
|
||||
// Called when slicing to SVG (see Print.pm sub export_svg), and used by perimeters.t
|
||||
void slice();
|
||||
|
|
@ -172,13 +162,16 @@ protected:
|
|||
// Invalidate steps based on a set of parameters changed.
|
||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||
|
||||
static PrintObjectConfig object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders);
|
||||
static PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders);
|
||||
|
||||
private:
|
||||
void make_perimeters();
|
||||
void prepare_infill();
|
||||
void infill();
|
||||
void generate_support_material();
|
||||
|
||||
void _slice();
|
||||
void _slice(const std::vector<coordf_t> &layer_height_profile);
|
||||
std::string _fix_slicing_errors();
|
||||
void _simplify_slices(double distance);
|
||||
void _make_perimeters();
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ void PrintConfigDef::init_fff_params()
|
|||
"This is mostly useful with Bowden extruders which suffer from oozing. "
|
||||
"This feature slows down both the print and the G-code generation.");
|
||||
def->cli = "avoid-crossing-perimeters!";
|
||||
def->mode = comAdvanced;
|
||||
def->mode = comExpert;
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("bed_temperature", coInts);
|
||||
|
|
@ -172,6 +172,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->cli = "bridge-speed=f";
|
||||
def->aliases = { "bridge_feed_rate" };
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(60);
|
||||
|
||||
def = this->add("brim_width", coFloat);
|
||||
|
|
@ -326,7 +327,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->sidetext = L("mm");
|
||||
def->cli = "elefant-foot-compensation=f";
|
||||
def->min = 0;
|
||||
def->mode = comExpert;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(0);
|
||||
|
||||
def = this->add("end_gcode", coString);
|
||||
|
|
@ -403,6 +404,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->cli = "external-perimeter-speed=s";
|
||||
def->ratio_over = "perimeter_speed";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloatOrPercent(50, true);
|
||||
|
||||
def = this->add("external_perimeters_first", coBool);
|
||||
|
|
@ -421,7 +423,7 @@ void PrintConfigDef::init_fff_params()
|
|||
"Slic3r keeps adding perimeters, until more than 70% of the loop immediately above "
|
||||
"is supported.");
|
||||
def->cli = "extra-perimeters!";
|
||||
def->mode = comAdvanced;
|
||||
def->mode = comExpert;
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
def = this->add("extruder", coInt);
|
||||
|
|
@ -706,12 +708,14 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_values.push_back("EDGE");
|
||||
def->enum_values.push_back("NGEN");
|
||||
def->enum_values.push_back("PVA");
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionStrings { "PLA" };
|
||||
|
||||
def = this->add("filament_soluble", coBools);
|
||||
def->label = L("Soluble material");
|
||||
def->tooltip = L("Soluble material is most likely used for a soluble support.");
|
||||
def->cli = "filament-soluble!";
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionBools { false };
|
||||
|
||||
def = this->add("filament_cost", coFloats);
|
||||
|
|
@ -884,6 +888,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->sidetext = L("mm/s");
|
||||
def->cli = "gap-fill-speed=f";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(20);
|
||||
|
||||
def = this->add("gcode_comments", coBool);
|
||||
|
|
@ -1014,6 +1019,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->cli = "infill-speed=f";
|
||||
def->aliases = { "print_feed_rate", "infill_feed_rate" };
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(80);
|
||||
|
||||
def = this->add("inherits", coString);
|
||||
|
|
@ -1419,6 +1425,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->cli = "perimeter-speed=f";
|
||||
def->aliases = { "perimeter_feed_rate" };
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(60);
|
||||
|
||||
def = this->add("perimeters", coInt);
|
||||
|
|
@ -1627,7 +1634,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_labels.push_back(L("Nearest"));
|
||||
def->enum_labels.push_back(L("Aligned"));
|
||||
def->enum_labels.push_back(L("Rear"));
|
||||
def->mode = comAdvanced;
|
||||
def->mode = comSimple;
|
||||
def->default_value = new ConfigOptionEnum<SeamPosition>(spAligned);
|
||||
|
||||
#if 0
|
||||
|
|
@ -1673,6 +1680,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->max = 300000;
|
||||
def->enum_values.push_back("115200");
|
||||
def->enum_values.push_back("250000");
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionInt(250000);
|
||||
|
||||
def = this->add("skirt_distance", coFloat);
|
||||
|
|
@ -1726,6 +1734,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->cli = "small-perimeter-speed=s";
|
||||
def->ratio_over = "perimeter_speed";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloatOrPercent(15, false);
|
||||
|
||||
def = this->add("solid_infill_below_area", coFloat);
|
||||
|
|
@ -1782,6 +1791,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->ratio_over = "infill_speed";
|
||||
def->aliases = { "solid_infill_feed_rate" };
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloatOrPercent(20, false);
|
||||
|
||||
def = this->add("solid_layers", coInt);
|
||||
|
|
@ -1873,7 +1883,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->tooltip = L("If checked, supports will be generated automatically based on the overhang threshold value."\
|
||||
" If unchecked, supports will be generated inside the \"Support Enforcer\" volumes only.");
|
||||
def->cli = "support-material-auto!";
|
||||
def->mode = comAdvanced;
|
||||
def->mode = comSimple;
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
def = this->add("support_material_xy_spacing", coFloatOrPercent);
|
||||
|
|
@ -1905,7 +1915,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->category = L("Support material");
|
||||
def->tooltip = L("Only create support if it lies on a build plate. Don't create support on a print.");
|
||||
def->cli = "support-material-buildplate-only!";
|
||||
def->mode = comAdvanced;
|
||||
def->mode = comSimple;
|
||||
def->default_value = new ConfigOptionBool(false);
|
||||
|
||||
def = this->add("support_material_contact_distance", coFloat);
|
||||
|
|
@ -2007,6 +2017,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->cli = "support-material-interface-speed=s";
|
||||
def->ratio_over = "support_material_speed";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloatOrPercent(100, true);
|
||||
|
||||
def = this->add("support_material_pattern", coEnum);
|
||||
|
|
@ -2041,6 +2052,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->sidetext = L("mm/s");
|
||||
def->cli = "support-material-speed=f";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloat(60);
|
||||
|
||||
def = this->add("support_material_synchronize_layers", coBool);
|
||||
|
|
@ -2143,6 +2155,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->cli = "top-solid-infill-speed=s";
|
||||
def->ratio_over = "solid_infill_speed";
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->default_value = new ConfigOptionFloatOrPercent(15, false);
|
||||
|
||||
def = this->add("top_solid_layers", coInt);
|
||||
|
|
@ -2487,7 +2500,6 @@ void PrintConfigDef::init_sla_params()
|
|||
def->label = L("Generate supports");
|
||||
def->category = L("Supports");
|
||||
def->tooltip = L("Generate supports for the models");
|
||||
def->sidetext = L("");
|
||||
def->cli = "";
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
|
|
@ -2549,7 +2561,6 @@ void PrintConfigDef::init_sla_params()
|
|||
def->tooltip = L("Merging bridges or pillars into another pillars can "
|
||||
"increase the radius. Zero means no increase, one means "
|
||||
"full increase.");
|
||||
def->sidetext = L("");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
def->max = 1;
|
||||
|
|
@ -2631,14 +2642,13 @@ void PrintConfigDef::init_sla_params()
|
|||
def->label = L("Use pad");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("Add a pad underneath the supported model");
|
||||
def->sidetext = L("");
|
||||
def->cli = "";
|
||||
def->default_value = new ConfigOptionBool(true);
|
||||
|
||||
def = this->add("pad_wall_thickness", coFloat);
|
||||
def->label = L("Pad wall thickness");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("");
|
||||
// def->tooltip = L("");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
|
|
@ -2647,7 +2657,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def = this->add("pad_wall_height", coFloat);
|
||||
def->label = L("Pad wall height");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("");
|
||||
// def->tooltip = L("");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
|
|
@ -2656,7 +2666,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def = this->add("pad_max_merge_distance", coFloat);
|
||||
def->label = L("Max merge distance");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("");
|
||||
// def->tooltip = L("");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
|
|
@ -2665,7 +2675,7 @@ void PrintConfigDef::init_sla_params()
|
|||
def = this->add("pad_edge_radius", coFloat);
|
||||
def->label = L("Pad edge radius");
|
||||
def->category = L("Pad");
|
||||
def->tooltip = L("");
|
||||
// def->tooltip = L("");
|
||||
def->sidetext = L("mm");
|
||||
def->cli = "";
|
||||
def->min = 0;
|
||||
|
|
|
|||
|
|
@ -38,8 +38,7 @@ namespace Slic3r {
|
|||
PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_instances) :
|
||||
PrintObjectBaseWithState(print, model_object),
|
||||
typed_slices(false),
|
||||
size(Vec3crd::Zero()),
|
||||
layer_height_profile_valid(false)
|
||||
size(Vec3crd::Zero())
|
||||
{
|
||||
// Compute the translation to be applied to our meshes so that we work with smaller coordinates
|
||||
{
|
||||
|
|
@ -65,8 +64,6 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta
|
|||
}
|
||||
this->set_copies(copies);
|
||||
}
|
||||
|
||||
this->layer_height_profile = model_object->layer_height_profile;
|
||||
}
|
||||
|
||||
PrintBase::ApplyStatus PrintObject::set_copies(const Points &points)
|
||||
|
|
@ -106,7 +103,10 @@ void PrintObject::slice()
|
|||
if (! this->set_started(posSlice))
|
||||
return;
|
||||
m_print->set_status(10, "Processing triangulated mesh");
|
||||
this->_slice();
|
||||
std::vector<coordf_t> layer_height_profile;
|
||||
this->update_layer_height_profile(*this->model_object(), this->slicing_parameters(), layer_height_profile);
|
||||
m_print->throw_if_canceled();
|
||||
this->_slice(layer_height_profile);
|
||||
m_print->throw_if_canceled();
|
||||
// Fix the model.
|
||||
//FIXME is this the right place to do? It is done repeateadly at the UI and now here at the backend.
|
||||
|
|
@ -455,7 +455,6 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
|||
|| opt_key == "first_layer_height"
|
||||
|| opt_key == "raft_layers") {
|
||||
steps.emplace_back(posSlice);
|
||||
this->reset_layer_height_profile();
|
||||
}
|
||||
else if (
|
||||
opt_key == "clip_multipart_objects"
|
||||
|
|
@ -542,7 +541,6 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|
|||
} else {
|
||||
// for legacy, if we can't handle this option let's invalidate all steps
|
||||
this->invalidate_all_steps();
|
||||
this->reset_layer_height_profile();
|
||||
invalidated = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1329,55 +1327,107 @@ void PrintObject::bridge_over_infill()
|
|||
}
|
||||
}
|
||||
|
||||
static void clamp_exturder_to_default(ConfigOptionInt &opt, size_t num_extruders)
|
||||
{
|
||||
if (opt.value > (int)num_extruders)
|
||||
// assign the default extruder
|
||||
opt.value = 1;
|
||||
}
|
||||
|
||||
PrintObjectConfig PrintObject::object_config_from_model_object(const PrintObjectConfig &default_object_config, const ModelObject &object, size_t num_extruders)
|
||||
{
|
||||
PrintObjectConfig config = default_object_config;
|
||||
normalize_and_apply_config(config, object.config);
|
||||
// Clamp invalid extruders to the default extruder (with index 1).
|
||||
clamp_exturder_to_default(config.support_material_extruder, num_extruders);
|
||||
clamp_exturder_to_default(config.support_material_interface_extruder, num_extruders);
|
||||
return config;
|
||||
}
|
||||
|
||||
PrintRegionConfig PrintObject::region_config_from_model_volume(const PrintRegionConfig &default_region_config, const ModelVolume &volume, size_t num_extruders)
|
||||
{
|
||||
PrintRegionConfig config = default_region_config;
|
||||
normalize_and_apply_config(config, volume.get_object()->config);
|
||||
normalize_and_apply_config(config, volume.config);
|
||||
if (! volume.material_id().empty())
|
||||
normalize_and_apply_config(config, volume.material()->config);
|
||||
// Clamp invalid extruders to the default extruder (with index 1).
|
||||
clamp_exturder_to_default(config.infill_extruder, num_extruders);
|
||||
clamp_exturder_to_default(config.perimeter_extruder, num_extruders);
|
||||
clamp_exturder_to_default(config.solid_infill_extruder, num_extruders);
|
||||
return config;
|
||||
}
|
||||
|
||||
SlicingParameters PrintObject::slicing_parameters() const
|
||||
{
|
||||
return SlicingParameters::create_from_config(
|
||||
this->print()->config(), m_config,
|
||||
unscale<double>(this->size(2)), this->print()->object_extruders());
|
||||
unscale<double>(this->size(2)), this->object_extruders());
|
||||
}
|
||||
|
||||
bool PrintObject::update_layer_height_profile(std::vector<coordf_t> &layer_height_profile) const
|
||||
SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full_config, const ModelObject &model_object)
|
||||
{
|
||||
PrintConfig print_config;
|
||||
PrintObjectConfig object_config;
|
||||
PrintRegionConfig default_region_config;
|
||||
print_config .apply(full_config, true);
|
||||
object_config.apply(full_config, true);
|
||||
default_region_config.apply(full_config, true);
|
||||
size_t num_extruders = print_config.nozzle_diameter.size();
|
||||
object_config = object_config_from_model_object(object_config, model_object, num_extruders);
|
||||
|
||||
std::vector<unsigned int> object_extruders;
|
||||
for (const ModelVolume *model_volume : model_object.volumes)
|
||||
if (model_volume->is_model_part())
|
||||
PrintRegion::collect_object_printing_extruders(
|
||||
print_config,
|
||||
region_config_from_model_volume(default_region_config, *model_volume, num_extruders),
|
||||
object_extruders);
|
||||
sort_remove_duplicates(object_extruders);
|
||||
|
||||
return SlicingParameters::create_from_config(print_config, object_config, model_object.bounding_box().max.z(), object_extruders);
|
||||
}
|
||||
|
||||
// returns 0-based indices of extruders used to print the object (without brim, support and other helper extrusions)
|
||||
std::vector<unsigned int> PrintObject::object_extruders() const
|
||||
{
|
||||
std::vector<unsigned int> extruders;
|
||||
extruders.reserve(this->region_volumes.size() * 3);
|
||||
for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region)
|
||||
if (! this->region_volumes[idx_region].empty())
|
||||
m_print->get_region(idx_region)->collect_object_printing_extruders(extruders);
|
||||
sort_remove_duplicates(extruders);
|
||||
return extruders;
|
||||
}
|
||||
|
||||
bool PrintObject::update_layer_height_profile(const ModelObject &model_object, const SlicingParameters &slicing_parameters, std::vector<coordf_t> &layer_height_profile)
|
||||
{
|
||||
bool updated = false;
|
||||
|
||||
// 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 = this->model_object()->layer_height_profile;
|
||||
layer_height_profile = model_object.layer_height_profile;
|
||||
updated = true;
|
||||
}
|
||||
|
||||
// Verify the layer_height_profile.
|
||||
SlicingParameters slicing_params = this->slicing_parameters();
|
||||
if (! layer_height_profile.empty() &&
|
||||
// Must not be of even length.
|
||||
((layer_height_profile.size() & 1) != 0 ||
|
||||
// Last entry must be at the top of the object.
|
||||
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_height()) > 1e-3))
|
||||
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_height()) > 1e-3))
|
||||
layer_height_profile.clear();
|
||||
|
||||
if (layer_height_profile.empty()) {
|
||||
if (0)
|
||||
// if (this->layer_height_profile.empty())
|
||||
layer_height_profile = layer_height_profile_adaptive(slicing_params, this->model_object()->layer_height_ranges, this->model_object()->volumes);
|
||||
layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_height_ranges, model_object.volumes);
|
||||
else
|
||||
layer_height_profile = layer_height_profile_from_ranges(slicing_params, this->model_object()->layer_height_ranges);
|
||||
layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_height_ranges);
|
||||
updated = true;
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
// This must be called from the main thread as it modifies the layer_height_profile.
|
||||
bool PrintObject::update_layer_height_profile()
|
||||
{
|
||||
// If the layer height profile has been marked as invalid for some reason (modified at the UI level
|
||||
// or invalidated due to the slicing parameters), clear it now.
|
||||
if (! this->layer_height_profile_valid) {
|
||||
this->layer_height_profile.clear();
|
||||
this->layer_height_profile_valid = true;
|
||||
}
|
||||
return this->update_layer_height_profile(this->layer_height_profile);
|
||||
}
|
||||
|
||||
// 1) Decides Z positions of the layers,
|
||||
// 2) Initializes layers and their regions
|
||||
// 3) Slices the object meshes
|
||||
|
|
@ -1387,7 +1437,7 @@ bool PrintObject::update_layer_height_profile()
|
|||
// Resulting expolygons of layer regions are marked as Internal.
|
||||
//
|
||||
// this should be idempotent
|
||||
void PrintObject::_slice()
|
||||
void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info();
|
||||
|
||||
|
|
@ -1406,7 +1456,7 @@ void PrintObject::_slice()
|
|||
{
|
||||
this->clear_layers();
|
||||
// Object layers (pairs of bottom/top Z coordinate), without the raft.
|
||||
std::vector<coordf_t> object_layers = generate_object_layers(slicing_params, this->layer_height_profile);
|
||||
std::vector<coordf_t> object_layers = generate_object_layers(slicing_params, layer_height_profile);
|
||||
// Reserve object layers for the raft. Last layer of the raft is the contact layer.
|
||||
int id = int(slicing_params.raft_layers());
|
||||
slice_zs.reserve(object_layers.size());
|
||||
|
|
@ -2198,22 +2248,4 @@ void PrintObject::_generate_support_material()
|
|||
support_material.generate(*this);
|
||||
}
|
||||
|
||||
void PrintObject::reset_layer_height_profile()
|
||||
{
|
||||
// Reset the layer_heigth_profile.
|
||||
this->layer_height_profile.clear();
|
||||
this->layer_height_profile_valid = false;
|
||||
// Reset the source layer_height_profile if it exists at the ModelObject.
|
||||
this->model_object()->layer_height_profile.clear();
|
||||
this->model_object()->layer_height_profile_valid = false;
|
||||
}
|
||||
|
||||
void PrintObject::adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action)
|
||||
{
|
||||
update_layer_height_profile(m_model_object->layer_height_profile);
|
||||
Slic3r::adjust_layer_height_profile(slicing_parameters(), m_model_object->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action));
|
||||
m_model_object->layer_height_profile_valid = true;
|
||||
layer_height_profile_valid = false;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -61,4 +61,20 @@ coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const
|
|||
return this->nozzle_dmr_avg(print_config) * sqrt(m_config.bridge_flow_ratio.value);
|
||||
}
|
||||
|
||||
void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig ®ion_config, std::vector<unsigned int> &object_extruders)
|
||||
{
|
||||
// These checks reflect the same logic used in the GUI for enabling/disabling extruder selection fields.
|
||||
if (region_config.perimeters.value > 0 || print_config.brim_width.value > 0)
|
||||
object_extruders.emplace_back(region_config.perimeter_extruder - 1);
|
||||
if (region_config.fill_density.value > 0)
|
||||
object_extruders.emplace_back(region_config.infill_extruder - 1);
|
||||
if (region_config.top_solid_layers.value > 0 || region_config.bottom_solid_layers.value > 0)
|
||||
object_extruders.emplace_back(region_config.solid_infill_extruder - 1);
|
||||
}
|
||||
|
||||
void PrintRegion::collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const
|
||||
{
|
||||
collect_object_printing_extruders(print()->config(), this->config(), object_extruders);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
#include <functional>
|
||||
#include <numeric>
|
||||
|
||||
#include "ExPolygon.hpp"
|
||||
#include "TriangleMesh.hpp"
|
||||
#include <libslic3r/ExPolygon.hpp>
|
||||
#include <libslic3r/TriangleMesh.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace sla {
|
||||
|
|
@ -53,7 +53,7 @@ struct Contour3D {
|
|||
|
||||
void merge(const Contour3D& ctr) {
|
||||
auto s3 = coord_t(points.size());
|
||||
auto s = coord_t(indices.size());
|
||||
auto s = indices.size();
|
||||
|
||||
points.insert(points.end(), ctr.points.begin(), ctr.points.end());
|
||||
indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end());
|
||||
|
|
@ -62,6 +62,17 @@ struct Contour3D {
|
|||
auto& idx = indices[n]; x(idx) += s3; y(idx) += s3; z(idx) += s3;
|
||||
}
|
||||
}
|
||||
|
||||
// Write the index triangle structure to OBJ file for debugging purposes.
|
||||
void to_obj(std::ostream& stream) {
|
||||
for(auto& p : points) {
|
||||
stream << "v " << p.transpose() << "\n";
|
||||
}
|
||||
|
||||
for(auto& f : indices) {
|
||||
stream << "f " << (f + Vec3i(1, 1, 1)).transpose() << "\n";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//using PointSet = Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::DontAlign>; //Eigen::MatrixXd;
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
#include "SLASpatIndex.hpp"
|
||||
#include "SLABasePool.hpp"
|
||||
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Model.hpp"
|
||||
#include <libslic3r/ClipperUtils.hpp>
|
||||
#include <libslic3r/Model.hpp>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
|
|
@ -164,7 +164,13 @@ Contour3D sphere(double rho, Portion portion = make_portion(0.0, 2.0*PI),
|
|||
return ret;
|
||||
}
|
||||
|
||||
Contour3D cylinder(double r, double h, size_t ssteps) {
|
||||
// Down facing cylinder in Z direction with arguments:
|
||||
// r: radius
|
||||
// h: Height
|
||||
// ssteps: how many edges will create the base circle
|
||||
// sp: starting point
|
||||
Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d sp = {0,0,0})
|
||||
{
|
||||
Contour3D ret;
|
||||
|
||||
auto steps = int(ssteps);
|
||||
|
|
@ -173,9 +179,10 @@ Contour3D cylinder(double r, double h, size_t ssteps) {
|
|||
points.reserve(2*ssteps);
|
||||
double a = 2*PI/steps;
|
||||
|
||||
Vec3d jp = {0, 0, 0};
|
||||
Vec3d endp = {0, 0, h};
|
||||
Vec3d jp = sp;
|
||||
Vec3d endp = {sp(X), sp(Y), sp(Z) + h};
|
||||
|
||||
// Upper circle points
|
||||
for(int i = 0; i < steps; ++i) {
|
||||
double phi = i*a;
|
||||
double ex = endp(X) + r*std::cos(phi);
|
||||
|
|
@ -183,6 +190,7 @@ Contour3D cylinder(double r, double h, size_t ssteps) {
|
|||
points.emplace_back(ex, ey, endp(Z));
|
||||
}
|
||||
|
||||
// Lower circle points
|
||||
for(int i = 0; i < steps; ++i) {
|
||||
double phi = i*a;
|
||||
double x = jp(X) + r*std::cos(phi);
|
||||
|
|
@ -190,6 +198,7 @@ Contour3D cylinder(double r, double h, size_t ssteps) {
|
|||
points.emplace_back(x, y, jp(Z));
|
||||
}
|
||||
|
||||
// Now create long triangles connecting upper and lower circles
|
||||
indices.reserve(2*ssteps);
|
||||
auto offs = steps;
|
||||
for(int i = 0; i < steps - 1; ++i) {
|
||||
|
|
@ -197,10 +206,26 @@ Contour3D cylinder(double r, double h, size_t ssteps) {
|
|||
indices.emplace_back(i, offs + i + 1, i + 1);
|
||||
}
|
||||
|
||||
// Last triangle connecting the first and last vertices
|
||||
auto last = steps - 1;
|
||||
indices.emplace_back(0, last, offs);
|
||||
indices.emplace_back(last, offs + last, offs);
|
||||
|
||||
// According to the slicing algorithms, we need to aid them with generating
|
||||
// a watertight body. So we create a triangle fan for the upper and lower
|
||||
// ending of the cylinder to close the geometry.
|
||||
points.emplace_back(jp); size_t ci = points.size() - 1;
|
||||
for(int i = 0; i < steps - 1; ++i)
|
||||
indices.emplace_back(i + offs + 1, i + offs, ci);
|
||||
|
||||
indices.emplace_back(offs, steps + offs - 1, ci);
|
||||
|
||||
points.emplace_back(endp); ci = points.size() - 1;
|
||||
for(int i = 0; i < steps - 1; ++i)
|
||||
indices.emplace_back(ci, i, i + 1);
|
||||
|
||||
indices.emplace_back(steps - 1, 0, ci);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -352,36 +377,15 @@ struct Pillar {
|
|||
r(radius), steps(st), endpoint(endp), starts_from_head(false)
|
||||
{
|
||||
assert(steps > 0);
|
||||
int steps_1 = int(steps - 1);
|
||||
|
||||
auto& points = mesh.points;
|
||||
auto& indices = mesh.indices;
|
||||
points.reserve(2*steps);
|
||||
double a = 2*PI/steps;
|
||||
double h = jp(Z) - endp(Z);
|
||||
assert(h > 0); // Endpoint is below the starting point
|
||||
|
||||
for(size_t i = 0; i < steps; ++i) {
|
||||
double phi = i*a;
|
||||
double x = jp(X) + r*std::cos(phi);
|
||||
double y = jp(Y) + r*std::sin(phi);
|
||||
points.emplace_back(x, y, jp(Z));
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < steps; ++i) {
|
||||
double phi = i*a;
|
||||
double ex = endp(X) + r*std::cos(phi);
|
||||
double ey = endp(Y) + r*std::sin(phi);
|
||||
points.emplace_back(ex, ey, endp(Z));
|
||||
}
|
||||
|
||||
indices.reserve(2*steps);
|
||||
int offs = int(steps);
|
||||
for(int i = 0; i < steps_1 ; ++i) {
|
||||
indices.emplace_back(i, i + offs, offs + i + 1);
|
||||
indices.emplace_back(i, offs + i + 1, i + 1);
|
||||
}
|
||||
|
||||
indices.emplace_back(0, steps_1, offs);
|
||||
indices.emplace_back(steps_1, offs + steps_1, offs);
|
||||
// We just create a bridge geometry with the pillar parameters and
|
||||
// move the data.
|
||||
Contour3D body = cylinder(radius, h, st, endp);
|
||||
mesh.points.swap(body.points);
|
||||
mesh.indices.swap(body.indices);
|
||||
}
|
||||
|
||||
Pillar(const Junction& junc, const Vec3d& endp):
|
||||
|
|
@ -826,45 +830,46 @@ public:
|
|||
const TriangleMesh& merged_mesh() const {
|
||||
if(meshcache_valid) return meshcache;
|
||||
|
||||
meshcache = TriangleMesh();
|
||||
Contour3D merged;
|
||||
|
||||
for(auto& head : heads()) {
|
||||
if(m_ctl.stopcondition()) break;
|
||||
if(head.is_valid()) {
|
||||
auto&& m = mesh(head.mesh);
|
||||
meshcache.merge(m);
|
||||
}
|
||||
if(head.is_valid())
|
||||
merged.merge(head.mesh);
|
||||
}
|
||||
|
||||
for(auto& stick : pillars()) {
|
||||
if(m_ctl.stopcondition()) break;
|
||||
meshcache.merge(mesh(stick.mesh));
|
||||
meshcache.merge(mesh(stick.base));
|
||||
merged.merge(stick.mesh);
|
||||
merged.merge(stick.base);
|
||||
}
|
||||
|
||||
for(auto& j : junctions()) {
|
||||
if(m_ctl.stopcondition()) break;
|
||||
meshcache.merge(mesh(j.mesh));
|
||||
merged.merge(j.mesh);
|
||||
}
|
||||
|
||||
for(auto& cb : compact_bridges()) {
|
||||
if(m_ctl.stopcondition()) break;
|
||||
meshcache.merge(mesh(cb.mesh));
|
||||
merged.merge(cb.mesh);
|
||||
}
|
||||
|
||||
for(auto& bs : bridges()) {
|
||||
if(m_ctl.stopcondition()) break;
|
||||
meshcache.merge(mesh(bs.mesh));
|
||||
merged.merge(bs.mesh);
|
||||
}
|
||||
|
||||
|
||||
if(m_ctl.stopcondition()) {
|
||||
// In case of failure we have to return an empty mesh
|
||||
meshcache = TriangleMesh();
|
||||
return meshcache;
|
||||
}
|
||||
|
||||
meshcache = mesh(merged);
|
||||
|
||||
// TODO: Is this necessary?
|
||||
meshcache.repair();
|
||||
//meshcache.repair();
|
||||
|
||||
BoundingBoxf3&& bb = meshcache.bounding_box();
|
||||
model_height = bb.max(Z) - bb.min(Z);
|
||||
|
|
@ -1555,7 +1560,7 @@ bool SLASupportTree::generate(const PointSet &points,
|
|||
}
|
||||
|
||||
double d = distance(jp, jn);
|
||||
if(jn(Z) <= gndlvl + nearhead.r_back_mm || d > max_len) break;
|
||||
break;
|
||||
|
||||
double chkd = bridge_mesh_intersect(jp, dirv(jp, jn),
|
||||
pradius,
|
||||
|
|
|
|||
|
|
@ -440,10 +440,6 @@ std::vector<float> SLAPrint::calculate_heights(const BoundingBoxf3& bb3d,
|
|||
auto flh = float(layer_height);
|
||||
auto gnd = float(bb3d.min(Z));
|
||||
|
||||
// The first layer (the one before the initial height) is added only
|
||||
// if there is no pad and no elevation value
|
||||
if(minZ >= gnd) heights.emplace_back(minZ);
|
||||
|
||||
for(float h = minZ + initial_layer_height; h < maxZ; h += flh)
|
||||
if(h >= gnd) heights.emplace_back(h);
|
||||
|
||||
|
|
@ -510,6 +506,9 @@ void SLAPrint::process()
|
|||
po.m_supportdata.reset(new SLAPrintObject::SupportData());
|
||||
po.m_supportdata->emesh = EigenMesh3D(po.transformed_mesh());
|
||||
|
||||
// If supports are disabled, we can skip the model scan.
|
||||
if(!po.m_config.supports_enable.getBool()) return;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Support point count "
|
||||
<< mo.sla_support_points.size();
|
||||
|
||||
|
|
@ -693,7 +692,6 @@ void SLAPrint::process()
|
|||
// model_slice method. Only difference is that here it works with
|
||||
// scaled coordinates
|
||||
po.m_level_ids.clear();
|
||||
if(sminZ >= smodelgnd) po.m_level_ids.emplace_back(sminZ);
|
||||
for(LevelID h = sminZ + sih; h < smaxZ; h += slh)
|
||||
if(h >= smodelgnd) po.m_level_ids.emplace_back(h);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,14 @@
|
|||
#ifndef slic3r_Slicing_hpp_
|
||||
#define slic3r_Slicing_hpp_
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Utils.hpp"
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
|
|
@ -91,6 +94,8 @@ struct SlicingParameters
|
|||
coordf_t object_print_z_min;
|
||||
coordf_t object_print_z_max;
|
||||
};
|
||||
static_assert(IsTriviallyCopyable<SlicingParameters>::value, "SlicingParameters class is not POD (and it should be - see constructor).");
|
||||
|
||||
|
||||
// The two slicing parameters lead to the same layering as long as the variable layer thickness is not in action.
|
||||
inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters &sp2)
|
||||
|
|
@ -131,7 +136,7 @@ extern std::vector<coordf_t> layer_height_profile_adaptive(
|
|||
const ModelVolumePtrs &volumes);
|
||||
|
||||
|
||||
enum LayerHeightEditActionType {
|
||||
enum LayerHeightEditActionType : unsigned int {
|
||||
LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0,
|
||||
LAYER_HEIGHT_EDIT_ACTION_DECREASE = 1,
|
||||
LAYER_HEIGHT_EDIT_ACTION_REDUCE = 2,
|
||||
|
|
|
|||
|
|
@ -9,20 +9,16 @@
|
|||
#define ENABLE_SHOW_CAMERA_TARGET 0
|
||||
// Log debug messages to console when changing selection
|
||||
#define ENABLE_SELECTION_DEBUG_OUTPUT 0
|
||||
// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active
|
||||
#define ENABLE_RENDER_SELECTION_CENTER 0
|
||||
|
||||
//====================
|
||||
// 1.42.0.alpha1 techs
|
||||
//====================
|
||||
#define ENABLE_1_42_0_ALPHA1 1
|
||||
|
||||
// Uses a unique opengl context
|
||||
#define ENABLE_USE_UNIQUE_GLCONTEXT (1 && ENABLE_1_42_0_ALPHA1)
|
||||
// Disable synchronization of unselected instances
|
||||
#define DISABLE_INSTANCES_SYNCH (0 && ENABLE_1_42_0_ALPHA1)
|
||||
// Keeps objects on bed while scaling them using the scale gizmo
|
||||
#define ENABLE_ENSURE_ON_BED_WHILE_SCALING (1 && ENABLE_1_42_0_ALPHA1)
|
||||
// All rotations made using the rotate gizmo are done with respect to the world reference system
|
||||
#define ENABLE_WORLD_ROTATIONS (1 && ENABLE_1_42_0_ALPHA1)
|
||||
// Scene's GUI made using imgui library
|
||||
#define ENABLE_IMGUI (1 && ENABLE_1_42_0_ALPHA1)
|
||||
#define DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI (1 && ENABLE_IMGUI)
|
||||
|
|
@ -30,12 +26,6 @@
|
|||
#define ENABLE_SLA_SUPPORT_GIZMO_MOD (1 && ENABLE_1_42_0_ALPHA1)
|
||||
// Use wxDataViewRender instead of wxDataViewCustomRenderer
|
||||
#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1)
|
||||
// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active
|
||||
#define ENABLE_RENDER_SELECTION_CENTER (0 && ENABLE_1_42_0_ALPHA1)
|
||||
// Show visual hints in the 3D scene when sidebar matrix fields have focus
|
||||
#define ENABLE_SIDEBAR_VISUAL_HINTS (1 && ENABLE_1_42_0_ALPHA1)
|
||||
// Separate rendering for opaque and transparent volumes
|
||||
#define ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_1_42_0_ALPHA1)
|
||||
|
||||
//====================
|
||||
// 1.42.0.alpha2 techs
|
||||
|
|
@ -45,7 +35,7 @@
|
|||
// Improves navigation between sidebar fields
|
||||
#define ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION (1 && ENABLE_1_42_0_ALPHA2)
|
||||
// Adds print bed models to 3D scene
|
||||
#define ENABLE_PRINT_BED_MODELS (0 && ENABLE_1_42_0_ALPHA2)
|
||||
#define ENABLE_PRINT_BED_MODELS (1 && ENABLE_1_42_0_ALPHA2)
|
||||
#endif // _technologies_h_
|
||||
|
||||
|
||||
|
|
@ -62,3 +52,7 @@
|
|||
#define ENABLE_GENERIC_SUBPARTS_PLACEMENT (1 && ENABLE_1_42_0_ALPHA4)
|
||||
// Reworked management of bed shape changes
|
||||
#define ENABLE_REWORKED_BED_SHAPE_CHANGE (1 && ENABLE_1_42_0_ALPHA4)
|
||||
// Use anisotropic filtering on bed plate texture
|
||||
#define ENABLE_ANISOTROPIC_FILTER_ON_BED_TEXTURES (1 && ENABLE_1_42_0_ALPHA4)
|
||||
// Bunch of fixes related to volumes centering
|
||||
#define ENABLE_VOLUMES_CENTERING_FIXES (1 && ENABLE_1_42_0_ALPHA4)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd>& facets )
|
||||
TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd>& facets)
|
||||
: repaired(false)
|
||||
{
|
||||
stl_initialize(&this->stl);
|
||||
|
|
@ -99,6 +99,8 @@ TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other)
|
|||
return *this;
|
||||
}
|
||||
|
||||
// #define SLIC3R_TRACE_REPAIR
|
||||
|
||||
void TriangleMesh::repair()
|
||||
{
|
||||
if (this->repaired) return;
|
||||
|
|
@ -109,7 +111,9 @@ void TriangleMesh::repair()
|
|||
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() started";
|
||||
|
||||
// checking exact
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_exact";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_check_facets_exact(&stl);
|
||||
stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge);
|
||||
stl.stats.facets_w_2_bad_edge = (stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_edge);
|
||||
|
|
@ -124,7 +128,9 @@ void TriangleMesh::repair()
|
|||
for (int i = 0; i < iterations; i++) {
|
||||
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
||||
//printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations);
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_nearby";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_check_facets_nearby(&stl, tolerance);
|
||||
//printf(" Fixed %d edges.\n", stl.stats.edges_fixed - last_edges_fixed);
|
||||
//last_edges_fixed = stl.stats.edges_fixed;
|
||||
|
|
@ -137,7 +143,9 @@ void TriangleMesh::repair()
|
|||
|
||||
// remove_unconnected
|
||||
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_remove_unconnected_facets";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_remove_unconnected_facets(&stl);
|
||||
}
|
||||
|
||||
|
|
@ -146,26 +154,36 @@ void TriangleMesh::repair()
|
|||
// Don't fill holes, the current algorithm does more harm than good on complex holes.
|
||||
// Rather let the slicing algorithm close gaps in 2D slices.
|
||||
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_fill_holes";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_fill_holes(&stl);
|
||||
stl_clear_error(&stl);
|
||||
}
|
||||
#endif
|
||||
|
||||
// normal_directions
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_directions";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_fix_normal_directions(&stl);
|
||||
|
||||
// normal_values
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_values";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_fix_normal_values(&stl);
|
||||
|
||||
// always calculate the volume and reverse all normals if volume is negative
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_calculate_volume";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_calculate_volume(&stl);
|
||||
|
||||
// neighbors
|
||||
#ifdef SLIC3R_TRACE_REPAIR
|
||||
BOOST_LOG_TRIVIAL(trace) << "\tstl_verify_neighbors";
|
||||
#endif /* SLIC3R_TRACE_REPAIR */
|
||||
stl_verify_neighbors(&stl);
|
||||
|
||||
this->repaired = true;
|
||||
|
|
@ -507,67 +525,22 @@ BoundingBoxf3 TriangleMesh::bounding_box() const
|
|||
return bb;
|
||||
}
|
||||
|
||||
BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& t) const
|
||||
BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) const
|
||||
{
|
||||
bool has_shared = (stl.v_shared != nullptr);
|
||||
if (!has_shared)
|
||||
stl_generate_shared_vertices(const_cast<stl_file*>(&stl));
|
||||
|
||||
unsigned int vertices_count = (stl.stats.shared_vertices > 0) ? (unsigned int)stl.stats.shared_vertices : 3 * (unsigned int)stl.stats.number_of_facets;
|
||||
|
||||
if (vertices_count == 0)
|
||||
return BoundingBoxf3();
|
||||
|
||||
Eigen::MatrixXd src_vertices(3, vertices_count);
|
||||
|
||||
if (stl.stats.shared_vertices > 0)
|
||||
{
|
||||
assert(stl.v_shared != nullptr);
|
||||
stl_vertex* vertex_ptr = stl.v_shared;
|
||||
for (int i = 0; i < stl.stats.shared_vertices; ++i)
|
||||
{
|
||||
src_vertices(0, i) = (double)(*vertex_ptr)(0);
|
||||
src_vertices(1, i) = (double)(*vertex_ptr)(1);
|
||||
src_vertices(2, i) = (double)(*vertex_ptr)(2);
|
||||
vertex_ptr += 1;
|
||||
BoundingBoxf3 bbox;
|
||||
if (stl.v_shared == nullptr) {
|
||||
// Using the STL faces.
|
||||
for (int i = 0; i < this->facets_count(); ++ i) {
|
||||
const stl_facet &facet = this->stl.facet_start[i];
|
||||
for (size_t j = 0; j < 3; ++ j)
|
||||
bbox.merge(trafo * facet.vertex[j].cast<double>());
|
||||
}
|
||||
} else {
|
||||
// Using the shared vertices should be a bit quicker than using the STL faces.
|
||||
for (int i = 0; i < stl.stats.shared_vertices; ++ i)
|
||||
bbox.merge(trafo * this->stl.v_shared[i].cast<double>());
|
||||
}
|
||||
else
|
||||
{
|
||||
stl_facet* facet_ptr = stl.facet_start;
|
||||
unsigned int v_id = 0;
|
||||
while (facet_ptr < stl.facet_start + stl.stats.number_of_facets)
|
||||
{
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
src_vertices(0, v_id) = (double)facet_ptr->vertex[i](0);
|
||||
src_vertices(1, v_id) = (double)facet_ptr->vertex[i](1);
|
||||
src_vertices(2, v_id) = (double)facet_ptr->vertex[i](2);
|
||||
++v_id;
|
||||
}
|
||||
facet_ptr += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_shared && (stl.stats.shared_vertices > 0))
|
||||
stl_invalidate_shared_vertices(const_cast<stl_file*>(&stl));
|
||||
|
||||
Eigen::MatrixXd dst_vertices(3, vertices_count);
|
||||
dst_vertices = t * src_vertices.colwise().homogeneous();
|
||||
|
||||
Vec3d v_min(dst_vertices(0, 0), dst_vertices(1, 0), dst_vertices(2, 0));
|
||||
Vec3d v_max = v_min;
|
||||
|
||||
for (int i = 1; i < vertices_count; ++i)
|
||||
{
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
v_min(j) = std::min(v_min(j), dst_vertices(j, i));
|
||||
v_max(j) = std::max(v_max(j), dst_vertices(j, i));
|
||||
}
|
||||
}
|
||||
|
||||
return BoundingBoxf3(v_min, v_max);
|
||||
return bbox;
|
||||
}
|
||||
|
||||
TriangleMesh TriangleMesh::convex_hull_3d() const
|
||||
|
|
@ -1992,4 +1965,5 @@ TriangleMesh make_sphere(double rho, double fa) {
|
|||
TriangleMesh mesh(vertices, facets);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ public:
|
|||
Polygon convex_hull();
|
||||
BoundingBoxf3 bounding_box() const;
|
||||
// Returns the bbox of this TriangleMesh transformed by the given transformation
|
||||
BoundingBoxf3 transformed_bounding_box(const Transform3d& t) const;
|
||||
BoundingBoxf3 transformed_bounding_box(const Transform3d &trafo) const;
|
||||
// Returns the convex hull of this TriangleMesh
|
||||
TriangleMesh convex_hull_3d() const;
|
||||
void reset_repair_stats();
|
||||
|
|
|
|||
|
|
@ -159,6 +159,16 @@ template<class T> size_t next_highest_power_of_2(T v,
|
|||
extern std::string xml_escape(std::string text);
|
||||
|
||||
|
||||
#if defined __GNUC__ & __GNUC__ < 5
|
||||
// Older GCCs don't have std::is_trivially_copyable
|
||||
// cf. https://gcc.gnu.org/onlinedocs/gcc-4.9.4/libstdc++/manual/manual/status.html#status.iso.2011
|
||||
#warning "GCC version < 5, faking std::is_trivially_copyable"
|
||||
template<typename T> struct IsTriviallyCopyable { static constexpr bool value = true; };
|
||||
#else
|
||||
template<typename T> struct IsTriviallyCopyable : public std::is_trivially_copyable<T> {};
|
||||
#endif
|
||||
|
||||
|
||||
class ScopeGuard
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue