Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/xs
diff options
context:
space:
mode:
authorbubnikv <bubnikv@gmail.com>2018-09-17 16:12:13 +0300
committerbubnikv <bubnikv@gmail.com>2018-09-17 16:12:13 +0300
commitfe3b92870f217a3356094a0a6ad60b25ccf18996 (patch)
treedd9ebd271884eef3be0270cbb34809738950b390 /xs
parentd934b63424ab76ddcd8a87f272838df611dfcd0c (diff)
Merged with dev
Diffstat (limited to 'xs')
-rw-r--r--xs/CMakeLists.txt3
-rw-r--r--xs/src/libnest2d/libnest2d/libnest2d.hpp8
-rw-r--r--xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp14
-rw-r--r--xs/src/libnest2d/libnest2d/selections/filler.hpp2
-rw-r--r--xs/src/libnest2d/libnest2d/selections/firstfit.hpp11
-rw-r--r--xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp8
-rw-r--r--xs/src/libslic3r/EdgeGrid.cpp6
-rw-r--r--xs/src/libslic3r/EdgeGrid.hpp2
-rw-r--r--xs/src/libslic3r/ExPolygonCollection.cpp9
-rw-r--r--xs/src/libslic3r/ExtrusionEntity.hpp40
-rw-r--r--xs/src/libslic3r/ExtrusionEntityCollection.hpp16
-rw-r--r--xs/src/libslic3r/Fill/FillHoneycomb.cpp4
-rw-r--r--xs/src/libslic3r/Flow.cpp9
-rw-r--r--xs/src/libslic3r/Format/3mf.cpp20
-rw-r--r--xs/src/libslic3r/Format/AMF.cpp64
-rw-r--r--xs/src/libslic3r/Format/PRUS.cpp18
-rw-r--r--xs/src/libslic3r/GCode.cpp11
-rw-r--r--xs/src/libslic3r/GCode.hpp1
-rw-r--r--xs/src/libslic3r/GCode/WipeTower.hpp6
-rw-r--r--xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp47
-rw-r--r--xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp10
-rw-r--r--xs/src/libslic3r/LayerRegion.cpp3
-rw-r--r--xs/src/libslic3r/Model.cpp103
-rw-r--r--xs/src/libslic3r/Model.hpp69
-rw-r--r--xs/src/libslic3r/ModelArrange.hpp83
-rw-r--r--xs/src/libslic3r/MultiPoint.hpp4
-rw-r--r--xs/src/libslic3r/Point.hpp1
-rw-r--r--xs/src/libslic3r/Polygon.hpp6
-rw-r--r--xs/src/libslic3r/Polyline.cpp14
-rw-r--r--xs/src/libslic3r/Polyline.hpp2
-rw-r--r--xs/src/libslic3r/Print.cpp13
-rw-r--r--xs/src/libslic3r/Print.hpp18
-rw-r--r--xs/src/libslic3r/PrintConfig.cpp31
-rw-r--r--xs/src/libslic3r/PrintConfig.hpp11
-rw-r--r--xs/src/libslic3r/PrintObject.cpp97
-rw-r--r--xs/src/libslic3r/PrintRegion.cpp5
-rw-r--r--xs/src/libslic3r/Slicing.cpp6
-rw-r--r--xs/src/libslic3r/SupportMaterial.cpp671
-rw-r--r--xs/src/libslic3r/SupportMaterial.hpp16
-rw-r--r--xs/src/libslic3r/SurfaceCollection.hpp5
-rw-r--r--xs/src/libslic3r/Technologies.hpp12
-rw-r--r--xs/src/libslic3r/TriangleMesh.cpp468
-rw-r--r--xs/src/libslic3r/TriangleMesh.hpp37
-rw-r--r--xs/src/libslic3r/Utils.hpp1
-rw-r--r--xs/src/libslic3r/libslic3r.h2
-rw-r--r--xs/src/libslic3r/utils.cpp10
-rw-r--r--xs/src/slic3r/AppController.cpp382
-rw-r--r--xs/src/slic3r/AppController.hpp72
-rw-r--r--xs/src/slic3r/AppControllerWx.cpp66
-rw-r--r--xs/src/slic3r/GUI/3DScene.cpp28
-rw-r--r--xs/src/slic3r/GUI/3DScene.hpp1
-rw-r--r--xs/src/slic3r/GUI/AppConfig.cpp8
-rw-r--r--xs/src/slic3r/GUI/AppConfig.hpp8
-rw-r--r--xs/src/slic3r/GUI/ConfigWizard.cpp16
-rw-r--r--xs/src/slic3r/GUI/FirmwareDialog.cpp25
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3D.cpp16
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3D.hpp3
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3DManager.cpp6
-rw-r--r--xs/src/slic3r/GUI/GLCanvas3DManager.hpp1
-rw-r--r--xs/src/slic3r/GUI/GLGizmo.cpp255
-rw-r--r--xs/src/slic3r/GUI/GLGizmo.hpp23
-rw-r--r--xs/src/slic3r/GUI/GUI.cpp169
-rw-r--r--xs/src/slic3r/GUI/GUI.hpp30
-rw-r--r--xs/src/slic3r/GUI/GUI_ObjectParts.cpp669
-rw-r--r--xs/src/slic3r/GUI/GUI_ObjectParts.hpp30
-rw-r--r--xs/src/slic3r/GUI/LambdaObjectDialog.cpp57
-rw-r--r--xs/src/slic3r/GUI/LambdaObjectDialog.hpp11
-rw-r--r--xs/src/slic3r/GUI/OptionsGroup.cpp2
-rw-r--r--xs/src/slic3r/GUI/OptionsGroup.hpp6
-rw-r--r--xs/src/slic3r/GUI/Preset.cpp2
-rw-r--r--xs/src/slic3r/GUI/ProgressIndicator.hpp10
-rw-r--r--xs/src/slic3r/GUI/ProgressStatusBar.cpp2
-rw-r--r--xs/src/slic3r/GUI/ProgressStatusBar.hpp3
-rw-r--r--xs/src/slic3r/GUI/Tab.cpp5
-rw-r--r--xs/src/slic3r/GUI/wxExtensions.cpp261
-rw-r--r--xs/src/slic3r/GUI/wxExtensions.hpp136
-rw-r--r--xs/src/slic3r/Utils/Serial.cpp7
-rw-r--r--xs/xsp/GUI.xsp58
-rw-r--r--xs/xsp/GUI_3DScene.xsp9
-rw-r--r--xs/xsp/Layer.xsp2
-rw-r--r--xs/xsp/Model.xsp27
-rw-r--r--xs/xsp/Print.xsp6
-rw-r--r--xs/xsp/ProgressStatusBar.xsp3
-rw-r--r--xs/xsp/XS.xsp5
84 files changed, 2943 insertions, 1474 deletions
diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt
index a442530db..878ce4dfe 100644
--- a/xs/CMakeLists.txt
+++ b/xs/CMakeLists.txt
@@ -179,6 +179,7 @@ add_library(libslic3r STATIC
${LIBDIR}/libslic3r/SurfaceCollection.hpp
${LIBDIR}/libslic3r/SVG.cpp
${LIBDIR}/libslic3r/SVG.hpp
+ ${LIBDIR}/libslic3r/Technologies.hpp
${LIBDIR}/libslic3r/TriangleMesh.cpp
${LIBDIR}/libslic3r/TriangleMesh.hpp
${LIBDIR}/libslic3r/SLABasePool.hpp
@@ -568,7 +569,7 @@ if (WIN32)
endif ()
# SLIC3R_MSVC_PDB
-if (MSVC AND SLIC3R_MSVC_PDB AND ${CMAKE_BUILD_TYPE} STREQUAL "Release")
+if (MSVC AND SLIC3R_MSVC_PDB AND "${CMAKE_BUILD_TYPE}" STREQUAL "Release")
set_target_properties(XS PROPERTIES
COMPILE_FLAGS "/Zi"
LINK_FLAGS "/DEBUG /OPT:REF /OPT:ICF"
diff --git a/xs/src/libnest2d/libnest2d/libnest2d.hpp b/xs/src/libnest2d/libnest2d/libnest2d.hpp
index 4d1e62f99..8841d1b73 100644
--- a/xs/src/libnest2d/libnest2d/libnest2d.hpp
+++ b/xs/src/libnest2d/libnest2d/libnest2d.hpp
@@ -640,6 +640,7 @@ public:
// The progress function will be called with the number of placed items
using ProgressFunction = std::function<void(unsigned)>;
+using StopCondition = std::function<bool(void)>;
/**
* A wrapper interface (trait) class for any selections strategy provider.
@@ -674,6 +675,8 @@ public:
*/
void progressIndicator(ProgressFunction fn) { impl_.progressIndicator(fn); }
+ void stopCondition(StopCondition cond) { impl_.stopCondition(cond); }
+
/**
* \brief A method to start the calculation on the input sequence.
*
@@ -864,6 +867,11 @@ public:
selector_.progressIndicator(func); return *this;
}
+ /// Set a predicate to tell when to abort nesting.
+ inline Nester& stopCondition(StopCondition fn) {
+ selector_.stopCondition(fn); return *this;
+ }
+
inline PackGroup lastResult() {
PackGroup ret;
for(size_t i = 0; i < selector_.binCount(); i++) {
diff --git a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp
index ee93d0592..39761f557 100644
--- a/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp
+++ b/xs/src/libnest2d/libnest2d/selections/djd_heuristic.hpp
@@ -551,7 +551,7 @@ public:
// Safety test: try to pack each item into an empty bin. If it fails
// then it should be removed from the not_packed list
{ auto it = store_.begin();
- while (it != store_.end()) {
+ while (it != store_.end() && !this->stopcond_()) {
Placer p(bin); p.configure(pconfig);
if(!p.pack(*it, rem(it, store_))) {
it = store_.erase(it);
@@ -592,9 +592,11 @@ public:
bool do_pairs = config_.try_pairs;
bool do_triplets = config_.try_triplets;
+ StopCondition stopcond = this->stopcond_;
// The DJD heuristic algorithm itself:
auto packjob = [INITIAL_FILL_AREA, bin_area, w, do_triplets, do_pairs,
+ stopcond,
&tryOneByOne,
&tryGroupsOfTwo,
&tryGroupsOfThree,
@@ -606,12 +608,12 @@ public:
double waste = .0;
bool lasttry = false;
- while(!not_packed.empty()) {
+ while(!not_packed.empty() && !stopcond()) {
{// Fill the bin up to INITIAL_FILL_PROPORTION of its capacity
auto it = not_packed.begin();
- while(it != not_packed.end() &&
+ while(it != not_packed.end() && !stopcond() &&
filled_area < INITIAL_FILL_AREA)
{
if(placer.pack(*it, rem(it, not_packed))) {
@@ -623,14 +625,14 @@ public:
}
}
- // try pieses one by one
+ // try pieces one by one
while(tryOneByOne(placer, not_packed, waste, free_area,
filled_area)) {
waste = 0; lasttry = false;
makeProgress(placer, idx, 1);
}
- // try groups of 2 pieses
+ // try groups of 2 pieces
while(do_pairs &&
tryGroupsOfTwo(placer, not_packed, waste, free_area,
filled_area)) {
@@ -638,7 +640,7 @@ public:
makeProgress(placer, idx, 2);
}
- // try groups of 3 pieses
+ // try groups of 3 pieces
while(do_triplets &&
tryGroupsOfThree(placer, not_packed, waste, free_area,
filled_area)) {
diff --git a/xs/src/libnest2d/libnest2d/selections/filler.hpp b/xs/src/libnest2d/libnest2d/selections/filler.hpp
index 0da7220a1..5f95a6eff 100644
--- a/xs/src/libnest2d/libnest2d/selections/filler.hpp
+++ b/xs/src/libnest2d/libnest2d/selections/filler.hpp
@@ -60,7 +60,7 @@ public:
placer.configure(pconfig);
auto it = store_.begin();
- while(it != store_.end()) {
+ while(it != store_.end() && !this->stopcond_()) {
if(!placer.pack(*it, {std::next(it), store_.end()})) {
if(packed_bins_.back().empty()) ++it;
placer.clearItems();
diff --git a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp
index bca7497db..6bb9c60e4 100644
--- a/xs/src/libnest2d/libnest2d/selections/firstfit.hpp
+++ b/xs/src/libnest2d/libnest2d/selections/firstfit.hpp
@@ -56,10 +56,12 @@ public:
this->progress_(static_cast<unsigned>(--total));
};
+ auto& cancelled = this->stopcond_;
+
// Safety test: try to pack each item into an empty bin. If it fails
// then it should be removed from the list
{ auto it = store_.begin();
- while (it != store_.end()) {
+ while (it != store_.end() && !cancelled()) {
Placer p(bin); p.configure(pconfig);
if(!p.pack(*it)) {
it = store_.erase(it);
@@ -67,13 +69,14 @@ public:
}
}
+
auto it = store_.begin();
- while(it != store_.end()) {
+ while(it != store_.end() && !cancelled()) {
bool was_packed = false;
size_t j = 0;
- while(!was_packed) {
- for(; j < placers.size() && !was_packed; j++) {
+ while(!was_packed && !cancelled()) {
+ for(; j < placers.size() && !was_packed && !cancelled(); j++) {
if((was_packed = placers[j].pack(*it, rem(it, store_) )))
makeProgress(placers[j], j);
}
diff --git a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp b/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp
index 05bbae658..cfb98a9c8 100644
--- a/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp
+++ b/xs/src/libnest2d/libnest2d/selections/selection_boilerplate.hpp
@@ -2,6 +2,7 @@
#define SELECTION_BOILERPLATE_HPP
#include "../libnest2d.hpp"
+#include <atomic>
namespace libnest2d { namespace selections {
@@ -25,14 +26,15 @@ public:
return packed_bins_[binIndex];
}
- inline void progressIndicator(ProgressFunction fn) {
- progress_ = fn;
- }
+ inline void progressIndicator(ProgressFunction fn) { progress_ = fn; }
+
+ inline void stopCondition(StopCondition cond) { stopcond_ = cond; }
protected:
PackGroup packed_bins_;
ProgressFunction progress_ = [](unsigned){};
+ StopCondition stopcond_ = [](){ return false; };
};
}
diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/xs/src/libslic3r/EdgeGrid.cpp
index 50f424e6d..2b2893c80 100644
--- a/xs/src/libslic3r/EdgeGrid.cpp
+++ b/xs/src/libslic3r/EdgeGrid.cpp
@@ -1225,8 +1225,10 @@ bool EdgeGrid::Grid::signed_distance(const Point &pt, coord_t search_radius, coo
return true;
}
-Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
+Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) const
{
+ assert(std::abs(2 * offset) < m_resolution);
+
typedef std::unordered_multimap<Point, int, PointHash> EndPointMapType;
// 0) Prepare a binary grid.
size_t cell_rows = m_rows + 2;
@@ -1237,7 +1239,7 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
cell_inside[r * cell_cols + c] = cell_inside_or_crossing(r - 1, c - 1);
// Fill in empty cells, which have a left / right neighbor filled.
// Fill in empty cells, which have the top / bottom neighbor filled.
- {
+ if (fill_holes) {
std::vector<char> cell_inside2(cell_inside);
for (int r = 1; r + 1 < int(cell_rows); ++ r) {
for (int c = 1; c + 1 < int(cell_cols); ++ c) {
diff --git a/xs/src/libslic3r/EdgeGrid.hpp b/xs/src/libslic3r/EdgeGrid.hpp
index 3eb741865..ab1aa4ed0 100644
--- a/xs/src/libslic3r/EdgeGrid.hpp
+++ b/xs/src/libslic3r/EdgeGrid.hpp
@@ -58,7 +58,7 @@ public:
const size_t cols() const { return m_cols; }
// For supports: Contours enclosing the rasterized edges.
- Polygons contours_simplified(coord_t offset) const;
+ Polygons contours_simplified(coord_t offset, bool fill_holes) const;
protected:
struct Cell {
diff --git a/xs/src/libslic3r/ExPolygonCollection.cpp b/xs/src/libslic3r/ExPolygonCollection.cpp
index e52498ecb..6933544b6 100644
--- a/xs/src/libslic3r/ExPolygonCollection.cpp
+++ b/xs/src/libslic3r/ExPolygonCollection.cpp
@@ -61,12 +61,11 @@ ExPolygonCollection::rotate(double angle, const Point &center)
}
template <class T>
-bool
-ExPolygonCollection::contains(const T &item) const
+bool ExPolygonCollection::contains(const T &item) const
{
- for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) {
- if (it->contains(item)) return true;
- }
+ for (const ExPolygon &poly : this->expolygons)
+ if (poly.contains(item))
+ return true;
return false;
}
template bool ExPolygonCollection::contains<Point>(const Point &item) const;
diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp
index 2bd03600c..cce3020f8 100644
--- a/xs/src/libslic3r/ExtrusionEntity.hpp
+++ b/xs/src/libslic3r/ExtrusionEntity.hpp
@@ -91,6 +91,8 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
virtual double min_mm3_per_mm() const = 0;
virtual Polyline as_polyline() const = 0;
+ virtual void collect_polylines(Polylines &dst) const = 0;
+ virtual Polylines as_polylines() const { Polylines dst; this->collect_polylines(dst); return dst; }
virtual double length() const = 0;
virtual double total_volume() const = 0;
};
@@ -123,8 +125,11 @@ public:
ExtrusionPath* clone() const { return new ExtrusionPath (*this); }
void reverse() { this->polyline.reverse(); }
- Point first_point() const { return this->polyline.points.front(); }
- Point last_point() const { return this->polyline.points.back(); }
+ Point first_point() const override { return this->polyline.points.front(); }
+ Point last_point() const override { return this->polyline.points.back(); }
+ size_t size() const { return this->polyline.size(); }
+ bool empty() const { return this->polyline.empty(); }
+ bool is_closed() const { return ! this->empty() && this->polyline.points.front() == this->polyline.points.back(); }
// Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection.
// Currently not used.
void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
@@ -133,8 +138,8 @@ public:
void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const;
void clip_end(double distance);
void simplify(double tolerance);
- virtual double length() const;
- virtual ExtrusionRole role() const { return m_role; }
+ double length() const override;
+ ExtrusionRole role() const override { return m_role; }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@@ -149,7 +154,8 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const { return this->mm3_per_mm; }
Polyline as_polyline() const { return this->polyline; }
- virtual double total_volume() const { return mm3_per_mm * unscale<double>(length()); }
+ void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); }
+ double total_volume() const override { return mm3_per_mm * unscale<double>(length()); }
private:
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
@@ -178,10 +184,10 @@ public:
bool can_reverse() const { return true; }
ExtrusionMultiPath* clone() const { return new ExtrusionMultiPath(*this); }
void reverse();
- Point first_point() const { return this->paths.front().polyline.points.front(); }
- Point last_point() const { return this->paths.back().polyline.points.back(); }
- virtual double length() const;
- virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
+ Point first_point() const override { return this->paths.front().polyline.points.front(); }
+ Point last_point() const override { return this->paths.back().polyline.points.back(); }
+ double length() const override;
+ ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@@ -196,7 +202,8 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const;
Polyline as_polyline() const;
- virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
+ void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
+ double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
};
// Single continuous extrusion loop, possibly with varying extrusion thickness, extrusion height or bridging / non bridging.
@@ -218,18 +225,18 @@ public:
bool make_clockwise();
bool make_counter_clockwise();
void reverse();
- Point first_point() const { return this->paths.front().polyline.points.front(); }
- Point last_point() const { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); }
+ Point first_point() const override { return this->paths.front().polyline.points.front(); }
+ Point last_point() const override { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); }
Polygon polygon() const;
- virtual double length() const;
+ double length() const override;
bool split_at_vertex(const Point &point);
void split_at(const Point &point, bool prefer_non_overhang);
void clip_end(double distance, ExtrusionPaths* paths) const;
// Test, whether the point is extruded by a bridging flow.
// This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead.
bool has_overhang_point(const Point &point) const;
- virtual ExtrusionRole role() const { return this->paths.empty() ? erNone : this->paths.front().role(); }
- ExtrusionLoopRole loop_role() const { return m_loop_role; }
+ ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
+ ExtrusionLoopRole loop_role() const { return m_loop_role; }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
@@ -244,7 +251,8 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const;
Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
- virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
+ void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
+ double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
private:
ExtrusionLoopRole m_loop_role;
diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
index 3b34145f8..ee0d3d5cd 100644
--- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp
+++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
@@ -24,7 +24,7 @@ public:
explicit operator ExtrusionPaths() const;
bool is_collection() const { return true; };
- virtual ExtrusionRole role() const {
+ ExtrusionRole role() const override {
ExtrusionRole out = erNone;
for (const ExtrusionEntity *ee : entities) {
ExtrusionRole er = ee->role();
@@ -71,11 +71,11 @@ public:
Point last_point() const { return this->entities.back()->last_point(); }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
- virtual void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const;
+ void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override;
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
// Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill.
- virtual void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const;
+ void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override;
Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const
{ Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; }
Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const
@@ -84,14 +84,20 @@ public:
void flatten(ExtrusionEntityCollection* retval) const;
ExtrusionEntityCollection flatten() const;
double min_mm3_per_mm() const;
- virtual double total_volume() const {double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
+ double total_volume() const override { double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
// Following methods shall never be called on an ExtrusionEntityCollection.
Polyline as_polyline() const {
CONFESS("Calling as_polyline() on a ExtrusionEntityCollection");
return Polyline();
};
- virtual double length() const {
+
+ void collect_polylines(Polylines &dst) const override {
+ for (ExtrusionEntity* extrusion_entity : this->entities)
+ extrusion_entity->collect_polylines(dst);
+ }
+
+ double length() const override {
CONFESS("Calling length() on a ExtrusionEntityCollection");
return 0.;
}
diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.cpp b/xs/src/libslic3r/Fill/FillHoneycomb.cpp
index 6f26167a2..cbfe926f2 100644
--- a/xs/src/libslic3r/Fill/FillHoneycomb.cpp
+++ b/xs/src/libslic3r/Fill/FillHoneycomb.cpp
@@ -86,8 +86,8 @@ void FillHoneycomb::_fill_surface_single(
Polylines paths;
{
Polylines p;
- for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it)
- p.push_back((Polyline)(*it));
+ for (Polygon &poly : polygons)
+ p.emplace_back(poly.points);
paths = intersection_pl(p, to_polygons(expolygon));
}
diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp
index c33de8b6e..9ad482daf 100644
--- a/xs/src/libslic3r/Flow.cpp
+++ b/xs/src/libslic3r/Flow.cpp
@@ -115,7 +115,8 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
// if object->config().support_material_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config().layer_height.value),
- false);
+ // bridge_flow_ratio
+ 0.f);
}
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
@@ -127,7 +128,8 @@ Flow support_material_1st_layer_flow(const PrintObject *object, float layer_heig
(width.value > 0) ? width : object->config().extrusion_width,
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value)),
- false);
+ // bridge_flow_ratio
+ 0.f);
}
Flow support_material_interface_flow(const PrintObject *object, float layer_height)
@@ -139,7 +141,8 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig
// if object->config().support_material_interface_extruder == 0 (which means to not trigger tool change, but use the current extruder instead), get_at will return the 0th component.
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_interface_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config().layer_height.value),
- false);
+ // bridge_flow_ratio
+ 0.f);
}
}
diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp
index 464cbb8e6..43c99f19f 100644
--- a/xs/src/libslic3r/Format/3mf.cpp
+++ b/xs/src/libslic3r/Format/3mf.cpp
@@ -71,6 +71,7 @@ const char* VOLUME_TYPE = "volume";
const char* NAME_KEY = "name";
const char* MODIFIER_KEY = "modifier";
+const char* VOLUME_TYPE_KEY = "volume_type";
const unsigned int VALID_OBJECT_TYPES_COUNT = 1;
const char* VALID_OBJECT_TYPES[] =
@@ -1252,9 +1253,13 @@ namespace Slic3r {
// we extract from the given matrix only the values currently used
// translation
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ Vec3d offset(transform(0, 3), transform(1, 3), transform(2, 3));
+#else
double offset_x = transform(0, 3);
double offset_y = transform(1, 3);
double offset_z = transform(2, 3);
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
// scale
double sx = ::sqrt(sqr(transform(0, 0)) + sqr(transform(1, 0)) + sqr(transform(2, 0)));
@@ -1287,8 +1292,12 @@ namespace Slic3r {
double angle_z = (rotation.axis() == Vec3d::UnitZ()) ? rotation.angle() : -rotation.angle();
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ instance.set_offset(offset);
+#else
instance.offset(0) = offset_x;
instance.offset(1) = offset_y;
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
instance.scaling_factor = sx;
instance.rotation = angle_z;
}
@@ -1434,7 +1443,9 @@ namespace Slic3r {
if (metadata.key == NAME_KEY)
volume->name = metadata.value;
else if ((metadata.key == MODIFIER_KEY) && (metadata.value == "1"))
- volume->modifier = true;
+ volume->set_type(ModelVolume::PARAMETER_MODIFIER);
+ else if (metadata.key == VOLUME_TYPE_KEY)
+ volume->set_type(ModelVolume::type_from_string(metadata.value));
else
volume->config.set_deserialize(metadata.key, metadata.value);
}
@@ -1949,9 +1960,12 @@ namespace Slic3r {
if (!volume->name.empty())
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
- // stores volume's modifier field
- if (volume->modifier)
+ // stores volume's modifier field (legacy, to support old slicers)
+ if (volume->is_modifier())
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
+ // stores volume's type (overrides the modifier field above)
+ stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " <<
+ VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n";
// stores volume's config data
for (const std::string& key : volume->config.keys())
diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp
index 81617ad72..803d9ee54 100644
--- a/xs/src/libslic3r/Format/AMF.cpp
+++ b/xs/src/libslic3r/Format/AMF.cpp
@@ -29,7 +29,12 @@
// VERSION NUMBERS
// 0 : .amf, .amf.xml and .zip.amf files saved by older slic3r. No version definition in them.
// 1 : Introduction of amf versioning. No other change in data saved into amf files.
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+// 2 : Added z component of offset.
+const unsigned int VERSION_AMF = 2;
+#else
const unsigned int VERSION_AMF = 1;
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
const char* SLIC3RPE_AMF_VERSION = "slic3rpe_amf_version";
const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config";
@@ -119,19 +124,31 @@ struct AMFParserContext
NODE_TYPE_INSTANCE, // amf/constellation/instance
NODE_TYPE_DELTAX, // amf/constellation/instance/deltax
NODE_TYPE_DELTAY, // amf/constellation/instance/deltay
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ NODE_TYPE_DELTAZ, // amf/constellation/instance/deltaz
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
NODE_TYPE_RZ, // amf/constellation/instance/rz
NODE_TYPE_SCALE, // amf/constellation/instance/scale
NODE_TYPE_METADATA, // anywhere under amf/*/metadata
};
struct Instance {
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ Instance() : deltax_set(false), deltay_set(false), deltaz_set(false), rz_set(false), scale_set(false) {}
+#else
Instance() : deltax_set(false), deltay_set(false), rz_set(false), scale_set(false) {}
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
// Shift in the X axis.
float deltax;
bool deltax_set;
// Shift in the Y axis.
float deltay;
bool deltay_set;
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ // Shift in the Z axis.
+ float deltaz;
+ bool deltaz_set;
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
// Rotation around the Z axis.
float rz;
bool rz_set;
@@ -254,6 +271,10 @@ void AMFParserContext::startElement(const char *name, const char **atts)
node_type_new = NODE_TYPE_DELTAX;
else if (strcmp(name, "deltay") == 0)
node_type_new = NODE_TYPE_DELTAY;
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ else if (strcmp(name, "deltaz") == 0)
+ node_type_new = NODE_TYPE_DELTAZ;
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
else if (strcmp(name, "rz") == 0)
node_type_new = NODE_TYPE_RZ;
else if (strcmp(name, "scale") == 0)
@@ -314,7 +335,15 @@ void AMFParserContext::characters(const XML_Char *s, int len)
{
switch (m_path.size()) {
case 4:
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ if (m_path.back() == NODE_TYPE_DELTAX ||
+ m_path.back() == NODE_TYPE_DELTAY ||
+ m_path.back() == NODE_TYPE_DELTAZ ||
+ m_path.back() == NODE_TYPE_RZ ||
+ m_path.back() == NODE_TYPE_SCALE)
+#else
if (m_path.back() == NODE_TYPE_DELTAX || m_path.back() == NODE_TYPE_DELTAY || m_path.back() == NODE_TYPE_RZ || m_path.back() == NODE_TYPE_SCALE)
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
m_value[0].append(s, len);
break;
case 6:
@@ -354,6 +383,14 @@ void AMFParserContext::endElement(const char * /* name */)
m_instance->deltay_set = true;
m_value[0].clear();
break;
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ case NODE_TYPE_DELTAZ:
+ assert(m_instance);
+ m_instance->deltaz = float(atof(m_value[0].c_str()));
+ m_instance->deltaz_set = true;
+ m_value[0].clear();
+ break;
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
case NODE_TYPE_RZ:
assert(m_instance);
m_instance->rz = float(atof(m_value[0].c_str()));
@@ -458,9 +495,14 @@ void AMFParserContext::endElement(const char * /* name */)
p = end + 1;
}
m_object->layer_height_profile_valid = true;
- } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume && strcmp(opt_key, "modifier") == 0) {
- // Is this volume a modifier volume?
- m_volume->modifier = atoi(m_value[1].c_str()) == 1;
+ } else if (m_path.size() == 5 && m_path[3] == NODE_TYPE_VOLUME && m_volume) {
+ if (strcmp(opt_key, "modifier") == 0) {
+ // Is this volume a modifier volume?
+ // "modifier" flag comes first in the XML file, so it may be later overwritten by the "type" flag.
+ m_volume->set_type((atoi(m_value[1].c_str()) == 1) ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART);
+ } else if (strcmp(opt_key, "volume_type") == 0) {
+ m_volume->set_type(ModelVolume::type_from_string(m_value[1]));
+ }
}
} else if (m_path.size() == 3) {
if (m_path[1] == NODE_TYPE_MATERIAL) {
@@ -498,8 +540,12 @@ void AMFParserContext::endDocument()
for (const Instance &instance : object.second.instances)
if (instance.deltax_set && instance.deltay_set) {
ModelInstance *mi = m_model.objects[object.second.idx]->add_instance();
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ mi->set_offset(Vec3d((double)instance.deltax, (double)instance.deltay, (double)instance.deltaz));
+#else
mi->offset(0) = instance.deltax;
mi->offset(1) = instance.deltay;
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
mi->rotation = instance.rz_set ? instance.rz : 0.f;
mi->scaling_factor = instance.scale_set ? instance.scale : 1.f;
}
@@ -781,8 +827,9 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
if (!volume->name.empty())
stream << " <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n";
- if (volume->modifier)
+ if (volume->is_modifier())
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
+ stream << " <metadata type=\"slic3r.volume_type\">" << ModelVolume::type_to_string(volume->type()) << "</metadata>\n";
for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) {
stream << " <triangle>\n";
for (int j = 0; j < 3; ++j)
@@ -800,12 +847,21 @@ bool store_amf(const char *path, Model *model, Print* print, bool export_print_c
" <instance objectid=\"" PRINTF_ZU "\">\n"
" <deltax>%lf</deltax>\n"
" <deltay>%lf</deltay>\n"
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ " <deltaz>%lf</deltaz>\n"
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
" <rz>%lf</rz>\n"
" <scale>%lf</scale>\n"
" </instance>\n",
object_id,
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ instance->get_offset(X),
+ instance->get_offset(Y),
+ instance->get_offset(Z),
+#else
instance->offset(0),
instance->offset(1),
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
instance->rotation,
instance->scaling_factor);
//FIXME missing instance->scaling_factor
diff --git a/xs/src/libslic3r/Format/PRUS.cpp b/xs/src/libslic3r/Format/PRUS.cpp
index 3cf3fc075..45eb56c63 100644
--- a/xs/src/libslic3r/Format/PRUS.cpp
+++ b/xs/src/libslic3r/Format/PRUS.cpp
@@ -166,7 +166,11 @@ bool load_prus(const char *path, Model *model)
float trafo[3][4] = { 0 };
double instance_rotation = 0.;
double instance_scaling_factor = 1.f;
- Vec2d instance_offset(0., 0.);
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ Vec3d instance_offset = Vec3d::Zero();
+#else
+ Vec2d instance_offset(0., 0.);
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
bool trafo_set = false;
unsigned int group_id = (unsigned int)-1;
unsigned int extruder_id = (unsigned int)-1;
@@ -207,8 +211,12 @@ bool load_prus(const char *path, Model *model)
for (size_t c = 0; c < 3; ++ c)
trafo[r][c] += mat_trafo(r, c);
}
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2]));
+#else
instance_offset(0) = position[0] - zero[0];
instance_offset(1) = position[1] - zero[1];
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
trafo[2][3] = position[2] / instance_scaling_factor;
trafo_set = true;
}
@@ -360,8 +368,12 @@ bool load_prus(const char *path, Model *model)
ModelInstance *instance = model_object->add_instance();
instance->rotation = instance_rotation;
instance->scaling_factor = instance_scaling_factor;
- instance->offset = instance_offset;
- ++ num_models;
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ instance->set_offset(instance_offset);
+#else
+ instance->offset = instance_offset;
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
+ ++num_models;
if (group_id != (size_t)-1)
group_to_model_object[group_id] = model_object;
} else {
diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index ac09db4bd..3c6e6de53 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -277,7 +277,6 @@ std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gco
}
-
std::string WipeTowerIntegration::prime(GCode &gcodegen)
{
assert(m_layer_idx == 0);
@@ -1000,8 +999,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
for (const Extruder &extruder : m_writer.extruders()) {
- double used_filament = extruder.used_filament();
- double extruded_volume = extruder.extruded_volume();
+ double used_filament = extruder.used_filament() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] : 0.f);
+ double extruded_volume = extruder.extruded_volume() + (has_wipe_tower ? print.wipe_tower_data().used_filament[extruder.id()] * 2.4052f : 0.f); // assumes 1.75mm filament diameter
double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
double filament_cost = filament_weight * extruder.filament_cost() * 0.001;
print.m_print_statistics.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament));
@@ -1014,8 +1013,10 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
_write_format(file, "; filament cost = %.1lf\n", filament_cost);
}
}
- print.m_print_statistics.total_used_filament = print.m_print_statistics.total_used_filament + used_filament;
- print.m_print_statistics.total_extruded_volume = print.m_print_statistics.total_extruded_volume + extruded_volume;
+ print.m_print_statistics.total_used_filament += used_filament;
+ print.m_print_statistics.total_extruded_volume += extruded_volume;
+ print.m_print_statistics.total_wipe_tower_filament += has_wipe_tower ? used_filament - extruder.used_filament() : 0.;
+ print.m_print_statistics.total_wipe_tower_cost += has_wipe_tower ? (extruded_volume - extruder.extruded_volume())* extruder.filament_density() * 0.001 * extruder.filament_cost() * 0.001 : 0.;
}
_write_format(file, "; total filament cost = %.1lf\n", print.m_print_statistics.total_cost);
_write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str());
diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp
index 34183012a..45f17a68a 100644
--- a/xs/src/libslic3r/GCode.hpp
+++ b/xs/src/libslic3r/GCode.hpp
@@ -98,6 +98,7 @@ public:
void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; }
std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer);
std::string finalize(GCode &gcodegen);
+ std::vector<float> used_filament_length() const;
private:
WipeTowerIntegration& operator=(const WipeTowerIntegration&);
diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp
index c35a0feb3..5cbbc1ca9 100644
--- a/xs/src/libslic3r/GCode/WipeTower.hpp
+++ b/xs/src/libslic3r/GCode/WipeTower.hpp
@@ -154,6 +154,12 @@ public:
// the wipe tower has been completely covered by the tool change extrusions,
// or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
virtual bool layer_finished() const = 0;
+
+ // Returns used filament length per extruder:
+ virtual std::vector<float> get_used_filament() const = 0;
+
+ // Returns total number of toolchanges:
+ virtual int get_number_of_toolchanges() const = 0;
};
}; // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 2c96cce7b..54bdfdfd6 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -111,9 +111,10 @@ public:
const WipeTower::xy start_pos_rotated() const { return m_start_pos; }
const WipeTower::xy pos_rotated() const { return WipeTower::xy(m_current_pos, 0.f, m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); }
float elapsed_time() const { return m_elapsed_time; }
+ float get_and_reset_used_filament_length() { float temp = m_used_filament_length; m_used_filament_length = 0.f; return temp; }
// Extrude with an explicitely provided amount of extrusion.
- Writer& extrude_explicit(float x, float y, float e, float f = 0.f)
+ Writer& extrude_explicit(float x, float y, float e, float f = 0.f, bool record_length = false)
{
if (x == m_current_pos.x && y == m_current_pos.y && e == 0.f && (f == 0.f || f == m_current_feedrate))
// Neither extrusion nor a travel move.
@@ -122,6 +123,8 @@ public:
float dx = x - m_current_pos.x;
float dy = y - m_current_pos.y;
double len = sqrt(dx*dx+dy*dy);
+ if (record_length)
+ m_used_filament_length += e;
// Now do the "internal rotation" with respect to the wipe tower center
@@ -162,8 +165,8 @@ public:
return *this;
}
- Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f)
- { return extrude_explicit(dest.x, dest.y, e, f); }
+ Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f, bool record_length = false)
+ { return extrude_explicit(dest.x, dest.y, e, f, record_length); }
// Travel to a new XY position. f=0 means use the current value.
Writer& travel(float x, float y, float f = 0.f)
@@ -177,7 +180,7 @@ public:
{
float dx = x - m_current_pos.x;
float dy = y - m_current_pos.y;
- return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f);
+ return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true);
}
Writer& extrude(const WipeTower::xy &dest, const float f = 0.f)
@@ -259,8 +262,8 @@ public:
// extrude quickly amount e to x2 with feed f.
Writer& ram(float x1, float x2, float dy, float e0, float e, float f)
{
- extrude_explicit(x1, m_current_pos.y + dy, e0, f);
- extrude_explicit(x2, m_current_pos.y, e);
+ extrude_explicit(x1, m_current_pos.y + dy, e0, f, true);
+ extrude_explicit(x2, m_current_pos.y, e, 0.f, true);
return *this;
}
@@ -404,6 +407,7 @@ private:
float m_last_fan_speed = 0.f;
int current_temp = -1;
const float m_default_analyzer_line_width;
+ float m_used_filament_length = 0.f;
std::string set_format_X(float x)
{
@@ -525,6 +529,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
++ m_num_tool_changes;
}
+ m_old_temperature = -1; // If the priming is turned off in config, the temperature changing commands will not actually appear
+ // in the output gcode - we should not remember emitting them (we will output them twice in the worst case)
+
// Reset the extruder current to a normal value.
writer.set_extruder_trimpot(550)
.feedrate(6000)
@@ -537,6 +544,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
// so that tool_change() will know to extrude the wipe tower brim:
m_print_brim = true;
+ // Ask our writer about how much material was consumed:
+ m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
ToolChangeResult result;
result.priming = true;
result.print_z = this->m_z_pos;
@@ -606,10 +616,10 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
toolchange_Load(writer, cleaning_box);
writer.travel(writer.x(),writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road
toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area.
+ ++ m_num_tool_changes;
} else
toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature);
- ++ m_num_tool_changes;
m_depth_traversed += wipe_area;
if (last_change_in_layer) {// draw perimeter line
@@ -632,6 +642,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo
";------------------\n"
"\n\n");
+ // Ask our writer about how much material was consumed:
+ m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
ToolChangeResult result;
result.priming = false;
result.print_z = this->m_z_pos;
@@ -683,6 +696,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo
m_print_brim = false; // Mark the brim as extruded
+ // Ask our writer about how much material was consumed:
+ m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
ToolChangeResult result;
result.priming = false;
result.print_z = this->m_z_pos;
@@ -804,8 +820,9 @@ void WipeTowerPrusaMM::toolchange_Unload(
.load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
.travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/
.resume_preview();
-
- if (new_temperature != 0 && new_temperature != m_old_temperature ) { // Set the extruder temperature, but don't wait.
+ if (new_temperature != 0 && (new_temperature != m_old_temperature || m_is_first_layer) ) { // Set the extruder temperature, but don't wait.
+ // If the required temperature is the same as last time, don't emit the M104 again (if user adjusted the value, it would be reset)
+ // However, always change temperatures on the first layer (this is to avoid issues with priming lines turned off).
writer.set_extruder_temp(new_temperature, false);
m_old_temperature = new_temperature;
}
@@ -849,6 +866,9 @@ void WipeTowerPrusaMM::toolchange_Change(
const unsigned int new_tool,
material_type new_material)
{
+ // Ask the writer about how much of the old filament we consumed:
+ m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
// Speed override for the material. Go slow for flex and soluble materials.
int speed_override;
switch (new_material) {
@@ -911,7 +931,6 @@ void WipeTowerPrusaMM::toolchange_Wipe(
const float& xl = cleaning_box.ld.x;
const float& xr = cleaning_box.rd.x;
-
// Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least
// the ordered volume, even if it means violating the box. This can later be removed and simply
// wipe until the end of the assigned area.
@@ -926,7 +945,6 @@ void WipeTowerPrusaMM::toolchange_Wipe(
m_left_to_right = !m_left_to_right;
}
-
// now the wiping itself:
for (int i = 0; true; ++i) {
if (i!=0) {
@@ -935,7 +953,7 @@ void WipeTowerPrusaMM::toolchange_Wipe(
else if (wipe_speed < 2210.f) wipe_speed = 4200.f;
else wipe_speed = std::min(4800.f, wipe_speed + 50.f);
}
-
+
float traversed_x = writer.x();
if (m_left_to_right)
writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
@@ -1050,6 +1068,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
+ // Ask our writer about how much material was consumed:
+ m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
+
ToolChangeResult result;
result.priming = false;
result.print_z = this->m_z_pos;
@@ -1166,6 +1187,8 @@ void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeRes
m_layer_info = m_plan.begin();
m_current_tool = (unsigned int)(-2); // we don't know which extruder to start with - we'll set it according to the first toolchange
+ for (auto& used : m_used_filament_length) // reset used filament stats
+ used = 0.f;
std::vector<WipeTower::ToolChangeResult> layer_result;
for (auto layer : m_plan)
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index 305dbc40a..06625d189 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -46,7 +46,7 @@ public:
WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, float bridging,
const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
- m_wipe_tower_pos(x, y),
+ m_wipe_tower_pos(x, y),
m_wipe_tower_width(width),
m_wipe_tower_rotation_angle(rotation_angle),
m_y_shift(0.f),
@@ -94,6 +94,8 @@ public:
m_filpar[idx].ramming_step_multiplicator /= 100;
while (stream >> speed)
m_filpar[idx].ramming_speed.push_back(speed);
+
+ m_used_filament_length.resize(std::max(m_used_filament_length.size(), idx + 1)); // makes sure that the vector is big enough so we don't have to check later
}
@@ -172,6 +174,9 @@ public:
return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
}
+ virtual std::vector<float> get_used_filament() const override { return m_used_filament_length; }
+ virtual int get_number_of_toolchanges() const override { return m_num_tool_changes; }
+
private:
WipeTowerPrusaMM();
@@ -331,6 +336,9 @@ private:
std::vector<WipeTowerInfo> m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
+ // Stores information about used filament length per extruder:
+ std::vector<float> m_used_filament_length;
+
// Returns gcode for wipe tower brim
// sideOnly -- set to false -- experimental, draw brim on sides of wipe tower
diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp
index 65184c9d1..e0f97703c 100644
--- a/xs/src/libslic3r/LayerRegion.cpp
+++ b/xs/src/libslic3r/LayerRegion.cpp
@@ -15,8 +15,7 @@
namespace Slic3r {
-Flow
-LayerRegion::flow(FlowRole role, bool bridge, double width) const
+Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const
{
return m_region->flow(
role,
diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index dc21ab4ae..8e8396e7a 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -242,10 +242,19 @@ void Model::center_instances_around_point(const Vec2d &point)
for (size_t i = 0; i < o->instances.size(); ++ i)
bb.merge(o->instance_bounding_box(i, false));
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ Vec2d shift2 = point - to_2d(bb.center());
+ Vec3d shift3 = Vec3d(shift2(0), shift2(1), 0.0);
+#else
Vec2d shift = point - 0.5 * to_2d(bb.size()) - to_2d(bb.min);
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
for (ModelObject *o : this->objects) {
for (ModelInstance *i : o->instances)
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ i->set_offset(i->get_offset() + shift3);
+#else
i->offset += shift;
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
o->invalidate_bounding_box();
}
}
@@ -311,8 +320,13 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
size_t idx = 0;
for (ModelObject *o : this->objects) {
for (ModelInstance *i : o->instances) {
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ Vec2d offset_xy = positions[idx] - instance_centers[idx];
+ i->set_offset(Vec3d(offset_xy(0), offset_xy(1), i->get_offset(Z)));
+#else
i->offset = positions[idx] - instance_centers[idx];
- ++ idx;
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
+ ++idx;
}
o->invalidate_bounding_box();
}
@@ -336,7 +350,11 @@ void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
for (const ModelInstance *i : instances) {
for (const Vec2d &pos : positions) {
ModelInstance *instance = o->add_instance(*i);
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ instance->set_offset(instance->get_offset() + Vec3d(pos(0), pos(1), 0.0));
+#else
instance->offset += pos;
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
}
}
o->invalidate_bounding_box();
@@ -366,13 +384,21 @@ void Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist)
ModelObject* object = this->objects.front();
object->clear_instances();
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ Vec3d ext_size = object->bounding_box().size() + dist * Vec3d::Ones();
+#else
Vec3d size = object->bounding_box().size();
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
for (size_t x_copy = 1; x_copy <= x; ++x_copy) {
for (size_t y_copy = 1; y_copy <= y; ++y_copy) {
ModelInstance* instance = object->add_instance();
- instance->offset(0) = (size(0) + dist) * (x_copy-1);
- instance->offset(1) = (size(1) + dist) * (y_copy-1);
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ instance->set_offset(Vec3d(ext_size(0) * (double)(x_copy - 1), ext_size(1) * (double)(y_copy - 1), 0.0));
+#else
+ instance->offset(0) = (size(0) + dist) * (x_copy - 1);
+ instance->offset(1) = (size(1) + dist) * (y_copy - 1);
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
}
}
}
@@ -601,7 +627,8 @@ const BoundingBoxf3& ModelObject::bounding_box() const
if (! m_bounding_box_valid) {
BoundingBoxf3 raw_bbox;
for (const ModelVolume *v : this->volumes)
- if (! v->modifier)
+ if (v->is_model_part())
+ // mesh.bounding_box() returns a cached value.
raw_bbox.merge(v->mesh.bounding_box());
BoundingBoxf3 bb;
for (const ModelInstance *i : this->instances)
@@ -632,7 +659,7 @@ TriangleMesh ModelObject::raw_mesh() const
{
TriangleMesh mesh;
for (const ModelVolume *v : this->volumes)
- if (! v->modifier)
+ if (v->is_model_part())
mesh.merge(v->mesh);
return mesh;
}
@@ -643,7 +670,7 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const
{
BoundingBoxf3 bb;
for (const ModelVolume *v : this->volumes)
- if (! v->modifier) {
+ if (v->is_model_part()) {
if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances");
bb.merge(this->instances.front()->transform_mesh_bounding_box(&v->mesh, true));
}
@@ -655,7 +682,7 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_
{
BoundingBoxf3 bb;
for (ModelVolume *v : this->volumes)
- if (! v->modifier)
+ if (v->is_model_part())
bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&v->mesh, dont_translate));
return bb;
}
@@ -666,7 +693,7 @@ void ModelObject::center_around_origin()
// center this object around the origin
BoundingBoxf3 bb;
for (ModelVolume *v : this->volumes)
- if (! v->modifier)
+ if (v->is_model_part())
bb.merge(v->mesh.bounding_box());
// Shift is the vector from the center of the bottom face of the bounding box to the origin
@@ -676,12 +703,21 @@ void ModelObject::center_around_origin()
this->translate(shift);
this->origin_translation += shift;
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ // set z to zero, translation in z has already been done within the mesh
+ shift(2) = 0.0;
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
+
if (!this->instances.empty()) {
for (ModelInstance *i : this->instances) {
// apply rotation and scaling to vector as well before translating instance,
// in order to leave final position unaltered
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ i->set_offset(i->get_offset() + i->transform_vector(-shift, true));
+#else
Vec3d i_shift = i->world_matrix(true) * shift;
i->offset -= to_2d(i_shift);
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
}
this->invalidate_bounding_box();
}
@@ -763,7 +799,7 @@ size_t ModelObject::facets_count() const
{
size_t num = 0;
for (const ModelVolume *v : this->volumes)
- if (! v->modifier)
+ if (v->is_model_part())
num += v->mesh.stl.stats.number_of_facets;
return num;
}
@@ -771,7 +807,7 @@ size_t ModelObject::facets_count() const
bool ModelObject::needed_repair() const
{
for (const ModelVolume *v : this->volumes)
- if (! v->modifier && v->mesh.needed_repair())
+ if (v->is_model_part() && v->mesh.needed_repair())
return true;
return false;
}
@@ -787,7 +823,7 @@ void ModelObject::cut(coordf_t z, Model* model) const
lower->input_file = "";
for (ModelVolume *volume : this->volumes) {
- if (volume->modifier) {
+ if (! volume->is_model_part()) {
// don't cut modifiers
upper->add_volume(*volume);
lower->add_volume(*volume);
@@ -839,7 +875,7 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
ModelVolume* new_volume = new_object->add_volume(*mesh);
new_volume->name = volume->name;
new_volume->config = volume->config;
- new_volume->modifier = volume->modifier;
+ new_volume->set_type(volume->type());
new_volume->material_id(volume->material_id());
new_objects->push_back(new_object);
@@ -854,7 +890,7 @@ void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_
{
for (const ModelVolume* vol : this->volumes)
{
- if (!vol->modifier)
+ if (vol->is_model_part())
{
for (ModelInstance* inst : this->instances)
{
@@ -951,6 +987,38 @@ const TriangleMesh& ModelVolume::get_convex_hull() const
return m_convex_hull;
}
+ModelVolume::Type ModelVolume::type_from_string(const std::string &s)
+{
+ // Legacy support
+ if (s == "1")
+ return PARAMETER_MODIFIER;
+ // New type (supporting the support enforcers & blockers)
+ if (s == "ModelPart")
+ return MODEL_PART;
+ if (s == "ParameterModifier")
+ return PARAMETER_MODIFIER;
+ if (s == "SupportEnforcer")
+ return SUPPORT_ENFORCER;
+ if (s == "SupportBlocker")
+ return SUPPORT_BLOCKER;
+ assert(s == "0");
+ // Default value if invalud type string received.
+ return MODEL_PART;
+}
+
+std::string ModelVolume::type_to_string(const Type t)
+{
+ switch (t) {
+ case MODEL_PART: return "ModelPart";
+ case PARAMETER_MODIFIER: return "ParameterModifier";
+ case SUPPORT_ENFORCER: return "SupportEnforcer";
+ case SUPPORT_BLOCKER: return "SupportBlocker";
+ default:
+ assert(false);
+ return "ModelPart";
+ }
+}
+
// 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.
@@ -1005,8 +1073,13 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
}
// Translate the bounding box.
if (! dont_translate) {
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ bbox.min += this->m_offset;
+ bbox.max += this->m_offset;
+#else
Eigen::Map<Vec2d>(bbox.min.data()) += this->offset;
Eigen::Map<Vec2d>(bbox.max.data()) += this->offset;
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
}
}
return bbox;
@@ -1033,7 +1106,11 @@ Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, b
Transform3d m = Transform3d::Identity();
if (!dont_translate)
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ m.translate(m_offset);
+#else
m.translate(Vec3d(offset(0), offset(1), 0.0));
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
if (!dont_rotate)
m.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ()));
diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp
index 8ca1d8ea9..4c2356429 100644
--- a/xs/src/libslic3r/Model.hpp
+++ b/xs/src/libslic3r/Model.hpp
@@ -166,15 +166,27 @@ public:
// Configuration parameters specific to an object model geometry or a modifier volume,
// overriding the global Slic3r settings and the ModelObject settings.
DynamicPrintConfig config;
- // Is it an object to be printed, or a modifier volume?
- bool modifier;
-
+
+ enum Type {
+ MODEL_TYPE_INVALID = -1,
+ MODEL_PART = 0,
+ PARAMETER_MODIFIER,
+ SUPPORT_ENFORCER,
+ SUPPORT_BLOCKER,
+ };
+
// A parent object owning this modifier volume.
- ModelObject* get_object() const { return this->object; };
+ ModelObject* get_object() const { return this->object; };
+ Type type() const { return m_type; }
+ void set_type(const Type t) { m_type = t; }
+ bool is_model_part() const { return m_type == MODEL_PART; }
+ bool is_modifier() const { return m_type == PARAMETER_MODIFIER; }
+ bool is_support_enforcer() const { return m_type == SUPPORT_ENFORCER; }
+ bool is_support_blocker() const { return m_type == SUPPORT_BLOCKER; }
t_model_material_id material_id() const { return this->_material_id; }
- void material_id(t_model_material_id material_id);
- ModelMaterial* material() const;
- void set_material(t_model_material_id material_id, const ModelMaterial &material);
+ void material_id(t_model_material_id material_id);
+ ModelMaterial* material() const;
+ void set_material(t_model_material_id material_id, const ModelMaterial &material);
// 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.
@@ -185,24 +197,30 @@ public:
void calculate_convex_hull();
const TriangleMesh& get_convex_hull() const;
+ // Helpers for loading / storing into AMF / 3MF files.
+ static Type type_from_string(const std::string &s);
+ static std::string type_to_string(const Type t);
+
private:
// Parent object owning this ModelVolume.
- ModelObject* object;
- t_model_material_id _material_id;
+ ModelObject* object;
+ // Is it an object to be printed, or a modifier volume?
+ Type m_type;
+ t_model_material_id _material_id;
- ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object)
+ ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object)
{
if (mesh.stl.stats.number_of_facets > 1)
calculate_convex_hull();
}
- ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), modifier(false), object(object) {}
+ ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {}
ModelVolume(ModelObject *object, const ModelVolume &other) :
- name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), modifier(other.modifier), object(object)
+ name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object)
{
this->material_id(other.material_id());
}
ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
- name(other.name), mesh(std::move(mesh)), config(other.config), modifier(other.modifier), object(object)
+ name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object)
{
this->material_id(other.material_id());
if (mesh.stl.stats.number_of_facets > 1)
@@ -225,15 +243,32 @@ public:
friend class ModelObject;
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+private:
+ Vec3d m_offset; // in unscaled coordinates
+
+public:
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
+
double rotation; // Rotation around the Z axis, in radians around mesh center point
double scaling_factor;
+#if !ENABLE_MODELINSTANCE_3D_OFFSET
Vec2d offset; // in unscaled coordinates
-
+#endif // !ENABLE_MODELINSTANCE_3D_OFFSET
+
// flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state())
EPrintVolumeState print_volume_state;
ModelObject* get_object() const { return this->object; }
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ const Vec3d& get_offset() const { return m_offset; }
+ double get_offset(Axis axis) const { return m_offset(axis); }
+
+ void set_offset(const Vec3d& offset) { m_offset = offset; }
+ void set_offset(Axis axis, double offset) { m_offset(axis) = offset; }
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
+
// To be called on an external mesh
void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const;
// Calculate a bounding box of a transformed mesh. To be called on an external mesh.
@@ -253,9 +288,15 @@ private:
// Parent object, owning this instance.
ModelObject* object;
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), m_offset(Vec3d::Zero()), object(object), print_volume_state(PVS_Inside) {}
+ ModelInstance(ModelObject *object, const ModelInstance &other) :
+ rotation(other.rotation), scaling_factor(other.scaling_factor), m_offset(other.m_offset), object(object), print_volume_state(PVS_Inside) {}
+#else
ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), offset(Vec2d::Zero()), object(object), print_volume_state(PVS_Inside) {}
ModelInstance(ModelObject *object, const ModelInstance &other) :
rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object), print_volume_state(PVS_Inside) {}
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
};
diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp
index 36bd8ed97..372689c39 100644
--- a/xs/src/libslic3r/ModelArrange.hpp
+++ b/xs/src/libslic3r/ModelArrange.hpp
@@ -130,6 +130,7 @@ objfunc(const PointImpl& bincenter,
double norm, // A norming factor for physical dimensions
// a spatial index to quickly get neighbors of the candidate item
const SpatIndex& spatindex,
+ const SpatIndex& smalls_spatindex,
const ItemGroup& remaining
)
{
@@ -161,7 +162,7 @@ objfunc(const PointImpl& bincenter,
// Will hold the resulting score
double score = 0;
- if(isBig(item.area())) {
+ if(isBig(item.area()) || spatindex.empty()) {
// This branch is for the bigger items..
auto minc = ibb.minCorner(); // bottom left corner
@@ -183,6 +184,8 @@ objfunc(const PointImpl& bincenter,
// The smalles distance from the arranged pile center:
auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
+ auto bindist = pl::distance(ibb.center(), bincenter) / norm;
+ dist = 0.8*dist + 0.2*bindist;
// Density is the pack density: how big is the arranged pile
double density = 0;
@@ -207,14 +210,20 @@ objfunc(const PointImpl& bincenter,
// candidate to be aligned with only one item.
auto alignment_score = 1.0;
- density = (fullbb.width()*fullbb.height()) / (norm*norm);
+ density = std::sqrt((fullbb.width() / norm )*
+ (fullbb.height() / norm));
auto querybb = item.boundingBox();
// Query the spatial index for the neighbors
std::vector<SpatElement> result;
result.reserve(spatindex.size());
- spatindex.query(bgi::intersects(querybb),
- std::back_inserter(result));
+ if(isBig(item.area())) {
+ spatindex.query(bgi::intersects(querybb),
+ std::back_inserter(result));
+ } else {
+ smalls_spatindex.query(bgi::intersects(querybb),
+ std::back_inserter(result));
+ }
for(auto& e : result) { // now get the score for the best alignment
auto idx = e.second;
@@ -235,12 +244,8 @@ objfunc(const PointImpl& bincenter,
if(result.empty())
score = 0.5 * dist + 0.5 * density;
else
- score = 0.45 * dist + 0.45 * density + 0.1 * alignment_score;
+ score = 0.40 * dist + 0.40 * density + 0.2 * alignment_score;
}
- } else if( !isBig(item.area()) && spatindex.empty()) {
- auto bindist = pl::distance(ibb.center(), bincenter) / norm;
- // Bindist is surprisingly enough...
- score = bindist;
} else {
// Here there are the small items that should be placed around the
// already processed bigger items.
@@ -291,6 +296,7 @@ protected:
PConfig pconf_; // Placement configuration
double bin_area_;
SpatIndex rtree_;
+ SpatIndex smallsrtree_;
double norm_;
Pile merged_pile_;
Box pilebb_;
@@ -299,7 +305,8 @@ protected:
public:
_ArrBase(const TBin& bin, Distance dist,
- std::function<void(unsigned)> progressind):
+ std::function<void(unsigned)> progressind,
+ std::function<bool(void)> stopcond):
pck_(bin, dist), bin_area_(sl::area(bin)),
norm_(std::sqrt(sl::area(bin)))
{
@@ -317,6 +324,7 @@ public:
pilebb_ = sl::boundingBox(merged_pile);
rtree_.clear();
+ smallsrtree_.clear();
// We will treat big items (compared to the print bed) differently
auto isBig = [this](double a) {
@@ -326,10 +334,12 @@ public:
for(unsigned idx = 0; idx < items.size(); ++idx) {
Item& itm = items[idx];
if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx});
+ smallsrtree_.insert({itm.boundingBox(), idx});
}
};
pck_.progressIndicator(progressind);
+ pck_.stopCondition(stopcond);
}
template<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
@@ -343,8 +353,9 @@ class AutoArranger<Box>: public _ArrBase<Box> {
public:
AutoArranger(const Box& bin, Distance dist,
- std::function<void(unsigned)> progressind):
- _ArrBase<Box>(bin, dist, progressind)
+ std::function<void(unsigned)> progressind,
+ std::function<bool(void)> stopcond):
+ _ArrBase<Box>(bin, dist, progressind, stopcond)
{
pconf_.object_function = [this, bin] (const Item &item) {
@@ -357,6 +368,7 @@ public:
bin_area_,
norm_,
rtree_,
+ smallsrtree_,
remaining_);
double score = std::get<0>(result);
@@ -380,8 +392,9 @@ class AutoArranger<lnCircle>: public _ArrBase<lnCircle> {
public:
AutoArranger(const lnCircle& bin, Distance dist,
- std::function<void(unsigned)> progressind):
- _ArrBase<lnCircle>(bin, dist, progressind) {
+ std::function<void(unsigned)> progressind,
+ std::function<bool(void)> stopcond):
+ _ArrBase<lnCircle>(bin, dist, progressind, stopcond) {
pconf_.object_function = [this, &bin] (const Item &item) {
@@ -393,6 +406,7 @@ public:
bin_area_,
norm_,
rtree_,
+ smallsrtree_,
remaining_);
double score = std::get<0>(result);
@@ -421,8 +435,9 @@ template<>
class AutoArranger<PolygonImpl>: public _ArrBase<PolygonImpl> {
public:
AutoArranger(const PolygonImpl& bin, Distance dist,
- std::function<void(unsigned)> progressind):
- _ArrBase<PolygonImpl>(bin, dist, progressind)
+ std::function<void(unsigned)> progressind,
+ std::function<bool(void)> stopcond):
+ _ArrBase<PolygonImpl>(bin, dist, progressind, stopcond)
{
pconf_.object_function = [this, &bin] (const Item &item) {
@@ -435,6 +450,7 @@ public:
bin_area_,
norm_,
rtree_,
+ smallsrtree_,
remaining_);
double score = std::get<0>(result);
@@ -449,8 +465,9 @@ template<> // Specialization with no bin
class AutoArranger<bool>: public _ArrBase<Box> {
public:
- AutoArranger(Distance dist, std::function<void(unsigned)> progressind):
- _ArrBase<Box>(Box(0, 0), dist, progressind)
+ AutoArranger(Distance dist, std::function<void(unsigned)> progressind,
+ std::function<bool(void)> stopcond):
+ _ArrBase<Box>(Box(0, 0), dist, progressind, stopcond)
{
this->pconf_.object_function = [this] (const Item &item) {
@@ -462,6 +479,7 @@ public:
0,
norm_,
rtree_,
+ smallsrtree_,
remaining_);
return std::get<0>(result);
};
@@ -511,8 +529,13 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
if(item.vertexCount() > 3) {
item.rotation(objinst->rotation);
item.translation( {
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ ClipperLib::cInt(objinst->get_offset(X) / SCALING_FACTOR),
+ ClipperLib::cInt(objinst->get_offset(Y) / SCALING_FACTOR)
+#else
ClipperLib::cInt(objinst->offset(0)/SCALING_FACTOR),
ClipperLib::cInt(objinst->offset(1)/SCALING_FACTOR)
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
});
ret.emplace_back(objinst, item);
}
@@ -649,11 +672,19 @@ void applyResult(
// appropriately
auto off = item.translation();
Radians rot = item.rotation();
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ Vec3d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR, 0.0);
+#else
Vec2d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR);
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
// write the tranformation data into the model instance
inst_ptr->rotation = rot;
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ inst_ptr->set_offset(foff);
+#else
inst_ptr->offset = foff;
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
}
}
@@ -680,12 +711,16 @@ void applyResult(
* remaining items which do not fit onto the print area next to the print
* bed or leave them untouched (let the user arrange them by hand or remove
* them).
+ * \param progressind Progress indicator callback called when an object gets
+ * packed. The unsigned argument is the number of items remaining to pack.
+ * \param stopcondition A predicate returning true if abort is needed.
*/
bool arrange(Model &model, coordf_t min_obj_distance,
const Slic3r::Polyline& bed,
BedShapeHint bedhint,
bool first_bin_only,
- std::function<void(unsigned)> progressind)
+ std::function<void(unsigned)> progressind,
+ std::function<bool(void)> stopcondition)
{
using ArrangeResult = _IndexedPackGroup<PolygonImpl>;
@@ -710,6 +745,8 @@ bool arrange(Model &model, coordf_t min_obj_distance,
BoundingBox bbb(bed);
+ auto& cfn = stopcondition;
+
auto binbb = Box({
static_cast<libnest2d::Coord>(bbb.min(0)),
static_cast<libnest2d::Coord>(bbb.min(1))
@@ -723,7 +760,7 @@ bool arrange(Model &model, coordf_t min_obj_distance,
case BedShapeType::BOX: {
// Create the arranger for the box shaped bed
- AutoArranger<Box> arrange(binbb, min_obj_distance, progressind);
+ AutoArranger<Box> arrange(binbb, min_obj_distance, progressind, cfn);
// Arrange and return the items with their respective indices within the
// input sequence.
@@ -735,7 +772,7 @@ bool arrange(Model &model, coordf_t min_obj_distance,
auto c = bedhint.shape.circ;
auto cc = lnCircle(c);
- AutoArranger<lnCircle> arrange(cc, min_obj_distance, progressind);
+ AutoArranger<lnCircle> arrange(cc, min_obj_distance, progressind, cfn);
result = arrange(shapes.begin(), shapes.end());
break;
}
@@ -747,7 +784,7 @@ bool arrange(Model &model, coordf_t min_obj_distance,
auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
P irrbed = sl::create<PolygonImpl>(std::move(ctour));
- AutoArranger<P> arrange(irrbed, min_obj_distance, progressind);
+ AutoArranger<P> arrange(irrbed, min_obj_distance, progressind, cfn);
// Arrange and return the items with their respective indices within the
// input sequence.
@@ -756,7 +793,7 @@ bool arrange(Model &model, coordf_t min_obj_distance,
}
};
- if(result.empty()) return false;
+ if(result.empty() || stopcondition()) return false;
if(first_bin_only) {
applyResult(result.front(), 0, shapemap);
diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp
index 1fef4083b..03b89df51 100644
--- a/xs/src/libslic3r/MultiPoint.hpp
+++ b/xs/src/libslic3r/MultiPoint.hpp
@@ -35,8 +35,10 @@ public:
Point first_point() const;
virtual Point last_point() const = 0;
virtual Lines lines() const = 0;
+ size_t size() const { return points.size(); }
+ bool empty() const { return points.empty(); }
double length() const;
- bool is_valid() const { return this->points.size() >= 2; }
+ bool is_valid() const { return this->points.size() >= 2; }
int find_point(const Point &point) const;
bool has_boundary_point(const Point &point) const;
diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp
index c1aff561f..6d9d82d25 100644
--- a/xs/src/libslic3r/Point.hpp
+++ b/xs/src/libslic3r/Point.hpp
@@ -22,6 +22,7 @@ typedef Point Vector;
// Vector types with a fixed point coordinate base type.
typedef Eigen::Matrix<coord_t, 2, 1, Eigen::DontAlign> Vec2crd;
typedef Eigen::Matrix<coord_t, 3, 1, Eigen::DontAlign> Vec3crd;
+typedef Eigen::Matrix<int, 3, 1, Eigen::DontAlign> Vec3i;
typedef Eigen::Matrix<int64_t, 2, 1, Eigen::DontAlign> Vec2i64;
typedef Eigen::Matrix<int64_t, 3, 1, Eigen::DontAlign> Vec3i64;
diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp
index 54909352c..63162d953 100644
--- a/xs/src/libslic3r/Polygon.hpp
+++ b/xs/src/libslic3r/Polygon.hpp
@@ -103,6 +103,12 @@ inline void polygons_rotate(Polygons &polys, double angle)
p.rotate(cos_angle, sin_angle);
}
+inline void polygons_reverse(Polygons &polys)
+{
+ for (Polygon &p : polys)
+ p.reverse();
+}
+
inline Points to_points(const Polygon &poly)
{
return poly.points;
diff --git a/xs/src/libslic3r/Polyline.cpp b/xs/src/libslic3r/Polyline.cpp
index b2e50bca3..e0cd5221c 100644
--- a/xs/src/libslic3r/Polyline.cpp
+++ b/xs/src/libslic3r/Polyline.cpp
@@ -184,15 +184,13 @@ void Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const
bool Polyline::is_straight() const
{
- /* Check that each segment's direction is equal to the line connecting
- first point and last point. (Checking each line against the previous
- one would cause the error to accumulate.) */
+ // Check that each segment's direction is equal to the line connecting
+ // first point and last point. (Checking each line against the previous
+ // one would cause the error to accumulate.)
double dir = Line(this->first_point(), this->last_point()).direction();
-
- Lines lines = this->lines();
- for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
- if (!line->parallel_to(dir)) return false;
- }
+ for (const auto &line: this->lines())
+ if (! line.parallel_to(dir))
+ return false;
return true;
}
diff --git a/xs/src/libslic3r/Polyline.hpp b/xs/src/libslic3r/Polyline.hpp
index 0c934e074..925b88aca 100644
--- a/xs/src/libslic3r/Polyline.hpp
+++ b/xs/src/libslic3r/Polyline.hpp
@@ -21,6 +21,8 @@ public:
Polyline(Polyline &&other) : MultiPoint(std::move(other.points)) {}
Polyline(std::initializer_list<Point> list) : MultiPoint(list) {}
explicit Polyline(const Point &p1, const Point &p2) { points.reserve(2); points.emplace_back(p1); points.emplace_back(p2); }
+ explicit Polyline(const Points &points) : MultiPoint(points) {}
+ explicit Polyline(Points &&points) : MultiPoint(std::move(points)) {}
Polyline& operator=(const Polyline &other) { points = other.points; return *this; }
Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; }
static Polyline new_scale(const std::vector<Vec2d> &points) {
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index 03034cfdc..85634d1bb 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -392,9 +392,12 @@ void Print::add_model_object(ModelObject* model_object, int idx)
//FIXME lock mutex!
this->invalidate_all_steps();
- for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
+ size_t volume_id = 0;
+ for (const ModelVolume *volume : model_object->volumes) {
+ if (! volume->is_model_part() && ! volume->is_modifier())
+ continue;
// Get the config applied to this volume.
- PrintRegionConfig config = this->_region_config_from_model_volume(*model_object->volumes[volume_id]);
+ PrintRegionConfig config = this->_region_config_from_model_volume(*volume);
// 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)
@@ -409,6 +412,7 @@ void Print::add_model_object(ModelObject* model_object, int idx)
}
// Assign volume to a region.
object->add_region_volume(region_id, volume_id);
+ ++ volume_id;
}
// Apply config to print object.
@@ -893,7 +897,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const
for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
ModelVolume *volume = model_object->volumes[volume_id];
//FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned.
- if (! volume->material_id().empty() && ! volume->config.has("extruder"))
+ if ((volume->is_model_part() || volume->is_modifier()) && ! volume->material_id().empty() && ! volume->config.has("extruder"))
volume->config.opt<ConfigOptionInt>("extruder", true)->value = int(volume_id + 1);
}
}
@@ -1295,6 +1299,9 @@ void Print::_make_wipe_tower()
}
m_wipe_tower_data.final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(
wipe_tower.tool_change((unsigned int)-1, false));
+
+ m_wipe_tower_data.used_filament = wipe_tower.get_used_filament();
+ m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
}
std::string Print::output_filename() const
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index 8999f27d7..19da875ab 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -135,7 +135,10 @@ public:
const Print* print() const { return m_print; }
const PrintRegionConfig& config() const { return m_config; }
Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
+ // Average diameter of nozzles participating on extruding this region.
coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const;
+ // Average diameter of nozzles participating on extruding this region.
+ coordf_t bridging_height_avg(const PrintConfig &print_config) const;
// Methods modifying the PrintRegion's state:
public:
@@ -252,6 +255,10 @@ public:
// Called when slicing to SVG (see Print.pm sub export_svg), and used by perimeters.t
void slice();
+ // Helpers to slice support enforcer / blocker meshes by the support generator.
+ std::vector<ExPolygons> slice_support_enforcers() const;
+ std::vector<ExPolygons> slice_support_blockers() const;
+
private:
void make_perimeters();
void prepare_infill();
@@ -297,6 +304,7 @@ private:
void set_started(PrintObjectStep step);
void set_done(PrintObjectStep step);
std::vector<ExPolygons> _slice_region(size_t region_id, const std::vector<float> &z, bool modifier);
+ std::vector<ExPolygons> _slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const;
};
struct WipeTowerData
@@ -309,6 +317,8 @@ struct WipeTowerData
std::unique_ptr<WipeTower::ToolChangeResult> priming;
std::vector<std::vector<WipeTower::ToolChangeResult>> tool_changes;
std::unique_ptr<WipeTower::ToolChangeResult> final_purge;
+ std::vector<float> used_filament;
+ int number_of_toolchanges;
// Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
float depth;
@@ -318,6 +328,8 @@ struct WipeTowerData
priming.reset(nullptr);
tool_changes.clear();
final_purge.reset(nullptr);
+ used_filament.clear();
+ number_of_toolchanges = -1;
depth = 0.f;
}
};
@@ -331,6 +343,8 @@ struct PrintStatistics
double total_extruded_volume;
double total_cost;
double total_weight;
+ double total_wipe_tower_cost;
+ double total_wipe_tower_filament;
std::map<size_t, float> filament_stats;
void clear() {
@@ -340,6 +354,8 @@ struct PrintStatistics
total_extruded_volume = 0.;
total_cost = 0.;
total_weight = 0.;
+ total_wipe_tower_cost = 0.;
+ total_wipe_tower_filament = 0.;
filament_stats.clear();
}
};
@@ -463,7 +479,7 @@ private:
// If the background processing stop was requested, throw CanceledException.
// To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly.
- void throw_if_canceled() { if (m_canceled) throw CanceledException(); }
+ void throw_if_canceled() const { if (m_canceled) throw CanceledException(); }
void _make_skirt();
void _make_brim();
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index a3e84a356..ed02f6d43 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -161,7 +161,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Speed for printing bridges.");
def->sidetext = L("mm/s");
def->cli = "bridge-speed=f";
- def->aliases.push_back("bridge_feed_rate");
+ def->aliases = { "bridge_feed_rate" };
def->min = 0;
def->default_value = new ConfigOptionFloat(60);
@@ -274,7 +274,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Distance used for the auto-arrange feature of the plater.");
def->sidetext = L("mm");
def->cli = "duplicate-distance=f";
- def->aliases.push_back("multiply_distance");
+ def->aliases = { "multiply_distance" };
def->min = 0;
def->default_value = new ConfigOptionFloat(6);
@@ -335,7 +335,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back(L("Archimedean Chords"));
def->enum_labels.push_back(L("Octagram Spiral"));
// solid_fill_pattern is an obsolete equivalent to external_fill_pattern.
- def->aliases.push_back("solid_fill_pattern");
+ def->aliases = { "solid_fill_pattern" };
def->default_value = new ConfigOptionEnum<InfillPattern>(ipRectilinear);
def = this->add("external_perimeter_extrusion_width", coFloatOrPercent);
@@ -923,8 +923,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Speed for printing the internal fill. Set to zero for auto.");
def->sidetext = L("mm/s");
def->cli = "infill-speed=f";
- def->aliases.push_back("print_feed_rate");
- def->aliases.push_back("infill_feed_rate");
+ def->aliases = { "print_feed_rate", "infill_feed_rate" };
def->min = 0;
def->default_value = new ConfigOptionFloat(80);
@@ -1272,7 +1271,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Extruders");
def->tooltip = L("The extruder to use when printing perimeters and brim. First extruder is 1.");
def->cli = "perimeter-extruder=i";
- def->aliases.push_back("perimeters_extruder");
+ def->aliases = { "perimeters_extruder" };
def->min = 1;
def->default_value = new ConfigOptionInt(1);
@@ -1285,7 +1284,7 @@ void PrintConfigDef::init_fff_params()
"If expressed as percentage (for example 200%) it will be computed over layer height.");
def->sidetext = L("mm or % (leave 0 for default)");
def->cli = "perimeter-extrusion-width=s";
- def->aliases.push_back("perimeters_extrusion_width");
+ def->aliases = { "perimeters_extrusion_width" };
def->default_value = new ConfigOptionFloatOrPercent(0, false);
def = this->add("perimeter_speed", coFloat);
@@ -1294,7 +1293,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Speed for perimeters (contours, aka vertical shells). Set to zero for auto.");
def->sidetext = L("mm/s");
def->cli = "perimeter-speed=f";
- def->aliases.push_back("perimeter_feed_rate");
+ def->aliases = { "perimeter_feed_rate" };
def->min = 0;
def->default_value = new ConfigOptionFloat(60);
@@ -1307,7 +1306,7 @@ void PrintConfigDef::init_fff_params()
"if the Extra Perimeters option is enabled.");
def->sidetext = L("(minimum)");
def->cli = "perimeters=i";
- def->aliases.push_back("perimeter_offsets");
+ def->aliases = { "perimeter_offsets" };
def->min = 0;
def->default_value = new ConfigOptionInt(3);
@@ -1635,7 +1634,7 @@ void PrintConfigDef::init_fff_params()
def->sidetext = L("mm/s or %");
def->cli = "solid-infill-speed=s";
def->ratio_over = "infill_speed";
- def->aliases.push_back("solid_infill_feed_rate");
+ def->aliases = { "solid_infill_feed_rate" };
def->min = 0;
def->default_value = new ConfigOptionFloatOrPercent(20, false);
@@ -1717,6 +1716,14 @@ void PrintConfigDef::init_fff_params()
def->cli = "support-material!";
def->default_value = new ConfigOptionBool(false);
+ def = this->add("support_material_auto", coBool);
+ def->label = L("Auto generated supports");
+ def->category = L("Support material");
+ 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->default_value = new ConfigOptionBool(true);
+
def = this->add("support_material_xy_spacing", coFloatOrPercent);
def->label = L("XY separation between an object and its support");
def->category = L("Support material");
@@ -1755,7 +1762,7 @@ void PrintConfigDef::init_fff_params()
"for the first object layer.");
def->sidetext = L("mm");
def->cli = "support-material-contact-distance=f";
- def->min = 0;
+// def->min = 0;
def->enum_values.push_back("0");
def->enum_values.push_back("0.2");
def->enum_labels.push_back((boost::format("0 (%1%)") % L("soluble")).str());
@@ -1981,7 +1988,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("Speed for travel moves (jumps between distant extrusion points).");
def->sidetext = L("mm/s");
def->cli = "travel-speed=f";
- def->aliases.push_back("travel_feed_rate");
+ def->aliases = { "travel_feed_rate" };
def->min = 1;
def->default_value = new ConfigOptionFloat(130);
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index 5bc99a51a..bc3b0ef49 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -345,6 +345,7 @@ public:
ConfigOptionFloatOrPercent extrusion_width;
ConfigOptionFloatOrPercent first_layer_height;
ConfigOptionBool infill_only_where_needed;
+ // Force the generation of solid shells between adjacent materials/volumes.
ConfigOptionBool interface_shells;
ConfigOptionFloat layer_height;
ConfigOptionInt raft_layers;
@@ -352,6 +353,9 @@ public:
// ConfigOptionFloat seam_preferred_direction;
// ConfigOptionFloat seam_preferred_direction_jitter;
ConfigOptionBool support_material;
+ // Automatic supports (generated based on support_material_threshold).
+ ConfigOptionBool support_material_auto;
+ // Direction of the support pattern (in XY plane).
ConfigOptionFloat support_material_angle;
ConfigOptionBool support_material_buildplate_only;
ConfigOptionFloat support_material_contact_distance;
@@ -361,12 +365,15 @@ public:
ConfigOptionBool support_material_interface_contact_loops;
ConfigOptionInt support_material_interface_extruder;
ConfigOptionInt support_material_interface_layers;
+ // Spacing between interface lines (the hatching distance). Set zero to get a solid interface.
ConfigOptionFloat support_material_interface_spacing;
ConfigOptionFloatOrPercent support_material_interface_speed;
ConfigOptionEnum<SupportMaterialPattern> support_material_pattern;
+ // Spacing between support material lines (the hatching distance).
ConfigOptionFloat support_material_spacing;
ConfigOptionFloat support_material_speed;
ConfigOptionBool support_material_synchronize_layers;
+ // Overhang angle threshold.
ConfigOptionInt support_material_threshold;
ConfigOptionBool support_material_with_sheath;
ConfigOptionFloatOrPercent support_material_xy_spacing;
@@ -389,6 +396,7 @@ protected:
// OPT_PTR(seam_preferred_direction);
// OPT_PTR(seam_preferred_direction_jitter);
OPT_PTR(support_material);
+ OPT_PTR(support_material_auto);
OPT_PTR(support_material_angle);
OPT_PTR(support_material_buildplate_only);
OPT_PTR(support_material_contact_distance);
@@ -436,10 +444,12 @@ public:
ConfigOptionInt infill_every_layers;
ConfigOptionFloatOrPercent infill_overlap;
ConfigOptionFloat infill_speed;
+ // Detect bridging perimeters
ConfigOptionBool overhangs;
ConfigOptionInt perimeter_extruder;
ConfigOptionFloatOrPercent perimeter_extrusion_width;
ConfigOptionFloat perimeter_speed;
+ // Total number of perimeters.
ConfigOptionInt perimeters;
ConfigOptionFloatOrPercent small_perimeter_speed;
ConfigOptionFloat solid_infill_below_area;
@@ -447,6 +457,7 @@ public:
ConfigOptionFloatOrPercent solid_infill_extrusion_width;
ConfigOptionInt solid_infill_every_layers;
ConfigOptionFloatOrPercent solid_infill_speed;
+ // Detect thin walls.
ConfigOptionBool thin_walls;
ConfigOptionFloatOrPercent top_infill_extrusion_width;
ConfigOptionInt top_solid_layers;
diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp
index 7882c58a8..ef2364dc4 100644
--- a/xs/src/libslic3r/PrintObject.cpp
+++ b/xs/src/libslic3r/PrintObject.cpp
@@ -112,8 +112,18 @@ bool PrintObject::reload_model_instances()
Points copies;
copies.reserve(m_model_object->instances.size());
for (const ModelInstance *mi : m_model_object->instances)
+ {
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ if (mi->is_printable())
+ {
+ const Vec3d& offset = mi->get_offset();
+ copies.emplace_back(Point::new_scale(offset(0), offset(1)));
+ }
+#else
if (mi->is_printable())
copies.emplace_back(Point::new_scale(mi->offset(0), mi->offset(1)));
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
+ }
return this->set_copies(copies);
}
@@ -490,6 +500,7 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
steps.emplace_back(posSlice);
} else if (
opt_key == "support_material"
+ || opt_key == "support_material_auto"
|| opt_key == "support_material_angle"
|| opt_key == "support_material_buildplate_only"
|| opt_key == "support_material_enforce_layers"
@@ -1560,34 +1571,66 @@ end:
std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::vector<float> &z, bool modifier)
{
- std::vector<ExPolygons> layers;
+ std::vector<const ModelVolume*> volumes;
if (region_id < this->region_volumes.size()) {
- std::vector<int> &volumes = this->region_volumes[region_id];
- if (! volumes.empty()) {
- // Compose mesh.
- //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
- TriangleMesh mesh;
- for (int volume_id : volumes) {
- ModelVolume *volume = this->model_object()->volumes[volume_id];
- if (volume->modifier == modifier)
- mesh.merge(volume->mesh);
- m_print->throw_if_canceled();
- }
- if (mesh.stl.stats.number_of_facets > 0) {
- // transform mesh
- // we ignore the per-instance transformations currently and only
- // consider the first one
- this->model_object()->instances.front()->transform_mesh(&mesh, true);
- // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
- mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), - float(this->model_object()->bounding_box().min(2)));
- // perform actual slicing
- TriangleMeshSlicer mslicer;
- Print *print = this->print();
- auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
- mslicer.init(&mesh, callback);
- mslicer.slice(z, &layers, callback);
- m_print->throw_if_canceled();
- }
+ for (int volume_id : this->region_volumes[region_id]) {
+ const ModelVolume *volume = this->model_object()->volumes[volume_id];
+ if (modifier ? volume->is_modifier() : volume->is_model_part())
+ volumes.emplace_back(volume);
+ }
+ }
+ return this->_slice_volumes(z, volumes);
+}
+
+std::vector<ExPolygons> PrintObject::slice_support_enforcers() const
+{
+ std::vector<const ModelVolume*> volumes;
+ for (const ModelVolume *volume : this->model_object()->volumes)
+ if (volume->is_support_enforcer())
+ volumes.emplace_back(volume);
+ std::vector<float> zs;
+ zs.reserve(this->layers().size());
+ for (const Layer *l : this->layers())
+ zs.emplace_back(l->slice_z);
+ return this->_slice_volumes(zs, volumes);
+}
+
+std::vector<ExPolygons> PrintObject::slice_support_blockers() const
+{
+ std::vector<const ModelVolume*> volumes;
+ for (const ModelVolume *volume : this->model_object()->volumes)
+ if (volume->is_support_blocker())
+ volumes.emplace_back(volume);
+ std::vector<float> zs;
+ zs.reserve(this->layers().size());
+ for (const Layer *l : this->layers())
+ zs.emplace_back(l->slice_z);
+ return this->_slice_volumes(zs, volumes);
+}
+
+std::vector<ExPolygons> PrintObject::_slice_volumes(const std::vector<float> &z, const std::vector<const ModelVolume*> &volumes) const
+{
+ std::vector<ExPolygons> layers;
+ if (! volumes.empty()) {
+ // Compose mesh.
+ //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them.
+ TriangleMesh mesh;
+ for (const ModelVolume *v : volumes)
+ mesh.merge(v->mesh);
+ if (mesh.stl.stats.number_of_facets > 0) {
+ // transform mesh
+ // we ignore the per-instance transformations currently and only
+ // consider the first one
+ this->model_object()->instances.front()->transform_mesh(&mesh, true);
+ // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift
+ mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), - float(this->model_object()->bounding_box().min(2)));
+ // perform actual slicing
+ TriangleMeshSlicer mslicer;
+ const Print *print = this->print();
+ auto callback = TriangleMeshSlicer::throw_on_cancel_callback_type([print](){print->throw_if_canceled();});
+ mslicer.init(&mesh, callback);
+ mslicer.slice(z, &layers, callback);
+ m_print->throw_if_canceled();
}
}
return layers;
diff --git a/xs/src/libslic3r/PrintRegion.cpp b/xs/src/libslic3r/PrintRegion.cpp
index cdc8c532a..c112548b4 100644
--- a/xs/src/libslic3r/PrintRegion.cpp
+++ b/xs/src/libslic3r/PrintRegion.cpp
@@ -56,4 +56,9 @@ coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const
print_config.nozzle_diameter.get_at(m_config.solid_infill_extruder.value - 1)) / 3.;
}
+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);
+}
+
}
diff --git a/xs/src/libslic3r/Slicing.cpp b/xs/src/libslic3r/Slicing.cpp
index 1bc38502b..b3e314549 100644
--- a/xs/src/libslic3r/Slicing.cpp
+++ b/xs/src/libslic3r/Slicing.cpp
@@ -224,9 +224,9 @@ std::vector<coordf_t> layer_height_profile_adaptive(
// 1) Initialize the SlicingAdaptive class with the object meshes.
SlicingAdaptive as;
as.set_slicing_parameters(slicing_params);
- for (ModelVolumePtrs::const_iterator it = volumes.begin(); it != volumes.end(); ++ it)
- if (! (*it)->modifier)
- as.add_mesh(&(*it)->mesh);
+ for (const ModelVolume *volume : volumes)
+ if (volume->is_model_part())
+ as.add_mesh(&volume->mesh);
as.prepare();
// 2) Generate layers using the algorithm of @platsch
diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp
index a6b6c1bb8..2bcf597e6 100644
--- a/xs/src/libslic3r/SupportMaterial.cpp
+++ b/xs/src/libslic3r/SupportMaterial.cpp
@@ -248,10 +248,10 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
#ifdef SLIC3R_DEBUG
static int iRun = 0;
iRun ++;
- for (MyLayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it)
+ for (const MyLayer *layer : top_contacts)
Slic3r::SVG::export_expolygons(
- debug_out_path("support-top-contacts-%d-%lf.svg", iRun, (*it)->print_z),
- union_ex((*it)->polygons, false));
+ debug_out_path("support-top-contacts-%d-%lf.svg", iRun, layer->print_z),
+ union_ex(layer->polygons, false));
#endif /* SLIC3R_DEBUG */
BOOST_LOG_TRIVIAL(info) << "Support generator - Creating bottom contacts";
@@ -282,7 +282,17 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
MyLayersPtr intermediate_layers = this->raft_and_intermediate_support_layers(
object, bottom_contacts, top_contacts, layer_storage);
- this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
+// this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
+ this->trim_support_layers_by_object(object, top_contacts,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy);
+
+#ifdef SLIC3R_DEBUG
+ for (const MyLayer *layer : top_contacts)
+ Slic3r::SVG::export_expolygons(
+ debug_out_path("support-top-contacts-trimmed-by-object-%d-%lf.svg", iRun, layer->print_z),
+ union_ex(layer->polygons, false));
+#endif
BOOST_LOG_TRIVIAL(info) << "Support generator - Creating base layers";
@@ -420,29 +430,17 @@ Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_t
{
// 1) Count the new polygons first.
size_t n_polygons_new = 0;
- for (const LayerRegion* pregion : layer.regions()) {
- const LayerRegion &region = *pregion;
- const SurfaceCollection &slices = region.slices;
- for (Surfaces::const_iterator it = slices.surfaces.begin(); it != slices.surfaces.end(); ++ it) {
- const Surface &surface = *it;
+ for (const LayerRegion *region : layer.regions())
+ for (const Surface &surface : region->slices.surfaces)
if (surface.surface_type == surface_type)
n_polygons_new += surface.expolygon.holes.size() + 1;
- }
- }
-
// 2) Collect the new polygons.
Polygons out;
out.reserve(n_polygons_new);
- for (const LayerRegion *pregion : layer.regions()) {
- const LayerRegion &region = *pregion;
- const SurfaceCollection &slices = region.slices;
- for (Surfaces::const_iterator it = slices.surfaces.begin(); it != slices.surfaces.end(); ++ it) {
- const Surface &surface = *it;
+ for (const LayerRegion *region : layer.regions())
+ for (const Surface &surface : region->slices.surfaces)
if (surface.surface_type == surface_type)
polygons_append(out, surface.expolygon);
- }
- }
-
return out;
}
@@ -452,8 +450,8 @@ Polygons collect_slices_outer(const Layer &layer)
{
Polygons out;
out.reserve(out.size() + layer.slices.expolygons.size());
- for (ExPolygons::const_iterator it = layer.slices.expolygons.begin(); it != layer.slices.expolygons.end(); ++ it)
- out.push_back(it->contour);
+ for (const ExPolygon &expoly : layer.slices.expolygons)
+ out.emplace_back(expoly.contour);
return out;
}
@@ -461,8 +459,11 @@ class SupportGridPattern
{
public:
SupportGridPattern(
+ // Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy)
const Polygons &support_polygons,
- const Polygons &trimming_polygons,
+ // Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons.
+ const Polygons &trimming_polygons,
+ // Grid spacing, given by "support_material_spacing" + m_support_material_flow.spacing()
coordf_t support_spacing,
coordf_t support_angle) :
m_support_polygons(&support_polygons), m_trimming_polygons(&trimming_polygons),
@@ -485,7 +486,8 @@ public:
m_grid.set_bbox(bbox);
m_grid.create(*m_support_polygons, grid_resolution);
m_grid.calculate_sdf();
- // Extract a bounding contour from the grid, trim by the object.
+ // Sample a single point per input support polygon, keep it as a reference to maintain corresponding
+ // polygons if ever these polygons get split into parts by the trimming polygons.
m_island_samples = island_samples(*m_support_polygons);
}
@@ -493,22 +495,22 @@ public:
// and trim the extracted polygons by trimming_polygons.
// Trimming by the trimming_polygons may split the extracted polygons into pieces.
// Remove all the pieces, which do not contain any of the island_samples.
- Polygons extract_support(const coord_t offset_in_grid)
+ Polygons extract_support(const coord_t offset_in_grid, bool fill_holes)
{
// Generate islands, so each island may be tested for overlap with m_island_samples.
+ assert(std::abs(2 * offset_in_grid) < m_grid.resolution());
ExPolygons islands = diff_ex(
- m_grid.contours_simplified(offset_in_grid),
+ m_grid.contours_simplified(offset_in_grid, fill_holes),
*m_trimming_polygons, false);
// Extract polygons, which contain some of the m_island_samples.
Polygons out;
- std::vector<std::pair<Point,bool>> samples_inside;
-
for (ExPolygon &island : islands) {
BoundingBox bbox = get_extents(island.contour);
+ // Samples are sorted lexicographically.
auto it_lower = std::lower_bound(m_island_samples.begin(), m_island_samples.end(), Point(bbox.min - Point(1, 1)));
auto it_upper = std::upper_bound(m_island_samples.begin(), m_island_samples.end(), Point(bbox.max + Point(1, 1)));
- samples_inside.clear();
+ std::vector<std::pair<Point,bool>> samples_inside;
for (auto it = it_lower; it != it_upper; ++ it)
if (bbox.contains(*it))
samples_inside.push_back(std::make_pair(*it, false));
@@ -569,8 +571,10 @@ public:
private:
SupportGridPattern& operator=(const SupportGridPattern &rhs);
+#if 0
// Get some internal point of an expolygon, to be used as a representative
// sample to test, whether this island is inside another island.
+ //FIXME this was quick, but not sufficiently robust.
static Point island_sample(const ExPolygon &expoly)
{
// Find the lowest point lexicographically.
@@ -591,7 +595,10 @@ private:
double coef = 20. / sqrt(l2);
return Point(p2(0) + coef * v(0), p2(1) + coef * v(1));
}
+#endif
+ // Sample one internal point per expolygon.
+ // FIXME this is quite an overkill to calculate a complete offset just to get a single point, but at least it is robust.
static Points island_samples(const ExPolygons &expolygons)
{
Points pts;
@@ -629,9 +636,164 @@ private:
coordf_t m_support_spacing;
Slic3r::EdgeGrid::Grid m_grid;
+ // Internal sample points of supporting expolygons. These internal points are used to pick regions corresponding
+ // to the initial supporting regions, after these regions werre grown and possibly split to many by the trimming polygons.
Points m_island_samples;
};
+namespace SupportMaterialInternal {
+ static inline bool has_bridging_perimeters(const ExtrusionLoop &loop)
+ {
+ for (const ExtrusionPath &ep : loop.paths)
+ if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty())
+ return ep.size() >= (ep.is_closed() ? 3 : 2);
+ return false;
+ }
+ static bool has_bridging_perimeters(const ExtrusionEntityCollection &perimeters)
+ {
+ for (const ExtrusionEntity *ee : perimeters.entities) {
+ if (ee->is_collection()) {
+ for (const ExtrusionEntity *ee2 : static_cast<const ExtrusionEntityCollection*>(ee)->entities) {
+ assert(! ee2->is_collection());
+ if (ee2->is_loop())
+ if (has_bridging_perimeters(*static_cast<const ExtrusionLoop*>(ee2)))
+ return true;
+ }
+ } else if (ee->is_loop() && has_bridging_perimeters(*static_cast<const ExtrusionLoop*>(ee)))
+ return true;
+ }
+ return false;
+ }
+ static bool has_bridging_fills(const ExtrusionEntityCollection &fills)
+ {
+ for (const ExtrusionEntity *ee : fills.entities) {
+ assert(ee->is_collection());
+ for (const ExtrusionEntity *ee2 : static_cast<const ExtrusionEntityCollection*>(ee)->entities) {
+ assert(! ee2->is_collection());
+ assert(! ee2->is_loop());
+ if (ee2->role() == erBridgeInfill)
+ return true;
+ }
+ }
+ return false;
+ }
+ static bool has_bridging_extrusions(const Layer &layer)
+ {
+ for (const LayerRegion *region : layer.regions()) {
+ if (SupportMaterialInternal::has_bridging_perimeters(region->perimeters))
+ return true;
+ if (region->fill_surfaces.has(stBottomBridge) && has_bridging_fills(region->fills))
+ return true;
+ }
+ return false;
+ }
+
+ static inline void collect_bridging_perimeter_areas(const ExtrusionLoop &loop, const float expansion_scaled, Polygons &out)
+ {
+ assert(expansion_scaled >= 0.f);
+ for (const ExtrusionPath &ep : loop.paths)
+ if (ep.role() == erOverhangPerimeter && ! ep.polyline.empty()) {
+ float exp = 0.5f * scale_(ep.width) + expansion_scaled;
+ if (ep.is_closed()) {
+ if (ep.size() >= 3) {
+ // This is a complete loop.
+ // Add the outer contour first.
+ Polygon poly;
+ poly.points = ep.polyline.points;
+ poly.points.pop_back();
+ if (poly.area() < 0)
+ poly.reverse();
+ polygons_append(out, offset(poly, exp, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ Polygons holes = offset(poly, - exp, SUPPORT_SURFACES_OFFSET_PARAMETERS);
+ polygons_reverse(holes);
+ polygons_append(out, holes);
+ }
+ } else if (ep.size() >= 2) {
+ // Offset the polyline.
+ polygons_append(out, offset(ep.polyline, exp, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ }
+ }
+ }
+ static void collect_bridging_perimeter_areas(const ExtrusionEntityCollection &perimeters, const float expansion_scaled, Polygons &out)
+ {
+ for (const ExtrusionEntity *ee : perimeters.entities) {
+ if (ee->is_collection()) {
+ for (const ExtrusionEntity *ee2 : static_cast<const ExtrusionEntityCollection*>(ee)->entities) {
+ assert(! ee2->is_collection());
+ if (ee2->is_loop())
+ collect_bridging_perimeter_areas(*static_cast<const ExtrusionLoop*>(ee2), expansion_scaled, out);
+ }
+ } else if (ee->is_loop())
+ collect_bridging_perimeter_areas(*static_cast<const ExtrusionLoop*>(ee), expansion_scaled, out);
+ }
+ }
+
+ static void remove_bridges_from_contacts(
+ const PrintConfig &print_config,
+ const Layer &lower_layer,
+ const Polygons &lower_layer_polygons,
+ LayerRegion *layerm,
+ float fw,
+ Polygons &contact_polygons)
+ {
+ // compute the area of bridging perimeters
+ Polygons bridges;
+ {
+ // Surface supporting this layer, expanded by 0.5 * nozzle_diameter, as we consider this kind of overhang to be sufficiently supported.
+ Polygons lower_grown_slices = offset(lower_layer_polygons,
+ //FIXME to mimic the decision in the perimeter generator, we should use half the external perimeter width.
+ 0.5f * float(scale_(print_config.nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder-1))),
+ SUPPORT_SURFACES_OFFSET_PARAMETERS);
+ // Collect perimeters of this layer.
+ //FIXME split_at_first_point() could split a bridge mid-way
+ #if 0
+ Polylines overhang_perimeters = layerm->perimeters.as_polylines();
+ // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
+ for (Polyline &polyline : overhang_perimeters)
+ polyline.points[0].x += 1;
+ // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters.
+ overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
+ #else
+ Polylines overhang_perimeters = diff_pl(layerm->perimeters.as_polylines(), lower_grown_slices);
+ #endif
+
+ // only consider straight overhangs
+ // only consider overhangs having endpoints inside layer's slices
+ // convert bridging polylines into polygons by inflating them with their thickness
+ // since we're dealing with bridges, we can't assume width is larger than spacing,
+ // so we take the largest value and also apply safety offset to be ensure no gaps
+ // are left in between
+ Flow bridge_flow = layerm->flow(frPerimeter, true);
+ float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing()));
+ for (Polyline &polyline : overhang_perimeters)
+ if (polyline.is_straight()) {
+ // This is a bridge
+ polyline.extend_start(fw);
+ polyline.extend_end(fw);
+ // Is the straight perimeter segment supported at both sides?
+ if (lower_layer.slices.contains(polyline.first_point()) && lower_layer.slices.contains(polyline.last_point()))
+ // Offset a polyline into a thick line.
+ polygons_append(bridges, offset(polyline, 0.5f * w + 10.f));
+ }
+ bridges = union_(bridges);
+ }
+ // remove the entire bridges and only support the unsupported edges
+ //FIXME the brided regions are already collected as layerm->bridged. Use it?
+ for (const Surface &surface : layerm->fill_surfaces.surfaces)
+ if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
+ polygons_append(bridges, surface.expolygon);
+ //FIXME add the gap filled areas. Extrude the gaps with a bridge flow?
+ contact_polygons = diff(contact_polygons, bridges, true);
+ // Add the bridge anchors into the region.
+ //FIXME add supports at regular intervals to support long bridges!
+ polygons_append(contact_polygons,
+ intersection(
+ // Offset unsupported edges into polygons.
+ offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS),
+ bridges));
+ }
+}
+
// Generate top contact layers supporting overhangs.
// For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined.
// If supports over bed surface only are requested, don't generate contact layers over an object.
@@ -643,9 +805,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
++ iRun;
#endif /* SLIC3R_DEBUG */
+ // Slice support enforcers / support blockers.
+ std::vector<ExPolygons> enforcers = object.slice_support_enforcers();
+ std::vector<ExPolygons> blockers = object.slice_support_blockers();
+
// Output layers, sorted by top Z.
MyLayersPtr contact_out;
+ const bool support_auto = m_object_config->support_material_auto.value;
// If user specified a custom angle threshold, convert it to radians.
// Zero means automatic overhang detection.
const double threshold_rad = (m_object_config->support_material_threshold.value > 0) ?
@@ -680,10 +847,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// Note that layer_id < layer->id when raft_layers > 0 as the layer->id incorporates the raft layers.
// So layer_id == 0 means first object layer and layer->id == 0 means first print layer if there are no explicit raft layers.
size_t num_layers = this->has_support() ? object.layer_count() : 1;
- contact_out.assign(num_layers, nullptr);
+ // For each overhang layer, two supporting layers may be generated: One for the overhangs extruded with a bridging flow,
+ // and the other for the overhangs extruded with a normal flow.
+ contact_out.assign(num_layers * 2, nullptr);
tbb::spin_mutex layer_storage_mutex;
tbb::parallel_for(tbb::blocked_range<size_t>(this->has_raft() ? 0 : 1, num_layers),
- [this, &object, &buildplate_covered, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out](const tbb::blocked_range<size_t>& range) {
+ [this, &object, &buildplate_covered, &enforcers, &blockers, support_auto, threshold_rad, &layer_storage, &layer_storage_mutex, &contact_out]
+ (const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id)
{
const Layer &layer = *object.layers()[layer_id];
@@ -694,6 +864,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
Polygons contact_polygons;
Polygons slices_margin_cached;
float slices_margin_cached_offset = -1.;
+ Polygons lower_layer_polygons = (layer_id == 0) ? Polygons() : to_polygons(object.layers()[layer_id-1]->slices.expolygons);
+ // Offset of the lower layer, to trim the support polygons with to calculate dense supports.
+ float no_interface_offset = 0.f;
if (layer_id == 0) {
// This is the first object layer, so the object is being printed on a raft and
// we're here just to get the object footprint for the raft.
@@ -708,6 +881,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// Extrusion width accounts for the roundings of the extrudates.
// It is the maximum widh of the extrudate.
float fw = float(layerm->flow(frExternalPerimeter).scaled_width());
+ no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw);
float lower_layer_offset =
(layer_id < m_object_config->support_material_enforce_layers.value) ?
// Enforce a full possible support, ignore the overhang angle.
@@ -720,7 +894,6 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// Overhang polygons for this layer and region.
Polygons diff_polygons;
Polygons layerm_polygons = to_polygons(layerm->slices);
- Polygons lower_layer_polygons = to_polygons(lower_layer.slices.expolygons);
if (lower_layer_offset == 0.f) {
// Support everything.
diff_polygons = diff(layerm_polygons, lower_layer_polygons);
@@ -730,28 +903,61 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
}
} else {
- // Get the regions needing a suport, collapse very tiny spots.
- //FIXME cache the lower layer offset if this layer has multiple regions.
- diff_polygons = offset2(
- diff(layerm_polygons,
- offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)),
- -0.1f*fw, +0.1f*fw);
- if (! buildplate_covered.empty()) {
- // Don't support overhangs above the top surfaces.
- // This step is done before the contact surface is calculated by growing the overhang region.
- diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
+ if (support_auto) {
+ // Get the regions needing a suport, collapse very tiny spots.
+ //FIXME cache the lower layer offset if this layer has multiple regions.
+ #if 1
+ diff_polygons = offset2(
+ diff(layerm_polygons,
+ offset2(lower_layer_polygons, - 0.5f * fw, lower_layer_offset + 0.5f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS)),
+ //FIXME This offset2 is targeted to reduce very thin regions to support, but it may lead to
+ // no support at all for not so steep overhangs.
+ - 0.1f * fw, 0.1f * fw);
+ #else
+ diff_polygons =
+ diff(layerm_polygons,
+ offset(lower_layer_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ #endif
+ if (! buildplate_covered.empty()) {
+ // Don't support overhangs above the top surfaces.
+ // This step is done before the contact surface is calculated by growing the overhang region.
+ diff_polygons = diff(diff_polygons, buildplate_covered[layer_id]);
+ }
+ if (! diff_polygons.empty()) {
+ // Offset the support regions back to a full overhang, restrict them to the full overhang.
+ // This is done to increase size of the supporting columns below, as they are calculated by
+ // propagating these contact surfaces downwards.
+ diff_polygons = diff(
+ intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons),
+ lower_layer_polygons);
+ }
+ }
+ if (! enforcers.empty()) {
+ // Apply the "support enforcers".
+ //FIXME add the "enforcers" to the sparse support regions only.
+ const ExPolygons &enforcer = enforcers[layer_id - 1];
+ if (! enforcer.empty()) {
+ // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
+ Polygons new_contacts = diff(intersection(layerm_polygons, to_polygons(enforcer)),
+ offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ if (! new_contacts.empty()) {
+ if (diff_polygons.empty())
+ diff_polygons = std::move(new_contacts);
+ else
+ diff_polygons = union_(diff_polygons, new_contacts);
+ }
+ }
}
- if (diff_polygons.empty())
- continue;
- // Offset the support regions back to a full overhang, restrict them to the full overhang.
- diff_polygons = diff(
- intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons),
- lower_layer_polygons);
+ }
+ // Apply the "support blockers".
+ if (! diff_polygons.empty() && ! blockers.empty() && ! blockers[layer_id].empty()) {
+ // Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
+ diff_polygons = diff(diff_polygons, to_polygons(blockers[layer_id]));
}
if (diff_polygons.empty())
continue;
- #ifdef SLIC3R_DEBUG
+ #ifdef SLIC3R_DEBUG
{
::Slic3r::SVG svg(debug_out_path("support-top-contacts-raw-run%d-layer%d-region%d.svg",
iRun, layer_id,
@@ -762,73 +968,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
}
#endif /* SLIC3R_DEBUG */
- if (m_object_config->dont_support_bridges) {
- // compute the area of bridging perimeters
- // Note: this is duplicate code from GCode.pm, we need to refactor
- if (true) {
- Polygons bridged_perimeters;
- {
- Flow bridge_flow = layerm->flow(frPerimeter, true);
- coordf_t nozzle_diameter = m_print_config->nozzle_diameter.get_at(layerm->region()->config().perimeter_extruder-1);
- Polygons lower_grown_slices = offset(lower_layer_polygons, 0.5f*float(scale_(nozzle_diameter)), SUPPORT_SURFACES_OFFSET_PARAMETERS);
-
- // Collect perimeters of this layer.
- // TODO: split_at_first_point() could split a bridge mid-way
- Polylines overhang_perimeters;
- for (ExtrusionEntity* extrusion_entity : layerm->perimeters.entities) {
- const ExtrusionEntityCollection *island = dynamic_cast<ExtrusionEntityCollection*>(extrusion_entity);
- assert(island != NULL);
- for (size_t i = 0; i < island->entities.size(); ++ i) {
- ExtrusionEntity *entity = island->entities[i];
- ExtrusionLoop *loop = dynamic_cast<Slic3r::ExtrusionLoop*>(entity);
- overhang_perimeters.push_back(loop ?
- loop->as_polyline() :
- dynamic_cast<const Slic3r::ExtrusionPath*>(entity)->polyline);
- }
- }
-
- // workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
- for (Polyline &polyline : overhang_perimeters)
- polyline.points[0](0) += 1;
- // Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters.
- overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
-
- // only consider straight overhangs
- // only consider overhangs having endpoints inside layer's slices
- // convert bridging polylines into polygons by inflating them with their thickness
- // since we're dealing with bridges, we can't assume width is larger than spacing,
- // so we take the largest value and also apply safety offset to be ensure no gaps
- // are left in between
- float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing()));
- for (Polyline &polyline : overhang_perimeters)
- if (polyline.is_straight()) {
- // This is a bridge
- polyline.extend_start(fw);
- polyline.extend_end(fw);
- // Is the straight perimeter segment supported at both sides?
- if (layer.slices.contains(polyline.first_point()) && layer.slices.contains(polyline.last_point()))
- // Offset a polyline into a thick line.
- polygons_append(bridged_perimeters, offset(polyline, 0.5f * w + 10.f));
- }
- bridged_perimeters = union_(bridged_perimeters);
- }
- // remove the entire bridges and only support the unsupported edges
- Polygons bridges;
- for (const Surface &surface : layerm->fill_surfaces.surfaces)
- if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
- polygons_append(bridges, surface.expolygon);
- diff_polygons = diff(diff_polygons, bridges, true);
- polygons_append(bridges, bridged_perimeters);
- polygons_append(diff_polygons,
- intersection(
- // Offset unsupported edges into polygons.
- offset(layerm->unsupported_bridge_edges.polylines, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS),
- bridges));
- } else {
- // just remove bridged areas
- diff_polygons = diff(diff_polygons, layerm->bridged, true);
- }
- } // if (m_objconfig->dont_support_bridges)
+ if (this->m_object_config->dont_support_bridges)
+ SupportMaterialInternal::remove_bridges_from_contacts(
+ *m_print_config, lower_layer, lower_layer_polygons, layerm, fw, diff_polygons);
if (diff_polygons.empty())
continue;
@@ -842,7 +984,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
union_ex(diff_polygons, false));
#endif /* SLIC3R_DEBUG */
- if (this->has_contact_loops())
+ //FIXME the overhang_polygons are used to construct the support towers as well.
+ //if (this->has_contact_loops())
+ // Store the exact contour of the overhang for the contact loops.
polygons_append(overhang_polygons, diff_polygons);
// Let's define the required contact area by using a max gap of half the upper
@@ -851,12 +995,15 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// on the other side of the object (if it's very thin).
{
//FIMXE 1) Make the offset configurable, 2) Make the Z span configurable.
+ //FIXME one should trim with the layer span colliding with the support layer, this layer
+ // may be lower than lower_layer, so the support area needed may need to be actually bigger!
+ // For the same reason, the non-bridging support area may be smaller than the bridging support area!
float slices_margin_offset = std::min(lower_layer_offset, float(scale_(m_gap_xy)));
if (slices_margin_cached_offset != slices_margin_offset) {
slices_margin_cached_offset = slices_margin_offset;
slices_margin_cached = (slices_margin_offset == 0.f) ?
- to_polygons(lower_layer.slices.expolygons) :
- offset(lower_layer.slices.expolygons, slices_margin_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS);
+ lower_layer_polygons :
+ offset2(to_polygons(lower_layer.slices.expolygons), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS);
if (! buildplate_covered.empty()) {
// Trim the inflated contact surfaces by the top surfaces as well.
polygons_append(slices_margin_cached, buildplate_covered[layer_id]);
@@ -879,58 +1026,72 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
} // for each layer.region
} // end of Generate overhang/contact_polygons for non-raft layers.
- // now apply the contact areas to the layer were they need to be made
+ // Now apply the contact areas to the layer where they need to be made.
if (! contact_polygons.empty()) {
- // get the average nozzle diameter used on this layer
MyLayer &new_layer = layer_allocate(layer_storage, layer_storage_mutex, sltTopContact);
new_layer.idx_object_layer_above = layer_id;
- if (m_slicing_params.soluble_interface) {
+ MyLayer *bridging_layer = nullptr;
+ if (layer_id == 0) {
+ // This is a raft contact layer sitting directly on the print bed.
+ assert(this->has_raft());
+ new_layer.print_z = m_slicing_params.raft_contact_top_z;
+ new_layer.bottom_z = m_slicing_params.raft_interface_top_z;
+ new_layer.height = m_slicing_params.contact_raft_layer_height;
+ } else if (m_slicing_params.soluble_interface) {
// Align the contact surface height with a layer immediately below the supported layer.
- new_layer.print_z = layer.print_z - layer.height;
- if (layer_id == 0) {
- // This is a raft contact layer sitting directly on the print bed.
- new_layer.height = m_slicing_params.contact_raft_layer_height;
- new_layer.bottom_z = m_slicing_params.raft_interface_top_z;
- } else {
- // Interface layer will be synchronized with the object.
- assert(layer_id > 0);
- new_layer.height = object.layers()[layer_id - 1]->height;
- new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers()[layer_id - 2]->print_z;
- }
+ // Interface layer will be synchronized with the object.
+ new_layer.print_z = layer.print_z - layer.height;
+ new_layer.height = object.layers()[layer_id - 1]->height;
+ new_layer.bottom_z = (layer_id == 1) ? m_slicing_params.object_print_z_min : object.layers()[layer_id - 2]->print_z;
} else {
- // Contact layer will be printed with a normal flow, but
- // it will support layers printed with a bridging flow.
- //FIXME Probably printing with the bridge flow? How about the unsupported perimeters? Are they printed with the bridging flow?
- // In the future we may switch to a normal extrusion flow for the supported bridges.
- // Get the average nozzle diameter used on this layer.
- coordf_t nozzle_dmr = 0.;
- for (const LayerRegion *region : layer.regions())
- nozzle_dmr += region->region()->nozzle_dmr_avg(*m_print_config);
- nozzle_dmr /= coordf_t(layer.regions().size());
- new_layer.print_z = layer.print_z - nozzle_dmr - m_object_config->support_material_contact_distance;
+ new_layer.print_z = layer.print_z - layer.height - m_object_config->support_material_contact_distance;
new_layer.bottom_z = new_layer.print_z;
new_layer.height = 0.;
- if (layer_id == 0) {
- // This is a raft contact layer sitting directly on the print bed.
- assert(this->has_raft());
- new_layer.bottom_z = m_slicing_params.raft_interface_top_z;
- new_layer.height = m_slicing_params.contact_raft_layer_height;
+ // Ignore this contact area if it's too low.
+ // Don't want to print a layer below the first layer height as it may not stick well.
+ //FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact
+ // and it may actually make sense to do it with a thinner layer than the first layer height.
+ if (new_layer.print_z < m_slicing_params.first_print_layer_height - EPSILON) {
+ // This contact layer is below the first layer height, therefore not printable. Don't support this surface.
+ continue;
+ } else if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) {
+ // Align the layer with the 1st layer height.
+ new_layer.print_z = m_slicing_params.first_print_layer_height;
+ new_layer.bottom_z = 0;
+ new_layer.height = m_slicing_params.first_print_layer_height;
} else {
- // Ignore this contact area if it's too low.
- // Don't want to print a layer below the first layer height as it may not stick well.
- //FIXME there may be a need for a single layer support, then one may decide to print it either as a bottom contact or a top contact
- // and it may actually make sense to do it with a thinner layer than the first layer height.
- if (new_layer.print_z < m_slicing_params.first_print_layer_height - EPSILON) {
- // This contact layer is below the first layer height, therefore not printable. Don't support this surface.
- continue;
- } else if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) {
- // Align the layer with the 1st layer height.
- new_layer.print_z = m_slicing_params.first_print_layer_height;
- new_layer.bottom_z = 0;
- new_layer.height = m_slicing_params.first_print_layer_height;
- } else {
- // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and
- // its height will be set adaptively later on.
+ // Don't know the height of the top contact layer yet. The top contact layer is printed with a normal flow and
+ // its height will be set adaptively later on.
+ }
+
+ // Contact layer will be printed with a normal flow, but
+ // it will support layers printed with a bridging flow.
+ if (SupportMaterialInternal::has_bridging_extrusions(layer)) {
+ coordf_t bridging_height = 0.;
+ for (const LayerRegion *region : layer.regions())
+ bridging_height += region->region()->bridging_height_avg(*m_print_config);
+ bridging_height /= coordf_t(layer.regions().size());
+ coordf_t bridging_print_z = layer.print_z - bridging_height - m_object_config->support_material_contact_distance;
+ if (bridging_print_z >= m_slicing_params.first_print_layer_height - EPSILON) {
+ // Not below the first layer height means this layer is printable.
+ if (new_layer.print_z < m_slicing_params.first_print_layer_height + EPSILON) {
+ // Align the layer with the 1st layer height.
+ bridging_print_z = m_slicing_params.first_print_layer_height;
+ }
+ if (bridging_print_z < new_layer.print_z - EPSILON) {
+ // Allocate the new layer.
+ bridging_layer = &layer_allocate(layer_storage, layer_storage_mutex, sltTopContact);
+ bridging_layer->idx_object_layer_above = layer_id;
+ bridging_layer->print_z = bridging_print_z;
+ if (bridging_print_z == m_slicing_params.first_print_layer_height) {
+ bridging_layer->bottom_z = 0;
+ bridging_layer->height = m_slicing_params.first_print_layer_height;
+ } else {
+ // Don't know the height yet.
+ bridging_layer->bottom_z = bridging_print_z;
+ bridging_layer->height = 0;
+ }
+ }
}
}
}
@@ -940,27 +1101,112 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
contact_polygons,
// Trimming polygons, to trim the stretched support islands.
slices_margin_cached,
- // How much to offset the extracted contour outside of the grid.
+ // Grid resolution.
m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
Geometry::deg2rad(m_object_config->support_material_angle.value));
- // 1) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
- new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5);
- // 2) Contact polygons will be projected down. To keep the interface and base layers to grow, return a contour a tiny bit smaller than the grid cells.
- new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3));
+ // 1) Contact polygons will be projected down. To keep the interface and base layers from growing, return a contour a tiny bit smaller than the grid cells.
+ new_layer.contact_polygons = new Polygons(support_grid_pattern.extract_support(-3, true));
+ // 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
+ if (layer_id == 0 || m_slicing_params.soluble_interface) {
+ // if (no_interface_offset == 0.f) {
+ new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, true);
+ } else {
+ // Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions.
+ Polygons dense_interface_polygons = diff(overhang_polygons,
+ offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS));
+// offset(lower_layer_polygons, no_interface_offset * 0.6f, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ if (! dense_interface_polygons.empty()) {
+ //FIXME do it for the bridges only?
+ SupportGridPattern support_grid_pattern(
+ // Support islands, to be stretched into a grid.
+ dense_interface_polygons,
+ // Trimming polygons, to trim the stretched support islands.
+ slices_margin_cached,
+ // Grid resolution.
+ m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
+ Geometry::deg2rad(m_object_config->support_material_angle.value));
+ new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, false);
+ }
+ }
// Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded.
// Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons.
// Store the overhang polygons.
// The overhang polygons are used in the path generator for planning of the contact loops.
- // if (this->has_contact_loops())
+ // if (this->has_contact_loops()). Compared to "polygons", "overhang_polygons" are snug.
new_layer.overhang_polygons = new Polygons(std::move(overhang_polygons));
- contact_out[layer_id] = &new_layer;
+ contact_out[layer_id * 2] = &new_layer;
+ if (bridging_layer != nullptr) {
+ bridging_layer->polygons = new_layer.polygons;
+ bridging_layer->contact_polygons = new Polygons(*new_layer.contact_polygons);
+ bridging_layer->overhang_polygons = new Polygons(*new_layer.overhang_polygons);
+ contact_out[layer_id * 2 + 1] = bridging_layer;
+ }
}
}
});
+
// Compress contact_out, remove the nullptr items.
remove_nulls(contact_out);
+ // Sort the layers, as one layer may produce bridging and non-bridging contact layers with different print_z.
+ std::sort(contact_out.begin(), contact_out.end(), [](const MyLayer *l1, const MyLayer *l2) { return l1->print_z < l2->print_z; });
+
+ // Merge close contact layers conservatively: If two layers are closer than the minimum allowed print layer height (the min_layer_height parameter),
+ // the top contact layer is merged into the bottom contact layer.
+ {
+ int i = 0;
+ int k = 0;
+ {
+ // Find the span of layers, which are to be printed at the first layer height.
+ int j = 0;
+ for (; j < contact_out.size() && contact_out[j]->print_z < m_slicing_params.first_print_layer_height + this->m_support_layer_height_min - EPSILON; ++ j);
+ if (j > 0) {
+ // Merge the contact_out layers (0) to (j - 1) into the contact_out[0].
+ MyLayer &dst = *contact_out.front();
+ for (int u = 1; u < j; ++ u) {
+ MyLayer &src = *contact_out[u];
+ // The union_() does not support move semantic yet, but maybe one day it will.
+ dst.polygons = union_(dst.polygons, std::move(src.polygons));
+ *dst.contact_polygons = union_(*dst.contact_polygons, std::move(*src.contact_polygons));
+ *dst.overhang_polygons = union_(*dst.overhang_polygons, std::move(*src.overhang_polygons));
+ // Source polygon is no more needed, it will not be refrenced. Release its data.
+ src.reset();
+ }
+ // Snap the first layer to the 1st layer height.
+ dst.print_z = m_slicing_params.first_print_layer_height;
+ dst.height = m_slicing_params.first_print_layer_height;
+ dst.bottom_z = 0;
+ ++ k;
+ }
+ i = j;
+ }
+ for (; i < int(contact_out.size()); ++ k) {
+ // Find the span of layers closer than m_support_layer_height_min.
+ int j = i + 1;
+ coordf_t zmax = contact_out[i]->print_z + m_support_layer_height_min + EPSILON;
+ for (; j < contact_out.size() && contact_out[j]->print_z < zmax; ++ j) ;
+ if (i + 1 < j) {
+ // Merge the contact_out layers (i + 1) to (j - 1) into the contact_out[i].
+ MyLayer &dst = *contact_out[i];
+ for (int u = i + 1; u < j; ++ u) {
+ MyLayer &src = *contact_out[u];
+ // The union_() does not support move semantic yet, but maybe one day it will.
+ dst.polygons = union_(dst.polygons, std::move(src.polygons));
+ *dst.contact_polygons = union_(*dst.contact_polygons, std::move(*src.contact_polygons));
+ *dst.overhang_polygons = union_(*dst.overhang_polygons, std::move(*src.overhang_polygons));
+ // Source polygon is no more needed, it will not be refrenced. Release its data.
+ src.reset();
+ }
+ }
+ if (k < i)
+ contact_out[k] = contact_out[i];
+ i = j;
+ }
+ if (k < contact_out.size())
+ contact_out.erase(contact_out.begin() + k, contact_out.end());
+ }
+
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::top_contact_layers() in parallel - end";
return contact_out;
@@ -996,7 +1242,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
BOOST_LOG_TRIVIAL(trace) << "Support generator - bottom_contact_layers - layer " << layer_id;
const Layer &layer = *object.get_layer(layer_id);
// Collect projections of all contact areas above or at the same level as this top surface.
- for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z >= layer.print_z; -- contact_idx) {
+ for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z > layer.print_z - EPSILON; -- contact_idx) {
Polygons polygons_new;
// Contact surfaces are expanded away from the object, trimmed by the object.
// Use a slight positive offset to overlap the touching regions.
@@ -1004,7 +1250,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
// Merge and collect the contact polygons. The contact polygons are inflated, but not extended into a grid form.
polygons_append(polygons_new, offset(*top_contacts[contact_idx]->contact_polygons, SCALED_EPSILON));
#else
- // Consume the contact_polygons. The contact polygons are already expanded into a grid form.
+ // Consume the contact_polygons. The contact polygons are already expanded into a grid form, and they are a tiny bit smaller
+ // than the grid cells.
polygons_append(polygons_new, std::move(*top_contacts[contact_idx]->contact_polygons));
#endif
// These are the overhang surfaces. They are touching the object and they are not expanded away from the object.
@@ -1016,9 +1263,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
continue;
Polygons projection_raw = union_(projection);
- // Top surfaces of this layer, to be used to stop the surface volume from growing down.
tbb::task_group task_group;
if (! m_object_config->support_material_buildplate_only)
+ // Find the bottom contact layers above the top surfaces of this layer.
task_group.run([this, &object, &top_contacts, contact_idx, &layer, layer_id, &layer_storage, &layer_support_areas, &bottom_contacts, &projection_raw] {
Polygons top = collect_region_slices_by_type(layer, stTop);
#ifdef SLIC3R_DEBUG
@@ -1046,28 +1293,34 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
// Grow top surfaces so that interface and support generation are generated
// with some spacing from object - it looks we don't need the actual
// top shapes so this can be done here
+ //FIXME calculate layer height based on the actual thickness of the layer:
+ // If the layer is extruded with no bridging flow, support just the normal extrusions.
layer_new.height = m_slicing_params.soluble_interface ?
// Align the interface layer with the object's layer height.
object.layers()[layer_id + 1]->height :
// Place a bridge flow interface layer over the top surface.
+ //FIXME Check whether the bottom bridging surfaces are extruded correctly (no bridging flow correction applied?)
+ // According to Jindrich the bottom surfaces work well.
+ //FIXME test the bridging flow instead?
m_support_material_interface_flow.nozzle_diameter;
layer_new.print_z = m_slicing_params.soluble_interface ? object.layers()[layer_id + 1]->print_z :
layer.print_z + layer_new.height + m_object_config->support_material_contact_distance.value;
layer_new.bottom_z = layer.print_z;
layer_new.idx_object_layer_below = layer_id;
layer_new.bridging = ! m_slicing_params.soluble_interface;
- //FIXME how much to inflate the top surface?
+ //FIXME how much to inflate the bottom surface, as it is being extruded with a bridging flow? The following line uses a normal flow.
+ //FIXME why is the offset positive? It will be trimmed by the object later on anyway, but then it just wastes CPU clocks.
layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS);
if (! m_slicing_params.soluble_interface) {
// Walk the top surfaces, snap the top of the new bottom surface to the closest top of the top surface,
// so there will be no support surfaces generated with thickness lower than m_support_layer_height_min.
for (size_t top_idx = size_t(std::max<int>(0, contact_idx));
- top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + m_support_layer_height_min;
+ top_idx < top_contacts.size() && top_contacts[top_idx]->print_z < layer_new.print_z + this->m_support_layer_height_min + EPSILON;
++ top_idx) {
- if (top_contacts[top_idx]->print_z > layer_new.print_z - m_support_layer_height_min) {
+ if (top_contacts[top_idx]->print_z > layer_new.print_z - this->m_support_layer_height_min - EPSILON) {
// A top layer has been found, which is close to the new bottom layer.
coordf_t diff = layer_new.print_z - top_contacts[top_idx]->print_z;
- assert(std::abs(diff) <= m_support_layer_height_min);
+ assert(std::abs(diff) <= this->m_support_layer_height_min + EPSILON);
if (diff > 0.) {
// The top contact layer is below this layer. Make the bridging layer thinner to align with the existing top layer.
assert(diff < layer_new.height + EPSILON);
@@ -1091,10 +1344,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
union_ex(layer_new.polygons, false));
#endif /* SLIC3R_DEBUG */
// Trim the already created base layers above the current layer intersecting with the new bottom contacts layer.
+ //FIXME Maybe this is no more needed, as the overlapping base layers are trimmed by the bottom layers at the final stage?
touching = offset(touching, float(SCALED_EPSILON));
for (int layer_id_above = layer_id + 1; layer_id_above < int(object.total_layer_count()); ++ layer_id_above) {
const Layer &layer_above = *object.layers()[layer_id_above];
- if (layer_above.print_z > layer_new.print_z + EPSILON)
+ if (layer_above.print_z > layer_new.print_z - EPSILON)
break;
if (! layer_support_areas[layer_id_above].empty()) {
#ifdef SLIC3R_DEBUG
@@ -1147,7 +1401,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
projection,
// Trimming polygons, to trim the stretched support islands.
trimming,
- // How much to offset the extracted contour outside of the grid.
+ // Grid spacing.
m_object_config->support_material_spacing.value + m_support_material_flow.spacing(),
Geometry::deg2rad(m_object_config->support_material_angle.value));
tbb::task_group task_group_inner;
@@ -1158,7 +1412,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
, &layer
#endif /* SLIC3R_DEBUG */
] {
- layer_support_area = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 25);
+ layer_support_area = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 25, true);
#ifdef SLIC3R_DEBUG
Slic3r::SVG::export_expolygons(
debug_out_path("support-layer_support_area-gridded-%d-%lf.svg", iRun, layer.print_z),
@@ -1172,7 +1426,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
, &layer
#endif /* SLIC3R_DEBUG */
] {
- projection_new = support_grid_pattern.extract_support(-5);
+ projection_new = support_grid_pattern.extract_support(-5, true);
#ifdef SLIC3R_DEBUG
Slic3r::SVG::export_expolygons(
debug_out_path("support-projection_new-gridded-%d-%lf.svg", iRun, layer.print_z),
@@ -1185,7 +1439,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
task_group.wait();
}
std::reverse(bottom_contacts.begin(), bottom_contacts.end());
- trim_support_layers_by_object(object, bottom_contacts, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, 0., m_gap_xy);
+// trim_support_layers_by_object(object, bottom_contacts, 0., 0., m_gap_xy);
+ trim_support_layers_by_object(object, bottom_contacts,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy);
+
} // ! top_contacts.empty()
return bottom_contacts;
@@ -1502,9 +1760,6 @@ void PrintObjectSupportMaterial::generate_base_layers(
assert(idx_intermediate == 0 || layer_intermediate.print_z >= intermediate_layers[idx_intermediate - 1]->print_z);
// Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new.
- idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above,
- [&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; });
-
// New polygons for layer_intermediate.
Polygons polygons_new;
@@ -1523,12 +1778,10 @@ void PrintObjectSupportMaterial::generate_base_layers(
// 3) base.print_z > top.print_z && base.bottom_z >= top.bottom_z -> Overlap, which will be solved inside generate_toolpaths() by reducing the base layer height where it overlaps the top layer. No trimming needed here.
// 4) base.print_z > top.bottom_z && base.bottom_z < top.bottom_z -> Base overlaps with top.bottom_z. This must not happen.
// 5) base.print_z <= top.print_z && base.bottom_z >= top.bottom_z -> Base is fully inside top. Trim base by top.
- int idx_top_contact_overlapping = idx_top_contact_above;
- while (idx_top_contact_overlapping >= 0 &&
- top_contacts[idx_top_contact_overlapping]->bottom_z > layer_intermediate.print_z - EPSILON)
- -- idx_top_contact_overlapping;
+ idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above,
+ [&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; });
// Collect all the top_contact layer intersecting with this layer.
- for (; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) {
+ for ( int idx_top_contact_overlapping = idx_top_contact_above; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) {
MyLayer &layer_top_overlapping = *top_contacts[idx_top_contact_overlapping];
if (layer_top_overlapping.print_z < layer_intermediate.bottom_z + EPSILON)
break;
@@ -1608,7 +1861,10 @@ void PrintObjectSupportMaterial::generate_base_layers(
++ iRun;
#endif /* SLIC3R_DEBUG */
- trim_support_layers_by_object(object, intermediate_layers, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, m_slicing_params.soluble_interface ? 0. : m_support_layer_height_min, m_gap_xy);
+// trim_support_layers_by_object(object, intermediate_layers, 0., 0., m_gap_xy);
+ this->trim_support_layers_by_object(object, intermediate_layers,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value,
+ m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy);
}
void PrintObjectSupportMaterial::trim_support_layers_by_object(
@@ -1653,19 +1909,23 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
const Layer &object_layer = *object.layers()[i];
if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON)
break;
- polygons_append(polygons_trimming, (Polygons)object_layer.slices);
+ polygons_append(polygons_trimming, offset(object_layer.slices.expolygons, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
}
if (! m_slicing_params.soluble_interface) {
// Collect all bottom surfaces, which will be extruded with a bridging flow.
for (; i < object.layers().size(); ++ i) {
const Layer &object_layer = *object.layers()[i];
bool some_region_overlaps = false;
- for (LayerRegion* region : object_layer.regions()) {
- coordf_t nozzle_dmr = region->region()->nozzle_dmr_avg(*m_print_config);
- if (object_layer.print_z - nozzle_dmr > support_layer.print_z + gap_extra_above - EPSILON)
+ for (LayerRegion *region : object_layer.regions()) {
+ coordf_t bridging_height = region->region()->bridging_height_avg(*this->m_print_config);
+ if (object_layer.print_z - bridging_height > support_layer.print_z + gap_extra_above - EPSILON)
break;
some_region_overlaps = true;
- polygons_append(polygons_trimming, to_polygons(region->slices.filter_by_type(stBottomBridge)));
+ polygons_append(polygons_trimming,
+ offset(to_expolygons(region->fill_surfaces.filter_by_type(stBottomBridge)),
+ gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ if (region->region()->config().overhangs.value)
+ SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming);
}
if (! some_region_overlaps)
break;
@@ -1675,9 +1935,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
// perimeter's width. $support contains the full shape of support
// material, thus including the width of its foremost extrusion.
// We leave a gap equal to a full extrusion width.
- support_layer.polygons = diff(
- support_layer.polygons,
- offset(polygons_trimming, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
+ support_layer.polygons = diff(support_layer.polygons, polygons_trimming);
}
});
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::trim_support_layers_by_object() in parallel - end";
@@ -1800,11 +2058,12 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_int
coordf_t top_z = intermediate_layers[std::min<int>(intermediate_layers.size()-1, idx_intermediate_layer + m_object_config->support_material_interface_layers - 1)]->print_z;
coordf_t bottom_z = intermediate_layers[std::max<int>(0, int(idx_intermediate_layer) - int(m_object_config->support_material_interface_layers) + 1)]->bottom_z;
// Move idx_top_contact_first up until above the current print_z.
- idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; });
+ idx_top_contact_first = idx_higher_or_equal(top_contacts, idx_top_contact_first, [&intermediate_layer](const MyLayer *layer){ return layer->print_z >= intermediate_layer.print_z; }); // - EPSILON
// Collect the top contact areas above this intermediate layer, below top_z.
Polygons polygons_top_contact_projected;
for (size_t idx_top_contact = idx_top_contact_first; idx_top_contact < top_contacts.size(); ++ idx_top_contact) {
const MyLayer &top_contact_layer = *top_contacts[idx_top_contact];
+ //FIXME maybe this adds one interface layer in excess?
if (top_contact_layer.bottom_z - EPSILON > top_z)
break;
polygons_append(polygons_top_contact_projected, top_contact_layer.polygons);
@@ -1861,8 +2120,8 @@ static inline void fill_expolygons_generate_paths(
fill_params.density = density;
fill_params.complete = true;
fill_params.dont_adjust = true;
- for (ExPolygons::const_iterator it_expolygon = expolygons.begin(); it_expolygon != expolygons.end(); ++ it_expolygon) {
- Surface surface(stInternal, *it_expolygon);
+ for (const ExPolygon &expoly : expolygons) {
+ Surface surface(stInternal, expoly);
extrusion_entities_append_paths(
dst,
filler->fill_surface(&surface, fill_params),
@@ -1883,8 +2142,8 @@ static inline void fill_expolygons_generate_paths(
fill_params.density = density;
fill_params.complete = true;
fill_params.dont_adjust = true;
- for (ExPolygons::iterator it_expolygon = expolygons.begin(); it_expolygon != expolygons.end(); ++ it_expolygon) {
- Surface surface(stInternal, std::move(*it_expolygon));
+ for (ExPolygon &expoly : expolygons) {
+ Surface surface(stInternal, std::move(expoly));
extrusion_entities_append_paths(
dst,
filler->fill_surface(&surface, fill_params),
@@ -2359,7 +2618,7 @@ void modulate_extrusion_by_overlapping_layers(
(fragment_end.is_start ? &polyline.points.front() : &polyline.points.back());
}
private:
- ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) = delete;
+ ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {}
const std::vector<ExtrusionPathFragment> &m_path_fragments;
};
const coord_t search_radius = 7;
@@ -2711,6 +2970,8 @@ void PrintObjectSupportMaterial::generate_toolpaths(
continue;
//FIXME When paralellizing, each thread shall have its own copy of the fillers.
bool interface_as_base = (&layer_ex == &interface_layer) && m_object_config->support_material_interface_layers.value == 0;
+ //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore
+ // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b)
Flow interface_flow(
float(layer_ex.layer->bridging ? layer_ex.layer->height : (interface_as_base ? m_support_material_flow.width : m_support_material_interface_flow.width)),
float(layer_ex.layer->height),
diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp
index e20d7c69f..2e1a05946 100644
--- a/xs/src/libslic3r/SupportMaterial.hpp
+++ b/xs/src/libslic3r/SupportMaterial.hpp
@@ -12,6 +12,7 @@ class PrintConfig;
class PrintObjectConfig;
// how much we extend support around the actual contact area
+//FIXME this should be dependent on the nozzle diameter!
#define SUPPORT_MATERIAL_MARGIN 1.5
// This class manages raft and supports for a single PrintObject.
@@ -71,6 +72,21 @@ public:
overhang_polygons = nullptr;
}
+ void reset() {
+ layer_type = sltUnknown;
+ print_z = 0.;
+ bottom_z = 0.;
+ height = 0.;
+ idx_object_layer_above = size_t(-1);
+ idx_object_layer_below = size_t(-1);
+ bridging = false;
+ polygons.clear();
+ delete contact_polygons;
+ contact_polygons = nullptr;
+ delete overhang_polygons;
+ overhang_polygons = nullptr;
+ }
+
bool operator==(const MyLayer &layer2) const {
return print_z == layer2.print_z && height == layer2.height && bridging == layer2.bridging;
}
diff --git a/xs/src/libslic3r/SurfaceCollection.hpp b/xs/src/libslic3r/SurfaceCollection.hpp
index 29cfeb1db..9544748e9 100644
--- a/xs/src/libslic3r/SurfaceCollection.hpp
+++ b/xs/src/libslic3r/SurfaceCollection.hpp
@@ -37,6 +37,11 @@ public:
void clear() { surfaces.clear(); }
bool empty() const { return surfaces.empty(); }
+ bool has(SurfaceType type) const {
+ for (const Surface &surface : this->surfaces)
+ if (surface.surface_type == type) return true;
+ return false;
+ }
void set(const SurfaceCollection &coll) { surfaces = coll.surfaces; }
void set(SurfaceCollection &&coll) { surfaces = std::move(coll.surfaces); }
diff --git a/xs/src/libslic3r/Technologies.hpp b/xs/src/libslic3r/Technologies.hpp
new file mode 100644
index 000000000..5c4f8617d
--- /dev/null
+++ b/xs/src/libslic3r/Technologies.hpp
@@ -0,0 +1,12 @@
+#ifndef _technologies_h_
+#define _technologies_h_
+
+// 1.42.0 techs
+#define ENABLE_1_42_0 1
+
+// Add z coordinate to model instances' offset
+#define ENABLE_MODELINSTANCE_3D_OFFSET (1 && ENABLE_1_42_0)
+
+#endif // _technologies_h_
+
+
diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp
index 7b8f85b6b..01adac337 100644
--- a/xs/src/libslic3r/TriangleMesh.cpp
+++ b/xs/src/libslic3r/TriangleMesh.cpp
@@ -21,16 +21,20 @@
#include <Eigen/Dense>
+// for SLIC3R_DEBUG_SLICE_PROCESSING
+#include "libslic3r.h"
+
#if 0
#define DEBUG
#define _DEBUG
#undef NDEBUG
+ #define SLIC3R_DEBUG
+// #define SLIC3R_TRIANGLEMESH_DEBUG
#endif
#include <assert.h>
-#ifdef SLIC3R_DEBUG
-// #define SLIC3R_TRIANGLEMESH_DEBUG
+#if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING)
#include "SVG.hpp"
#endif
@@ -156,7 +160,6 @@ void TriangleMesh::repair()
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished";
}
-
float TriangleMesh::volume()
{
if (this->stl.stats.volume == -1)
@@ -320,7 +323,7 @@ bool TriangleMesh::has_multiple_patches() const
facet_visited[facet_idx] = true;
for (int j = 0; j < 3; ++ j) {
int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
- if (! facet_visited[neighbor_idx])
+ if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
facet_queue[facet_queue_cnt ++] = neighbor_idx;
}
}
@@ -363,7 +366,7 @@ size_t TriangleMesh::number_of_patches() const
facet_visited[facet_idx] = true;
for (int j = 0; j < 3; ++ j) {
int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
- if (! facet_visited[neighbor_idx])
+ if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
facet_queue[facet_queue_cnt ++] = neighbor_idx;
}
}
@@ -623,6 +626,20 @@ void TriangleMesh::require_shared_vertices()
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices";
stl_generate_shared_vertices(&(this->stl));
}
+#ifdef _DEBUG
+ // Verify validity of neighborship data.
+ for (int facet_idx = 0; facet_idx < stl.stats.number_of_facets; ++facet_idx) {
+ const stl_neighbors &nbr = stl.neighbors_start[facet_idx];
+ const int *vertices = stl.v_indices[facet_idx].vertex;
+ for (int nbr_idx = 0; nbr_idx < 3; ++nbr_idx) {
+ int nbr_face = this->stl.neighbors_start[facet_idx].neighbor[nbr_idx];
+ if (nbr_face != -1) {
+ assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[(nbr_idx + 1) % 3]);
+ assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[nbr_idx]);
+ }
+ }
+ }
+#endif /* _DEBUG */
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end";
}
@@ -699,13 +716,13 @@ void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type
}
}
// Assign an edge index to the 1st face.
- this->facets_edges[edge_i.face * 3 + std::abs(edge_i.face_edge) - 1] = num_edges;
+ this->facets_edges[edge_i.face * 3 + std::abs(edge_i.face_edge) - 1] = num_edges;
if (found) {
EdgeToFace &edge_j = edges_map[j];
- this->facets_edges[edge_j.face * 3 + std::abs(edge_j.face_edge) - 1] = num_edges;
- // Mark the edge as connected.
- edge_j.face = -1;
- }
+ this->facets_edges[edge_j.face * 3 + std::abs(edge_j.face_edge) - 1] = num_edges;
+ // Mark the edge as connected.
+ edge_j.face = -1;
+ }
++ num_edges;
if ((i & 0x0ffff) == 0)
throw_on_cancel();
@@ -781,13 +798,30 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons
{
static int iRun = 0;
for (size_t i = 0; i < z.size(); ++ i) {
- Polygons &polygons = (*layers)[i];
- SVG::export_expolygons(debug_out_path("slice_%d_%d.svg", iRun, i).c_str(), union_ex(polygons, true));
+ Polygons &polygons = (*layers)[i];
+ ExPolygons expolygons = union_ex(polygons, true);
+ SVG::export_expolygons(debug_out_path("slice_%d_%d.svg", iRun, i).c_str(), expolygons);
+ {
+ BoundingBox bbox;
+ for (const IntersectionLine &l : lines[i]) {
+ bbox.merge(l.a);
+ bbox.merge(l.b);
+ }
+ SVG svg(debug_out_path("slice_loops_%d_%d.svg", iRun, i).c_str(), bbox);
+ svg.draw(expolygons);
+ for (const IntersectionLine &l : lines[i])
+ svg.draw(l, "red", 0);
+ svg.draw_outline(expolygons, "black", "blue", 0);
+ svg.Close();
+ }
+#if 0
+//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
for (Polygon &poly : polygons) {
for (size_t i = 1; i < poly.points.size(); ++ i)
assert(poly.points[i-1] != poly.points[i]);
assert(poly.points.front() != poly.points.back());
}
+#endif
}
++ iRun;
}
@@ -803,46 +837,51 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
const float min_z = fminf(facet.vertex[0](2), fminf(facet.vertex[1](2), facet.vertex[2](2)));
const float max_z = fmaxf(facet.vertex[0](2), fmaxf(facet.vertex[1](2), facet.vertex[2](2)));
- #ifdef SLIC3R_DEBUG
+ #ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0](2),
facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1](2),
facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2](2));
printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
- #endif
+ #endif /* SLIC3R_TRIANGLEMESH_DEBUG */
// find layer extents
std::vector<float>::const_iterator min_layer, max_layer;
min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
- #ifdef SLIC3R_DEBUG
+ #ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
- #endif
+ #endif /* SLIC3R_TRIANGLEMESH_DEBUG */
for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
std::vector<float>::size_type layer_idx = it - z.begin();
IntersectionLine il;
- if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il)) {
+ if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il) == TriangleMeshSlicer::Slicing) {
boost::lock_guard<boost::mutex> l(*lines_mutex);
if (il.edge_type == feHorizontal) {
- // Insert all three edges of the face.
+ // Insert all marked edges of the face. The marked edges do not share an edge with another horizontal face
+ // (they may not have a nighbor, or their neighbor is vertical)
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
const bool reverse = this->mesh->stl.facet_start[facet_idx].normal(2) < 0;
- for (int j = 0; j < 3; ++ j) {
- int a_id = vertices[j % 3];
- int b_id = vertices[(j+1) % 3];
- if (reverse)
- std::swap(a_id, b_id);
- const stl_vertex &a = this->v_scaled_shared[a_id];
- const stl_vertex &b = this->v_scaled_shared[b_id];
- il.a(0) = a(0);
- il.a(1) = a(1);
- il.b(0) = b(0);
- il.b(1) = b(1);
- il.a_id = a_id;
- il.b_id = b_id;
- (*lines)[layer_idx].emplace_back(il);
- }
+ for (int j = 0; j < 3; ++ j)
+ if (il.flags & ((IntersectionLine::EDGE0_NO_NEIGHBOR | IntersectionLine::EDGE0_FOLD) << j)) {
+ int a_id = vertices[j % 3];
+ int b_id = vertices[(j+1) % 3];
+ if (reverse)
+ std::swap(a_id, b_id);
+ const stl_vertex &a = this->v_scaled_shared[a_id];
+ const stl_vertex &b = this->v_scaled_shared[b_id];
+ il.a(0) = a(0);
+ il.a(1) = a(1);
+ il.b(0) = b(0);
+ il.b(1) = b(1);
+ il.a_id = a_id;
+ il.b_id = b_id;
+ assert(il.a != il.b);
+ // This edge will not be used as a seed for loop extraction if it was added due to a fold of two overlapping horizontal faces.
+ il.set_no_seed((IntersectionLine::EDGE0_FOLD << j) != 0);
+ (*lines)[layer_idx].emplace_back(il);
+ }
} else
(*lines)[layer_idx].emplace_back(il);
}
@@ -861,7 +900,7 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygo
[&layers_p, layers, throw_on_cancel, this](const tbb::blocked_range<size_t>& range) {
for (size_t layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
#ifdef SLIC3R_TRIANGLEMESH_DEBUG
- printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]);
+ printf("Layer " PRINTF_ZU " (slice_z = %.2f):\n", layer_id, z[layer_id]);
#endif
throw_on_cancel();
this->make_expolygons(layers_p[layer_id], &(*layers)[layer_id]);
@@ -871,23 +910,22 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygo
}
// Return true, if the facet has been sliced and line_out has been filled.
-bool TriangleMeshSlicer::slice_facet(
+TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
float slice_z, const stl_facet &facet, const int facet_idx,
const float min_z, const float max_z,
IntersectionLine *line_out) const
{
IntersectionPoint points[3];
size_t num_points = 0;
- size_t points_on_layer[3];
- size_t num_points_on_layer = 0;
+ size_t point_on_layer = size_t(-1);
// Reorder vertices so that the first one is the one with lowest Z.
// This is needed to get all intersection lines in a consistent order
// (external on the right of the line)
+ const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
int i = (facet.vertex[1](2) == min_z) ? 1 : ((facet.vertex[2](2) == min_z) ? 2 : 0);
- for (int j = i; j - i < 3; ++ j) { // loop through facet edges
+ for (int j = i; j - i < 3; ++j ) { // loop through facet edges
int edge_id = this->facets_edges[facet_idx * 3 + (j % 3)];
- const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
int a_id = vertices[j % 3];
int b_id = vertices[(j+1) % 3];
const stl_vertex &a = this->v_scaled_shared[a_id];
@@ -900,116 +938,279 @@ bool TriangleMeshSlicer::slice_facet(
const stl_vertex &v1 = this->v_scaled_shared[vertices[1]];
const stl_vertex &v2 = this->v_scaled_shared[vertices[2]];
bool swap = false;
+ const stl_normal &normal = this->mesh->stl.facet_start[facet_idx].normal;
+ // We may ignore this edge for slicing purposes, but we may still use it for object cutting.
+ FacetSliceType result = Slicing;
+ const stl_neighbors &nbr = this->mesh->stl.neighbors_start[facet_idx];
if (min_z == max_z) {
// All three vertices are aligned with slice_z.
line_out->edge_type = feHorizontal;
- if (this->mesh->stl.facet_start[facet_idx].normal(2) < 0) {
+ // Mark neighbor edges, which do not have a neighbor.
+ uint32_t edges = 0;
+ for (int nbr_idx = 0; nbr_idx != 3; ++ nbr_idx) {
+ // If the neighbor with an edge starting with a vertex idx (nbr_idx - 2) shares no
+ // opposite face, add it to the edges to process when slicing.
+ if (nbr.neighbor[nbr_idx] == -1) {
+ // Mark this edge to be added to the slice.
+ edges |= (IntersectionLine::EDGE0_NO_NEIGHBOR << nbr_idx);
+ }
+#if 1
+ else if (normal(2) > 0) {
+ // Produce edges for opposite faced overlapping horizontal faces aka folds.
+ // This method often produces connecting lines (noise) at the cutting plane.
+ // Produce the edges for the top facing face of the pair of top / bottom facing faces.
+
+ // Index of a neighbor face.
+ const int nbr_face = nbr.neighbor[nbr_idx];
+ const int *nbr_vertices = this->mesh->stl.v_indices[nbr_face].vertex;
+ int idx_vertex_opposite = nbr_vertices[nbr.which_vertex_not[nbr_idx]];
+ const stl_vertex &c2 = this->v_scaled_shared[idx_vertex_opposite];
+ if (c2(2) == slice_z) {
+ // Edge shared by facet_idx and nbr_face.
+ int a_id = vertices[nbr_idx];
+ int b_id = vertices[(nbr_idx + 1) % 3];
+ int c1_id = vertices[(nbr_idx + 2) % 3];
+ const stl_vertex &a = this->v_scaled_shared[a_id];
+ const stl_vertex &b = this->v_scaled_shared[b_id];
+ const stl_vertex &c1 = this->v_scaled_shared[c1_id];
+ // Verify that the two neighbor faces share a common edge.
+ assert(nbr_vertices[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == b_id);
+ assert(nbr_vertices[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id);
+ double n1 = (double(c1(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c1(1)) - double(a(1))) * (double(b(0)) - double(a(0)));
+ double n2 = (double(c2(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c2(1)) - double(a(1))) * (double(b(0)) - double(a(0)));
+ if (n1 * n2 > 0)
+ // The two faces overlap. This indicates an invalid mesh geometry (non-manifold),
+ // but these are the real world objects, and leaving out these edges leads to missing contours.
+ edges |= (IntersectionLine::EDGE0_FOLD << nbr_idx);
+ }
+ }
+#endif
+ }
+ // Use some edges of this triangle for slicing only if at least one of its edge does not have an opposite face.
+ result = (edges == 0) ? Cutting : Slicing;
+ line_out->flags |= edges;
+ if (normal(2) < 0) {
// If normal points downwards this is a bottom horizontal facet so we reverse its point order.
swap = true;
}
- } else if (v0(2) < slice_z || v1(2) < slice_z || v2(2) < slice_z) {
- // Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
- line_out->edge_type = feTop;
- swap = true;
} else {
- // Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
- line_out->edge_type = feBottom;
+ // Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane.
+ int nbr_idx = j % 3;
+ int nbr_face = nbr.neighbor[nbr_idx];
+ // Is the third vertex below the cutting plane?
+ bool third_below = v0(2) < slice_z || v1(2) < slice_z || v2(2) < slice_z;
+ // Is this a concave corner?
+ if (nbr_face == -1) {
+#ifdef _DEBUG
+ printf("Face has no neighbor!\n");
+#endif
+ } else {
+ assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == b_id);
+ assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id);
+ int idx_vertex_opposite = this->mesh->stl.v_indices[nbr_face].vertex[nbr.which_vertex_not[nbr_idx]];
+ const stl_vertex &c = this->v_scaled_shared[idx_vertex_opposite];
+ if (c(2) == slice_z) {
+ double normal_nbr = (double(c(0)) - double(a(0))) * (double(b(1)) - double(a(1))) - (double(c(1)) - double(a(1))) * (double(b(0)) - double(a(0)));
+#if 0
+ if ((normal_nbr < 0) == third_below) {
+ printf("Flipped normal?\n");
+ }
+#endif
+ result =
+ // A vertical face shares edge with a horizontal face. Verify, whether the shared edge makes a convex or concave corner.
+ // Unfortunately too often there are flipped normals, which brake our assumption. Let's rather return every edge,
+ // and leth the code downstream hopefully handle it.
+ #if 1
+ // Ignore concave corners for slicing.
+ // This method has the unfortunate property, that folds in a horizontal plane create concave corners,
+ // leading to broken contours, if these concave corners are not replaced by edges of the folds, see above.
+ ((normal_nbr < 0) == third_below) ? Cutting : Slicing;
+ #else
+ // Use concave corners for slicing. This leads to the test 01_trianglemesh.t "slicing a top tangent plane includes its area" failing,
+ // and rightly so.
+ Slicing;
+ #endif
+ } else {
+ // For a pair of faces touching exactly at the cutting plane, ignore one of them. An arbitrary rule is to ignore the face with a higher index.
+ result = (facet_idx < nbr_face) ? Slicing : Cutting;
+ }
+ }
+ if (third_below) {
+ line_out->edge_type = feTop;
+ swap = true;
+ } else
+ line_out->edge_type = feBottom;
}
line_out->a = to_2d(swap ? b : a).cast<coord_t>();
line_out->b = to_2d(swap ? a : b).cast<coord_t>();
line_out->a_id = swap ? b_id : a_id;
line_out->b_id = swap ? a_id : b_id;
- return true;
+ assert(line_out->a != line_out->b);
+ return result;
}
if (a(2) == slice_z) {
// Only point a alings with the cutting plane.
- points_on_layer[num_points_on_layer ++] = num_points;
- IntersectionPoint &point = points[num_points ++];
- point(0) = a(0);
- point(1) = a(1);
- point.point_id = a_id;
+ if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
+ point_on_layer = num_points;
+ IntersectionPoint &point = points[num_points ++];
+ point(0) = a(0);
+ point(1) = a(1);
+ point.point_id = a_id;
+ }
} else if (b(2) == slice_z) {
// Only point b alings with the cutting plane.
- points_on_layer[num_points_on_layer ++] = num_points;
- IntersectionPoint &point = points[num_points ++];
- point(0) = b(0);
- point(1) = b(1);
- point.point_id = b_id;
+ if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
+ point_on_layer = num_points;
+ IntersectionPoint &point = points[num_points ++];
+ point(0) = b(0);
+ point(1) = b(1);
+ point.point_id = b_id;
+ }
} else if ((a(2) < slice_z && b(2) > slice_z) || (b(2) < slice_z && a(2) > slice_z)) {
// A general case. The face edge intersects the cutting plane. Calculate the intersection point.
- IntersectionPoint &point = points[num_points ++];
- point(0) = b(0) + (a(0) - b(0)) * (slice_z - b(2)) / (a(2) - b(2));
- point(1) = b(1) + (a(1) - b(1)) * (slice_z - b(2)) / (a(2) - b(2));
- point.edge_id = edge_id;
+ assert(a_id != b_id);
+ // Sort the edge to give a consistent answer.
+ const stl_vertex *pa = &a;
+ const stl_vertex *pb = &b;
+ if (a_id > b_id) {
+ std::swap(a_id, b_id);
+ std::swap(pa, pb);
+ }
+ IntersectionPoint &point = points[num_points];
+ double t = (double(slice_z) - double((*pb)(2))) / (double((*pa)(2)) - double((*pb)(2)));
+ if (t <= 0.) {
+ if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
+ point(0) = (*pa)(0);
+ point(1) = (*pa)(1);
+ point_on_layer = num_points ++;
+ point.point_id = a_id;
+ }
+ } else if (t >= 1.) {
+ if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
+ point(0) = (*pb)(0);
+ point(1) = (*pb)(1);
+ point_on_layer = num_points ++;
+ point.point_id = b_id;
+ }
+ } else {
+ point(0) = coord_t(floor(double((*pb)(0)) + (double((*pa)(0)) - double((*pb)(0))) * t + 0.5));
+ point(1) = coord_t(floor(double((*pb)(1)) + (double((*pa)(1)) - double((*pb)(1))) * t + 0.5));
+ point.edge_id = edge_id;
+ ++ num_points;
+ }
}
}
- // We can't have only one point on layer because each vertex gets detected
- // twice (once for each edge), and we can't have three points on layer,
- // because we assume this code is not getting called for horizontal facets.
- assert(num_points_on_layer == 0 || num_points_on_layer == 2);
- if (num_points_on_layer > 0) {
- assert(points[points_on_layer[0]].point_id == points[points_on_layer[1]].point_id);
- assert(num_points == 2 || num_points == 3);
- if (num_points < 3)
- // This triangle touches the cutting plane with a single vertex. Ignore it.
- return false;
- // Erase one of the duplicate points.
- -- num_points;
- for (int i = points_on_layer[1]; i < num_points; ++ i)
- points[i] = points[i + 1];
- }
-
- // Facets must intersect each plane 0 or 2 times.
- assert(num_points == 0 || num_points == 2);
+ // Facets must intersect each plane 0 or 2 times, or it may touch the plane at a single vertex only.
+ assert(num_points < 3);
if (num_points == 2) {
- line_out->edge_type = feNone;
+ line_out->edge_type = feGeneral;
line_out->a = (Point)points[1];
line_out->b = (Point)points[0];
line_out->a_id = points[1].point_id;
line_out->b_id = points[0].point_id;
line_out->edge_a_id = points[1].edge_id;
line_out->edge_b_id = points[0].edge_id;
- return true;
+ // Not a zero lenght edge.
+ //FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
+ //assert(line_out->a != line_out->b);
+ // The plane cuts at least one edge in a general position.
+ assert(line_out->a_id == -1 || line_out->b_id == -1);
+ assert(line_out->edge_a_id != -1 || line_out->edge_b_id != -1);
+ // General slicing position, use the segment for both slicing and object cutting.
+#if 0
+ if (line_out->a_id != -1 && line_out->b_id != -1) {
+ // Solving a degenerate case, where both the intersections snapped to an edge.
+ // Correctly classify the face as below or above based on the position of the 3rd point.
+ int i = vertices[0];
+ if (i == line_out->a_id || i == line_out->b_id)
+ i = vertices[1];
+ if (i == line_out->a_id || i == line_out->b_id)
+ i = vertices[2];
+ assert(i != line_out->a_id && i != line_out->b_id);
+ line_out->edge_type = (this->v_scaled_shared[i].z < slice_z) ? feTop : feBottom;
+ }
+#endif
+ return Slicing;
}
- return false;
+ return NoSlice;
}
-void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
+//FIXME Should this go away? For valid meshes the function slice_facet() returns Slicing
+// and sets edges of vertical triangles to produce only a single edge per pair of neighbor faces.
+// So the following code makes only sense now to handle degenerate meshes with more than two faces
+// sharing a single edge.
+static inline void remove_tangent_edges(std::vector<IntersectionLine> &lines)
{
- // Remove tangent edges.
- //FIXME This is O(n^2) in rare cases when many faces intersect the cutting plane.
- for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++ line)
- if (! line->skip && line->edge_type != feNone) {
- // This line is af facet edge. There may be a duplicate line with the same end vertices.
- // If the line is is an edge connecting two facets, find another facet edge
- // having the same endpoints but in reverse order.
- for (IntersectionLines::iterator line2 = line + 1; line2 != lines.end(); ++ line2)
- if (! line2->skip && line2->edge_type != feNone) {
- // Are these facets adjacent? (sharing a common edge on this layer)
- if (line->a_id == line2->a_id && line->b_id == line2->b_id) {
- line2->skip = true;
- /* if they are both oriented upwards or downwards (like a 'V')
- then we can remove both edges from this layer since it won't
- affect the sliced shape */
- /* if one of them is oriented upwards and the other is oriented
- downwards, let's only keep one of them (it doesn't matter which
- one since all 'top' lines were reversed at slicing) */
- if (line->edge_type == line2->edge_type) {
- line->skip = true;
- break;
- }
- } else if (line->a_id == line2->b_id && line->b_id == line2->a_id) {
- /* if this edge joins two horizontal facets, remove both of them */
- if (line->edge_type == feHorizontal && line2->edge_type == feHorizontal) {
- line->skip = true;
- line2->skip = true;
- break;
- }
+ std::vector<IntersectionLine*> by_vertex_pair;
+ by_vertex_pair.reserve(lines.size());
+ for (IntersectionLine& line : lines)
+ if (line.edge_type != feGeneral && line.a_id != -1)
+ // This is a face edge. Check whether there is its neighbor stored in lines.
+ by_vertex_pair.emplace_back(&line);
+ auto edges_lower_sorted = [](const IntersectionLine *l1, const IntersectionLine *l2) {
+ // Sort vertices of l1, l2 lexicographically
+ int l1a = l1->a_id;
+ int l1b = l1->b_id;
+ int l2a = l2->a_id;
+ int l2b = l2->b_id;
+ if (l1a > l1b)
+ std::swap(l1a, l1b);
+ if (l2a > l2b)
+ std::swap(l2a, l2b);
+ // Lexicographical "lower" operator on lexicographically sorted vertices should bring equal edges together when sored.
+ return l1a < l2a || (l1a == l2a && l1b < l2b);
+ };
+ std::sort(by_vertex_pair.begin(), by_vertex_pair.end(), edges_lower_sorted);
+ for (auto line = by_vertex_pair.begin(); line != by_vertex_pair.end(); ++ line) {
+ IntersectionLine &l1 = **line;
+ if (! l1.skip()) {
+ // Iterate as long as line and line2 edges share the same end points.
+ for (auto line2 = line + 1; line2 != by_vertex_pair.end() && ! edges_lower_sorted(*line, *line2); ++ line2) {
+ // Lines must share the end points.
+ assert(! edges_lower_sorted(*line, *line2));
+ assert(! edges_lower_sorted(*line2, *line));
+ IntersectionLine &l2 = **line2;
+ if (l2.skip())
+ continue;
+ if (l1.a_id == l2.a_id) {
+ assert(l1.b_id == l2.b_id);
+ l2.set_skip();
+ // If they are both oriented upwards or downwards (like a 'V'),
+ // then we can remove both edges from this layer since it won't
+ // affect the sliced shape.
+ // If one of them is oriented upwards and the other is oriented
+ // downwards, let's only keep one of them (it doesn't matter which
+ // one since all 'top' lines were reversed at slicing).
+ if (l1.edge_type == l2.edge_type) {
+ l1.set_skip();
+ break;
+ }
+ } else {
+ assert(l1.a_id == l2.b_id && l1.b_id == l2.a_id);
+ // If this edge joins two horizontal facets, remove both of them.
+ if (l1.edge_type == feHorizontal && l2.edge_type == feHorizontal) {
+ l1.set_skip();
+ l2.set_skip();
+ break;
}
}
+ }
}
+ }
+}
+
+void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
+{
+#if 0
+//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
+//#ifdef _DEBUG
+ for (const Line &l : lines)
+ assert(l.a != l.b);
+#endif /* _DEBUG */
+
+ remove_tangent_edges(lines);
struct OpenPolyline {
OpenPolyline() {};
@@ -1032,7 +1233,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
by_edge_a_id.reserve(lines.size());
by_a_id.reserve(lines.size());
for (IntersectionLine &line : lines) {
- if (! line.skip) {
+ if (! line.skip()) {
if (line.edge_a_id != -1)
by_edge_a_id.emplace_back(&line);
if (line.a_id != -1)
@@ -1049,13 +1250,14 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
// take first spare line and start a new loop
IntersectionLine *first_line = nullptr;
for (; it_line_seed != lines.end(); ++ it_line_seed)
- if (! it_line_seed->skip) {
+ if (it_line_seed->is_seed_candidate()) {
+ //if (! it_line_seed->skip()) {
first_line = &(*it_line_seed ++);
break;
}
if (first_line == nullptr)
break;
- first_line->skip = true;
+ first_line->set_skip();
Points loop_pts;
loop_pts.emplace_back(first_line->a);
IntersectionLine *last_line = first_line;
@@ -1076,7 +1278,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
if (it_begin != by_edge_a_id.end()) {
auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower);
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
- if (! (*it_line)->skip) {
+ if (! (*it_line)->skip()) {
next_line = *it_line;
break;
}
@@ -1088,7 +1290,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
if (it_begin != by_a_id.end()) {
auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower);
for (auto it_line = it_begin; it_line != it_end; ++ it_line)
- if (! (*it_line)->skip) {
+ if (! (*it_line)->skip()) {
next_line = *it_line;
break;
}
@@ -1119,7 +1321,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
*/
loop_pts.emplace_back(next_line->a);
last_line = next_line;
- next_line->skip = true;
+ next_line->set_skip();
}
}
}
@@ -1186,12 +1388,12 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
}
}
}
- if (next_start == nullptr) {
- // The current loop could not be closed. Unmark the segment.
- opl.consumed = false;
- break;
- }
- // Attach this polyline to the end of the initial polyline.
+ if (next_start == nullptr) {
+ // The current loop could not be closed. Unmark the segment.
+ opl.consumed = false;
+ break;
+ }
+ // Attach this polyline to the end of the initial polyline.
if (next_start->start) {
auto it = next_start->polyline->points.begin();
std::copy(++ it, next_start->polyline->points.end(), back_inserter(opl.points));
@@ -1211,8 +1413,8 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
if ((ip1.edge_id != -1 && ip1.edge_id == ip2.edge_id) ||
(ip1.point_id != -1 && ip1.point_id == ip2.point_id)) {
// The current loop is complete. Add it to the output.
- /*assert(opl.points.front().point_id == opl.points.back().point_id);
- assert(opl.points.front().edge_id == opl.points.back().edge_id);*/
+ //assert(opl.points.front().point_id == opl.points.back().point_id);
+ //assert(opl.points.front().edge_id == opl.points.back().edge_id);
// Remove the duplicate last point.
opl.points.pop_back();
if (opl.points.size() >= 3) {
@@ -1227,9 +1429,9 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
loops->emplace_back(std::move(opl.points));
}
opl.points.clear();
- break;
+ break;
}
- // Continue with the current loop.
+ // Continue with the current loop.
}
}
}
@@ -1277,15 +1479,15 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &l
if (slice_idx == -1)
// Ignore this hole.
continue;
- assert(current_contour_area < std::numeric_limits<double>::max() && current_contour_area >= -hole->area());
- (*slices)[slice_idx].holes.emplace_back(std::move(*hole));
+ assert(current_contour_area < std::numeric_limits<double>::max() && current_contour_area >= -hole->area());
+ (*slices)[slice_idx].holes.emplace_back(std::move(*hole));
}
#if 0
// If the input mesh is not valid, the holes may intersect with the external contour.
// Rather subtract them from the outer contour.
Polygons poly;
- for (auto it_slice = slices->begin(); it_slice != slices->end(); ++ it_slice) {
+ for (auto it_slice = slices->begin(); it_slice != slices->end(); ++ it_slice) {
if (it_slice->holes.empty()) {
poly.emplace_back(std::move(it_slice->contour));
} else {
@@ -1295,7 +1497,7 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &l
it->reverse();
polygons_append(poly, diff(contours, it_slice->holes));
}
- }
+ }
// If the input mesh is not valid, the input contours may intersect.
*slices = union_ex(poly);
#endif
@@ -1412,7 +1614,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
// intersect facet with cutting plane
IntersectionLine line;
- if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line)) {
+ if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line) != TriangleMeshSlicer::NoSlice) {
// Save intersection lines for generating correct triangulations.
if (line.edge_type == feTop) {
lower_lines.emplace_back(line);
diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp
index ed3e6022d..f6e0baea9 100644
--- a/xs/src/libslic3r/TriangleMesh.hpp
+++ b/xs/src/libslic3r/TriangleMesh.hpp
@@ -83,7 +83,7 @@ private:
enum FacetEdgeType {
// A general case, the cutting plane intersect a face at two different edges.
- feNone,
+ feGeneral,
// Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
feTop,
// Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
@@ -117,6 +117,14 @@ public:
class IntersectionLine : public Line
{
public:
+ IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feGeneral), flags(0) {}
+
+ bool skip() const { return (this->flags & SKIP) != 0; }
+ void set_skip() { this->flags |= SKIP; }
+
+ bool is_seed_candidate() const { return (this->flags & NO_SEED) == 0 && ! this->skip(); }
+ void set_no_seed(bool set) { if (set) this->flags |= NO_SEED; else this->flags &= ~NO_SEED; }
+
// Inherits Point a, b
// For each line end point, either {a,b}_id or {a,b}edge_a_id is set, the other is left to -1.
// Vertex indices of the line end points.
@@ -125,11 +133,23 @@ public:
// Source mesh edges of the line end points.
int edge_a_id;
int edge_b_id;
- // feNone, feTop, feBottom, feHorizontal
+ // feGeneral, feTop, feBottom, feHorizontal
FacetEdgeType edge_type;
- // Used by TriangleMeshSlicer::make_loops() to skip duplicate edges.
- bool skip;
- IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feNone), skip(false) {};
+ // Used by TriangleMeshSlicer::slice() to skip duplicate edges.
+ enum {
+ // Triangle edge added, because it has no neighbor.
+ EDGE0_NO_NEIGHBOR = 0x001,
+ EDGE1_NO_NEIGHBOR = 0x002,
+ EDGE2_NO_NEIGHBOR = 0x004,
+ // Triangle edge added, because it makes a fold with another horizontal edge.
+ EDGE0_FOLD = 0x010,
+ EDGE1_FOLD = 0x020,
+ EDGE2_FOLD = 0x040,
+ // The edge cannot be a seed of a greedy loop extraction (folds are not safe to become seeds).
+ NO_SEED = 0x100,
+ SKIP = 0x200,
+ };
+ uint32_t flags;
};
typedef std::vector<IntersectionLine> IntersectionLines;
typedef std::vector<IntersectionLine*> IntersectionLinePtrs;
@@ -144,7 +164,12 @@ public:
void init(TriangleMesh *mesh, throw_on_cancel_callback_type throw_on_cancel);
void slice(const std::vector<float> &z, std::vector<Polygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers, throw_on_cancel_callback_type throw_on_cancel) const;
- bool slice_facet(float slice_z, const stl_facet &facet, const int facet_idx,
+ enum FacetSliceType {
+ NoSlice = 0,
+ Slicing = 1,
+ Cutting = 2
+ };
+ FacetSliceType slice_facet(float slice_z, const stl_facet &facet, const int facet_idx,
const float min_z, const float max_z, IntersectionLine *line_out) const;
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;
diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp
index dd05891dd..0d2df5a0b 100644
--- a/xs/src/libslic3r/Utils.hpp
+++ b/xs/src/libslic3r/Utils.hpp
@@ -9,6 +9,7 @@ namespace Slic3r {
extern void set_logging_level(unsigned int level);
extern void trace(unsigned int level, const char *message);
+extern void disable_multi_threading();
// Set a path with GUI resource files.
void set_var_dir(const std::string &path);
diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h
index 925e93031..cf0a19f7a 100644
--- a/xs/src/libslic3r/libslic3r.h
+++ b/xs/src/libslic3r/libslic3r.h
@@ -13,6 +13,8 @@
#include <vector>
#include <boost/thread.hpp>
+#include "Technologies.hpp"
+
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
#define SLIC3R_VERSION "1.41.0"
#define SLIC3R_BUILD "UNKNOWN"
diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp
index 4c2d65605..46ee65a35 100644
--- a/xs/src/libslic3r/utils.cpp
+++ b/xs/src/libslic3r/utils.cpp
@@ -25,6 +25,8 @@
#include <boost/nowide/convert.hpp>
#include <boost/nowide/cstdio.hpp>
+#include <tbb/task_scheduler_init.h>
+
namespace Slic3r {
static boost::log::trivial::severity_level logSeverity = boost::log::trivial::error;
@@ -83,6 +85,14 @@ void trace(unsigned int level, const char *message)
(::boost::log::keywords::severity = severity)) << message;
}
+void disable_multi_threading()
+{
+ // Disable parallelization so the Shiny profiler works
+ static tbb::task_scheduler_init *tbb_init = nullptr;
+ if (tbb_init == nullptr)
+ tbb_init = new tbb::task_scheduler_init(1);
+}
+
static std::string g_var_dir;
void set_var_dir(const std::string &dir)
diff --git a/xs/src/slic3r/AppController.cpp b/xs/src/slic3r/AppController.cpp
index 5e259c367..4a36b5d7f 100644
--- a/xs/src/slic3r/AppController.cpp
+++ b/xs/src/slic3r/AppController.cpp
@@ -11,12 +11,11 @@
#include <ModelArrange.hpp>
#include <slic3r/GUI/PresetBundle.hpp>
-#include <Print.hpp>
-#include <PrintExport.hpp>
#include <Geometry.hpp>
+#include <PrintConfig.hpp>
+#include <Print.hpp>
#include <Model.hpp>
#include <Utils.hpp>
-#include <SLABasePool.hpp>
namespace Slic3r {
@@ -44,15 +43,6 @@ namespace GUI {
PresetBundle* get_preset_bundle();
}
-static const PrintObjectStep STEP_SLICE = posSlice;
-static const PrintObjectStep STEP_PERIMETERS = posPerimeters;
-static const PrintObjectStep STEP_PREPARE_INFILL = posPrepareInfill;
-static const PrintObjectStep STEP_INFILL = posInfill;
-static const PrintObjectStep STEP_SUPPORTMATERIAL = posSupportMaterial;
-static const PrintStep STEP_SKIRT = psSkirt;
-static const PrintStep STEP_BRIM = psBrim;
-static const PrintStep STEP_WIPE_TOWER = psWipeTower;
-
AppControllerBoilerplate::ProgresIndicatorPtr
AppControllerBoilerplate::global_progress_indicator() {
ProgresIndicatorPtr ret;
@@ -72,343 +62,8 @@ void AppControllerBoilerplate::global_progress_indicator(
pri_data_->m.unlock();
}
-#if 0
-void PrintController::make_skirt()
-{
- assert(print_ != nullptr);
-
- // prerequisites
- for(auto obj : print_->objects()) make_perimeters(obj);
- for(auto obj : print_->objects()) infill(obj);
- for(auto obj : print_->objects()) gen_support_material(obj);
-
- if(!print_->state.is_done(STEP_SKIRT)) {
- print_->state.set_started(STEP_SKIRT);
- print_->skirt.clear();
- if(print_->has_skirt()) print_->_make_skirt();
-
- print_->state.set_done(STEP_SKIRT);
- }
-}
-
-void PrintController::make_brim()
-{
- assert(print_ != nullptr);
-
- // prerequisites
- for(auto obj : print_->objects()) make_perimeters(obj);
- for(auto obj : print_->objects()) infill(obj);
- for(auto obj : print_->objects()) gen_support_material(obj);
- make_skirt();
-
- if(!print_->state.is_done(STEP_BRIM)) {
- print_->state.set_started(STEP_BRIM);
-
- // since this method must be idempotent, we clear brim paths *before*
- // checking whether we need to generate them
- print_->brim.clear();
-
- if(print_->config.brim_width > 0) print_->_make_brim();
-
- print_->state.set_done(STEP_BRIM);
- }
-}
-
-void PrintController::make_wipe_tower()
-{
- assert(print_ != nullptr);
-
- // prerequisites
- for(auto obj : print_->objects()) make_perimeters(obj);
- for(auto obj : print_->objects()) infill(obj);
- for(auto obj : print_->objects()) gen_support_material(obj);
- make_skirt();
- make_brim();
-
- if(!print_->state.is_done(STEP_WIPE_TOWER)) {
- print_->state.set_started(STEP_WIPE_TOWER);
-
- // since this method must be idempotent, we clear brim paths *before*
- // checking whether we need to generate them
- print_->brim.clear();
-
- if(print_->has_wipe_tower()) print_->_make_wipe_tower();
-
- print_->state.set_done(STEP_WIPE_TOWER);
- }
-}
-
-void PrintController::slice(PrintObject *pobj)
-{
- assert(pobj != nullptr && print_ != nullptr);
-
- if(pobj->state.is_done(STEP_SLICE)) return;
-
- pobj->state.set_started(STEP_SLICE);
-
- pobj->_slice();
-
- auto msg = pobj->_fix_slicing_errors();
- if(!msg.empty()) report_issue(IssueType::WARN, msg);
-
- // simplify slices if required
- if (print_->config.resolution)
- pobj->_simplify_slices(scale_(print_->config.resolution));
-
-
- if(pobj->layers.empty())
- report_issue(IssueType::ERR,
- _(L("No layers were detected. You might want to repair your "
- "STL file(s) or check their size or thickness and retry"))
- );
-
- pobj->state.set_done(STEP_SLICE);
-}
-
-void PrintController::make_perimeters(PrintObject *pobj)
-{
- assert(pobj != nullptr);
-
- slice(pobj);
-
- if (!pobj->state.is_done(STEP_PERIMETERS)) {
- pobj->_make_perimeters();
- }
-}
-
-void PrintController::infill(PrintObject *pobj)
-{
- assert(pobj != nullptr);
-
- make_perimeters(pobj);
-
- if (!pobj->state.is_done(STEP_PREPARE_INFILL)) {
- pobj->state.set_started(STEP_PREPARE_INFILL);
-
- pobj->_prepare_infill();
-
- pobj->state.set_done(STEP_PREPARE_INFILL);
- }
-
- pobj->_infill();
-}
-
-void PrintController::gen_support_material(PrintObject *pobj)
-{
- assert(pobj != nullptr);
-
- // prerequisites
- slice(pobj);
-
- if(!pobj->state.is_done(STEP_SUPPORTMATERIAL)) {
- pobj->state.set_started(STEP_SUPPORTMATERIAL);
-
- pobj->clear_support_layers();
-
- if((pobj->config.support_material || pobj->config.raft_layers > 0)
- && pobj->layers.size() > 1) {
- pobj->_generate_support_material();
- }
-
- pobj->state.set_done(STEP_SUPPORTMATERIAL);
- }
-}
-
-PrintController::PngExportData
-PrintController::query_png_export_data(const DynamicPrintConfig& conf)
-{
- PngExportData ret;
-
- auto zippath = query_destination_path("Output zip file", "*.zip", "out");
-
- ret.zippath = zippath;
-
- ret.width_mm = conf.opt_float("display_width");
- ret.height_mm = conf.opt_float("display_height");
-
- ret.width_px = conf.opt_int("display_pixels_x");
- ret.height_px = conf.opt_int("display_pixels_y");
-
- auto opt_corr = conf.opt<ConfigOptionFloats>("printer_correction");
-
- if(opt_corr) {
- ret.corr_x = opt_corr->values[0];
- ret.corr_y = opt_corr->values[1];
- ret.corr_z = opt_corr->values[2];
- }
-
- ret.exp_time_first_s = conf.opt_float("initial_exposure_time");
- ret.exp_time_s = conf.opt_float("exposure_time");
-
- return ret;
-}
-
-void PrintController::slice(AppControllerBoilerplate::ProgresIndicatorPtr pri)
-{
- auto st = pri->state();
-
- Slic3r::trace(3, "Starting the slicing process.");
-
- pri->update(st+20, _(L("Generating perimeters")));
- for(auto obj : print_->objects()) make_perimeters(obj);
-
- pri->update(st+60, _(L("Infilling layers")));
- for(auto obj : print_->objects()) infill(obj);
-
- pri->update(st+70, _(L("Generating support material")));
- for(auto obj : print_->objects()) gen_support_material(obj);
-
- pri->message_fmt(_(L("Weight: %.1fg, Cost: %.1f")),
- print_->total_weight, print_->total_cost);
- pri->state(st+85);
-
-
- pri->update(st+88, _(L("Generating skirt")));
- make_skirt();
-
-
- pri->update(st+90, _(L("Generating brim")));
- make_brim();
-
- pri->update(st+95, _(L("Generating wipe tower")));
- make_wipe_tower();
-
- pri->update(st+100, _(L("Done")));
-
- // time to make some statistics..
-
- Slic3r::trace(3, _(L("Slicing process finished.")));
-}
-
-void PrintController::slice()
-{
- auto pri = global_progress_indicator();
- if(!pri) pri = create_progress_indicator(100, L("Slicing"));
- slice(pri);
-}
-
-void PrintController::slice_to_png()
-{
- using Pointf3 = Vec3d;
-
- auto presetbundle = GUI::get_preset_bundle();
-
- assert(presetbundle);
-
- auto pt = presetbundle->printers.get_selected_preset().printer_technology();
- if(pt != ptSLA) {
- report_issue(IssueType::ERR, _("Printer technology is not SLA!"),
- _("Error"));
- return;
- }
-
- auto conf = presetbundle->full_config();
- conf.validate();
-
- auto exd = query_png_export_data(conf);
- if(exd.zippath.empty()) return;
-
- try {
- print_->apply_config(conf);
- print_->validate();
- } catch(std::exception& e) {
- report_issue(IssueType::ERR, e.what(), "Error");
- return;
- }
-
- // TODO: copy the model and work with the copy only
- bool correction = false;
- if(exd.corr_x != 1.0 || exd.corr_y != 1.0 || exd.corr_z != 1.0) {
- correction = true;
- print_->invalidate_all_steps();
-
- for(auto po : print_->objects()) {
- po->model_object()->scale(
- Pointf3(exd.corr_x, exd.corr_y, exd.corr_z)
- );
- po->model_object()->invalidate_bounding_box();
- po->reload_model_instances();
- po->invalidate_all_steps();
- }
- }
-
- // Turn back the correction scaling on the model.
- auto scale_back = [this, correction, exd]() {
- if(correction) { // scale the model back
- print_->invalidate_all_steps();
- for(auto po : print_->objects()) {
- po->model_object()->scale(
- Pointf3(1.0/exd.corr_x, 1.0/exd.corr_y, 1.0/exd.corr_z)
- );
- po->model_object()->invalidate_bounding_box();
- po->reload_model_instances();
- po->invalidate_all_steps();
- }
- }
- };
-
- auto print_bb = print_->bounding_box();
- Vec2d punsc = unscale(print_bb.size());
-
- // If the print does not fit into the print area we should cry about it.
- if(px(punsc) > exd.width_mm || py(punsc) > exd.height_mm) {
- std::stringstream ss;
-
- ss << _(L("Print will not fit and will be truncated!")) << "\n"
- << _(L("Width needed: ")) << px(punsc) << " mm\n"
- << _(L("Height needed: ")) << py(punsc) << " mm\n";
-
- if(!report_issue(IssueType::WARN_Q, ss.str(), _(L("Warning")))) {
- scale_back();
- return;
- }
- }
-
-// std::async(supports_asynch()? std::launch::async : std::launch::deferred,
-// [this, exd, scale_back]()
-// {
-
- auto pri = create_progress_indicator(
- 200, _(L("Slicing to zipped png files...")));
-
- try {
- pri->update(0, _(L("Slicing...")));
- slice(pri);
- } catch (std::exception& e) {
- pri->cancel();
- report_issue(IssueType::ERR, e.what(), _(L("Exception occured")));
- scale_back();
- return;
- }
-
- auto pbak = print_->progressindicator;
- print_->progressindicator = pri;
-
- try {
- print_to<FilePrinterFormat::PNG>( *print_, exd.zippath,
- exd.width_mm, exd.height_mm,
- exd.width_px, exd.height_px,
- exd.exp_time_s, exd.exp_time_first_s);
-
- } catch (std::exception& e) {
- pri->cancel();
- report_issue(IssueType::ERR, e.what(), _(L("Exception occured")));
- }
-
- print_->progressindicator = pbak;
- scale_back();
-
-// });
-}
-
-const PrintConfig &PrintController::config() const
-{
- return print_->config;
-}
-#endif
-
void ProgressIndicator::message_fmt(
- const wxString &fmtstr, ...) {
+ const std::string &fmtstr, ...) {
std::stringstream ss;
va_list args;
va_start(args, fmtstr);
@@ -438,6 +93,11 @@ void AppController::arrange_model()
{
using Coord = libnest2d::TCoord<libnest2d::PointImpl>;
+ if(arranging_.load()) return;
+
+ // to prevent UI reentrancies
+ arranging_.store(true);
+
unsigned count = 0;
for(auto obj : model_->objects) count += obj->instances.size();
@@ -451,8 +111,8 @@ void AppController::arrange_model()
// Set the range of the progress to the object count
pind->max(count);
- pind->on_cancel([](){
- std::cout << "Cannot be cancelled!" << std::endl;
+ pind->on_cancel([this](){
+ arranging_.store(false);
});
}
@@ -466,7 +126,7 @@ void AppController::arrange_model()
for(auto& v : bedpoints)
bed.append(Point::new_scale(v(0), v(1)));
- if(pind) pind->update(0, _(L("Arranging objects...")));
+ if(pind) pind->update(0, L("Arranging objects..."));
try {
arr::BedShapeHint hint;
@@ -478,24 +138,30 @@ void AppController::arrange_model()
bed,
hint,
false, // create many piles not just one pile
- [pind, count](unsigned rem) {
+ [this, pind, count](unsigned rem) {
if(pind)
- pind->update(count - rem, _(L("Arranging objects...")));
- });
+ pind->update(count - rem, L("Arranging objects..."));
+
+ process_events();
+ }, [this] () { return !arranging_.load(); });
} catch(std::exception& e) {
std::cerr << e.what() << std::endl;
report_issue(IssueType::ERR,
- _(L("Could not arrange model objects! "
- "Some geometries may be invalid.")),
- _(L("Exception occurred")));
+ L("Could not arrange model objects! "
+ "Some geometries may be invalid."),
+ L("Exception occurred"));
}
// Restore previous max value
if(pind) {
pind->max(pmax);
- pind->update(0, _(L("Arranging done.")));
+ pind->update(0, arranging_.load() ? L("Arranging done.") :
+ L("Arranging canceled."));
+
pind->on_cancel(/*remove cancel function*/);
}
+
+ arranging_.store(false);
}
}
diff --git a/xs/src/slic3r/AppController.hpp b/xs/src/slic3r/AppController.hpp
index 740f9a630..71472835e 100644
--- a/xs/src/slic3r/AppController.hpp
+++ b/xs/src/slic3r/AppController.hpp
@@ -18,6 +18,7 @@ class Print;
class PrintObject;
class PrintConfig;
class ProgressStatusBar;
+class DynamicPrintConfig;
/**
* @brief A boilerplate class for creating application logic. It should provide
@@ -48,7 +49,7 @@ public:
AppControllerBoilerplate();
~AppControllerBoilerplate();
- using Path = wxString;
+ using Path = std::string;
using PathList = std::vector<Path>;
/// Common runtime issue types
@@ -69,20 +70,20 @@ public:
* @return Returns a list of paths choosed by the user.
*/
PathList query_destination_paths(
- const wxString& title,
+ const std::string& title,
const std::string& extensions) const;
/**
* @brief Same as query_destination_paths but works for directories only.
*/
PathList query_destination_dirs(
- const wxString& title) const;
+ const std::string& title) const;
/**
* @brief Same as query_destination_paths but returns only one path.
*/
Path query_destination_path(
- const wxString& title,
+ const std::string& title,
const std::string& extensions,
const std::string& hint = "") const;
@@ -97,11 +98,11 @@ public:
* title.
*/
bool report_issue(IssueType issuetype,
- const wxString& description,
- const wxString& brief);
+ const std::string& description,
+ const std::string& brief);
bool report_issue(IssueType issuetype,
- const wxString& description);
+ const std::string& description);
/**
* @brief Return the global progress indicator for the current controller.
@@ -152,12 +153,12 @@ protected:
*/
ProgresIndicatorPtr create_progress_indicator(
unsigned statenum,
- const wxString& title,
- const wxString& firstmsg) const;
+ const std::string& title,
+ const std::string& firstmsg) const;
ProgresIndicatorPtr create_progress_indicator(
unsigned statenum,
- const wxString& title) const;
+ const std::string& title) const;
// This is a global progress indicator placeholder. In the Slic3r UI it can
// contain the progress indicator on the statusbar.
@@ -170,43 +171,6 @@ protected:
*/
class PrintController: public AppControllerBoilerplate {
Print *print_ = nullptr;
-protected:
-
- void make_skirt();
- void make_brim();
- void make_wipe_tower();
-
- void make_perimeters(PrintObject *pobj);
- void infill(PrintObject *pobj);
- void gen_support_material(PrintObject *pobj);
-
- // Data structure with the png export input data
- struct PngExportData {
- std::string zippath; // output zip file
- unsigned long width_px = 1440; // resolution - rows
- unsigned long height_px = 2560; // resolution columns
- double width_mm = 68.0, height_mm = 120.0; // dimensions in mm
- double exp_time_first_s = 35.0; // first exposure time
- double exp_time_s = 8.0; // global exposure time
- double corr_x = 1.0; // offsetting in x
- double corr_y = 1.0; // offsetting in y
- double corr_z = 1.0; // offsetting in y
- };
-
- // Should display a dialog with the input fields for printing to png
- PngExportData query_png_export_data(const DynamicPrintConfig&);
-
- // The previous export data, to pre-populate the dialog
- PngExportData prev_expdata_;
-
- /**
- * @brief Slice one pront object.
- * @param pobj The print object.
- */
- void slice(PrintObject *pobj);
-
- void slice(ProgresIndicatorPtr pri);
-
public:
// Must be public for perl to use it
@@ -221,15 +185,8 @@ public:
return PrintController::Ptr( new PrintController(print) );
}
- /**
- * @brief Slice the loaded print scene.
- */
- void slice();
-
- /**
- * @brief Slice the print into zipped png files.
- */
- void slice_to_png();
+ void slice() {}
+ void slice_to_png() {}
const PrintConfig& config() const;
};
@@ -253,6 +210,7 @@ public:
class AppController: public AppControllerBoilerplate {
Model *model_ = nullptr;
PrintController::Ptr printctl;
+ std::atomic<bool> arranging_;
public:
/**
@@ -288,7 +246,7 @@ public:
* In perl we have a progress indicating status bar on the bottom of the
* window which is defined and created in perl. We can pass the ID-s of the
* gauge and the statusbar id and make a wrapper implementation of the
- * IProgressIndicator interface so we can use this GUI widget from C++.
+ * ProgressIndicator interface so we can use this GUI widget from C++.
*
* This function should be called from perl.
*
diff --git a/xs/src/slic3r/AppControllerWx.cpp b/xs/src/slic3r/AppControllerWx.cpp
index bd2859fce..4d67d5f66 100644
--- a/xs/src/slic3r/AppControllerWx.cpp
+++ b/xs/src/slic3r/AppControllerWx.cpp
@@ -28,16 +28,16 @@ bool AppControllerBoilerplate::supports_asynch() const
void AppControllerBoilerplate::process_events()
{
- wxSafeYield();
+ wxYieldIfNeeded();
}
AppControllerBoilerplate::PathList
AppControllerBoilerplate::query_destination_paths(
- const wxString &title,
+ const std::string &title,
const std::string &extensions) const
{
- wxFileDialog dlg(wxTheApp->GetTopWindow(), title );
+ wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
dlg.SetWildcard(extensions);
dlg.ShowModal();
@@ -53,11 +53,11 @@ AppControllerBoilerplate::query_destination_paths(
AppControllerBoilerplate::Path
AppControllerBoilerplate::query_destination_path(
- const wxString &title,
+ const std::string &title,
const std::string &extensions,
const std::string& hint) const
{
- wxFileDialog dlg(wxTheApp->GetTopWindow(), title );
+ wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
dlg.SetWildcard(extensions);
dlg.SetFilename(hint);
@@ -72,8 +72,8 @@ AppControllerBoilerplate::query_destination_path(
}
bool AppControllerBoilerplate::report_issue(IssueType issuetype,
- const wxString &description,
- const wxString &brief)
+ const std::string &description,
+ const std::string &brief)
{
auto icon = wxICON_INFORMATION;
auto style = wxOK|wxCENTRE;
@@ -85,15 +85,15 @@ bool AppControllerBoilerplate::report_issue(IssueType issuetype,
case IssueType::FATAL: icon = wxICON_ERROR;
}
- auto ret = wxMessageBox(description, brief, icon | style);
+ auto ret = wxMessageBox(_(description), _(brief), icon | style);
return ret != wxCANCEL;
}
bool AppControllerBoilerplate::report_issue(
AppControllerBoilerplate::IssueType issuetype,
- const wxString &description)
+ const std::string &description)
{
- return report_issue(issuetype, description, wxString());
+ return report_issue(issuetype, description, std::string());
}
wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent);
@@ -126,7 +126,9 @@ class GuiProgressIndicator:
void _state( unsigned st) {
if(!gauge_.IsShown()) gauge_.ShowModal();
Base::state(st);
- gauge_.Update(static_cast<int>(st), message_);
+ if(!gauge_.Update(static_cast<int>(st), message_)) {
+ cancel();
+ }
}
public:
@@ -140,7 +142,8 @@ public:
inline GuiProgressIndicator(int range, const wxString& title,
const wxString& firstmsg) :
gauge_(title, firstmsg, range, wxTheApp->GetTopWindow(),
- wxPD_APP_MODAL | wxPD_AUTO_HIDE),
+ wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT),
+
message_(firstmsg),
range_(range), title_(title)
{
@@ -152,11 +155,6 @@ public:
this, id_);
}
- virtual void cancel() override {
- update(max(), "Abort");
- ProgressIndicator::cancel();
- }
-
virtual void state(float val) override {
state(static_cast<unsigned>(val));
}
@@ -171,26 +169,28 @@ public:
} else _state(st);
}
- virtual void message(const wxString & msg) override {
- message_ = msg;
+ virtual void message(const std::string & msg) override {
+ message_ = _(msg);
}
- virtual void messageFmt(const wxString& fmt, ...) {
+ virtual void messageFmt(const std::string& fmt, ...) {
va_list arglist;
va_start(arglist, fmt);
- message_ = wxString::Format(wxString(fmt), arglist);
+ message_ = wxString::Format(_(fmt), arglist);
va_end(arglist);
}
- virtual void title(const wxString & title) override {
- title_ = title;
+ virtual void title(const std::string & title) override {
+ title_ = _(title);
}
};
}
AppControllerBoilerplate::ProgresIndicatorPtr
AppControllerBoilerplate::create_progress_indicator(
- unsigned statenum, const wxString& title, const wxString& firstmsg) const
+ unsigned statenum,
+ const std::string& title,
+ const std::string& firstmsg) const
{
auto pri =
std::make_shared<GuiProgressIndicator>(statenum, title, firstmsg);
@@ -203,10 +203,10 @@ AppControllerBoilerplate::create_progress_indicator(
}
AppControllerBoilerplate::ProgresIndicatorPtr
-AppControllerBoilerplate::create_progress_indicator(unsigned statenum,
- const wxString &title) const
+AppControllerBoilerplate::create_progress_indicator(
+ unsigned statenum, const std::string &title) const
{
- return create_progress_indicator(statenum, title, wxString());
+ return create_progress_indicator(statenum, title, std::string());
}
namespace {
@@ -214,7 +214,7 @@ namespace {
class Wrapper: public ProgressIndicator, public wxEvtHandler {
ProgressStatusBar *sbar_;
using Base = ProgressIndicator;
- std::string message_;
+ wxString message_;
AppControllerBoilerplate& ctl_;
void showProgress(bool show = true) {
@@ -271,18 +271,18 @@ public:
}
}
- virtual void message(const wxString & msg) override {
- message_ = msg;
+ virtual void message(const std::string & msg) override {
+ message_ = _(msg);
}
- virtual void message_fmt(const wxString& fmt, ...) override {
+ virtual void message_fmt(const std::string& fmt, ...) override {
va_list arglist;
va_start(arglist, fmt);
- message_ = wxString::Format(fmt, arglist);
+ message_ = wxString::Format(_(fmt), arglist);
va_end(arglist);
}
- virtual void title(const wxString & /*title*/) override {}
+ virtual void title(const std::string & /*title*/) override {}
virtual void on_cancel(CancelFn fn) override {
sbar_->set_cancel_callback(fn);
diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp
index a488bfcde..b9f6a59bc 100644
--- a/xs/src/slic3r/GUI/3DScene.cpp
+++ b/xs/src/slic3r/GUI/3DScene.cpp
@@ -648,7 +648,7 @@ std::vector<int> GLVolumeCollection::load_object(
const ModelVolume *model_volume = model_object->volumes[volume_idx];
int extruder_id = -1;
- if (!model_volume->modifier)
+ if (model_volume->is_model_part())
{
extruder_id = model_volume->config.has("extruder") ? model_volume->config.option("extruder")->getInt() : 0;
if (extruder_id == 0)
@@ -661,7 +661,16 @@ std::vector<int> GLVolumeCollection::load_object(
volumes_idx.push_back(int(this->volumes.size()));
float color[4];
memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3);
- color[3] = model_volume->modifier ? 0.5f : 1.f;
+ if (model_volume->is_support_blocker()) {
+ color[0] = 1.0f;
+ color[1] = 0.2f;
+ color[2] = 0.2f;
+ } else if (model_volume->is_support_enforcer()) {
+ color[0] = 0.2f;
+ color[1] = 0.2f;
+ color[2] = 1.0f;
+ }
+ color[3] = model_volume->is_model_part() ? 1.f : 0.5f;
this->volumes.emplace_back(new GLVolume(color));
GLVolume &v = *this->volumes.back();
if (use_VBOs)
@@ -675,16 +684,20 @@ std::vector<int> GLVolumeCollection::load_object(
v.composite_id = obj_idx * 1000000 + volume_idx * 1000 + instance_idx;
v.set_select_group_id(select_by);
v.set_drag_group_id(drag_by);
- if (!model_volume->modifier)
+ if (model_volume->is_model_part())
{
v.set_convex_hull(model_volume->get_convex_hull());
v.layer_height_texture = layer_height_texture;
if (extruder_id != -1)
v.extruder_id = extruder_id;
}
- v.is_modifier = model_volume->modifier;
- v.shader_outside_printer_detection_enabled = !model_volume->modifier;
+ v.is_modifier = ! model_volume->is_model_part();
+ v.shader_outside_printer_detection_enabled = model_volume->is_model_part();
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ v.set_offset(instance->get_offset());
+#else
v.set_offset(Vec3d(instance->offset(0), instance->offset(1), 0.0));
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
v.set_rotation(instance->rotation);
v.set_scaling_factor(instance->scaling_factor);
}
@@ -2164,6 +2177,11 @@ int _3DScene::get_first_volume_id(wxGLCanvas* canvas, int obj_idx)
return s_canvas_mgr.get_first_volume_id(canvas, obj_idx);
}
+int _3DScene::get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx)
+{
+ return s_canvas_mgr.get_in_object_volume_id(canvas, scene_vol_idx);
+}
+
void _3DScene::reload_scene(wxGLCanvas* canvas, bool force)
{
s_canvas_mgr.reload_scene(canvas, force);
diff --git a/xs/src/slic3r/GUI/3DScene.hpp b/xs/src/slic3r/GUI/3DScene.hpp
index b53fd8a23..f2d1c0786 100644
--- a/xs/src/slic3r/GUI/3DScene.hpp
+++ b/xs/src/slic3r/GUI/3DScene.hpp
@@ -577,6 +577,7 @@ public:
static std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx);
static int get_first_volume_id(wxGLCanvas* canvas, int obj_idx);
+ static int get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx);
static void reload_scene(wxGLCanvas* canvas, bool force);
diff --git a/xs/src/slic3r/GUI/AppConfig.cpp b/xs/src/slic3r/GUI/AppConfig.cpp
index 0f77b1953..d7307cc32 100644
--- a/xs/src/slic3r/GUI/AppConfig.cpp
+++ b/xs/src/slic3r/GUI/AppConfig.cpp
@@ -60,6 +60,14 @@ void AppConfig::set_defaults()
if (get("remember_output_path").empty())
set("remember_output_path", "1");
+
+ // Remove legacy window positions/sizes
+ erase("", "main_frame_maximized");
+ erase("", "main_frame_pos");
+ erase("", "main_frame_size");
+ erase("", "object_settings_maximized");
+ erase("", "object_settings_pos");
+ erase("", "object_settings_size");
}
void AppConfig::load()
diff --git a/xs/src/slic3r/GUI/AppConfig.hpp b/xs/src/slic3r/GUI/AppConfig.hpp
index b742176ed..5af635a12 100644
--- a/xs/src/slic3r/GUI/AppConfig.hpp
+++ b/xs/src/slic3r/GUI/AppConfig.hpp
@@ -72,6 +72,14 @@ public:
bool has(const std::string &key) const
{ return this->has("", key); }
+ void erase(const std::string &section, const std::string &key)
+ {
+ auto it = m_storage.find(section);
+ if (it != m_storage.end()) {
+ it->second.erase(key);
+ }
+ }
+
void clear_section(const std::string &section)
{ m_storage[section].clear(); }
diff --git a/xs/src/slic3r/GUI/ConfigWizard.cpp b/xs/src/slic3r/GUI/ConfigWizard.cpp
index 9f736ded8..e784d8525 100644
--- a/xs/src/slic3r/GUI/ConfigWizard.cpp
+++ b/xs/src/slic3r/GUI/ConfigWizard.cpp
@@ -409,11 +409,10 @@ PageFirmware::PageFirmware(ConfigWizard *parent) :
void PageFirmware::apply_custom_config(DynamicPrintConfig &config)
{
- ConfigOptionEnum<GCodeFlavor> opt;
-
auto sel = gcode_picker->GetSelection();
- if (sel != wxNOT_FOUND && opt.deserialize(gcode_picker->GetString(sel).ToStdString())) {
- config.set_key_value("gcode_flavor", &opt);
+ if (sel >= 0 && sel < gcode_opt.enum_labels.size()) {
+ auto *opt = new ConfigOptionEnum<GCodeFlavor>(static_cast<GCodeFlavor>(sel));
+ config.set_key_value("gcode_flavor", opt);
}
}
@@ -871,10 +870,11 @@ ConfigWizard::ConfigWizard(wxWindow *parent, RunReason reason) :
// If the screen is smaller, resize wizrad to match, which will enable scrollbars.
auto wizard_size = GetSize();
unsigned width, height;
- GUI::get_current_screen_size(width, height);
- wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN)));
- wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN)));
- SetMinSize(wizard_size);
+ if (GUI::get_current_screen_size(this, width, height)) {
+ wizard_size.SetWidth(std::min(wizard_size.GetWidth(), (int)(width - 2 * DIALOG_MARGIN)));
+ wizard_size.SetHeight(std::min(wizard_size.GetHeight(), (int)(height - 2 * DIALOG_MARGIN)));
+ SetMinSize(wizard_size);
+ }
Fit();
p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &evt) { this->p->go_prev(); });
diff --git a/xs/src/slic3r/GUI/FirmwareDialog.cpp b/xs/src/slic3r/GUI/FirmwareDialog.cpp
index d0cd9f8cf..d5ac64d90 100644
--- a/xs/src/slic3r/GUI/FirmwareDialog.cpp
+++ b/xs/src/slic3r/GUI/FirmwareDialog.cpp
@@ -367,7 +367,7 @@ void FirmwareDialog::priv::wait_for_mmu_bootloader(unsigned retries)
auto ports = Utils::scan_serial_ports_extended();
ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) {
- return port.id_vendor != USB_VID_PRUSA && port.id_product != USB_PID_MMU_BOOT;
+ return port.id_vendor != USB_VID_PRUSA || port.id_product != USB_PID_MMU_BOOT;
}), ports.end());
if (ports.size() == 1) {
@@ -390,23 +390,22 @@ void FirmwareDialog::priv::mmu_reboot(const SerialPortInfo &port)
void FirmwareDialog::priv::lookup_port_mmu()
{
+ static const auto msg_not_found =
+ "The Multi Material Control device was not found.\n"
+ "If the device is connected, please press the Reset button next to the USB connector ...";
+
BOOST_LOG_TRIVIAL(info) << "Flashing MMU 2.0, looking for VID/PID 0x2c99/3 or 0x2c99/4 ...";
auto ports = Utils::scan_serial_ports_extended();
ports.erase(std::remove_if(ports.begin(), ports.end(), [=](const SerialPortInfo &port ) {
- return port.id_vendor != USB_VID_PRUSA &&
+ return port.id_vendor != USB_VID_PRUSA ||
port.id_product != USB_PID_MMU_BOOT &&
port.id_product != USB_PID_MMU_APP;
}), ports.end());
if (ports.size() == 0) {
BOOST_LOG_TRIVIAL(info) << "MMU 2.0 device not found, asking the user to press Reset and waiting for the device to show up ...";
-
- queue_status(_(L(
- "The Multi Material Control device was not found.\n"
- "If the device is connected, please press the Reset button next to the USB connector ..."
- )));
-
+ queue_status(_(L(msg_not_found)));
wait_for_mmu_bootloader(30);
} else if (ports.size() > 1) {
BOOST_LOG_TRIVIAL(error) << "Several VID/PID 0x2c99/3 devices found";
@@ -417,6 +416,13 @@ void FirmwareDialog::priv::lookup_port_mmu()
BOOST_LOG_TRIVIAL(info) << boost::format("Found VID/PID 0x2c99/4 at `%1%`, rebooting the device ...") % ports[0].port;
mmu_reboot(ports[0]);
wait_for_mmu_bootloader(10);
+
+ if (! port) {
+ // The device in bootloader mode was not found, inform the user and wait some more...
+ BOOST_LOG_TRIVIAL(info) << "MMU 2.0 bootloader device not found after reboot, asking the user to press Reset and waiting for the device to show up ...";
+ queue_status(_(L(msg_not_found)));
+ wait_for_mmu_bootloader(30);
+ }
} else {
port = ports[0];
}
@@ -702,7 +708,8 @@ FirmwareDialog::FirmwareDialog(wxWindow *parent) :
panel->SetSizer(vsizer);
auto *label_hex_picker = new wxStaticText(panel, wxID_ANY, _(L("Firmware image:")));
- p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY);
+ p->hex_picker = new wxFilePickerCtrl(panel, wxID_ANY, wxEmptyString, wxFileSelectorPromptStr,
+ "Hex files (*.hex)|*.hex|All files|*.*");
auto *label_port_picker = new wxStaticText(panel, wxID_ANY, _(L("Serial port:")));
p->port_picker = new wxComboBox(panel, wxID_ANY);
diff --git a/xs/src/slic3r/GUI/GLCanvas3D.cpp b/xs/src/slic3r/GUI/GLCanvas3D.cpp
index ae42f014b..cb3250916 100644
--- a/xs/src/slic3r/GUI/GLCanvas3D.cpp
+++ b/xs/src/slic3r/GUI/GLCanvas3D.cpp
@@ -1142,8 +1142,10 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent)
if (!gizmo->init())
return false;
+#if !ENABLE_MODELINSTANCE_3D_OFFSET
// temporary disable z grabber
gizmo->disable_grabber(2);
+#endif // !ENABLE_MODELINSTANCE_3D_OFFSET
m_gizmos.insert(GizmosMap::value_type(Move, gizmo));
@@ -2377,7 +2379,11 @@ void GLCanvas3D::update_gizmos_data()
ModelInstance* model_instance = model_object->instances[0];
if (model_instance != nullptr)
{
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ m_gizmos.set_position(model_instance->get_offset());
+#else
m_gizmos.set_position(Vec3d(model_instance->offset(0), model_instance->offset(1), 0.0));
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
m_gizmos.set_scale(model_instance->scaling_factor);
m_gizmos.set_angle_z(model_instance->rotation);
m_gizmos.set_flattening_data(model_object);
@@ -2495,6 +2501,11 @@ int GLCanvas3D::get_first_volume_id(int obj_idx) const
return -1;
}
+int GLCanvas3D::get_in_object_volume_id(int scene_vol_idx) const
+{
+ return ((0 <= scene_vol_idx) && (scene_vol_idx < (int)m_volumes.volumes.size())) ? m_volumes.volumes[scene_vol_idx]->volume_idx() : -1;
+}
+
void GLCanvas3D::reload_scene(bool force)
{
if ((m_canvas == nullptr) || (m_config == nullptr) || (m_model == nullptr))
@@ -5361,8 +5372,12 @@ void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
ModelObject* model_object = m_model->objects[obj_idx];
if (model_object != nullptr)
{
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ model_object->instances[instance_idx]->set_offset(volume->get_offset());
+#else
const Vec3d& offset = volume->get_offset();
model_object->instances[instance_idx]->offset = Vec2d(offset(0), offset(1));
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
model_object->invalidate_bounding_box();
update_position_values();
object_moved = true;
@@ -5409,6 +5424,7 @@ void GLCanvas3D::_on_select(int volume_idx, int object_idx)
}
m_on_select_object_callback.call(obj_id, vol_id);
+ Slic3r::GUI::select_current_volume(obj_id, vol_id);
}
std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& colors)
diff --git a/xs/src/slic3r/GUI/GLCanvas3D.hpp b/xs/src/slic3r/GUI/GLCanvas3D.hpp
index a200e7fdb..528f73fc1 100644
--- a/xs/src/slic3r/GUI/GLCanvas3D.hpp
+++ b/xs/src/slic3r/GUI/GLCanvas3D.hpp
@@ -105,7 +105,6 @@ class GLCanvas3D
void reset() { first_volumes.clear(); }
};
-public:
struct Camera
{
enum EType : unsigned char
@@ -441,7 +440,6 @@ public:
void render(const GLCanvas3D& canvas) const;
};
-private:
wxGLCanvas* m_canvas;
wxGLContext* m_context;
LegendTexture m_legend_texture;
@@ -605,6 +603,7 @@ public:
std::vector<int> load_object(const Model& model, int obj_idx);
int get_first_volume_id(int obj_idx) const;
+ int get_in_object_volume_id(int scene_vol_idx) const;
void reload_scene(bool force);
diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
index 3445d4b65..495f49425 100644
--- a/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
+++ b/xs/src/slic3r/GUI/GLCanvas3DManager.cpp
@@ -548,6 +548,12 @@ int GLCanvas3DManager::get_first_volume_id(wxGLCanvas* canvas, int obj_idx) cons
return (it != m_canvases.end()) ? it->second->get_first_volume_id(obj_idx) : -1;
}
+int GLCanvas3DManager::get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx) const
+{
+ CanvasesMap::const_iterator it = _get_canvas(canvas);
+ return (it != m_canvases.end()) ? it->second->get_in_object_volume_id(scene_vol_idx) : -1;
+}
+
void GLCanvas3DManager::reload_scene(wxGLCanvas* canvas, bool force)
{
CanvasesMap::iterator it = _get_canvas(canvas);
diff --git a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
index b808c022e..4922b6171 100644
--- a/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
+++ b/xs/src/slic3r/GUI/GLCanvas3DManager.hpp
@@ -138,6 +138,7 @@ public:
std::vector<int> load_object(wxGLCanvas* canvas, const Model* model, int obj_idx);
int get_first_volume_id(wxGLCanvas* canvas, int obj_idx) const;
+ int get_in_object_volume_id(wxGLCanvas* canvas, int scene_vol_idx) const;
void reload_scene(wxGLCanvas* canvas, bool force);
diff --git a/xs/src/slic3r/GUI/GLGizmo.cpp b/xs/src/slic3r/GUI/GLGizmo.cpp
index 4aa5ab32f..e23958c1d 100644
--- a/xs/src/slic3r/GUI/GLGizmo.cpp
+++ b/xs/src/slic3r/GUI/GLGizmo.cpp
@@ -20,90 +20,91 @@ static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f
namespace Slic3r {
namespace GUI {
- // returns the intersection of the given ray with the plane parallel to plane XY and passing through the given center
- // coordinates are local to the plane
- Vec3d intersection_on_plane_xy(const Linef3& ray, const Vec3d& center)
+// returns the intersection of the given ray with the plane parallel to plane XY and passing through the given center
+// coordinates are local to the plane
+Vec3d intersection_on_plane_xy(const Linef3& ray, const Vec3d& center)
+{
+ Transform3d m = Transform3d::Identity();
+ m.translate(-center);
+ Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
+ return Vec3d(mouse_pos_2d(0), mouse_pos_2d(1), 0.0);
+}
+
+// returns the intersection of the given ray with the plane parallel to plane XZ and passing through the given center
+// coordinates are local to the plane
+Vec3d intersection_on_plane_xz(const Linef3& ray, const Vec3d& center)
+{
+ Transform3d m = Transform3d::Identity();
+ m.rotate(Eigen::AngleAxisd(-0.5 * (double)PI, Vec3d::UnitX()));
+ m.translate(-center);
+ Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
+ return Vec3d(mouse_pos_2d(0), 0.0, mouse_pos_2d(1));
+}
+
+// returns the intersection of the given ray with the plane parallel to plane YZ and passing through the given center
+// coordinates are local to the plane
+Vec3d intersection_on_plane_yz(const Linef3& ray, const Vec3d& center)
+{
+ Transform3d m = Transform3d::Identity();
+ m.rotate(Eigen::AngleAxisd(-0.5f * (double)PI, Vec3d::UnitY()));
+ m.translate(-center);
+ Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
+
+ return Vec3d(0.0, mouse_pos_2d(1), -mouse_pos_2d(0));
+}
+
+// return an index:
+// 0 for plane XY
+// 1 for plane XZ
+// 2 for plane YZ
+// which indicates which plane is best suited for intersecting the given unit vector
+// giving precedence to the plane with the given index
+unsigned int select_best_plane(const Vec3d& unit_vector, unsigned int preferred_plane)
+{
+ unsigned int ret = preferred_plane;
+
+ // 1st checks if the given vector is not parallel to the given preferred plane
+ double dot_to_normal = 0.0;
+ switch (ret)
{
- Transform3d m = Transform3d::Identity();
- m.translate(-center);
- Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
- return Vec3d(mouse_pos_2d(0), mouse_pos_2d(1), 0.0);
+ case 0: // plane xy
+ {
+ dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitZ()));
+ break;
}
-
- // returns the intersection of the given ray with the plane parallel to plane XZ and passing through the given center
- // coordinates are local to the plane
- Vec3d intersection_on_plane_xz(const Linef3& ray, const Vec3d& center)
+ case 1: // plane xz
{
- Transform3d m = Transform3d::Identity();
- m.rotate(Eigen::AngleAxisd(-0.5 * (double)PI, Vec3d::UnitX()));
- m.translate(-center);
- Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
- return Vec3d(mouse_pos_2d(0), 0.0, mouse_pos_2d(1));
+ dot_to_normal = std::abs(unit_vector.dot(-Vec3d::UnitY()));
+ break;
}
-
- // returns the intersection of the given ray with the plane parallel to plane YZ and passing through the given center
- // coordinates are local to the plane
- Vec3d intersection_on_plane_yz(const Linef3& ray, const Vec3d& center)
+ case 2: // plane yz
{
- Transform3d m = Transform3d::Identity();
- m.rotate(Eigen::AngleAxisd(-0.5f * (double)PI, Vec3d::UnitY()));
- m.translate(-center);
- Vec2d mouse_pos_2d = to_2d(transform(ray, m).intersect_plane(0.0));
-
- return Vec3d(0.0, mouse_pos_2d(1), -mouse_pos_2d(0));
+ dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitX()));
+ break;
}
-
- // return an index:
- // 0 for plane XY
- // 1 for plane XZ
- // 2 for plane YZ
- // which indicates which plane is best suited for intersecting the given unit vector
- // giving precedence to the plane with the given index
- unsigned int select_best_plane(const Vec3d& unit_vector, unsigned int preferred_plane)
+ default:
{
- unsigned int ret = preferred_plane;
-
- // 1st checks if the given vector is not parallel to the given preferred plane
- double dot_to_normal = 0.0;
- switch (ret)
- {
- case 0: // plane xy
- {
- dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitZ()));
- break;
- }
- case 1: // plane xz
- {
- dot_to_normal = std::abs(unit_vector.dot(-Vec3d::UnitY()));
- break;
- }
- case 2: // plane yz
- {
- dot_to_normal = std::abs(unit_vector.dot(Vec3d::UnitX()));
- break;
- }
- default:
- {
- break;
- }
- }
-
- // if almost parallel, select the plane whose normal direction is closest to the given vector direction,
- // otherwise return the given preferred plane index
- if (dot_to_normal < 0.1)
- {
- typedef std::map<double, unsigned int> ProjsMap;
- ProjsMap projs_map;
- projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitZ())), 0)); // plane xy
- projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(-Vec3d::UnitY())), 1)); // plane xz
- projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitX())), 2)); // plane yz
- ret = projs_map.rbegin()->second;
- }
+ break;
+ }
+ }
- return ret;
+ // if almost parallel, select the plane whose normal direction is closest to the given vector direction,
+ // otherwise return the given preferred plane index
+ if (dot_to_normal < 0.1)
+ {
+ typedef std::map<double, unsigned int> ProjsMap;
+ ProjsMap projs_map;
+ projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitZ())), 0)); // plane xy
+ projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(-Vec3d::UnitY())), 1)); // plane xz
+ projs_map.insert(ProjsMap::value_type(std::abs(unit_vector.dot(Vec3d::UnitX())), 2)); // plane yz
+ ret = projs_map.rbegin()->second;
}
+
+ return ret;
+}
- const float GLGizmoBase::Grabber::HalfSize = 2.0f;
+const float GLGizmoBase::Grabber::SizeFactor = 0.025f;
+const float GLGizmoBase::Grabber::MinHalfSize = 1.5f;
const float GLGizmoBase::Grabber::DraggingScaleFactor = 1.25f;
GLGizmoBase::Grabber::Grabber()
@@ -117,7 +118,7 @@ GLGizmoBase::Grabber::Grabber()
color[2] = 1.0f;
}
-void GLGizmoBase::Grabber::render(bool hover) const
+void GLGizmoBase::Grabber::render(bool hover, const BoundingBoxf3& box) const
{
float render_color[3];
if (hover)
@@ -129,12 +130,15 @@ void GLGizmoBase::Grabber::render(bool hover) const
else
::memcpy((void*)render_color, (const void*)color, 3 * sizeof(float));
- render(render_color, true);
+ render(box, render_color, true);
}
-void GLGizmoBase::Grabber::render(const float* render_color, bool use_lighting) const
+void GLGizmoBase::Grabber::render(const BoundingBoxf3& box, const float* render_color, bool use_lighting) const
{
- float half_size = dragging ? HalfSize * DraggingScaleFactor : HalfSize;
+ float max_size = (float)box.max_size();
+ float half_size = dragging ? max_size * SizeFactor * DraggingScaleFactor : max_size * SizeFactor;
+ half_size = std::max(half_size, MinHalfSize);
+
if (use_lighting)
::glEnable(GL_LIGHTING);
@@ -291,16 +295,16 @@ float GLGizmoBase::picking_color_component(unsigned int id) const
return (float)color / 255.0f;
}
-void GLGizmoBase::render_grabbers() const
+void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const
{
for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
if (m_grabbers[i].enabled)
- m_grabbers[i].render(m_hover_id == i);
+ m_grabbers[i].render((m_hover_id == i), box);
}
}
-void GLGizmoBase::render_grabbers_for_picking() const
+void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const
{
for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i)
{
@@ -309,7 +313,7 @@ void GLGizmoBase::render_grabbers_for_picking() const
m_grabbers[i].color[0] = 1.0f;
m_grabbers[i].color[1] = 1.0f;
m_grabbers[i].color[2] = picking_color_component(i);
- m_grabbers[i].render_for_picking();
+ m_grabbers[i].render_for_picking(box);
}
}
}
@@ -440,7 +444,7 @@ void GLGizmoRotate::on_render(const BoundingBoxf3& box) const
if (m_hover_id != -1)
render_angle();
- render_grabber();
+ render_grabber(box);
::glPopMatrix();
}
@@ -452,7 +456,7 @@ void GLGizmoRotate::on_render_for_picking(const BoundingBoxf3& box) const
::glPushMatrix();
transform_to_local();
- render_grabbers_for_picking();
+ render_grabbers_for_picking(box);
::glPopMatrix();
}
@@ -544,7 +548,7 @@ void GLGizmoRotate::render_angle() const
::glEnd();
}
-void GLGizmoRotate::render_grabber() const
+void GLGizmoRotate::render_grabber(const BoundingBoxf3& box) const
{
double grabber_radius = (double)(m_radius + GrabberOffset);
m_grabbers[0].center = Vec3d(::cos(m_angle) * grabber_radius, ::sin(m_angle) * grabber_radius, 0.0);
@@ -558,7 +562,7 @@ void GLGizmoRotate::render_grabber() const
::glEnd();
::memcpy((void*)m_grabbers[0].color, (const void*)m_highlight_color, 3 * sizeof(float));
- render_grabbers();
+ render_grabbers(box);
}
void GLGizmoRotate::transform_to_local() const
@@ -689,6 +693,7 @@ void GLGizmoRotate3D::on_render(const BoundingBoxf3& box) const
}
const float GLGizmoScale3D::Offset = 5.0f;
+const Vec3d GLGizmoScale3D::OffsetVec = (double)GLGizmoScale3D::Offset * Vec3d::Ones();
GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent)
: GLGizmoBase(parent)
@@ -738,7 +743,7 @@ void GLGizmoScale3D::on_start_dragging(const BoundingBoxf3& box)
{
m_starting_drag_position = m_grabbers[m_hover_id].center;
m_show_starting_box = true;
- m_starting_box = box;
+ m_starting_box = BoundingBoxf3(box.min - OffsetVec, box.max + OffsetVec);
}
}
@@ -772,9 +777,7 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
::glEnable(GL_DEPTH_TEST);
- Vec3d offset_vec = (double)Offset * Vec3d::Ones();
-
- m_box = BoundingBoxf3(box.min - offset_vec, box.max + offset_vec);
+ m_box = BoundingBoxf3(box.min - OffsetVec, box.max + OffsetVec);
const Vec3d& center = m_box.center();
// x axis
@@ -829,7 +832,7 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
render_grabbers_connection(4, 5);
}
// draw grabbers
- render_grabbers();
+ render_grabbers(m_box);
}
else if ((m_hover_id == 0) || (m_hover_id == 1))
{
@@ -846,8 +849,8 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
::glColor3fv(m_grabbers[0].color);
render_grabbers_connection(0, 1);
// draw grabbers
- m_grabbers[0].render(true);
- m_grabbers[1].render(true);
+ m_grabbers[0].render(true, m_box);
+ m_grabbers[1].render(true, m_box);
}
else if ((m_hover_id == 2) || (m_hover_id == 3))
{
@@ -864,8 +867,8 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
::glColor3fv(m_grabbers[2].color);
render_grabbers_connection(2, 3);
// draw grabbers
- m_grabbers[2].render(true);
- m_grabbers[3].render(true);
+ m_grabbers[2].render(true, m_box);
+ m_grabbers[3].render(true, m_box);
}
else if ((m_hover_id == 4) || (m_hover_id == 5))
{
@@ -882,8 +885,8 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
::glColor3fv(m_grabbers[4].color);
render_grabbers_connection(4, 5);
// draw grabbers
- m_grabbers[4].render(true);
- m_grabbers[5].render(true);
+ m_grabbers[4].render(true, m_box);
+ m_grabbers[5].render(true, m_box);
}
else if (m_hover_id >= 6)
{
@@ -899,7 +902,7 @@ void GLGizmoScale3D::on_render(const BoundingBoxf3& box) const
// draw grabbers
for (int i = 6; i < 10; ++i)
{
- m_grabbers[i].render(true);
+ m_grabbers[i].render(true, m_box);
}
}
}
@@ -908,7 +911,7 @@ void GLGizmoScale3D::on_render_for_picking(const BoundingBoxf3& box) const
{
::glDisable(GL_DEPTH_TEST);
- render_grabbers_for_picking();
+ render_grabbers_for_picking(box);
}
void GLGizmoScale3D::render_box(const BoundingBoxf3& box) const
@@ -1029,6 +1032,7 @@ GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent)
, m_position(Vec3d::Zero())
, m_starting_drag_position(Vec3d::Zero())
, m_starting_box_center(Vec3d::Zero())
+ , m_starting_box_bottom_center(Vec3d::Zero())
{
}
@@ -1062,17 +1066,19 @@ void GLGizmoMove3D::on_start_dragging(const BoundingBoxf3& box)
{
m_starting_drag_position = m_grabbers[m_hover_id].center;
m_starting_box_center = box.center();
+ m_starting_box_bottom_center = box.center();
+ m_starting_box_bottom_center(2) = box.min(2);
}
}
void GLGizmoMove3D::on_update(const Linef3& mouse_ray)
{
if (m_hover_id == 0)
- m_position(0) = 2.0 * m_starting_box_center(0) + calc_displacement(1, mouse_ray) - m_starting_drag_position(0);
+ m_position(0) = 2.0 * m_starting_box_center(0) + calc_projection(X, 1, mouse_ray) - m_starting_drag_position(0);
else if (m_hover_id == 1)
- m_position(1) = 2.0 * m_starting_box_center(1) + calc_displacement(2, mouse_ray) - m_starting_drag_position(1);
+ m_position(1) = 2.0 * m_starting_box_center(1) + calc_projection(Y, 2, mouse_ray) - m_starting_drag_position(1);
else if (m_hover_id == 2)
- m_position(2) = 2.0 * m_starting_box_center(2) + calc_displacement(1, mouse_ray) - m_starting_drag_position(2);
+ m_position(2) = 2.0 * m_starting_box_bottom_center(2) + calc_projection(Z, 1, mouse_ray) - m_starting_drag_position(2);
}
void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const
@@ -1118,7 +1124,7 @@ void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const
}
// draw grabbers
- render_grabbers();
+ render_grabbers(box);
}
else
{
@@ -1130,7 +1136,7 @@ void GLGizmoMove3D::on_render(const BoundingBoxf3& box) const
::glEnd();
// draw grabber
- m_grabbers[m_hover_id].render(true);
+ m_grabbers[m_hover_id].render(true, box);
}
}
@@ -1138,43 +1144,43 @@ void GLGizmoMove3D::on_render_for_picking(const BoundingBoxf3& box) const
{
::glDisable(GL_DEPTH_TEST);
- render_grabbers_for_picking();
+ render_grabbers_for_picking(box);
}
-double GLGizmoMove3D::calc_displacement(unsigned int preferred_plane_id, const Linef3& mouse_ray) const
+double GLGizmoMove3D::calc_projection(Axis axis, unsigned int preferred_plane_id, const Linef3& mouse_ray) const
{
- double displacement = 0.0;
+ double projection = 0.0;
- Vec3d starting_vec = m_starting_drag_position - m_starting_box_center;
+ Vec3d starting_vec = (axis == Z) ? m_starting_drag_position - m_starting_box_bottom_center : m_starting_drag_position - m_starting_box_center;
double len_starting_vec = starting_vec.norm();
if (len_starting_vec == 0.0)
- return displacement;
+ return projection;
Vec3d starting_vec_dir = starting_vec.normalized();
Vec3d mouse_dir = mouse_ray.unit_vector();
unsigned int plane_id = select_best_plane(mouse_dir, preferred_plane_id);
- switch (plane_id)
+ switch (plane_id)
{
case 0:
{
- displacement = starting_vec_dir.dot(intersection_on_plane_xy(mouse_ray, m_starting_box_center));
+ projection = starting_vec_dir.dot(intersection_on_plane_xy(mouse_ray, (axis == Z) ? m_starting_box_bottom_center : m_starting_box_center));
break;
}
case 1:
{
- displacement = starting_vec_dir.dot(intersection_on_plane_xz(mouse_ray, m_starting_box_center));
+ projection = starting_vec_dir.dot(intersection_on_plane_xz(mouse_ray, (axis == Z) ? m_starting_box_bottom_center : m_starting_box_center));
break;
}
case 2:
{
- displacement = starting_vec_dir.dot(intersection_on_plane_yz(mouse_ray, m_starting_box_center));
+ projection = starting_vec_dir.dot(intersection_on_plane_yz(mouse_ray, (axis == Z) ? m_starting_box_bottom_center : m_starting_box_center));
break;
}
}
- return displacement;
+ return projection;
}
GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent)
@@ -1230,10 +1236,19 @@ void GLGizmoFlatten::on_render(const BoundingBoxf3& box) const
else
::glColor4f(0.9f, 0.9f, 0.9f, 0.5f);
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ for (Vec3d offset : m_instances_positions) {
+ offset += dragged_offset;
+#else
for (Vec2d offset : m_instances_positions) {
offset += to_2d(dragged_offset);
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
::glPushMatrix();
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ ::glTranslated(offset(0), offset(1), offset(2));
+#else
::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f);
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
::glBegin(GL_POLYGON);
for (const Vec3d& vertex : m_planes[i].vertices)
::glVertex3f((GLfloat)vertex(0), (GLfloat)vertex(1), (GLfloat)vertex(2));
@@ -1252,9 +1267,17 @@ void GLGizmoFlatten::on_render_for_picking(const BoundingBoxf3& box) const
for (unsigned int i = 0; i < m_planes.size(); ++i)
{
::glColor3f(1.0f, 1.0f, picking_color_component(i));
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ for (const Vec3d& offset : m_instances_positions) {
+#else
for (const Vec2d& offset : m_instances_positions) {
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
::glPushMatrix();
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ ::glTranslated(offset(0), offset(1), offset(2));
+#else
::glTranslatef((GLfloat)offset(0), (GLfloat)offset(1), 0.0f);
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
::glBegin(GL_POLYGON);
for (const Vec3d& vertex : m_planes[i].vertices)
::glVertex3f((GLfloat)vertex(0), (GLfloat)vertex(1), (GLfloat)vertex(2));
@@ -1272,7 +1295,11 @@ void GLGizmoFlatten::set_flattening_data(const ModelObject* model_object)
if (m_model_object && !m_model_object->instances.empty()) {
m_instances_positions.clear();
for (const auto* instance : m_model_object->instances)
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ m_instances_positions.emplace_back(instance->get_offset());
+#else
m_instances_positions.emplace_back(instance->offset);
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
}
if (is_plane_update_necessary())
diff --git a/xs/src/slic3r/GUI/GLGizmo.hpp b/xs/src/slic3r/GUI/GLGizmo.hpp
index 8760b1c7d..2430b5af5 100644
--- a/xs/src/slic3r/GUI/GLGizmo.hpp
+++ b/xs/src/slic3r/GUI/GLGizmo.hpp
@@ -22,7 +22,8 @@ class GLGizmoBase
protected:
struct Grabber
{
- static const float HalfSize;
+ static const float SizeFactor;
+ static const float MinHalfSize;
static const float DraggingScaleFactor;
Vec3d center;
@@ -33,11 +34,11 @@ protected:
Grabber();
- void render(bool hover) const;
- void render_for_picking() const { render(color, false); }
+ void render(bool hover, const BoundingBoxf3& box) const;
+ void render_for_picking(const BoundingBoxf3& box) const { render(box, color, false); }
private:
- void render(const float* render_color, bool use_lighting) const;
+ void render(const BoundingBoxf3& box, const float* render_color, bool use_lighting) const;
void render_face(float half_size) const;
};
@@ -109,8 +110,8 @@ protected:
virtual void on_render_for_picking(const BoundingBoxf3& box) const = 0;
float picking_color_component(unsigned int id) const;
- void render_grabbers() const;
- void render_grabbers_for_picking() const;
+ void render_grabbers(const BoundingBoxf3& box) const;
+ void render_grabbers_for_picking(const BoundingBoxf3& box) const;
void set_tooltip(const std::string& tooltip) const;
std::string format(float value, unsigned int decimals) const;
@@ -163,7 +164,7 @@ private:
void render_snap_radii() const;
void render_reference_radius() const;
void render_angle() const;
- void render_grabber() const;
+ void render_grabber(const BoundingBoxf3& box) const;
void transform_to_local() const;
// returns the intersection of the mouse ray with the plane perpendicular to the gizmo axis, in local coordinate
@@ -234,6 +235,7 @@ protected:
class GLGizmoScale3D : public GLGizmoBase
{
static const float Offset;
+ static const Vec3d OffsetVec;
mutable BoundingBoxf3 m_box;
@@ -285,6 +287,7 @@ class GLGizmoMove3D : public GLGizmoBase
Vec3d m_position;
Vec3d m_starting_drag_position;
Vec3d m_starting_box_center;
+ Vec3d m_starting_box_bottom_center;
public:
explicit GLGizmoMove3D(GLCanvas3D& parent);
@@ -300,7 +303,7 @@ protected:
virtual void on_render_for_picking(const BoundingBoxf3& box) const;
private:
- double calc_displacement(unsigned int preferred_plane_id, const Linef3& mouse_ray) const;
+ double calc_projection(Axis axis, unsigned int preferred_plane_id, const Linef3& mouse_ray) const;
};
class GLGizmoFlatten : public GLGizmoBase
@@ -326,7 +329,11 @@ private:
SourceDataSummary m_source_data;
std::vector<PlaneData> m_planes;
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ Pointf3s m_instances_positions;
+#else
std::vector<Vec2d> m_instances_positions;
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
Vec3d m_starting_center;
const ModelObject* m_model_object = nullptr;
diff --git a/xs/src/slic3r/GUI/GUI.cpp b/xs/src/slic3r/GUI/GUI.cpp
index 7ce858aec..decdb5691 100644
--- a/xs/src/slic3r/GUI/GUI.cpp
+++ b/xs/src/slic3r/GUI/GUI.cpp
@@ -7,6 +7,7 @@
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
+#include <boost/lexical_cast.hpp>
#if __APPLE__
#import <IOKit/pwr_mgt/IOPMLib.h>
@@ -121,7 +122,6 @@ wxPanel *g_wxPlater = nullptr;
AppConfig *g_AppConfig = nullptr;
PresetBundle *g_PresetBundle= nullptr;
PresetUpdater *g_PresetUpdater = nullptr;
-_3DScene *g_3DScene = nullptr;
wxColour g_color_label_modified;
wxColour g_color_label_sys;
wxColour g_color_label_default;
@@ -141,14 +141,9 @@ wxButton* g_wiping_dialog_button = nullptr;
//showed/hided controls according to the view mode
wxWindow *g_right_panel = nullptr;
wxBoxSizer *g_frequently_changed_parameters_sizer = nullptr;
-wxBoxSizer *g_expert_mode_part_sizer = nullptr;
-wxBoxSizer *g_scrolled_window_sizer = nullptr;
+wxBoxSizer *g_info_sizer = nullptr;
wxBoxSizer *g_object_list_sizer = nullptr;
-wxButton *g_btn_export_gcode = nullptr;
-wxButton *g_btn_export_stl = nullptr;
-wxButton *g_btn_reslice = nullptr;
-wxButton *g_btn_print = nullptr;
-wxButton *g_btn_send_gcode = nullptr;
+std::vector<wxButton*> g_buttons;
wxStaticBitmap *g_manifold_warning_icon = nullptr;
bool g_show_print_info = false;
bool g_show_manifold_warning_icon = false;
@@ -241,27 +236,36 @@ void set_preset_updater(PresetUpdater *updater)
g_PresetUpdater = updater;
}
-void set_3DScene(_3DScene *scene)
+enum ActionButtons
{
- g_3DScene = scene;
-}
+ abExportGCode,
+ abReslice,
+ abPrint,
+ abSendGCode,
+};
-void set_objects_from_perl( wxWindow* parent, wxBoxSizer *frequently_changed_parameters_sizer,
- wxBoxSizer *expert_mode_part_sizer, wxBoxSizer *scrolled_window_sizer,
+void set_objects_from_perl( wxWindow* parent,
+ wxBoxSizer *frequently_changed_parameters_sizer,
+ wxBoxSizer *info_sizer,
wxButton *btn_export_gcode,
- wxButton *btn_export_stl, wxButton *btn_reslice,
- wxButton *btn_print, wxButton *btn_send_gcode,
+ wxButton *btn_reslice,
+ wxButton *btn_print,
+ wxButton *btn_send_gcode,
wxStaticBitmap *manifold_warning_icon)
{
- g_right_panel = parent;
+ g_right_panel = parent->GetParent();
g_frequently_changed_parameters_sizer = frequently_changed_parameters_sizer;
- g_expert_mode_part_sizer = expert_mode_part_sizer;
- g_scrolled_window_sizer = scrolled_window_sizer;
- g_btn_export_gcode = btn_export_gcode;
- g_btn_export_stl = btn_export_stl;
- g_btn_reslice = btn_reslice;
- g_btn_print = btn_print;
- g_btn_send_gcode = btn_send_gcode;
+ g_info_sizer = info_sizer;
+
+ g_buttons.push_back(btn_export_gcode);
+ g_buttons.push_back(btn_reslice);
+ g_buttons.push_back(btn_print);
+ g_buttons.push_back(btn_send_gcode);
+
+ // Update font style for buttons
+ for (auto btn : g_buttons)
+ btn->SetFont(bold_font());
+
g_manifold_warning_icon = manifold_warning_icon;
}
@@ -273,6 +277,15 @@ void set_show_print_info(bool show)
void set_show_manifold_warning_icon(bool show)
{
g_show_manifold_warning_icon = show;
+ if (!g_manifold_warning_icon)
+ return;
+
+ // update manifold_warning_icon showing
+ if (show && !g_info_sizer->IsShown(static_cast<size_t>(0)))
+ g_show_manifold_warning_icon = false;
+
+ g_manifold_warning_icon->Show(g_show_manifold_warning_icon);
+ g_manifold_warning_icon->GetParent()->Layout();
}
void set_objects_list_sizer(wxBoxSizer *objects_list_sizer){
@@ -963,12 +976,11 @@ wxString from_u8(const std::string &str)
return wxString::FromUTF8(str.c_str());
}
-void add_expert_mode_part( wxWindow* parent, wxBoxSizer* sizer,
- Model &model,
- int event_object_selection_changed,
- int event_object_settings_changed,
- int event_remove_object,
- int event_update_scene)
+void set_model_events_from_perl(Model &model,
+ int event_object_selection_changed,
+ int event_object_settings_changed,
+ int event_remove_object,
+ int event_update_scene)
{
set_event_object_selection_changed(event_object_selection_changed);
set_event_object_settings_changed(event_object_settings_changed);
@@ -1105,7 +1117,7 @@ void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFl
};
optgroup->append_line(line);
- sizer->Add(optgroup->sizer, 0, wxEXPAND | wxBOTTOM, 2);
+ sizer->Add(optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT, 2);
m_optgroups.push_back(optgroup);// ogFrequentlyChangingParameters
@@ -1132,24 +1144,23 @@ void show_frequently_changed_parameters(bool show)
void show_buttons(bool show)
{
- g_btn_export_stl->Show(show);
- g_btn_reslice->Show(show);
+ g_buttons[abReslice]->Show(show);
for (size_t i = 0; i < g_wxTabPanel->GetPageCount(); ++i) {
TabPrinter *tab = dynamic_cast<TabPrinter*>(g_wxTabPanel->GetPage(i));
if (!tab)
continue;
if (g_PresetBundle->printers.get_selected_preset().printer_technology() == ptFFF) {
- g_btn_print->Show(show && !tab->m_config->opt_string("serial_port").empty());
- g_btn_send_gcode->Show(show && !tab->m_config->opt_string("print_host").empty());
+ g_buttons[abPrint]->Show(show && !tab->m_config->opt_string("serial_port").empty());
+ g_buttons[abSendGCode]->Show(show && !tab->m_config->opt_string("print_host").empty());
}
break;
}
}
-void show_info_sizer(bool show)
+void show_info_sizer(const bool show)
{
- g_scrolled_window_sizer->Show(static_cast<size_t>(0), show);
- g_scrolled_window_sizer->Show(1, show && g_show_print_info);
+ g_info_sizer->Show(static_cast<size_t>(0), show);
+ g_info_sizer->Show(1, show && g_show_print_info);
g_manifold_warning_icon->Show(show && g_show_manifold_warning_icon);
}
@@ -1162,31 +1173,22 @@ void show_object_name(bool show)
void update_mode()
{
- wxWindowUpdateLocker noUpdates(g_right_panel);
-
- // TODO There is a not the best place of it!
- //*** Update style of the "Export G-code" button****
- if (g_btn_export_gcode->GetFont() != bold_font()){
- g_btn_export_gcode->SetBackgroundColour(wxColour(252, 77, 1));
- g_btn_export_gcode->SetFont(bold_font());
- }
- // ***********************************
+ wxWindowUpdateLocker noUpdates(g_right_panel->GetParent());
ConfigMenuIDs mode = get_view_mode();
-// show_frequently_changed_parameters(mode >= ConfigMenuModeRegular);
-// g_expert_mode_part_sizer->Show(mode == ConfigMenuModeExpert);
g_object_list_sizer->Show(mode == ConfigMenuModeExpert);
show_info_sizer(mode == ConfigMenuModeExpert);
show_buttons(mode == ConfigMenuModeExpert);
show_object_name(mode == ConfigMenuModeSimple);
+ show_manipulation_sizer(mode == ConfigMenuModeSimple);
// TODO There is a not the best place of it!
// *** Update showing of the collpane_settings
// show_collpane_settings(mode == ConfigMenuModeExpert);
// *************************
g_right_panel->Layout();
- g_right_panel->GetParent()->GetParent()->Layout();
+ g_right_panel->GetParent()->Layout();
}
bool is_expert_mode(){
@@ -1254,12 +1256,77 @@ int get_export_option(wxFileDialog* dlg)
}
-void get_current_screen_size(unsigned &width, unsigned &height)
+bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height)
{
- wxDisplay display(wxDisplay::GetFromWindow(g_wxMainFrame));
+ const auto idx = wxDisplay::GetFromWindow(window);
+ if (idx == wxNOT_FOUND) {
+ return false;
+ }
+
+ wxDisplay display(idx);
const auto disp_size = display.GetClientArea();
width = disp_size.GetWidth();
height = disp_size.GetHeight();
+
+ return true;
+}
+
+void save_window_size(wxTopLevelWindow *window, const std::string &name)
+{
+ const wxSize size = window->GetSize();
+ const wxPoint pos = window->GetPosition();
+ const auto maximized = window->IsMaximized() ? "1" : "0";
+
+ g_AppConfig->set((boost::format("window_%1%_size") % name).str(), (boost::format("%1%;%2%") % size.GetWidth() % size.GetHeight()).str());
+ g_AppConfig->set((boost::format("window_%1%_maximized") % name).str(), maximized);
+}
+
+void restore_window_size(wxTopLevelWindow *window, const std::string &name)
+{
+ // XXX: This still doesn't behave nicely in some situations (mostly on Linux).
+ // The problem is that it's hard to obtain window position with respect to screen geometry reliably
+ // from wxWidgets. Sometimes wxWidgets claim a window is located on a different screen than on which
+ // it's actually visible. I suspect this has something to do with window initialization (maybe we
+ // restore window geometry too early), but haven't yet found a workaround.
+
+ const auto display_idx = wxDisplay::GetFromWindow(window);
+ if (display_idx == wxNOT_FOUND) { return; }
+
+ const auto display = wxDisplay(display_idx).GetClientArea();
+ std::vector<std::string> pair;
+
+ try {
+ const auto key_size = (boost::format("window_%1%_size") % name).str();
+ if (g_AppConfig->has(key_size)) {
+ if (unescape_strings_cstyle(g_AppConfig->get(key_size), pair) && pair.size() == 2) {
+ auto width = boost::lexical_cast<int>(pair[0]);
+ auto height = boost::lexical_cast<int>(pair[1]);
+
+ window->SetSize(width, height);
+ }
+ }
+ } catch(const boost::bad_lexical_cast &) {}
+
+ // Maximizing should be the last thing to do.
+ // This ensure the size and position are sane when the user un-maximizes the window.
+ const auto key_maximized = (boost::format("window_%1%_maximized") % name).str();
+ if (g_AppConfig->get(key_maximized) == "1") {
+ window->Maximize(true);
+ }
+}
+
+void enable_action_buttons(bool enable)
+{
+ if (g_buttons.empty())
+ return;
+
+ // Update background colour for buttons
+ const wxColour bgrd_color = enable ? wxColour(224, 224, 224/*255, 96, 0*/) : wxColour(204, 204, 204);
+
+ for (auto btn : g_buttons) {
+ btn->Enable(enable);
+ btn->SetBackgroundColour(bgrd_color);
+ }
}
void about()
diff --git a/xs/src/slic3r/GUI/GUI.hpp b/xs/src/slic3r/GUI/GUI.hpp
index 05ed580a3..02327b325 100644
--- a/xs/src/slic3r/GUI/GUI.hpp
+++ b/xs/src/slic3r/GUI/GUI.hpp
@@ -27,6 +27,7 @@ class wxButton;
class wxFileDialog;
class wxStaticBitmap;
class wxFont;
+class wxTopLevelWindow;
namespace Slic3r {
@@ -38,7 +39,6 @@ class AppConfig;
class PresetUpdater;
class DynamicPrintConfig;
class TabIface;
-class _3DScene;
#define _(s) Slic3r::GUI::I18N::translate((s))
@@ -106,13 +106,10 @@ void set_plater(wxPanel *plater);
void set_app_config(AppConfig *app_config);
void set_preset_bundle(PresetBundle *preset_bundle);
void set_preset_updater(PresetUpdater *updater);
-void set_3DScene(_3DScene *scene);
void set_objects_from_perl( wxWindow* parent,
wxBoxSizer *frequently_changed_parameters_sizer,
- wxBoxSizer *expert_mode_part_sizer,
- wxBoxSizer *scrolled_window_sizer,
+ wxBoxSizer *info_sizer,
wxButton *btn_export_gcode,
- wxButton *btn_export_stl,
wxButton *btn_reslice,
wxButton *btn_print,
wxButton *btn_send_gcode,
@@ -196,6 +193,8 @@ bool select_language(wxArrayString & names, wxArrayLong & identifiers);
// update right panel of the Plater according to view mode
void update_mode();
+void show_info_sizer(const bool show);
+
std::vector<Tab *>& get_tabs_list();
bool checked_tab(Tab* tab);
void delete_tab_from_list(Tab* tab);
@@ -214,12 +213,11 @@ wxString L_str(const std::string &str);
// Return wxString from std::string in UTF8
wxString from_u8(const std::string &str);
-void add_expert_mode_part( wxWindow* parent, wxBoxSizer* sizer,
- Model &model,
- int event_object_selection_changed,
- int event_object_settings_changed,
- int event_remove_object,
- int event_update_scene);
+void set_model_events_from_perl(Model &model,
+ int event_object_selection_changed,
+ int event_object_settings_changed,
+ int event_remove_object,
+ int event_update_scene);
void add_frequently_changed_parameters(wxWindow* parent, wxBoxSizer* sizer, wxFlexGridSizer* preset_sizer);
// Update view mode according to selected menu
void update_mode();
@@ -236,7 +234,15 @@ void add_export_option(wxFileDialog* dlg, const std::string& format);
int get_export_option(wxFileDialog* dlg);
// Returns the dimensions of the screen on which the main frame is displayed
-void get_current_screen_size(unsigned &width, unsigned &height);
+bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height);
+
+// Save window size and maximized status into AppConfig
+void save_window_size(wxTopLevelWindow *window, const std::string &name);
+// Restore the above
+void restore_window_size(wxTopLevelWindow *window, const std::string &name);
+
+// Update buttons view according to enable/disable
+void enable_action_buttons(bool enable);
// Display an About dialog
extern void about();
diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp
index 0c7b7a5d8..ae34359ce 100644
--- a/xs/src/slic3r/GUI/GUI_ObjectParts.cpp
+++ b/xs/src/slic3r/GUI/GUI_ObjectParts.cpp
@@ -13,6 +13,9 @@
#include "Geometry.hpp"
#include "slic3r/Utils/FixModelByWin10.hpp"
+#include <wx/glcanvas.h>
+#include "3DScene.hpp"
+
namespace Slic3r
{
namespace GUI
@@ -23,10 +26,12 @@ wxSizer *m_sizer_object_movers = nullptr;
wxDataViewCtrl *m_objects_ctrl = nullptr;
PrusaObjectDataViewModel *m_objects_model = nullptr;
wxCollapsiblePane *m_collpane_settings = nullptr;
+PrusaDoubleSlider *m_slider = nullptr;
+wxGLCanvas *m_preview_canvas = nullptr;
-wxIcon m_icon_modifiermesh;
-wxIcon m_icon_solidmesh;
-wxIcon m_icon_manifold_warning;
+wxBitmap m_icon_modifiermesh;
+wxBitmap m_icon_solidmesh;
+wxBitmap m_icon_manifold_warning;
wxBitmap m_bmp_cog;
wxBitmap m_bmp_split;
@@ -64,8 +69,6 @@ bool m_part_settings_changed = false;
wxString g_selected_extruder = "";
#endif //__WXOSX__
-// typedef std::map<std::string, std::string> t_category_icon;
-typedef std::map<std::string, wxBitmap> t_category_icon;
inline t_category_icon& get_category_icon() {
static t_category_icon CATEGORY_ICON;
if (CATEGORY_ICON.empty()){
@@ -138,11 +141,11 @@ void set_objects_from_model(Model &model) {
}
void init_mesh_icons(){
- m_icon_modifiermesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
- m_icon_solidmesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
+ m_icon_modifiermesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("lambda.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("plugin.png")), wxBITMAP_TYPE_PNG);
+ m_icon_solidmesh = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
// init icon for manifold warning
- m_icon_manifold_warning = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG);
+ m_icon_manifold_warning = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("exclamation_mark_.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("error.png")), wxBITMAP_TYPE_PNG);
// init bitmap for "Split to sub-objects" context menu
m_bmp_split = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("split.png")), wxBITMAP_TYPE_PNG);
@@ -233,7 +236,7 @@ wxDataViewColumn* object_ctrl_create_extruder_column(int extruders_count)
void create_objects_ctrl(wxWindow* win, wxBoxSizer*& objects_sz)
{
m_objects_ctrl = new wxDataViewCtrl(win, wxID_ANY, wxDefaultPosition, wxDefaultSize);
- m_objects_ctrl->SetInitialSize(wxSize(-1, 150)); // TODO - Set correct height according to the opened/closed objects
+ m_objects_ctrl->SetMinSize(wxSize(-1, 150)); // TODO - Set correct height according to the opened/closed objects
objects_sz = new wxBoxSizer(wxVERTICAL);
objects_sz->Add(m_objects_ctrl, 1, wxGROW | wxLEFT, 20);
@@ -245,9 +248,10 @@ void create_objects_ctrl(wxWindow* win, wxBoxSizer*& objects_sz)
m_objects_ctrl->EnableDropTarget(wxDF_UNICODETEXT);
#endif // wxUSE_DRAG_AND_DROP && wxUSE_UNICODE
- // column 0(Icon+Text) of the view control:
- m_objects_ctrl->AppendIconTextColumn(_(L("Name")), 0, wxDATAVIEW_CELL_INERT, 200,
- wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE);
+ // column 0(Icon+Text) of the view control:
+ // And Icon can be consisting of several bitmaps
+ m_objects_ctrl->AppendColumn(new wxDataViewColumn(_(L("Name")), new PrusaBitmapTextRenderer(),
+ 0, 200, wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE));
// column 1 of the view control:
m_objects_ctrl->AppendTextColumn(_(L("Copy")), 1, wxDATAVIEW_CELL_INERT, 45,
@@ -278,7 +282,7 @@ wxBoxSizer* create_objects_list(wxWindow *win)
m_objects_ctrl->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, [](wxDataViewEvent& event) {
object_ctrl_context_menu();
- event.Skip();
+// event.Skip();
});
m_objects_ctrl->Bind(wxEVT_CHAR, [](wxKeyEvent& event) { object_ctrl_key_event(event); }); // doesn't work on OSX
@@ -316,15 +320,15 @@ wxBoxSizer* create_edit_object_buttons(wxWindow* win)
//*** button's functions
btn_load_part->Bind(wxEVT_BUTTON, [win](wxEvent&) {
- on_btn_load(win);
+// on_btn_load(win);
});
btn_load_modifier->Bind(wxEVT_BUTTON, [win](wxEvent&) {
- on_btn_load(win, true);
+// on_btn_load(win, true);
});
btn_load_lambda_modifier->Bind(wxEVT_BUTTON, [win](wxEvent&) {
- on_btn_load(win, true, true);
+// on_btn_load(win, true, true);
});
btn_delete ->Bind(wxEVT_BUTTON, [](wxEvent&) { on_btn_del(); });
@@ -660,7 +664,7 @@ void add_object_to_list(const std::string &name, ModelObject* model_object)
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed +
stats.facets_added + stats.facets_reversed + stats.backwards_edges;
if (errors > 0) {
- const wxDataViewIconText data(item_name, m_icon_manifold_warning);
+ const PrusaDataViewBitmapText data(item_name, m_icon_manifold_warning);
wxVariant variant;
variant << data;
m_objects_model->SetValue(variant, item, 0);
@@ -724,15 +728,26 @@ void select_current_object(int idx)
{
g_prevent_list_events = true;
m_objects_ctrl->UnselectAll();
- if (idx < 0) {
- g_prevent_list_events = false;
- return;
- }
- m_objects_ctrl->Select(m_objects_model->GetItemById(idx));
+ if (idx>=0)
+ m_objects_ctrl->Select(m_objects_model->GetItemById(idx));
part_selection_changed();
g_prevent_list_events = false;
}
+void select_current_volume(int idx, int vol_idx)
+{
+ if (vol_idx < 0) {
+ select_current_object(idx);
+ return;
+ }
+ g_prevent_list_events = true;
+ m_objects_ctrl->UnselectAll();
+ if (idx >= 0)
+ m_objects_ctrl->Select(m_objects_model->GetItemByVolumeId(idx, vol_idx));
+ part_selection_changed();
+ g_prevent_list_events = false;
+}
+
void remove()
{
auto item = m_objects_ctrl->GetSelection();
@@ -758,8 +773,17 @@ void object_ctrl_selection_changed()
if (m_event_object_selection_changed > 0) {
wxCommandEvent event(m_event_object_selection_changed);
- event.SetInt(int(m_objects_model->GetParent(m_objects_ctrl->GetSelection()) != wxDataViewItem(0)));
- event.SetId(m_selected_object_id);
+ event.SetId(m_selected_object_id); // set $obj_idx
+ const wxDataViewItem item = m_objects_ctrl->GetSelection();
+ if (!item || m_objects_model->GetParent(item) == wxDataViewItem(0))
+ event.SetInt(-1); // set $vol_idx
+ else {
+ const int vol_idx = m_objects_model->GetVolumeIdByItem(item);
+ if (vol_idx == -2) // is settings item
+ event.SetInt(m_objects_model->GetVolumeIdByItem(m_objects_model->GetParent(item))); // set $vol_idx
+ else
+ event.SetInt(vol_idx);
+ }
get_main_frame()->ProcessWindowEvent(event);
}
@@ -772,9 +796,9 @@ void object_ctrl_context_menu()
{
wxDataViewItem item;
wxDataViewColumn* col;
- printf("object_ctrl_context_menu\n");
+// printf("object_ctrl_context_menu\n");
const wxPoint pt = get_mouse_position_in_control();
- printf("mouse_position_in_control: x = %d, y = %d\n", pt.x, pt.y);
+// printf("mouse_position_in_control: x = %d, y = %d\n", pt.x, pt.y);
m_objects_ctrl->HitTest(pt, item, col);
if (!item)
#ifdef __WXOSX__ // #ys_FIXME temporary workaround for OSX
@@ -788,9 +812,9 @@ void object_ctrl_context_menu()
#else
return;
#endif // __WXOSX__
- printf("item exists\n");
+// printf("item exists\n");
const wxString title = col->GetTitle();
- printf("title = *%s*\n", title.data().AsChar());
+// printf("title = *%s*\n", title.data().AsChar());
if (title == " ")
show_context_menu();
@@ -836,6 +860,15 @@ void object_ctrl_item_value_change(wxDataViewEvent& event)
}
}
+void show_manipulation_og(const bool show)
+{
+ wxGridSizer* grid_sizer = get_optgroup(ogFrequentlyObjectSettings)->get_grid_sizer();
+ if (show == grid_sizer->IsShown(2))
+ return;
+ for (size_t id = 2; id < 12; id++)
+ grid_sizer->Show(id, show);
+}
+
//update_optgroup
void update_settings_list()
{
@@ -856,14 +889,19 @@ void update_settings_list()
m_option_sizer->Clear(true);
- if (m_config)
+ bool show_manipulations = true;
+ const auto item = m_objects_ctrl->GetSelection();
+ if (m_config && m_objects_model->IsSettingsItem(item))
{
auto extra_column = [](wxWindow* parent, const Line& line)
{
auto opt_key = (line.get_options())[0].opt_id; //we assume that we have one option per line
- auto btn = new wxBitmapButton(parent, wxID_ANY, wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("erase.png")), wxBITMAP_TYPE_PNG),
+ auto btn = new wxBitmapButton(parent, wxID_ANY, wxBitmap(from_u8(var("colorchange_delete_on.png")), wxBITMAP_TYPE_PNG),
wxDefaultPosition, wxDefaultSize, wxBORDER_NONE);
+#ifdef __WXMSW__
+ btn->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
+#endif // __WXMSW__
btn->Bind(wxEVT_BUTTON, [opt_key](wxEvent &event){
(*m_config)->erase(opt_key);
wxTheApp->CallAfter([]() { update_settings_list(); });
@@ -873,60 +911,75 @@ void update_settings_list()
std::map<std::string, std::vector<std::string>> cat_options;
auto opt_keys = (*m_config)->keys();
- if (opt_keys.size() == 1 && opt_keys[0] == "extruder")
- return;
-
- auto extruders_cnt = get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA ? 1 :
- get_preset_bundle()->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
-
- for (auto& opt_key : opt_keys) {
- auto category = (*m_config)->def()->get(opt_key)->category;
- if (category.empty() ||
- (category == "Extruders" && extruders_cnt==1)) continue;
-
- std::vector< std::string > new_category;
-
- auto& cat_opt = cat_options.find(category) == cat_options.end() ? new_category : cat_options.at(category);
- cat_opt.push_back(opt_key);
- if (cat_opt.size() == 1)
- cat_options[category] = cat_opt;
- }
-
-
- m_og_settings.resize(0);
- for (auto& cat : cat_options) {
- if (cat.second.size() == 1 && cat.second[0] == "extruder")
- continue;
-
- auto optgroup = std::make_shared<ConfigOptionsGroup>(parent, cat.first, *m_config, false, ogDEFAULT, extra_column);
- optgroup->label_width = 100;
- optgroup->sidetext_width = 70;
+ m_og_settings.resize(0);
+ std::vector<std::string> categories;
+ if (!(opt_keys.size() == 1 && opt_keys[0] == "extruder"))// return;
+ {
+ auto extruders_cnt = get_preset_bundle()->printers.get_selected_preset().printer_technology() == ptSLA ? 1 :
+ get_preset_bundle()->printers.get_edited_preset().config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
+
+ for (auto& opt_key : opt_keys) {
+ auto category = (*m_config)->def()->get(opt_key)->category;
+ if (category.empty() ||
+ (category == "Extruders" && extruders_cnt == 1)) continue;
+
+ std::vector< std::string > new_category;
+
+ auto& cat_opt = cat_options.find(category) == cat_options.end() ? new_category : cat_options.at(category);
+ cat_opt.push_back(opt_key);
+ if (cat_opt.size() == 1)
+ cat_options[category] = cat_opt;
+ }
+
+ for (auto& cat : cat_options) {
+ if (cat.second.size() == 1 && cat.second[0] == "extruder")
+ continue;
+
+ auto optgroup = std::make_shared<ConfigOptionsGroup>(parent, cat.first, *m_config, false, ogDEFAULT, extra_column);
+ optgroup->label_width = 150;
+ optgroup->sidetext_width = 70;
+
+ for (auto& opt : cat.second)
+ {
+ if (opt == "extruder")
+ continue;
+ Option option = optgroup->get_option(opt);
+ option.opt.width = 70;
+ optgroup->append_single_option_line(option);
+ }
+ optgroup->reload_config();
+ m_option_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0);
+ m_og_settings.push_back(optgroup);
+
+ categories.push_back(cat.first);
+ }
+ }
- for (auto& opt : cat.second)
- {
- if (opt == "extruder")
- continue;
- Option option = optgroup->get_option(opt);
- option.opt.width = 70;
- optgroup->append_single_option_line(option);
- }
- optgroup->reload_config();
- m_option_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0);
- m_og_settings.push_back(optgroup);
- }
+ if (m_og_settings.empty()) {
+ m_objects_ctrl->Select(m_objects_model->Delete(item));
+ part_selection_changed();
+ }
+ else {
+ if (!categories.empty())
+ m_objects_model->UpdateSettingsDigest(item, categories);
+ show_manipulations = false;
+ }
}
+ show_manipulation_og(show_manipulations);
+ show_info_sizer(show_manipulations && item && m_objects_model->GetParent(item) == wxDataViewItem(0));
+
#ifdef __linux__
no_updates.reset(nullptr);
#endif
- /*get_right_panel()*/parent->Layout();
- get_right_panel()->GetParent()->GetParent()->Layout();
+ parent->Layout();
+ get_right_panel()->GetParent()->Layout();
}
void get_settings_choice(wxMenu *menu, int id, bool is_part)
{
- auto category_name = menu->GetLabel(id);
+ const auto category_name = menu->GetLabel(id);
wxArrayString names;
wxArrayInt selections;
@@ -974,15 +1027,35 @@ void get_settings_choice(wxMenu *menu, int id, bool is_part)
(*m_config)->set_key_value(opt_key, m_default_config.get()->option(opt_key)->clone());
}
+
+ // Add settings item for object
+ const auto item = m_objects_ctrl->GetSelection();
+ if (item) {
+ const auto settings_item = m_objects_model->HasSettings(item);
+ m_objects_ctrl->Select(settings_item ? settings_item :
+ m_objects_model->AddSettingsChild(item));
+#ifndef __WXOSX__
+ part_selection_changed();
+#endif //no __WXOSX__
+ }
+ else
update_settings_list();
}
-bool cur_item_hase_children()
-{
- wxDataViewItemArray children;
- if (m_objects_model->GetChildren(m_objects_ctrl->GetSelection(), children) > 0)
- return true;
- return false;
+void menu_item_add_generic(wxMenuItem* &menu, int id) {
+ auto sub_menu = new wxMenu;
+
+ std::vector<std::string> menu_items = { L("Box"), L("Cylinder"), L("Sphere"), L("Slab") };
+ for (auto& item : menu_items)
+ sub_menu->Append(new wxMenuItem(sub_menu, ++id, _(item)));
+
+#ifndef __WXMSW__
+ sub_menu->Bind(wxEVT_MENU, [sub_menu](wxEvent &event) {
+ load_lambda(sub_menu->GetLabel(event.GetId()).ToStdString());
+ });
+#endif //no __WXMSW__
+
+ menu->SetSubMenu(sub_menu);
}
wxMenuItem* menu_item_split(wxMenu* menu, int id) {
@@ -991,11 +1064,11 @@ wxMenuItem* menu_item_split(wxMenu* menu, int id) {
return menu_item;
}
-wxMenuItem* menu_item_settings(wxMenu* menu, int id) {
+wxMenuItem* menu_item_settings(wxMenu* menu, int id, const bool is_part) {
auto menu_item = new wxMenuItem(menu, id, _(L("Add settings")));
menu_item->SetBitmap(m_bmp_cog);
- auto sub_menu = create_add_settings_popupmenu(false);
+ auto sub_menu = create_add_settings_popupmenu(is_part);
menu_item->SetSubMenu(sub_menu);
return menu_item;
}
@@ -1005,44 +1078,54 @@ wxMenu *create_add_part_popupmenu()
wxMenu *menu = new wxMenu;
std::vector<std::string> menu_items = { L("Add part"), L("Add modifier"), L("Add generic") };
- wxWindowID config_id_base = wxWindow::NewControlId(menu_items.size()+2);
+ wxWindowID config_id_base = wxWindow::NewControlId(menu_items.size()+4+2);
int i = 0;
for (auto& item : menu_items) {
auto menu_item = new wxMenuItem(menu, config_id_base + i, _(item));
menu_item->SetBitmap(i == 0 ? m_icon_solidmesh : m_icon_modifiermesh);
- menu->Append(menu_item);
+ if (item == "Add generic")
+ menu_item_add_generic(menu_item, config_id_base + i);
+ menu->Append(menu_item);
i++;
}
menu->AppendSeparator();
- auto menu_item = menu_item_split(menu, config_id_base + i);
+ auto menu_item = menu_item_split(menu, config_id_base + i + 4);
menu->Append(menu_item);
- menu_item->Enable(!cur_item_hase_children());
+ menu_item->Enable(is_splittable_object(false));
menu->AppendSeparator();
// Append settings popupmenu
- menu->Append(menu_item_settings(menu, config_id_base + i + 1));
-
- wxWindow* win = get_tab_panel()->GetPage(0);
+ menu->Append(menu_item_settings(menu, config_id_base + i + 5, false));
- menu->Bind(wxEVT_MENU, [config_id_base, win, menu](wxEvent &event){
+ menu->Bind(wxEVT_MENU, [config_id_base, menu](wxEvent &event){
switch (event.GetId() - config_id_base) {
case 0:
- on_btn_load(win);
+ on_btn_load();
break;
case 1:
- on_btn_load(win, true);
+ on_btn_load(true);
break;
case 2:
- on_btn_load(win, true, true);
+// on_btn_load(true, true);
break;
- case 3:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+#ifdef __WXMSW__
+ load_lambda(menu->GetLabel(event.GetId()).ToStdString());
+#endif // __WXMSW__
+ break;
+ case 7: //3:
on_btn_split(false);
break;
- default:{
+ default:
+#ifdef __WXMSW__
get_settings_choice(menu, event.GetId(), false);
- break;}
+#endif // __WXMSW__
+ break;
}
});
@@ -1054,11 +1137,13 @@ wxMenu *create_part_settings_popupmenu()
wxMenu *menu = new wxMenu;
wxWindowID config_id_base = wxWindow::NewControlId(2);
- menu->Append(menu_item_split(menu, config_id_base));
+ auto menu_item = menu_item_split(menu, config_id_base);
+ menu->Append(menu_item);
+ menu_item->Enable(is_splittable_object(true));
menu->AppendSeparator();
// Append settings popupmenu
- menu->Append(menu_item_settings(menu, config_id_base + 1));
+ menu->Append(menu_item_settings(menu, config_id_base + 1, true));
menu->Bind(wxEVT_MENU, [config_id_base, menu](wxEvent &event){
switch (event.GetId() - config_id_base) {
@@ -1090,35 +1175,35 @@ wxMenu *create_add_settings_popupmenu(bool is_part)
wxNullBitmap : categories.at(cat.first));
menu->Append(menu_item);
}
-
- menu->Bind(wxEVT_MENU, [menu](wxEvent &event) {
- get_settings_choice(menu, event.GetId(), true);
+#ifndef __WXMSW__
+ menu->Bind(wxEVT_MENU, [menu,is_part](wxEvent &event) {
+ get_settings_choice(menu, event.GetId(), is_part);
});
-
+#endif //no __WXMSW__
return menu;
}
void show_context_menu()
{
- auto item = m_objects_ctrl->GetSelection();
+ const auto item = m_objects_ctrl->GetSelection();
if (item)
{
- if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
- auto menu = create_add_part_popupmenu();
- get_tab_panel()->GetPage(0)->PopupMenu(menu);
- }
- else {
- auto menu = create_part_settings_popupmenu();
- get_tab_panel()->GetPage(0)->PopupMenu(menu);
- }
+ if (m_objects_model->IsSettingsItem(item))
+ return;
+ const auto menu = m_objects_model->GetParent(item) == wxDataViewItem(0) ?
+ create_add_part_popupmenu() :
+ create_part_settings_popupmenu();
+ get_tab_panel()->GetPage(0)->PopupMenu(menu);
}
}
// ******
-void load_part( wxWindow* parent, ModelObject* model_object,
+void load_part( ModelObject* model_object,
wxArrayString& part_names, const bool is_modifier)
{
+ wxWindow* parent = get_tab_panel()->GetPage(0);
+
wxArrayString input_files;
open_model(parent, input_files);
for (int i = 0; i < input_files.size(); ++i) {
@@ -1137,7 +1222,7 @@ void load_part( wxWindow* parent, ModelObject* model_object,
for ( auto object : model.objects) {
for (auto volume : object->volumes) {
auto new_volume = model_object->add_volume(*volume);
- new_volume->modifier = is_modifier;
+ new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART);
boost::filesystem::path(input_file).filename().string();
new_volume->name = boost::filesystem::path(input_file).filename().string();
@@ -1156,10 +1241,10 @@ void load_part( wxWindow* parent, ModelObject* model_object,
}
}
-void load_lambda( wxWindow* parent, ModelObject* model_object,
+void load_lambda( ModelObject* model_object,
wxArrayString& part_names, const bool is_modifier)
{
- auto dlg = new LambdaObjectDialog(parent);
+ auto dlg = new LambdaObjectDialog(m_objects_ctrl->GetMainWindow());
if (dlg->ShowModal() == wxID_CANCEL) {
return;
}
@@ -1195,7 +1280,8 @@ void load_lambda( wxWindow* parent, ModelObject* model_object,
mesh.repair();
auto new_volume = model_object->add_volume(mesh);
- new_volume->modifier = is_modifier;
+ new_volume->set_type(is_modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART);
+
new_volume->name = name;
// set a default extruder value, since user can't add it manually
new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
@@ -1205,7 +1291,50 @@ void load_lambda( wxWindow* parent, ModelObject* model_object,
m_parts_changed = true;
}
-void on_btn_load(wxWindow* parent, bool is_modifier /*= false*/, bool is_lambda/* = false*/)
+void load_lambda(const std::string& type_name)
+{
+ if (m_selected_object_id < 0) return;
+
+ auto dlg = new LambdaObjectDialog(m_objects_ctrl->GetMainWindow(), type_name);
+ if (dlg->ShowModal() == wxID_CANCEL)
+ return;
+
+ const std::string name = "lambda-"+type_name;
+ TriangleMesh mesh;
+
+ const auto params = dlg->ObjectParameters();
+ if (type_name == _("Box"))
+ mesh = make_cube(params.dim[0], params.dim[1], params.dim[2]);
+ else if (type_name == _("Cylinder"))
+ mesh = make_cylinder(params.cyl_r, params.cyl_h);
+ else if (type_name == _("Sphere"))
+ mesh = make_sphere(params.sph_rho);
+ else if (type_name == _("Slab")){
+ const auto& size = (*m_objects)[m_selected_object_id]->bounding_box().size();
+ mesh = make_cube(size(0)*1.5, size(1)*1.5, params.slab_h);
+ // box sets the base coordinate at 0, 0, move to center of plate and move it up to initial_z
+ mesh.translate(-size(0)*1.5 / 2.0, -size(1)*1.5 / 2.0, params.slab_z);
+ }
+ mesh.repair();
+
+ auto new_volume = (*m_objects)[m_selected_object_id]->add_volume(mesh);
+ new_volume->set_type(ModelVolume::PARAMETER_MODIFIER);
+
+ new_volume->name = name;
+ // set a default extruder value, since user can't add it manually
+ new_volume->config.set_key_value("extruder", new ConfigOptionInt(0));
+
+ m_parts_changed = true;
+ parts_changed(m_selected_object_id);
+
+ m_objects_ctrl->Select(m_objects_model->AddChild(m_objects_ctrl->GetSelection(),
+ name, m_icon_modifiermesh));
+#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
+ object_ctrl_selection_changed();
+#endif //no __WXOSX__ //__WXMSW__
+}
+
+void on_btn_load(bool is_modifier /*= false*/, bool is_lambda/* = false*/)
{
auto item = m_objects_ctrl->GetSelection();
if (!item)
@@ -1219,19 +1348,54 @@ void on_btn_load(wxWindow* parent, bool is_modifier /*= false*/, bool is_lambda/
if (obj_idx < 0) return;
wxArrayString part_names;
if (is_lambda)
- load_lambda(parent, (*m_objects)[obj_idx], part_names, is_modifier);
+ load_lambda((*m_objects)[obj_idx], part_names, is_modifier);
else
- load_part(parent, (*m_objects)[obj_idx], part_names, is_modifier);
+ load_part((*m_objects)[obj_idx], part_names, is_modifier);
parts_changed(obj_idx);
for (int i = 0; i < part_names.size(); ++i)
m_objects_ctrl->Select( m_objects_model->AddChild(item, part_names.Item(i),
is_modifier ? m_icon_modifiermesh : m_icon_solidmesh));
-// part_selection_changed();
-#ifdef __WXMSW__
+#ifndef __WXOSX__ //#ifdef __WXMSW__ // #ys_FIXME
object_ctrl_selection_changed();
-#endif //__WXMSW__
+#endif //no __WXOSX__//__WXMSW__
+}
+
+void remove_settings_from_config()
+{
+ auto opt_keys = (*m_config)->keys();
+ if (opt_keys.size() == 1 && opt_keys[0] == "extruder")
+ return;
+ int extruder = -1;
+ if ((*m_config)->has("extruder"))
+ extruder = (*m_config)->option<ConfigOptionInt>("extruder")->value;
+
+ (*m_config)->clear();
+
+ if (extruder >=0 )
+ (*m_config)->set_key_value("extruder", new ConfigOptionInt(extruder));
+}
+
+bool remove_subobject_from_object(const int volume_id)
+{
+ const auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id];
+
+ // if user is deleting the last solid part, throw error
+ int solid_cnt = 0;
+ for (auto vol : (*m_objects)[m_selected_object_id]->volumes)
+ if (vol->is_model_part())
+ ++solid_cnt;
+ if (volume->is_model_part() && solid_cnt == 1) {
+ Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last solid part from this object.")));
+ return false;
+ }
+
+ (*m_objects)[m_selected_object_id]->delete_volume(volume_id);
+ m_parts_changed = true;
+
+ parts_changed(m_selected_object_id);
+ return true;
}
void on_btn_del()
@@ -1239,50 +1403,67 @@ void on_btn_del()
auto item = m_objects_ctrl->GetSelection();
if (!item) return;
- auto volume_id = m_objects_model->GetVolumeIdByItem(item);
- if (volume_id < 0)
+ const auto volume_id = m_objects_model->GetVolumeIdByItem(item);
+ if (volume_id ==-1)
return;
- auto volume = (*m_objects)[m_selected_object_id]->volumes[volume_id];
-
- // if user is deleting the last solid part, throw error
- int solid_cnt = 0;
- for (auto vol : (*m_objects)[m_selected_object_id]->volumes)
- if (!vol->modifier)
- ++solid_cnt;
- if (!volume->modifier && solid_cnt == 1) {
- Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last solid part from this object.")));
- return;
- }
-
- (*m_objects)[m_selected_object_id]->delete_volume(volume_id);
- m_parts_changed = true;
-
- parts_changed(m_selected_object_id);
+
+ if (volume_id ==-2)
+ remove_settings_from_config();
+ else if (!remove_subobject_from_object(volume_id))
+ return;
m_objects_ctrl->Select(m_objects_model->Delete(item));
part_selection_changed();
-// #ifdef __WXMSW__
-// object_ctrl_selection_changed();
-// #endif //__WXMSW__
+}
+
+bool get_volume_by_item(const bool split_part, const wxDataViewItem& item, ModelVolume*& volume)
+{
+ if (!item || m_selected_object_id < 0)
+ return false;
+ const auto volume_id = m_objects_model->GetVolumeIdByItem(item);
+ if (volume_id < 0) {
+ if (split_part) return false;
+ volume = (*m_objects)[m_selected_object_id]->volumes[0];
+ }
+ else
+ volume = (*m_objects)[m_selected_object_id]->volumes[volume_id];
+ if (volume)
+ return true;
+ return false;
+}
+
+bool is_splittable_object(const bool split_part)
+{
+ const wxDataViewItem item = m_objects_ctrl->GetSelection();
+ if (!item) return false;
+
+ wxDataViewItemArray children;
+ if (!split_part && m_objects_model->GetChildren(item, children) > 0)
+ return false;
+
+ ModelVolume* volume;
+ if (!get_volume_by_item(split_part, item, volume) || !volume)
+ return false;
+
+ TriangleMeshPtrs meshptrs = volume->mesh.split();
+ if (meshptrs.size() <= 1) {
+ delete meshptrs.front();
+ return false;
+ }
+
+ return true;
}
void on_btn_split(const bool split_part)
{
- auto item = m_objects_ctrl->GetSelection();
+ const auto item = m_objects_ctrl->GetSelection();
if (!item || m_selected_object_id<0)
return;
- auto volume_id = m_objects_model->GetVolumeIdByItem(item);
ModelVolume* volume;
- if (volume_id < 0) {
- if (split_part) return;
- else
- volume = (*m_objects)[m_selected_object_id]->volumes[0]; }
- else
- volume = (*m_objects)[m_selected_object_id]->volumes[volume_id];
- DynamicPrintConfig& config = get_preset_bundle()->printers.get_edited_preset().config;
- auto nozzle_dmrs_cnt = config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
- auto split_rez = volume->split(nozzle_dmrs_cnt);
- if (split_rez == 1) {
+ if (!get_volume_by_item(split_part, item, volume)) return;
+ DynamicPrintConfig& config = get_preset_bundle()->printers.get_edited_preset().config;
+ const auto nozzle_dmrs_cnt = config.option<ConfigOptionFloats>("nozzle_diameter")->values.size();
+ if (volume->split(nozzle_dmrs_cnt) == 1) {
wxMessageBox(_(L("The selected object couldn't be split because it contains only one part.")));
return;
}
@@ -1295,8 +1476,9 @@ void on_btn_split(const bool split_part)
for (auto id = 0; id < model_object->volumes.size(); id++)
m_objects_model->AddChild(parent, model_object->volumes[id]->name,
- model_object->volumes[id]->modifier ? m_icon_modifiermesh : m_icon_solidmesh,
- model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value,
+ model_object->volumes[id]->is_modifier() ? m_icon_modifiermesh : m_icon_solidmesh,
+ model_object->volumes[id]->config.has("extruder") ?
+ model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0,
false);
m_objects_ctrl->Expand(parent);
@@ -1305,10 +1487,14 @@ void on_btn_split(const bool split_part)
for (auto id = 0; id < model_object->volumes.size(); id++)
m_objects_model->AddChild(item, model_object->volumes[id]->name,
m_icon_solidmesh,
- model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value,
+ model_object->volumes[id]->config.has("extruder") ?
+ model_object->volumes[id]->config.option<ConfigOptionInt>("extruder")->value : 0,
false);
m_objects_ctrl->Expand(item);
}
+
+ m_parts_changed = true;
+ parts_changed(m_selected_object_id);
}
void on_btn_move_up(){
@@ -1389,33 +1575,49 @@ void part_selection_changed()
auto item = m_objects_ctrl->GetSelection();
int obj_idx = -1;
auto og = get_optgroup(ogFrequentlyObjectSettings);
+ m_config = nullptr;
+ wxString object_name = wxEmptyString;
if (item)
{
+ const bool is_settings_item = m_objects_model->IsSettingsItem(item);
bool is_part = false;
- if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
+ wxString og_name = wxEmptyString;
+ if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
obj_idx = m_objects_model->GetIdByItem(item);
- og->set_name(" " + _(L("Object Settings")) + " ");
+ og_name = _(L("Object manipulation"));
m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->config);
}
else {
auto parent = m_objects_model->GetParent(item);
- // Take ID of the parent object to "inform" perl-side which object have to be selected on the scene
+ // Take ID of the parent object to "inform" perl-side which object have to be selected on the scene
obj_idx = m_objects_model->GetIdByItem(parent);
- og->set_name(" " + _(L("Part Settings")) + " ");
- is_part = true;
- auto volume_id = m_objects_model->GetVolumeIdByItem(item);
- m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->volumes[volume_id]->config);
+ if (is_settings_item) {
+ if (m_objects_model->GetParent(parent) == wxDataViewItem(0)) {
+ og_name = _(L("Object Settings to modify"));
+ m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->config);
+ }
+ else {
+ og_name = _(L("Part Settings to modify"));
+ is_part = true;
+ auto main_parent = m_objects_model->GetParent(parent);
+ obj_idx = m_objects_model->GetIdByItem(main_parent);
+ const auto volume_id = m_objects_model->GetVolumeIdByItem(parent);
+ m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->volumes[volume_id]->config);
+ }
+ }
+ else {
+ og_name = _(L("Part manipulation"));
+ is_part = true;
+ const auto volume_id = m_objects_model->GetVolumeIdByItem(item);
+ m_config = std::make_shared<DynamicPrintConfig*>(&(*m_objects)[obj_idx]->volumes[volume_id]->config);
+ }
}
- auto config = m_config;
- og->set_value("object_name", m_objects_model->GetName(item));
+ og->set_name(" " + og_name + " ");
+ object_name = m_objects_model->GetName(item);
m_default_config = std::make_shared<DynamicPrintConfig>(*DynamicPrintConfig::new_from_defaults_keys(get_options(is_part)));
}
- else {
- wxString empty_str = wxEmptyString;
- og->set_value("object_name", empty_str);
- m_config = nullptr;
- }
+ og->set_value("object_name", object_name);
update_settings_list();
@@ -1561,9 +1763,15 @@ void update_position_values()
auto og = get_optgroup(ogFrequentlyObjectSettings);
auto instance = (*m_objects)[m_selected_object_id]->instances.front();
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ og->set_value("position_x", int(instance->get_offset(X)));
+ og->set_value("position_y", int(instance->get_offset(Y)));
+ og->set_value("position_z", int(instance->get_offset(Z)));
+#else
og->set_value("position_x", int(instance->offset(0)));
og->set_value("position_y", int(instance->offset(1)));
og->set_value("position_z", 0);
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
}
void update_position_values(const Vec3d& position)
@@ -1638,7 +1846,7 @@ void on_begin_drag(wxDataViewEvent &event)
wxDataViewItem item(event.GetItem());
// only allow drags for item, not containers
- if (m_objects_model->GetParent(item) == wxDataViewItem(0)) {
+ if (m_objects_model->GetParent(item) == wxDataViewItem(0) || m_objects_model->IsSettingsItem(item)) {
event.Veto();
return;
}
@@ -1662,7 +1870,7 @@ void on_drop_possible(wxDataViewEvent &event)
// only allow drags for item or background, not containers
if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) ||
- event.GetDataFormat() != wxDF_UNICODETEXT)
+ event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->IsSettingsItem(item))
event.Veto();
}
@@ -1672,7 +1880,7 @@ void on_drop(wxDataViewEvent &event)
// only allow drops for item, not containers
if (item.IsOk() && m_objects_model->GetParent(item) == wxDataViewItem(0) ||
- event.GetDataFormat() != wxDF_UNICODETEXT) {
+ event.GetDataFormat() != wxDF_UNICODETEXT || m_objects_model->IsSettingsItem(item)) {
event.Veto();
return;
}
@@ -1700,6 +1908,9 @@ void on_drop(wxDataViewEvent &event)
for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id+=delta, cnt++)
std::swap(volumes[id], volumes[id +delta]);
+ m_parts_changed = true;
+ parts_changed(m_selected_object_id);
+
g_prevent_list_events = false;
}
@@ -1716,5 +1927,115 @@ void update_objects_list_extruder_column(int extruders_count)
set_extruder_column_hidden(extruders_count <= 1);
}
+void create_double_slider(wxWindow* parent, wxBoxSizer* sizer, wxGLCanvas* canvas)
+{
+ m_slider = new PrusaDoubleSlider(parent, wxID_ANY, 0, 0, 0, 100);
+ sizer->Add(m_slider, 0, wxEXPAND, 0);
+
+ m_preview_canvas = canvas;
+ m_preview_canvas->Bind(wxEVT_KEY_DOWN, update_double_slider_from_canvas);
+
+ m_slider->Bind(wxEVT_SCROLL_CHANGED, [parent](wxEvent& event) {
+ _3DScene::set_toolpaths_range(m_preview_canvas, m_slider->GetLowerValueD() - 1e-6, m_slider->GetHigherValueD() + 1e-6);
+ if (parent->IsShown())
+ m_preview_canvas->Refresh();
+ });
+}
+
+void fill_slider_values(std::vector<std::pair<int, double>> &values,
+ const std::vector<double> &layers_z)
+{
+ std::vector<double> layers_all_z = _3DScene::get_current_print_zs(m_preview_canvas, false);
+ if (layers_all_z.size() == layers_z.size())
+ for (int i = 0; i < layers_z.size(); i++)
+ values.push_back(std::pair<int, double>(i+1, layers_z[i]));
+ else if (layers_all_z.size() > layers_z.size()) {
+ int cur_id = 0;
+ for (int i = 0; i < layers_z.size(); i++)
+ for (int j = cur_id; j < layers_all_z.size(); j++)
+ if (layers_z[i] - 1e-6 < layers_all_z[j] && layers_all_z[j] < layers_z[i] + 1e-6) {
+ values.push_back(std::pair<int, double>(j+1, layers_z[i]));
+ cur_id = j;
+ break;
+ }
+ }
+}
+
+void set_double_slider_thumbs( const bool force_sliders_full_range,
+ const std::vector<double> &layers_z,
+ const double z_low, const double z_high)
+{
+ // Force slider full range only when slider is created.
+ // Support selected diapason on the all next steps
+ if (/*force_sliders_full_range*/z_high == 0.0) {
+ m_slider->SetLowerValue(0);
+ m_slider->SetHigherValue(layers_z.size() - 1);
+ return;
+ }
+
+ for (int i = layers_z.size() - 1; i >= 0; i--)
+ if (z_low >= layers_z[i]) {
+ m_slider->SetLowerValue(i);
+ break;
+ }
+ for (int i = layers_z.size() - 1; i >= 0 ; i--)
+ if (z_high >= layers_z[i]) {
+ m_slider->SetHigherValue(i);
+ break;
+ }
+}
+
+void update_double_slider(bool force_sliders_full_range)
+{
+ std::vector<std::pair<int, double>> values;
+ std::vector<double> layers_z = _3DScene::get_current_print_zs(m_preview_canvas, true);
+ fill_slider_values(values, layers_z);
+
+ const double z_low = m_slider->GetLowerValueD();
+ const double z_high = m_slider->GetHigherValueD();
+ m_slider->SetMaxValue(layers_z.size() - 1);
+ m_slider->SetSliderValues(values);
+
+ set_double_slider_thumbs(force_sliders_full_range, layers_z, z_low, z_high);
+}
+
+void reset_double_slider()
+{
+ m_slider->SetHigherValue(0);
+ m_slider->SetLowerValue(0);
+}
+
+void update_double_slider_from_canvas(wxKeyEvent& event)
+{
+ if (event.HasModifiers()) {
+ event.Skip();
+ return;
+ }
+
+ const auto key = event.GetKeyCode();
+
+ if (key == 'U' || key == 'D') {
+ const int new_pos = key == 'U' ? m_slider->GetHigherValue() + 1 : m_slider->GetHigherValue() - 1;
+ m_slider->SetHigherValue(new_pos);
+ if (event.ShiftDown()) m_slider->SetLowerValue(m_slider->GetHigherValue());
+ }
+ else if (key == 'S')
+ m_slider->ChangeOneLayerLock();
+ else
+ event.Skip();
+}
+
+void show_manipulation_sizer(const bool is_simple_mode)
+{
+ auto item = m_objects_ctrl->GetSelection();
+ if (!item || !is_simple_mode)
+ return;
+
+ if (m_objects_model->IsSettingsItem(item)) {
+ m_objects_ctrl->Select(m_objects_model->GetParent(item));
+ part_selection_changed();
+ }
+}
+
} //namespace GUI
} //namespace Slic3r \ No newline at end of file
diff --git a/xs/src/slic3r/GUI/GUI_ObjectParts.hpp b/xs/src/slic3r/GUI/GUI_ObjectParts.hpp
index 8a1499e03..e66b4d1db 100644
--- a/xs/src/slic3r/GUI/GUI_ObjectParts.hpp
+++ b/xs/src/slic3r/GUI/GUI_ObjectParts.hpp
@@ -9,13 +9,15 @@ class wxArrayString;
class wxMenu;
class wxDataViewEvent;
class wxKeyEvent;
-class wxControl;
+class wxGLCanvas;
+class wxBitmap;
namespace Slic3r {
class ModelObject;
class Model;
namespace GUI {
+//class wxGLCanvas;
enum ogGroup{
ogFrequentlyChangingParameters,
@@ -44,6 +46,9 @@ struct OBJECT_PARAMETERS
double slab_z = 0.0;
};
+typedef std::map<std::string, wxBitmap> t_category_icon;
+inline t_category_icon& get_category_icon();
+
void add_collapsible_panes(wxWindow* parent, wxBoxSizer* sizer);
void add_objects_list(wxWindow* parent, wxBoxSizer* sizer);
void add_object_settings(wxWindow* parent, wxBoxSizer* sizer);
@@ -66,16 +71,17 @@ void set_object_count(int idx, int count);
void unselect_objects();
// Select current object in the list on c++ side
void select_current_object(int idx);
+// Select current volume in the list on c++ side
+void select_current_volume(int idx, int vol_idx);
// Remove objects/sub-object from the list
void remove();
-//void create_double_slider(wxWindow* parent, wxControl* slider);
-
void object_ctrl_selection_changed();
void object_ctrl_context_menu();
void object_ctrl_key_event(wxKeyEvent& event);
void object_ctrl_item_value_change(wxDataViewEvent& event);
void show_context_menu();
+bool is_splittable_object(const bool split_part);
void init_mesh_icons();
void set_event_object_selection_changed(const int& event);
@@ -87,13 +93,14 @@ void set_objects_from_model(Model &model);
bool is_parts_changed();
bool is_part_settings_changed();
-void load_part( wxWindow* parent, ModelObject* model_object,
+void load_part( ModelObject* model_object,
wxArrayString& part_names, const bool is_modifier);
-void load_lambda(wxWindow* parent, ModelObject* model_object,
- wxArrayString& part_names, const bool is_modifier);
+void load_lambda( ModelObject* model_object,
+ wxArrayString& part_names, const bool is_modifier);
+void load_lambda( const std::string& type_name);
-void on_btn_load(wxWindow* parent, bool is_modifier = false, bool is_lambda = false);
+void on_btn_load(bool is_modifier = false, bool is_lambda = false);
void on_btn_del();
void on_btn_split(const bool split_part);
void on_btn_move_up();
@@ -126,6 +133,15 @@ void on_drop(wxDataViewEvent &event);
// update extruder column for objects_ctrl according to extruders count
void update_objects_list_extruder_column(int extruders_count);
+// Create/Update/Reset double slider on 3dPreview
+void create_double_slider(wxWindow* parent, wxBoxSizer* sizer, wxGLCanvas* canvas);
+void update_double_slider(bool force_sliders_full_range);
+void reset_double_slider();
+// update DoubleSlider after keyDown in canvas
+void update_double_slider_from_canvas(wxKeyEvent& event);
+
+void show_manipulation_sizer(const bool is_simple_mode);
+
} //namespace GUI
} //namespace Slic3r
#endif //slic3r_GUI_ObjectParts_hpp_ \ No newline at end of file
diff --git a/xs/src/slic3r/GUI/LambdaObjectDialog.cpp b/xs/src/slic3r/GUI/LambdaObjectDialog.cpp
index 7543821c0..7d741be7f 100644
--- a/xs/src/slic3r/GUI/LambdaObjectDialog.cpp
+++ b/xs/src/slic3r/GUI/LambdaObjectDialog.cpp
@@ -10,10 +10,12 @@ namespace GUI
{
static wxString dots("…", wxConvUTF8);
-LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
+LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent,
+ const wxString type_name):
+ m_type_name(type_name)
{
Create(parent, wxID_ANY, _(L("Lambda Object")),
- wxDefaultPosition, wxDefaultSize,
+ parent->GetScreenPosition(), wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
// instead of double dim[3] = { 1.0, 1.0, 1.0 };
@@ -24,11 +26,20 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
sizer = new wxBoxSizer(wxVERTICAL);
// modificator options
- m_modificator_options_book = new wxChoicebook( this, wxID_ANY, wxDefaultPosition,
- wxDefaultSize, wxCHB_TOP);
- sizer->Add(m_modificator_options_book, 1, wxEXPAND| wxALL, 10);
-
+ if (m_type_name == wxEmptyString) {
+ m_modificator_options_book = new wxChoicebook( this, wxID_ANY, wxDefaultPosition,
+ wxDefaultSize, wxCHB_TOP);
+ sizer->Add(m_modificator_options_book, 1, wxEXPAND | wxALL, 10);
+ }
+ else {
+ m_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize);
+ sizer->Add(m_panel, 1, wxEXPAND | wxALL, 10);
+ }
+
+ ConfigOptionDef def;
+ def.width = 70;
auto optgroup = init_modificator_options_page(_(L("Box")));
+ if (optgroup){
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
int opt_id = opt_key == "l" ? 0 :
opt_key == "w" ? 1 :
@@ -37,8 +48,6 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
object_parameters.dim[opt_id] = boost::any_cast<double>(value);
};
- ConfigOptionDef def;
- def.width = 70;
def.type = coFloat;
def.default_value = new ConfigOptionFloat{ 1.0 };
def.label = L("L");
@@ -52,8 +61,10 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
def.label = L("H");
option = Option(def, "h");
optgroup->append_single_option_line(option);
+ }
optgroup = init_modificator_options_page(_(L("Cylinder")));
+ if (optgroup){
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
int val = boost::any_cast<int>(value);
if (opt_key == "cyl_r")
@@ -66,14 +77,16 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
def.type = coInt;
def.default_value = new ConfigOptionInt{ 1 };
def.label = L("Radius");
- option = Option(def, "cyl_r");
+ auto option = Option(def, "cyl_r");
optgroup->append_single_option_line(option);
def.label = L("Height");
option = Option(def, "cyl_h");
optgroup->append_single_option_line(option);
+ }
optgroup = init_modificator_options_page(_(L("Sphere")));
+ if (optgroup){
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
if (opt_key == "sph_rho")
object_parameters.sph_rho = boost::any_cast<double>(value);
@@ -83,10 +96,12 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
def.type = coFloat;
def.default_value = new ConfigOptionFloat{ 1.0 };
def.label = L("Rho");
- option = Option(def, "sph_rho");
+ auto option = Option(def, "sph_rho");
optgroup->append_single_option_line(option);
+ }
optgroup = init_modificator_options_page(_(L("Slab")));
+ if (optgroup){
optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value){
double val = boost::any_cast<double>(value);
if (opt_key == "slab_z")
@@ -96,13 +111,16 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
else return;
};
+ def.type = coFloat;
+ def.default_value = new ConfigOptionFloat{ 1.0 };
def.label = L("H");
- option = Option(def, "slab_h");
+ auto option = Option(def, "slab_h");
optgroup->append_single_option_line(option);
def.label = L("Initial Z");
option = Option(def, "slab_z");
optgroup->append_single_option_line(option);
+ }
Bind(wxEVT_CHOICEBOOK_PAGE_CHANGED, ([this](wxCommandEvent e)
{
@@ -127,8 +145,7 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
}
}));
-
- auto button_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL);
+ const auto button_sizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL);
wxButton* btn_OK = static_cast<wxButton*>(FindWindowById(wxID_OK, this));
btn_OK->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
@@ -155,9 +172,11 @@ LambdaObjectDialog::LambdaObjectDialog(wxWindow* parent)
// Called from the constructor.
// Create a panel for a rectangular / circular / custom bed shape.
-ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(wxString title){
+ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(const wxString& title){
+ if (!m_type_name.IsEmpty() && m_type_name != title)
+ return nullptr;
- auto panel = new wxPanel(m_modificator_options_book);
+ auto panel = m_type_name.IsEmpty() ? new wxPanel(m_modificator_options_book) : m_panel;
ConfigOptionsGroupShp optgroup;
optgroup = std::make_shared<ConfigOptionsGroup>(panel, _(L("Add")) + " " +title + " " +dots);
@@ -165,8 +184,12 @@ ConfigOptionsGroupShp LambdaObjectDialog::init_modificator_options_page(wxString
m_optgroups.push_back(optgroup);
- panel->SetSizerAndFit(optgroup->sizer);
- m_modificator_options_book->AddPage(panel, title);
+ if (m_type_name.IsEmpty()) {
+ panel->SetSizerAndFit(optgroup->sizer);
+ m_modificator_options_book->AddPage(panel, title);
+ }
+ else
+ panel->SetSizer(optgroup->sizer);
return optgroup;
}
diff --git a/xs/src/slic3r/GUI/LambdaObjectDialog.hpp b/xs/src/slic3r/GUI/LambdaObjectDialog.hpp
index a70c12449..8f3e8cd80 100644
--- a/xs/src/slic3r/GUI/LambdaObjectDialog.hpp
+++ b/xs/src/slic3r/GUI/LambdaObjectDialog.hpp
@@ -7,6 +7,8 @@
#include <wx/sizer.h>
#include <wx/choicebk.h>
+class wxPanel;
+
namespace Slic3r
{
namespace GUI
@@ -14,16 +16,19 @@ namespace GUI
using ConfigOptionsGroupShp = std::shared_ptr<ConfigOptionsGroup>;
class LambdaObjectDialog : public wxDialog
{
- wxChoicebook* m_modificator_options_book;
+ wxChoicebook* m_modificator_options_book = nullptr;
std::vector <ConfigOptionsGroupShp> m_optgroups;
+ wxString m_type_name;
+ wxPanel* m_panel = nullptr;
public:
- LambdaObjectDialog(wxWindow* parent);
+ LambdaObjectDialog(wxWindow* parent,
+ const wxString type_name = wxEmptyString);
~LambdaObjectDialog(){}
bool CanClose() { return true; } // ???
OBJECT_PARAMETERS& ObjectParameters(){ return object_parameters; }
- ConfigOptionsGroupShp init_modificator_options_page(wxString title);
+ ConfigOptionsGroupShp init_modificator_options_page(const wxString& title);
// Note whether the window was already closed, so a pending update is not executed.
bool m_already_closed = false;
diff --git a/xs/src/slic3r/GUI/OptionsGroup.cpp b/xs/src/slic3r/GUI/OptionsGroup.cpp
index 81d02a4cd..ea22b2cb5 100644
--- a/xs/src/slic3r/GUI/OptionsGroup.cpp
+++ b/xs/src/slic3r/GUI/OptionsGroup.cpp
@@ -158,7 +158,7 @@ void OptionsGroup::append_line(const Line& line, wxStaticText** colored_Label/*
// if we have an extra column, build it
if (extra_column) {
if (extra_column) {
- grid_sizer->Add(extra_column(parent(), line), 0, wxALIGN_CENTER_VERTICAL, 0);
+ grid_sizer->Add(extra_column(parent(), line), 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 3);
}
else {
// if the callback provides no sizer for the extra cell, put a spacer
diff --git a/xs/src/slic3r/GUI/OptionsGroup.hpp b/xs/src/slic3r/GUI/OptionsGroup.hpp
index 5a18803a6..4941e5453 100644
--- a/xs/src/slic3r/GUI/OptionsGroup.hpp
+++ b/xs/src/slic3r/GUI/OptionsGroup.hpp
@@ -161,8 +161,10 @@ public:
ogDrawFlag flag = ogDEFAULT, column_t extra_clmn = nullptr) :
m_parent(_parent), title(title), m_show_modified_btns(is_tab_opt),
staticbox(title!=""), m_flag(flag), extra_column(extra_clmn){
- stb = new wxStaticBox(_parent, wxID_ANY, title);
- stb->SetFont(bold_font());
+ if (staticbox) {
+ stb = new wxStaticBox(_parent, wxID_ANY, title);
+ stb->SetFont(bold_font());
+ }
sizer = (staticbox ? new wxStaticBoxSizer(stb, wxVERTICAL) : new wxBoxSizer(wxVERTICAL));
auto num_columns = 1U;
if (label_width != 0) num_columns++;
diff --git a/xs/src/slic3r/GUI/Preset.cpp b/xs/src/slic3r/GUI/Preset.cpp
index 92912a3c4..9911caa5b 100644
--- a/xs/src/slic3r/GUI/Preset.cpp
+++ b/xs/src/slic3r/GUI/Preset.cpp
@@ -295,7 +295,7 @@ const std::vector<std::string>& Preset::print_options()
"top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed",
"bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration",
"bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height",
- "min_skirt_length", "brim_width", "support_material", "support_material_threshold", "support_material_enforce_layers",
+ "min_skirt_length", "brim_width", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
"raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing",
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers",
"support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance",
diff --git a/xs/src/slic3r/GUI/ProgressIndicator.hpp b/xs/src/slic3r/GUI/ProgressIndicator.hpp
index 106f5e7ea..0cf8b4a17 100644
--- a/xs/src/slic3r/GUI/ProgressIndicator.hpp
+++ b/xs/src/slic3r/GUI/ProgressIndicator.hpp
@@ -4,8 +4,6 @@
#include <string>
#include <functional>
-#include <wx/string.h>
-
namespace Slic3r {
/**
@@ -44,13 +42,13 @@ public:
}
/// Message shown on the next status update.
- virtual void message(const wxString&) = 0;
+ virtual void message(const std::string&) = 0;
/// Title of the operation.
- virtual void title(const wxString&) = 0;
+ virtual void title(const std::string&) = 0;
/// Formatted message for the next status update. Works just like sprintf.
- virtual void message_fmt(const wxString& fmt, ...);
+ virtual void message_fmt(const std::string& fmt, ...);
/// Set up a cancel callback for the operation if feasible.
virtual void on_cancel(CancelFn func = CancelFn()) { cancelfunc_ = func; }
@@ -62,7 +60,7 @@ public:
virtual void cancel() { cancelfunc_(); }
/// Convenience function to call message and status update in one function.
- void update(float st, const wxString& msg) {
+ void update(float st, const std::string& msg) {
message(msg); state(st);
}
};
diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.cpp b/xs/src/slic3r/GUI/ProgressStatusBar.cpp
index 67fcd1dcc..363e34cb2 100644
--- a/xs/src/slic3r/GUI/ProgressStatusBar.cpp
+++ b/xs/src/slic3r/GUI/ProgressStatusBar.cpp
@@ -134,7 +134,7 @@ void ProgressStatusBar::embed(wxFrame *frame)
mf->SetStatusBar(self);
}
-void ProgressStatusBar::set_status_text(const std::string& txt)
+void ProgressStatusBar::set_status_text(const wxString& txt)
{
self->SetStatusText(wxString::FromUTF8(txt.c_str()));
}
diff --git a/xs/src/slic3r/GUI/ProgressStatusBar.hpp b/xs/src/slic3r/GUI/ProgressStatusBar.hpp
index 91c180ba6..3075805da 100644
--- a/xs/src/slic3r/GUI/ProgressStatusBar.hpp
+++ b/xs/src/slic3r/GUI/ProgressStatusBar.hpp
@@ -13,6 +13,7 @@ class wxTimerEvent;
class wxStatusBar;
class wxWindow;
class wxFrame;
+class wxString;
namespace Slic3r {
@@ -46,7 +47,7 @@ public:
inline void remove_cancel_callback() { set_cancel_callback(); }
void run(int rate);
void embed(wxFrame *frame = nullptr);
- void set_status_text(const std::string& txt);
+ void set_status_text(const wxString& txt);
// Temporary methods to satisfy Perl side
void show_cancel_button();
diff --git a/xs/src/slic3r/GUI/Tab.cpp b/xs/src/slic3r/GUI/Tab.cpp
index 83b834219..e0db63803 100644
--- a/xs/src/slic3r/GUI/Tab.cpp
+++ b/xs/src/slic3r/GUI/Tab.cpp
@@ -880,6 +880,7 @@ void TabPrint::build()
page = add_options_page(_(L("Support material")), "building.png");
optgroup = page->new_optgroup(_(L("Support material")));
optgroup->append_single_option_line("support_material");
+ optgroup->append_single_option_line("support_material_auto");
optgroup->append_single_option_line("support_material_threshold");
optgroup->append_single_option_line("support_material_enforce_layers");
@@ -1219,13 +1220,15 @@ void TabPrint::update()
bool have_raft = m_config->opt_int("raft_layers") > 0;
bool have_support_material = m_config->opt_bool("support_material") || have_raft;
+ bool have_support_material_auto = have_support_material && m_config->opt_bool("support_material_auto");
bool have_support_interface = m_config->opt_int("support_material_interface_layers") > 0;
bool have_support_soluble = have_support_material && m_config->opt_float("support_material_contact_distance") == 0;
- for (auto el : {"support_material_threshold", "support_material_pattern", "support_material_with_sheath",
+ for (auto el : {"support_material_pattern", "support_material_with_sheath",
"support_material_spacing", "support_material_angle", "support_material_interface_layers",
"dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance",
"support_material_xy_spacing" })
get_field(el)->toggle(have_support_material);
+ get_field("support_material_threshold")->toggle(have_support_material_auto);
for (auto el : {"support_material_interface_spacing", "support_material_interface_extruder",
"support_material_interface_speed", "support_material_interface_contact_loops" })
diff --git a/xs/src/slic3r/GUI/wxExtensions.cpp b/xs/src/slic3r/GUI/wxExtensions.cpp
index e646ed0ec..13730a497 100644
--- a/xs/src/slic3r/GUI/wxExtensions.cpp
+++ b/xs/src/slic3r/GUI/wxExtensions.cpp
@@ -2,6 +2,7 @@
#include "GUI.hpp"
#include "../../libslic3r/Utils.hpp"
+#include "BitmapCache.hpp"
#include <wx/sizer.h>
#include <wx/statline.h>
@@ -357,11 +358,53 @@ void PrusaObjectDataViewModelNode::set_part_action_icon() {
m_action_icon = wxBitmap(Slic3r::GUI::from_u8(Slic3r::var("cog.png")), wxBITMAP_TYPE_PNG);
}
+Slic3r::GUI::BitmapCache *m_bitmap_cache = nullptr;
+bool PrusaObjectDataViewModelNode::update_settings_digest(const std::vector<std::string>& categories)
+{
+ if (m_type != "settings" || m_opt_categories == categories)
+ return false;
+
+ m_opt_categories = categories;
+ m_name = wxEmptyString;
+ m_icon = m_empty_icon;
+
+ auto categories_icon = Slic3r::GUI::get_category_icon();
+
+ for (auto& cat : m_opt_categories)
+ m_name += cat + "; ";
+
+ wxBitmap *bmp = m_bitmap_cache->find(m_name.ToStdString());
+ if (bmp == nullptr) {
+ std::vector<wxBitmap> bmps;
+ for (auto& cat : m_opt_categories)
+ bmps.emplace_back(categories_icon.find(cat) == categories_icon.end() ?
+ wxNullBitmap : categories_icon.at(cat));
+ bmp = m_bitmap_cache->insert(m_name.ToStdString(), bmps);
+ }
+
+ m_bmp = *bmp;
+
+ return true;
+}
+
// *****************************************************************************
// ----------------------------------------------------------------------------
// PrusaObjectDataViewModel
// ----------------------------------------------------------------------------
+PrusaObjectDataViewModel::PrusaObjectDataViewModel()
+{
+ m_bitmap_cache = new Slic3r::GUI::BitmapCache;
+}
+
+PrusaObjectDataViewModel::~PrusaObjectDataViewModel()
+{
+ for (auto object : m_objects)
+ delete object;
+ delete m_bitmap_cache;
+ m_bitmap_cache = nullptr;
+}
+
wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name)
{
auto root = new PrusaObjectDataViewModelNode(name);
@@ -386,34 +429,50 @@ wxDataViewItem PrusaObjectDataViewModel::Add(const wxString &name, const int ins
wxDataViewItem PrusaObjectDataViewModel::AddChild( const wxDataViewItem &parent_item,
const wxString &name,
- const wxIcon& icon,
+ const wxBitmap& icon,
const int extruder/* = 0*/,
const bool create_frst_child/* = true*/)
{
PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID();
if (!root) return wxDataViewItem(0);
- wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder);
+ const wxString extruder_str = extruder == 0 ? "default" : wxString::Format("%d", extruder);
- if (root->GetChildren().Count() == 0 && create_frst_child)
+ if (create_frst_child && (root->GetChildren().Count() == 0 ||
+ (root->GetChildren().Count() == 1 && root->GetNthChild(0)->m_type == "settings")))
{
- auto icon_solid_mesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);//(Slic3r::var("package.png")), wxBITMAP_TYPE_PNG);
- auto node = new PrusaObjectDataViewModelNode(root, root->m_name, icon_solid_mesh, extruder_str, 0);
+ const auto icon_solid_mesh = wxIcon(Slic3r::GUI::from_u8(Slic3r::var("object.png")), wxBITMAP_TYPE_PNG);
+ const auto node = new PrusaObjectDataViewModelNode(root, root->m_name, icon_solid_mesh, extruder_str, 0);
root->Append(node);
// notify control
- wxDataViewItem child((void*)node);
+ const wxDataViewItem child((void*)node);
ItemAdded(parent_item, child);
}
- auto volume_id = root->GetChildCount();
- auto node = new PrusaObjectDataViewModelNode(root, name, icon, extruder_str, volume_id);
+ const auto volume_id = root->GetChildCount() > 0 && root->GetNthChild(0)->m_type == "settings" ?
+ root->GetChildCount() - 1 : root->GetChildCount();
+
+ const auto node = new PrusaObjectDataViewModelNode(root, name, icon, extruder_str, volume_id);
root->Append(node);
// notify control
- wxDataViewItem child((void*)node);
+ const wxDataViewItem child((void*)node);
ItemAdded(parent_item, child);
return child;
}
+wxDataViewItem PrusaObjectDataViewModel::AddSettingsChild(const wxDataViewItem &parent_item)
+{
+ PrusaObjectDataViewModelNode *root = (PrusaObjectDataViewModelNode*)parent_item.GetID();
+ if (!root) return wxDataViewItem(0);
+
+ const auto node = new PrusaObjectDataViewModelNode(root);
+ root->Insert(node, 0);
+ // notify control
+ const wxDataViewItem child((void*)node);
+ ItemAdded(parent_item, child);
+ return child;
+}
+
wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item)
{
auto ret_item = wxDataViewItem(0);
@@ -438,7 +497,7 @@ wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item)
//update volume_id value for remaining child-nodes
auto children = node_parent->GetChildren();
- for (size_t i = 0; i < node_parent->GetChildCount(); i++)
+ for (size_t i = 0; i < node_parent->GetChildCount() && v_id>=0; i++)
{
auto volume_id = children[i]->GetVolumeId();
if (volume_id > v_id)
@@ -460,9 +519,10 @@ wxDataViewItem PrusaObjectDataViewModel::Delete(const wxDataViewItem &item)
delete node;
// set m_containet to FALSE if parent has no child
- if (node_parent && node_parent->GetChildCount() == 0){
+ if (node_parent) {
#ifndef __WXGTK__
- node_parent->m_container = false;
+ if (node_parent->GetChildCount() == 0)
+ node_parent->m_container = false;
#endif //__WXGTK__
ret_item = parent;
}
@@ -522,6 +582,26 @@ wxDataViewItem PrusaObjectDataViewModel::GetItemById(int obj_idx)
}
+wxDataViewItem PrusaObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_idx)
+{
+ if (obj_idx >= m_objects.size()) {
+ printf("Error! Out of objects range.\n");
+ return wxDataViewItem(0);
+ }
+
+ auto parent = m_objects[obj_idx];
+ if (parent->GetChildCount() == 0) {
+ printf("Error! Object has no one volume.\n");
+ return wxDataViewItem(0);
+ }
+
+ for (size_t i = 0; i < parent->GetChildCount(); i++)
+ if (parent->GetNthChild(i)->m_volume_id == volume_idx)
+ return wxDataViewItem(parent->GetNthChild(i));
+
+ return wxDataViewItem(0);
+}
+
int PrusaObjectDataViewModel::GetIdByItem(wxDataViewItem& item)
{
wxASSERT(item.IsOk());
@@ -534,7 +614,7 @@ int PrusaObjectDataViewModel::GetIdByItem(wxDataViewItem& item)
return it - m_objects.begin();
}
-int PrusaObjectDataViewModel::GetVolumeIdByItem(wxDataViewItem& item)
+int PrusaObjectDataViewModel::GetVolumeIdByItem(const wxDataViewItem& item)
{
wxASSERT(item.IsOk());
@@ -568,6 +648,12 @@ wxIcon& PrusaObjectDataViewModel::GetIcon(const wxDataViewItem &item) const
return node->m_icon;
}
+wxBitmap& PrusaObjectDataViewModel::GetBitmap(const wxDataViewItem &item) const
+{
+ PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+ return node->m_bmp;
+}
+
void PrusaObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &item, unsigned int col) const
{
wxASSERT(item.IsOk());
@@ -576,8 +662,8 @@ void PrusaObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem
switch (col)
{
case 0:{
- const wxDataViewIconText data(node->m_name, node->m_icon);
- variant << data;
+ const PrusaDataViewBitmapText data(node->m_name, node->m_bmp);
+ variant << data;
break;}
case 1:
variant = node->m_copy;
@@ -667,26 +753,35 @@ wxDataViewItem PrusaObjectDataViewModel::ReorganizeChildren(int current_volume_i
if (!node_parent) // happens if item.IsOk()==false
return ret_item;
- PrusaObjectDataViewModelNode *deleted_node = node_parent->GetNthChild(current_volume_id);
+ const size_t shift = node_parent->GetChildren().Item(0)->m_type == "settings" ? 1 : 0;
+
+ PrusaObjectDataViewModelNode *deleted_node = node_parent->GetNthChild(current_volume_id+shift);
node_parent->GetChildren().Remove(deleted_node);
ItemDeleted(parent, wxDataViewItem(deleted_node));
- node_parent->Insert(deleted_node, new_volume_id);
+ node_parent->Insert(deleted_node, new_volume_id+shift);
ItemAdded(parent, wxDataViewItem(deleted_node));
+ const auto settings_item = HasSettings(wxDataViewItem(deleted_node));
+ if (settings_item)
+ ItemAdded(wxDataViewItem(deleted_node), settings_item);
//update volume_id value for child-nodes
auto children = node_parent->GetChildren();
int id_frst = current_volume_id < new_volume_id ? current_volume_id : new_volume_id;
int id_last = current_volume_id > new_volume_id ? current_volume_id : new_volume_id;
for (int id = id_frst; id <= id_last; ++id)
- children[id]->SetVolumeId(id);
+ children[id+shift]->SetVolumeId(id);
- return wxDataViewItem(node_parent->GetNthChild(new_volume_id));
+ return wxDataViewItem(node_parent->GetNthChild(new_volume_id+shift));
}
-// bool MyObjectTreeModel::IsEnabled(const wxDataViewItem &item, unsigned int col) const
-// {
-//
-// }
+bool PrusaObjectDataViewModel::IsEnabled(const wxDataViewItem &item, unsigned int col) const
+{
+ wxASSERT(item.IsOk());
+ PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+
+ // disable extruder selection for the "Settings" item
+ return !(col == 2 && node->m_extruder.IsEmpty());
+}
wxDataViewItem PrusaObjectDataViewModel::GetParent(const wxDataViewItem &item) const
{
@@ -738,6 +833,87 @@ unsigned int PrusaObjectDataViewModel::GetChildren(const wxDataViewItem &parent,
return count;
}
+wxDataViewItem PrusaObjectDataViewModel::HasSettings(const wxDataViewItem &item) const
+{
+ if (!item.IsOk())
+ return wxDataViewItem(0);
+
+ PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+ if (node->GetChildCount() == 0)
+ return wxDataViewItem(0);
+
+ auto& children = node->GetChildren();
+ if (children[0]->m_type == "settings")
+ return wxDataViewItem((void*)children[0]);;
+
+ return wxDataViewItem(0);
+}
+
+bool PrusaObjectDataViewModel::IsSettingsItem(const wxDataViewItem &item) const
+{
+ if (!item.IsOk())
+ return false;
+ PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+ return node->m_type == "settings";
+}
+
+
+
+void PrusaObjectDataViewModel::UpdateSettingsDigest(const wxDataViewItem &item,
+ const std::vector<std::string>& categories)
+{
+ if (!item.IsOk()) return;
+ PrusaObjectDataViewModelNode *node = (PrusaObjectDataViewModelNode*)item.GetID();
+ if (!node->update_settings_digest(categories))
+ return;
+ ItemChanged(item);
+}
+
+IMPLEMENT_VARIANT_OBJECT(PrusaDataViewBitmapText)
+// ---------------------------------------------------------
+// PrusaIconTextRenderer
+// ---------------------------------------------------------
+
+bool PrusaBitmapTextRenderer::SetValue(const wxVariant &value)
+{
+ m_value << value;
+ return true;
+}
+
+bool PrusaBitmapTextRenderer::GetValue(wxVariant& WXUNUSED(value)) const
+{
+ return false;
+}
+
+bool PrusaBitmapTextRenderer::Render(wxRect rect, wxDC *dc, int state)
+{
+ int xoffset = 0;
+
+ const wxBitmap& icon = m_value.GetBitmap();
+ if (icon.IsOk())
+ {
+ dc->DrawBitmap(icon, rect.x, rect.y + (rect.height - icon.GetHeight()) / 2);
+ xoffset = icon.GetWidth() + 4;
+ }
+
+ RenderText(m_value.GetText(), xoffset, rect, dc, state);
+
+ return true;
+}
+
+wxSize PrusaBitmapTextRenderer::GetSize() const
+{
+ if (!m_value.GetText().empty())
+ {
+ wxSize size = GetTextExtent(m_value.GetText());
+
+ if (m_value.GetBitmap().IsOk())
+ size.x += m_value.GetBitmap().GetWidth() + 4;
+ return size;
+ }
+ return wxSize(80, 20);
+}
+
// ----------------------------------------------------------------------------
// PrusaDoubleSlider
@@ -829,16 +1005,28 @@ wxSize PrusaDoubleSlider::DoGetBestSize() const
void PrusaDoubleSlider::SetLowerValue(const int lower_val)
{
+ m_selection = ssLower;
m_lower_value = lower_val;
+ correct_lower_value();
Refresh();
Update();
+
+ wxCommandEvent e(wxEVT_SCROLL_CHANGED);
+ e.SetEventObject(this);
+ ProcessWindowEvent(e);
}
void PrusaDoubleSlider::SetHigherValue(const int higher_val)
{
+ m_selection = ssHigher;
m_higher_value = higher_val;
+ correct_higher_value();
Refresh();
Update();
+
+ wxCommandEvent e(wxEVT_SCROLL_CHANGED);
+ e.SetEventObject(this);
+ ProcessWindowEvent(e);
}
void PrusaDoubleSlider::SetMaxValue(const int max_value)
@@ -905,6 +1093,13 @@ void PrusaDoubleSlider::get_size(int *w, int *h)
is_horizontal() ? *w -= m_lock_icon_dim : *h -= m_lock_icon_dim;
}
+double PrusaDoubleSlider::get_double_value(const SelectedSlider& selection) const
+{
+ if (m_values.empty())
+ return 0.0;
+ return m_values[selection == ssLower ? m_lower_value : m_higher_value].second;
+}
+
void PrusaDoubleSlider::get_lower_and_higher_position(int& lower_pos, int& higher_pos)
{
const double step = get_scroll_step();
@@ -1010,7 +1205,7 @@ wxString PrusaDoubleSlider::get_label(const SelectedSlider& selection) const
void PrusaDoubleSlider::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const
{
- if (m_is_one_layer && selection != m_selection || !selection)
+ if ((m_is_one_layer || m_higher_value==m_lower_value) && selection != m_selection || !selection)
return;
wxCoord text_width, text_height;
const wxString label = get_label(selection);
@@ -1178,6 +1373,20 @@ bool PrusaDoubleSlider::is_point_in_rect(const wxPoint& pt, const wxRect& rect)
return false;
}
+void PrusaDoubleSlider::ChangeOneLayerLock()
+{
+ m_is_one_layer = !m_is_one_layer;
+ m_selection == ssLower ? correct_lower_value() : correct_higher_value();
+ if (!m_selection) m_selection = ssHigher;
+
+ Refresh();
+ Update();
+
+ wxCommandEvent e(wxEVT_SCROLL_CHANGED);
+ e.SetEventObject(this);
+ ProcessWindowEvent(e);
+}
+
void PrusaDoubleSlider::OnLeftDown(wxMouseEvent& event)
{
this->CaptureMouse();
@@ -1245,6 +1454,10 @@ void PrusaDoubleSlider::OnMotion(wxMouseEvent& event)
Refresh();
Update();
event.Skip();
+
+ wxCommandEvent e(wxEVT_SCROLL_CHANGED);
+ e.SetEventObject(this);
+ ProcessWindowEvent(e);
}
void PrusaDoubleSlider::OnLeftUp(wxMouseEvent& event)
diff --git a/xs/src/slic3r/GUI/wxExtensions.hpp b/xs/src/slic3r/GUI/wxExtensions.hpp
index 76e59f2eb..51c02035c 100644
--- a/xs/src/slic3r/GUI/wxExtensions.hpp
+++ b/xs/src/slic3r/GUI/wxExtensions.hpp
@@ -144,6 +144,49 @@ public:
#endif //__WXMSW__
// *****************************************************************************
+
+// ----------------------------------------------------------------------------
+// PrusaDataViewBitmapText: helper class used by PrusaBitmapTextRenderer
+// ----------------------------------------------------------------------------
+
+class PrusaDataViewBitmapText : public wxObject
+{
+public:
+ PrusaDataViewBitmapText(const wxString &text = wxEmptyString,
+ const wxBitmap& bmp = wxNullBitmap) :
+ m_text(text), m_bmp(bmp)
+ { }
+
+ PrusaDataViewBitmapText(const PrusaDataViewBitmapText &other)
+ : wxObject(),
+ m_text(other.m_text),
+ m_bmp(other.m_bmp)
+ { }
+
+ void SetText(const wxString &text) { m_text = text; }
+ wxString GetText() const { return m_text; }
+ void SetBitmap(const wxIcon &icon) { m_bmp = icon; }
+ const wxBitmap &GetBitmap() const { return m_bmp; }
+
+ bool IsSameAs(const PrusaDataViewBitmapText& other) const {
+ return m_text == other.m_text && m_bmp.IsSameAs(other.m_bmp);
+ }
+
+ bool operator==(const PrusaDataViewBitmapText& other) const {
+ return IsSameAs(other);
+ }
+
+ bool operator!=(const PrusaDataViewBitmapText& other) const {
+ return !IsSameAs(other);
+ }
+
+private:
+ wxString m_text;
+ wxBitmap m_bmp;
+};
+DECLARE_VARIANT_OBJECT(PrusaDataViewBitmapText)
+
+
// ----------------------------------------------------------------------------
// PrusaObjectDataViewModelNode: a node inside PrusaObjectDataViewModel
// ----------------------------------------------------------------------------
@@ -155,7 +198,9 @@ class PrusaObjectDataViewModelNode
{
PrusaObjectDataViewModelNode* m_parent;
MyObjectTreeModelNodePtrArray m_children;
- wxIcon m_empty_icon;
+ wxIcon m_empty_icon;
+ wxBitmap m_empty_bmp;
+ std::vector< std::string > m_opt_categories;
public:
PrusaObjectDataViewModelNode(const wxString &name, const int instances_count=1) {
m_parent = NULL;
@@ -174,18 +219,31 @@ public:
PrusaObjectDataViewModelNode( PrusaObjectDataViewModelNode* parent,
const wxString& sub_obj_name,
- const wxIcon& icon,
+ const wxBitmap& bmp,
const wxString& extruder,
const int volume_id=-1) {
m_parent = parent;
m_name = sub_obj_name;
m_copy = wxEmptyString;
- m_icon = icon;
+ m_bmp = bmp;
m_type = "volume";
m_volume_id = volume_id;
- m_extruder = extruder;
+ m_extruder = extruder;
+#ifdef __WXGTK__
+ // it's necessary on GTK because of control have to know if this item will be container
+ // in another case you couldn't to add subitem for this item
+ // it will be produce "segmentation fault"
+ m_container = true;
+#endif //__WXGTK__
set_part_action_icon();
- }
+ }
+
+ PrusaObjectDataViewModelNode( PrusaObjectDataViewModelNode* parent) :
+ m_parent(parent),
+ m_name("Settings to modified"),
+ m_copy(wxEmptyString),
+ m_type("settings"),
+ m_extruder(wxEmptyString) {}
~PrusaObjectDataViewModelNode()
{
@@ -200,9 +258,10 @@ public:
wxString m_name;
wxIcon& m_icon = m_empty_icon;
+ wxBitmap& m_bmp = m_empty_bmp;
wxString m_copy;
std::string m_type;
- int m_volume_id;
+ int m_volume_id = -2;
bool m_container = false;
wxString m_extruder = "default";
wxBitmap m_action_icon;
@@ -226,6 +285,8 @@ public:
}
void Insert(PrusaObjectDataViewModelNode* child, unsigned int n)
{
+ if (!m_container)
+ m_container = true;
m_children.Insert(child, n);
}
void Append(PrusaObjectDataViewModelNode* child)
@@ -258,9 +319,9 @@ public:
switch (col)
{
case 0:{
- wxDataViewIconText data;
+ PrusaDataViewBitmapText data;
data << variant;
- m_icon = data.GetIcon();
+ m_bmp = data.GetBitmap();
m_name = data.GetText();
return true;}
case 1:
@@ -281,6 +342,11 @@ public:
{
m_icon = icon;
}
+
+ void SetBitmap(const wxBitmap &icon)
+ {
+ m_bmp = icon;
+ }
void SetType(const std::string& type){
m_type = type;
@@ -326,6 +392,7 @@ public:
// Set action icons for node
void set_object_action_icon();
void set_part_action_icon();
+ bool update_settings_digest(const std::vector<std::string>& categories);
};
// ----------------------------------------------------------------------------
@@ -336,26 +403,24 @@ class PrusaObjectDataViewModel :public wxDataViewModel
{
std::vector<PrusaObjectDataViewModelNode*> m_objects;
public:
- PrusaObjectDataViewModel(){}
- ~PrusaObjectDataViewModel()
- {
- for (auto object : m_objects)
- delete object;
- }
+ PrusaObjectDataViewModel();
+ ~PrusaObjectDataViewModel();
wxDataViewItem Add(const wxString &name);
wxDataViewItem Add(const wxString &name, const int instances_count);
wxDataViewItem AddChild(const wxDataViewItem &parent_item,
const wxString &name,
- const wxIcon& icon,
- const int = 0,
+ const wxBitmap& icon,
+ const int extruder = 0,
const bool create_frst_child = true);
+ wxDataViewItem AddSettingsChild(const wxDataViewItem &parent_item);
wxDataViewItem Delete(const wxDataViewItem &item);
void DeleteAll();
void DeleteChildren(wxDataViewItem& parent);
wxDataViewItem GetItemById(int obj_idx);
+ wxDataViewItem GetItemByVolumeId(int obj_idx, int volume_idx);
int GetIdByItem(wxDataViewItem& item);
- int GetVolumeIdByItem(wxDataViewItem& item);
+ int GetVolumeIdByItem(const wxDataViewItem& item);
bool IsEmpty() { return m_objects.empty(); }
// helper method for wxLog
@@ -363,6 +428,7 @@ public:
wxString GetName(const wxDataViewItem &item) const;
wxString GetCopy(const wxDataViewItem &item) const;
wxIcon& GetIcon(const wxDataViewItem &item) const;
+ wxBitmap& GetBitmap(const wxDataViewItem &item) const;
// helper methods to change the model
@@ -383,8 +449,7 @@ public:
int new_volume_id,
const wxDataViewItem &parent);
-// virtual bool IsEnabled(const wxDataViewItem &item,
-// unsigned int col) const override;
+ virtual bool IsEnabled(const wxDataViewItem &item, unsigned int col) const override;
virtual wxDataViewItem GetParent(const wxDataViewItem &item) const override;
virtual bool IsContainer(const wxDataViewItem &item) const override;
@@ -394,8 +459,35 @@ public:
// Is the container just a header or an item with all columns
// In our case it is an item with all columns
virtual bool HasContainerColumns(const wxDataViewItem& WXUNUSED(item)) const override { return true; }
+
+ wxDataViewItem HasSettings(const wxDataViewItem &item) const;
+ bool IsSettingsItem(const wxDataViewItem &item) const;
+ void UpdateSettingsDigest(const wxDataViewItem &item, const std::vector<std::string>& categories);
};
+// ----------------------------------------------------------------------------
+// PrusaBitmapTextRenderer
+// ----------------------------------------------------------------------------
+
+class PrusaBitmapTextRenderer : public wxDataViewCustomRenderer
+{
+public:
+ PrusaBitmapTextRenderer( wxDataViewCellMode mode = wxDATAVIEW_CELL_INERT,
+ int align = wxDVR_DEFAULT_ALIGNMENT):
+ wxDataViewCustomRenderer(wxT("wxObject"), mode, align) {}
+
+ bool SetValue(const wxVariant &value);
+ bool GetValue(wxVariant &value) const;
+
+ virtual bool Render(wxRect cell, wxDC *dc, int state);
+ virtual wxSize GetSize() const;
+
+ virtual bool HasEditorCtrl() const { return false; }
+
+private:
+// wxDataViewIconText m_value;
+ PrusaDataViewBitmapText m_value;
+};
// ----------------------------------------------------------------------------
@@ -516,7 +608,7 @@ public:
int maxValue,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
- long style = wxSL_HORIZONTAL,
+ long style = wxSL_VERTICAL,
const wxValidator& val = wxDefaultValidator,
const wxString& name = wxEmptyString);
~PrusaDoubleSlider(){}
@@ -528,6 +620,8 @@ public:
return m_higher_value;
}
int GetActiveValue() const;
+ double GetLowerValueD() const { return get_double_value(ssLower); }
+ double GetHigherValueD() const { return get_double_value(ssHigher); }
wxSize DoGetBestSize() const override;
void SetLowerValue(const int lower_val);
void SetHigherValue(const int higher_val);
@@ -538,6 +632,7 @@ public:
void SetSliderValues(const std::vector<std::pair<int, double>>& values) {
m_values = values;
}
+ void ChangeOneLayerLock();
void OnPaint(wxPaintEvent& ){ render();}
void OnLeftDown(wxMouseEvent& event);
@@ -583,6 +678,7 @@ protected:
wxCoord get_position_from_value(const int value);
wxSize get_size();
void get_size(int *w, int *h);
+ double get_double_value(const SelectedSlider& selection) const;
private:
int m_min_value;
diff --git a/xs/src/slic3r/Utils/Serial.cpp b/xs/src/slic3r/Utils/Serial.cpp
index 183119b44..601719b50 100644
--- a/xs/src/slic3r/Utils/Serial.cpp
+++ b/xs/src/slic3r/Utils/Serial.cpp
@@ -231,7 +231,12 @@ std::vector<SerialPortInfo> scan_serial_ports_extended()
spi.port = path;
#ifdef __linux__
auto friendly_name = sysfs_tty_prop(name, "product");
- spi.friendly_name = friendly_name ? (boost::format("%1% (%2%)") % *friendly_name % path).str() : path;
+ if (friendly_name) {
+ spi.is_printer = looks_like_printer(*friendly_name);
+ spi.friendly_name = (boost::format("%1% (%2%)") % *friendly_name % path).str();
+ } else {
+ spi.friendly_name = path;
+ }
auto vid = sysfs_tty_prop_hex(name, "idVendor");
auto pid = sysfs_tty_prop_hex(name, "idProduct");
if (vid && pid) {
diff --git a/xs/xsp/GUI.xsp b/xs/xsp/GUI.xsp
index 6700a4765..a4d656616 100644
--- a/xs/xsp/GUI.xsp
+++ b/xs/xsp/GUI.xsp
@@ -96,26 +96,21 @@ void add_frequently_changed_parameters(SV *ui_parent, SV *ui_sizer, SV *ui_p_siz
void set_print_callback_event(Print *print, int id)
%code%{ Slic3r::GUI::set_print_callback_event(print, id); %};
-void add_expert_mode_part( SV *ui_parent, SV *ui_sizer,
- Model *model,
- int event_object_selection_changed,
- int event_object_settings_changed,
- int event_remove_object,
- int event_update_scene)
- %code%{ Slic3r::GUI::add_expert_mode_part( (wxWindow*)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"),
- (wxBoxSizer*)wxPli_sv_2_object(aTHX_ ui_sizer, "Wx::BoxSizer"),
- *model,
- event_object_selection_changed,
- event_object_settings_changed,
- event_remove_object,
- event_update_scene); %};
+void set_model_events_from_perl(Model *model,
+ int event_object_selection_changed,
+ int event_object_settings_changed,
+ int event_remove_object,
+ int event_update_scene)
+ %code%{ Slic3r::GUI::set_model_events_from_perl(*model,
+ event_object_selection_changed,
+ event_object_settings_changed,
+ event_remove_object,
+ event_update_scene); %};
void set_objects_from_perl( SV *ui_parent,
SV *frequently_changed_parameters_sizer,
- SV *expert_mode_part_sizer,
- SV *scrolled_window_sizer,
+ SV *info_sizer,
SV *btn_export_gcode,
- SV *btn_export_stl,
SV *btn_reslice,
SV *btn_print,
SV *btn_send_gcode,
@@ -123,10 +118,8 @@ void set_objects_from_perl( SV *ui_parent,
%code%{ Slic3r::GUI::set_objects_from_perl(
(wxWindow *)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"),
(wxBoxSizer *)wxPli_sv_2_object(aTHX_ frequently_changed_parameters_sizer, "Wx::BoxSizer"),
- (wxBoxSizer *)wxPli_sv_2_object(aTHX_ expert_mode_part_sizer, "Wx::BoxSizer"),
- (wxBoxSizer *)wxPli_sv_2_object(aTHX_ scrolled_window_sizer, "Wx::BoxSizer"),
+ (wxBoxSizer *)wxPli_sv_2_object(aTHX_ info_sizer, "Wx::BoxSizer"),
(wxButton *)wxPli_sv_2_object(aTHX_ btn_export_gcode, "Wx::Button"),
- (wxButton *)wxPli_sv_2_object(aTHX_ btn_export_stl, "Wx::Button"),
(wxButton *)wxPli_sv_2_object(aTHX_ btn_reslice, "Wx::Button"),
(wxButton *)wxPli_sv_2_object(aTHX_ btn_print, "Wx::Button"),
(wxButton *)wxPli_sv_2_object(aTHX_ btn_send_gcode, "Wx::Button"),
@@ -161,6 +154,9 @@ void unselect_objects()
void select_current_object(int idx)
%code%{ Slic3r::GUI::select_current_object(idx); %};
+void select_current_volume(int idx, int vol_idx)
+ %code%{ Slic3r::GUI::select_current_volume(idx, vol_idx); %};
+
void remove_obj()
%code%{ Slic3r::GUI::remove(); %};
@@ -179,16 +175,28 @@ void desktop_open_datadir_folder()
void fix_model_by_win10_sdk_gui(ModelObject *model_object_src, Print *print, Model *model_dst)
%code%{ Slic3r::fix_model_by_win10_sdk_gui(*model_object_src, *print, *model_dst); %};
-void set_3DScene(SV *scene)
- %code%{ Slic3r::GUI::set_3DScene((_3DScene *)wxPli_sv_2_object(aTHX_ scene, "Slic3r::Model::3DScene") ); %};
-
void register_on_request_update_callback(SV* callback)
%code%{ Slic3r::GUI::g_on_request_update_callback.register_callback(callback); %};
void deregister_on_request_update_callback()
%code%{ Slic3r::GUI::g_on_request_update_callback.deregister_callback(); %};
-//void create_double_slider(SV *ui_parent, SV *ui_ds)
-// %code%{ Slic3r::GUI::create_double_slider( (wxWindow*)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"),
-// (wxControl*)wxPli_sv_2_object(aTHX_ ui_ds, "Wx::Control")); %};
+void create_double_slider(SV *ui_parent, SV *ui_sizer, SV *ui_canvas)
+ %code%{ Slic3r::GUI::create_double_slider( (wxWindow*)wxPli_sv_2_object(aTHX_ ui_parent, "Wx::Window"),
+ (wxBoxSizer*)wxPli_sv_2_object(aTHX_ ui_sizer, "Wx::BoxSizer"),
+ (wxGLCanvas*)wxPli_sv_2_object(aTHX_ ui_canvas, "Wx::GLCanvas")); %};
+
+void update_double_slider(bool force_sliders_full_range)
+ %code%{ Slic3r::GUI::update_double_slider(force_sliders_full_range); %};
+
+void reset_double_slider()
+ %code%{ Slic3r::GUI::reset_double_slider(); %};
+
+void enable_action_buttons(bool enable)
+ %code%{ Slic3r::GUI::enable_action_buttons(enable); %};
+
+void save_window_size(SV *window, std::string name)
+ %code%{ Slic3r::GUI::save_window_size((wxTopLevelWindow*)wxPli_sv_2_object(aTHX_ window, "Wx::TopLevelWindow"), name); %};
+void restore_window_size(SV *window, std::string name)
+ %code%{ Slic3r::GUI::restore_window_size((wxTopLevelWindow*)wxPli_sv_2_object(aTHX_ window, "Wx::TopLevelWindow"), name); %};
diff --git a/xs/xsp/GUI_3DScene.xsp b/xs/xsp/GUI_3DScene.xsp
index 4c3c5501e..c3e4ba3b7 100644
--- a/xs/xsp/GUI_3DScene.xsp
+++ b/xs/xsp/GUI_3DScene.xsp
@@ -767,6 +767,15 @@ get_first_volume_id(canvas, obj_idx)
OUTPUT:
RETVAL
+int
+get_in_object_volume_id(canvas, scene_vol_idx)
+ SV *canvas;
+ int scene_vol_idx;
+ CODE:
+ RETVAL = _3DScene::get_in_object_volume_id((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), scene_vol_idx);
+ OUTPUT:
+ RETVAL
+
std::vector<int>
load_model(canvas, model, obj_idx)
SV *canvas;
diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp
index 4f09fb521..efd6c9ae6 100644
--- a/xs/xsp/Layer.xsp
+++ b/xs/xsp/Layer.xsp
@@ -17,8 +17,6 @@
%code%{ RETVAL = &THIS->thin_fills; %};
Ref<SurfaceCollection> fill_surfaces()
%code%{ RETVAL = &THIS->fill_surfaces; %};
- Ref<SurfaceCollection> perimeter_surfaces()
- %code%{ RETVAL = &THIS->perimeter_surfaces; %};
Polygons bridged()
%code%{ RETVAL = THIS->bridged; %};
Ref<PolylineCollection> unsupported_bridge_edges()
diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp
index a5925775d..101faf564 100644
--- a/xs/xsp/Model.xsp
+++ b/xs/xsp/Model.xsp
@@ -340,9 +340,19 @@ ModelMaterial::attributes()
%code%{ RETVAL = &THIS->mesh; %};
bool modifier()
- %code%{ RETVAL = THIS->modifier; %};
+ %code%{ RETVAL = THIS->is_modifier(); %};
void set_modifier(bool modifier)
- %code%{ THIS->modifier = modifier; %};
+ %code%{ THIS->set_type(modifier ? ModelVolume::PARAMETER_MODIFIER : ModelVolume::MODEL_PART); %};
+ bool model_part()
+ %code%{ RETVAL = THIS->is_model_part(); %};
+ bool support_enforcer()
+ %code%{ RETVAL = THIS->is_support_enforcer(); %};
+ void set_support_enforcer()
+ %code%{ THIS->set_type(ModelVolume::SUPPORT_ENFORCER); %};
+ bool support_blocker()
+ %code%{ RETVAL = THIS->is_support_blocker(); %};
+ void set_support_blocker()
+ %code%{ THIS->set_type(ModelVolume::SUPPORT_BLOCKER); %};
size_t split(unsigned int max_extruders);
@@ -358,15 +368,28 @@ ModelMaterial::attributes()
%code%{ RETVAL = THIS->rotation; %};
double scaling_factor()
%code%{ RETVAL = THIS->scaling_factor; %};
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ Vec2d* offset()
+ %code%{ RETVAL = new Vec2d(THIS->get_offset(X), THIS->get_offset(Y)); %};
+#else
Ref<Vec2d> offset()
%code%{ RETVAL = &THIS->offset; %};
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
void set_rotation(double val)
%code%{ THIS->rotation = val; THIS->get_object()->invalidate_bounding_box(); %};
void set_scaling_factor(double val)
%code%{ THIS->scaling_factor = val; THIS->get_object()->invalidate_bounding_box(); %};
+#if ENABLE_MODELINSTANCE_3D_OFFSET
+ void set_offset(Vec2d *offset)
+ %code%{
+ THIS->set_offset(X, (*offset)(0));
+ THIS->set_offset(Y, (*offset)(1));
+ %};
+#else
void set_offset(Vec2d *offset)
%code%{ THIS->offset = *offset; %};
+#endif // ENABLE_MODELINSTANCE_3D_OFFSET
void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const;
void transform_polygon(Polygon* polygon) const;
diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp
index f29cb52fc..0424d8b7b 100644
--- a/xs/xsp/Print.xsp
+++ b/xs/xsp/Print.xsp
@@ -103,6 +103,12 @@ _constant()
%code%{ RETVAL = THIS->print_statistics().total_weight; %};
double total_cost()
%code%{ RETVAL = THIS->print_statistics().total_cost; %};
+ double total_wipe_tower_cost()
+ %code%{ RETVAL = THIS->print_statistics().total_wipe_tower_cost; %};
+ double total_wipe_tower_filament()
+ %code%{ RETVAL = THIS->print_statistics().total_wipe_tower_filament; %};
+ int wipe_tower_number_of_toolchanges()
+ %code%{ RETVAL = THIS->wipe_tower_data().number_of_toolchanges; %};
PrintObjectPtrs* objects()
%code%{ RETVAL = const_cast<PrintObjectPtrs*>(&THIS->objects()); %};
void clear_objects();
diff --git a/xs/xsp/ProgressStatusBar.xsp b/xs/xsp/ProgressStatusBar.xsp
index c089cfd7c..703a53b67 100644
--- a/xs/xsp/ProgressStatusBar.xsp
+++ b/xs/xsp/ProgressStatusBar.xsp
@@ -3,6 +3,7 @@
%{
#include <xsinit.h>
#include "slic3r/GUI/ProgressStatusBar.hpp"
+#include "slic3r/GUI/GUI.hpp"
%}
%name{Slic3r::GUI::ProgressStatusBar} class ProgressStatusBar {
@@ -37,7 +38,7 @@
%code%{ THIS->embed(); %};
void SetStatusText(const char *txt)
- %code%{ THIS->set_status_text(txt); %};
+ %code%{ THIS->set_status_text(_(txt)); %};
void SetCancelCallback(SV* callback)
%code%{ THIS->m_perl_cancel_callback.register_callback(callback); THIS->show_cancel_button();%};
diff --git a/xs/xsp/XS.xsp b/xs/xsp/XS.xsp
index e900532aa..04969a7f9 100644
--- a/xs/xsp/XS.xsp
+++ b/xs/xsp/XS.xsp
@@ -49,6 +49,11 @@ trace(level, message)
Slic3r::trace(level, message);
void
+disable_multi_threading()
+ CODE:
+ Slic3r::disable_multi_threading();
+
+void
set_var_dir(dir)
char *dir;
CODE: