Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_gcode_window
This commit is contained in:
commit
db71a6308d
@ -146,21 +146,33 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
# WIN10SDK_PATH is used to point CMake to the WIN10 SDK installation directory.
|
||||
# We pick it from environment if it is not defined in another way
|
||||
if(WIN32)
|
||||
if(NOT DEFINED WIN10SDK_PATH)
|
||||
if(DEFINED ENV{WIN10SDK_PATH})
|
||||
set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}")
|
||||
endif()
|
||||
if(NOT DEFINED WIN10SDK_PATH)
|
||||
if(DEFINED ENV{WIN10SDK_PATH})
|
||||
set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}")
|
||||
endif()
|
||||
if(DEFINED WIN10SDK_PATH AND NOT EXISTS "${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h")
|
||||
message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}")
|
||||
message("${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h was not found")
|
||||
message("STL fixing by the Netfabb service will not be compiled")
|
||||
unset(WIN10SDK_PATH)
|
||||
endif()
|
||||
if(DEFINED WIN10SDK_PATH)
|
||||
if (EXISTS "${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h")
|
||||
set(WIN10SDK_INCLUDE_PATH "${WIN10SDK_PATH}/Include")
|
||||
else()
|
||||
message("WIN10SDK_PATH is invalid: ${WIN10SDK_PATH}")
|
||||
message("${WIN10SDK_PATH}/include/winrt/windows.graphics.printing3d.h was not found")
|
||||
message("STL fixing by the Netfabb service will not be compiled")
|
||||
unset(WIN10SDK_PATH)
|
||||
endif()
|
||||
if(WIN10SDK_PATH)
|
||||
else()
|
||||
# Try to use the default Windows 10 SDK path.
|
||||
set(WIN10SDK_INCLUDE_PATH "$ENV{WindowsSdkDir}/Include/$ENV{WindowsSDKVersion}")
|
||||
if (NOT EXISTS "${WIN10SDK_INCLUDE_PATH}/winrt/windows.graphics.printing3d.h")
|
||||
message("${WIN10SDK_INCLUDE_PATH}/winrt/windows.graphics.printing3d.h was not found")
|
||||
message("STL fixing by the Netfabb service will not be compiled")
|
||||
unset(WIN10SDK_INCLUDE_PATH)
|
||||
endif()
|
||||
endif()
|
||||
if(WIN10SDK_INCLUDE_PATH)
|
||||
message("Building with Win10 Netfabb STL fixing service support")
|
||||
add_definitions(-DHAS_WIN10SDK)
|
||||
include_directories("${WIN10SDK_PATH}/Include")
|
||||
include_directories("${WIN10SDK_INCLUDE_PATH}")
|
||||
else()
|
||||
message("Building without Win10 Netfabb STL fixing service support")
|
||||
endif()
|
||||
|
1
deps/wxWidgets/wxWidgets.cmake
vendored
1
deps/wxWidgets/wxWidgets.cmake
vendored
@ -20,6 +20,7 @@ prusaslicer_add_cmake_project(wxWidgets
|
||||
${_wx_toolkit}
|
||||
"-DCMAKE_DEBUG_POSTFIX:STRING="
|
||||
-DwxBUILD_DEBUG_LEVEL=0
|
||||
-DwxUSE_MEDIACTRL=OFF
|
||||
-DwxUSE_DETECT_SM=OFF
|
||||
-DwxUSE_UNICODE=ON
|
||||
-DwxUSE_OPENGL=ON
|
||||
|
67
resources/icons/notification_cancel.svg
Normal file
67
resources/icons/notification_cancel.svg
Normal file
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 800 800"
|
||||
style="enable-background:new 0 0 800 800;"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="notification_cancel.svg"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><metadata
|
||||
id="metadata15"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs13" /><sodipodi:namedview
|
||||
inkscape:document-rotation="0"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="2066"
|
||||
id="namedview11"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.26"
|
||||
inkscape:cx="400"
|
||||
inkscape:cy="389.28571"
|
||||
inkscape:window-x="-11"
|
||||
inkscape:window-y="-11"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#ED6B21;}
|
||||
</style>
|
||||
|
||||
|
||||
<g
|
||||
id="g4"
|
||||
transform="matrix(0.9775,0,0,0.9775,53.547,53.54775)">
|
||||
<path
|
||||
id="path2"
|
||||
class="st0"
|
||||
d="M 597.2,701.3 H 110.6 C 53.2,701.3 6.5,654.6 6.5,597.2 V 110.6 C 6.5,53.2 53.2,6.5 110.6,6.5 h 486.6 c 57.4,0 104.1,46.7 104.1,104.1 v 486.6 c 0,57.4 -46.7,104.1 -104.1,104.1 z M 110.6,52.4 c -32,0 -58.2,26 -58.2,58.2 v 486.6 c 0,32 26,58.2 58.2,58.2 h 486.6 c 32,0 58.2,-26 58.2,-58.2 V 110.6 c 0,-32 -26,-58.2 -58.2,-58.2 z" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.674603"
|
||||
d="m 150.65676,738.12999 c -12.4717,-1.39663 -26.66772,-5.94192 -37.84321,-12.11671 -17.754551,-9.80992 -33.768844,-26.68981 -42.418124,-44.71089 -5.985061,-12.4701 -8.760227,-23.35456 -9.821918,-38.52249 -0.48061,-6.8663 -0.640464,-87.42616 -0.497289,-250.61508 0.195544,-222.88027 0.294923,-240.94223 1.356742,-246.58759 4.2349,-22.51562 13.68014,-40.62012 29.200931,-55.97194 14.237938,-14.082924 31.958648,-23.427941 52.602238,-27.739791 5.87892,-1.227937 14.00696,-1.268146 256.3492,-1.268146 h 250.27778 l 7.08334,1.561512 c 21.30688,4.697075 36.90336,13.216072 51.96052,28.381502 14.67865,14.784203 23.1932,30.350373 27.76125,50.752683 l 1.56791,7.00271 v 250.95239 c 0,242.72256 -0.0418,251.15149 -1.26428,257.0238 -9.30592,44.69034 -45.18963,77.43352 -89.75566,81.90028 -9.17898,0.92002 -488.33076,0.87927 -496.55943,-0.0425 z M 652.87275,692.49 c 19.93824,-6.17834 34.6922,-21.42493 40.00111,-41.33675 l 1.51306,-5.67494 V 399.58544 153.69259 l -1.52571,-5.73412 c -5.66288,-21.28292 -21.4158,-36.89778 -42.2051,-41.83523 -5.63965,-1.33941 -7.66026,-1.3488 -253.17948,-1.17613 l -247.49447,0.17405 -4.72222,1.5953 c -18.05932,6.10093 -31.7315,19.23923 -37.4918,36.0278 -1.04762,3.05333 -2.22128,7.52472 -2.60813,9.93642 -0.47859,2.9836 -0.705,81.91876 -0.70847,246.99889 -0.005,218.14117 0.10226,243.1829 1.05916,248.25397 4.27172,22.63802 22.24346,40.86392 44.80877,45.4425 3.58848,0.72811 49.16893,0.87009 250.95237,0.78171 l 246.56747,-0.10801 z"
|
||||
id="path17" /><rect
|
||||
y="239.60318"
|
||||
x="240.87302"
|
||||
height="321.58731"
|
||||
width="322.22223"
|
||||
id="rect30"
|
||||
style="fill:#ed6b21;stroke-width:0.4" /></svg>
|
After Width: | Height: | Size: 3.6 KiB |
67
resources/icons/notification_cancel_hover.svg
Normal file
67
resources/icons/notification_cancel_hover.svg
Normal file
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 800 800"
|
||||
style="enable-background:new 0 0 800 800;"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="notification_cancel_hover.svg"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><metadata
|
||||
id="metadata15"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs13" /><sodipodi:namedview
|
||||
inkscape:document-rotation="0"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="3840"
|
||||
inkscape:window-height="2066"
|
||||
id="namedview11"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.26"
|
||||
inkscape:cx="400"
|
||||
inkscape:cy="389.28571"
|
||||
inkscape:window-x="-11"
|
||||
inkscape:window-y="-11"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" />
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#ED6B21;}
|
||||
</style>
|
||||
|
||||
|
||||
<g
|
||||
id="g4"
|
||||
transform="matrix(1.173,0,0,1.173,-15.64045,-15.6397)">
|
||||
<path
|
||||
id="path2"
|
||||
class="st0"
|
||||
d="M 597.2,701.3 H 110.6 C 53.2,701.3 6.5,654.6 6.5,597.2 V 110.6 C 6.5,53.2 53.2,6.5 110.6,6.5 h 486.6 c 57.4,0 104.1,46.7 104.1,104.1 v 486.6 c 0,57.4 -46.7,104.1 -104.1,104.1 z M 110.6,52.4 c -32,0 -58.2,26 -58.2,58.2 v 486.6 c 0,32 26,58.2 58.2,58.2 h 486.6 c 32,0 58.2,-26 58.2,-58.2 V 110.6 c 0,-32 -26,-58.2 -58.2,-58.2 z" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:#ed6b21;fill-opacity:1;stroke-width:0.809524"
|
||||
d="M 100.89126,805.85899 C 85.925222,804.18303 68.889998,798.72868 55.47941,791.31894 34.173949,779.54703 14.956797,759.29116 4.5776612,737.66587 -2.604412,722.70175 -5.9346112,709.6404 -7.2086404,691.43888 -7.7853724,683.19932 -7.9771972,586.52749 -7.8053872,390.70078 -7.5707344,123.24446 -7.4514796,101.57011 -6.1772968,94.795676 -1.0954168,67.776932 10.238871,46.051532 28.86382,27.629348 45.949346,10.729839 67.214198,-0.4841812 91.986506,-5.6584012 99.04121,-7.1319256 108.79486,-7.1801764 399.60555,-7.1801764 h 300.33333 l 8.50001,1.8738144 c 25.56826,5.63649 44.28403,15.859286 62.35262,34.057802 17.61438,17.741044 27.83184,36.420448 33.3135,60.90322 l 1.8815,8.403252 V 399.20078 c 0,291.26707 -0.0502,301.38179 -1.51714,308.42856 -11.1671,53.62841 -54.22756,92.92022 -107.70679,98.28034 -11.01478,1.10402 -585.99691,1.05512 -595.87132,-0.051 z M 703.55045,751.091 c 23.92589,-7.41401 41.63064,-25.70992 48.00133,-49.6041 l 1.81567,-6.80993 V 399.60553 104.53411 L 751.5366,97.653164 C 744.74115,72.11366 725.83764,53.375828 700.89048,47.450888 694.1229,45.843596 691.69817,45.832328 397.07511,46.039532 l -296.99337,0.20886 -5.666662,1.91436 c -21.671184,7.321116 -38.0778,23.087076 -44.99016,43.23336 -1.257144,3.663996 -2.665536,9.029668 -3.129756,11.923708 -0.574308,3.58032 -0.846,98.30251 -0.850164,296.39866 -0.006,261.76941 0.122712,291.81948 1.270992,297.90477 5.126064,27.16562 26.692152,49.0367 53.77052,54.531 4.30618,0.87373 59.00272,1.04411 301.14285,0.93805 l 295.88096,-0.12961 z"
|
||||
id="path17" /><rect
|
||||
y="207.62682"
|
||||
x="209.15077"
|
||||
height="385.90479"
|
||||
width="386.66669"
|
||||
id="rect30"
|
||||
style="fill:#ed6b21;stroke-width:0.48" /></svg>
|
After Width: | Height: | Size: 3.7 KiB |
@ -28,6 +28,7 @@ src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
|
||||
src/slic3r/GUI/GUI.cpp
|
||||
src/slic3r/GUI/GUI_App.cpp
|
||||
src/slic3r/GUI/GUI_Init.cpp
|
||||
src/slic3r/GUI/GUI_Factories.cpp
|
||||
src/slic3r/GUI/GUI_ObjectLayers.cpp
|
||||
src/slic3r/GUI/GUI_ObjectList.cpp
|
||||
src/slic3r/GUI/GUI_ObjectManipulation.cpp
|
||||
|
@ -123,6 +123,8 @@ namespace ImGui
|
||||
const char ErrorMarker = 0x11;
|
||||
const char EjectButton = 0x12;
|
||||
const char EjectHoverButton = 0x13;
|
||||
const char CancelButton = 0x14;
|
||||
const char CancelHoverButton = 0x15;
|
||||
// void MyFunction(const char* name, const MyMatrix44& v);
|
||||
|
||||
}
|
||||
|
@ -21,22 +21,30 @@ public:
|
||||
min(pmin), max(pmax), defined(pmin(0) < pmax(0) && pmin(1) < pmax(1)) {}
|
||||
BoundingBoxBase(const PointClass &p1, const PointClass &p2, const PointClass &p3) :
|
||||
min(p1), max(p1), defined(false) { merge(p2); merge(p3); }
|
||||
BoundingBoxBase(const std::vector<PointClass>& points) : min(PointClass::Zero()), max(PointClass::Zero())
|
||||
|
||||
template<class It, class = IteratorOnly<It> >
|
||||
BoundingBoxBase(It from, It to) : min(PointClass::Zero()), max(PointClass::Zero())
|
||||
{
|
||||
if (points.empty()) {
|
||||
if (from == to) {
|
||||
this->defined = false;
|
||||
// throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBoxBase constructor");
|
||||
} else {
|
||||
typename std::vector<PointClass>::const_iterator it = points.begin();
|
||||
this->min = *it;
|
||||
this->max = *it;
|
||||
for (++ it; it != points.end(); ++ it) {
|
||||
this->min = this->min.cwiseMin(*it);
|
||||
this->max = this->max.cwiseMax(*it);
|
||||
auto it = from;
|
||||
this->min = it->template cast<typename PointClass::Scalar>();
|
||||
this->max = this->min;
|
||||
for (++ it; it != to; ++ it) {
|
||||
auto vec = it->template cast<typename PointClass::Scalar>();
|
||||
this->min = this->min.cwiseMin(vec);
|
||||
this->max = this->max.cwiseMax(vec);
|
||||
}
|
||||
this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1));
|
||||
}
|
||||
}
|
||||
|
||||
BoundingBoxBase(const std::vector<PointClass> &points)
|
||||
: BoundingBoxBase(points.begin(), points.end())
|
||||
{}
|
||||
|
||||
void reset() { this->defined = false; this->min = PointClass::Zero(); this->max = PointClass::Zero(); }
|
||||
void merge(const PointClass &point);
|
||||
void merge(const std::vector<PointClass> &points);
|
||||
@ -74,19 +82,27 @@ public:
|
||||
{ if (pmin(2) >= pmax(2)) BoundingBoxBase<PointClass>::defined = false; }
|
||||
BoundingBox3Base(const PointClass &p1, const PointClass &p2, const PointClass &p3) :
|
||||
BoundingBoxBase<PointClass>(p1, p1) { merge(p2); merge(p3); }
|
||||
BoundingBox3Base(const std::vector<PointClass>& points)
|
||||
|
||||
template<class It, class = IteratorOnly<It> > BoundingBox3Base(It from, It to)
|
||||
{
|
||||
if (points.empty())
|
||||
if (from == to)
|
||||
throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor");
|
||||
typename std::vector<PointClass>::const_iterator it = points.begin();
|
||||
this->min = *it;
|
||||
this->max = *it;
|
||||
for (++ it; it != points.end(); ++ it) {
|
||||
this->min = this->min.cwiseMin(*it);
|
||||
this->max = this->max.cwiseMax(*it);
|
||||
|
||||
auto it = from;
|
||||
this->min = it->template cast<typename PointClass::Scalar>();
|
||||
this->max = this->min;
|
||||
for (++ it; it != to; ++ it) {
|
||||
auto vec = it->template cast<typename PointClass::Scalar>();
|
||||
this->min = this->min.cwiseMin(vec);
|
||||
this->max = this->max.cwiseMax(vec);
|
||||
}
|
||||
this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1)) && (this->min(2) < this->max(2));
|
||||
}
|
||||
|
||||
BoundingBox3Base(const std::vector<PointClass> &points)
|
||||
: BoundingBox3Base(points.begin(), points.end())
|
||||
{}
|
||||
|
||||
void merge(const PointClass &point);
|
||||
void merge(const std::vector<PointClass> &points);
|
||||
void merge(const BoundingBox3Base<PointClass> &bb);
|
||||
@ -188,9 +204,7 @@ public:
|
||||
class BoundingBoxf3 : public BoundingBox3Base<Vec3d>
|
||||
{
|
||||
public:
|
||||
BoundingBoxf3() : BoundingBox3Base<Vec3d>() {}
|
||||
BoundingBoxf3(const Vec3d &pmin, const Vec3d &pmax) : BoundingBox3Base<Vec3d>(pmin, pmax) {}
|
||||
BoundingBoxf3(const std::vector<Vec3d> &points) : BoundingBox3Base<Vec3d>(points) {}
|
||||
using BoundingBox3Base::BoundingBox3Base;
|
||||
|
||||
BoundingBoxf3 transformed(const Transform3d& matrix) const;
|
||||
};
|
||||
|
@ -320,7 +320,7 @@ static void make_inner_brim(const Print &print, const ConstPrintObjectPtrs &top_
|
||||
loops = union_pt_chained_outside_in(loops, false);
|
||||
std::reverse(loops.begin(), loops.end());
|
||||
extrusion_entities_append_loops(brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()),
|
||||
float(flow.width), float(print.skirt_first_layer_height()));
|
||||
float(flow.width()), float(print.skirt_first_layer_height()));
|
||||
}
|
||||
|
||||
// Produce brim lines around those objects, that have the brim enabled.
|
||||
@ -495,7 +495,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
|
||||
if (i + 1 == j && first_path.size() > 3 && first_path.front().X == first_path.back().X && first_path.front().Y == first_path.back().Y) {
|
||||
auto *loop = new ExtrusionLoop();
|
||||
brim.entities.emplace_back(loop);
|
||||
loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(print.skirt_first_layer_height()));
|
||||
loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()));
|
||||
Points &points = loop->paths.front().polyline.points;
|
||||
points.reserve(first_path.size());
|
||||
for (const ClipperLib_Z::IntPoint &pt : first_path)
|
||||
@ -506,7 +506,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
|
||||
ExtrusionEntityCollection this_loop_trimmed;
|
||||
this_loop_trimmed.entities.reserve(j - i);
|
||||
for (; i < j; ++ i) {
|
||||
this_loop_trimmed.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(print.skirt_first_layer_height())));
|
||||
this_loop_trimmed.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())));
|
||||
const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first;
|
||||
Points &points = static_cast<ExtrusionPath*>(this_loop_trimmed.entities.back())->polyline.points;
|
||||
points.reserve(path.size());
|
||||
@ -522,7 +522,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
|
||||
}
|
||||
}
|
||||
} else {
|
||||
extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(print.skirt_first_layer_height()));
|
||||
extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()));
|
||||
}
|
||||
|
||||
make_inner_brim(print, top_level_objects_with_brim, brim);
|
||||
|
@ -1441,6 +1441,24 @@ private:
|
||||
class ConfigOptionDef
|
||||
{
|
||||
public:
|
||||
enum class GUIType {
|
||||
undefined,
|
||||
// Open enums, integer value could be one of the enumerated values or something else.
|
||||
i_enum_open,
|
||||
// Open enums, float value could be one of the enumerated values or something else.
|
||||
f_enum_open,
|
||||
// Color picker, string value.
|
||||
color,
|
||||
// ???
|
||||
select_open,
|
||||
// Currently unused.
|
||||
slider,
|
||||
// Static text
|
||||
legend,
|
||||
// Vector value, but edited as a single string.
|
||||
one_string,
|
||||
};
|
||||
|
||||
// Identifier of this option. It is stored here so that it is accessible through the by_serialization_key_ordinal map.
|
||||
t_config_option_key opt_key;
|
||||
// What type? bool, int, string etc.
|
||||
@ -1524,7 +1542,7 @@ public:
|
||||
// Usually empty.
|
||||
// Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection,
|
||||
// "select_open" - to open a selection dialog (currently only a serial port selection).
|
||||
std::string gui_type;
|
||||
GUIType gui_type { GUIType::undefined };
|
||||
// Usually empty. Otherwise "serialized" or "show_value"
|
||||
// The flags may be combined.
|
||||
// "serialized" - vector valued option is entered in a single edit field. Values are separated by a semicolon.
|
||||
|
@ -621,7 +621,7 @@ ExPolygon elephant_foot_compensation(const ExPolygon &input_expoly, double min_c
|
||||
ExPolygon elephant_foot_compensation(const ExPolygon &input, const Flow &external_perimeter_flow, const double compensation)
|
||||
{
|
||||
// The contour shall be wide enough to apply the external perimeter plus compensation on both sides.
|
||||
double min_contour_width = double(external_perimeter_flow.width + external_perimeter_flow.spacing());
|
||||
double min_contour_width = double(external_perimeter_flow.width() + external_perimeter_flow.spacing());
|
||||
return elephant_foot_compensation(input, min_contour_width, compensation);
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,9 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale
|
||||
{
|
||||
// Instantiating the Flow class to get the line spacing.
|
||||
// Don't know the nozzle diameter, setting to zero. It shall not matter it shall be optimized out by the compiler.
|
||||
Flow flow(this->width, this->height, 0.f, is_bridge(this->role()));
|
||||
bool bridge = is_bridge(this->role());
|
||||
assert(! bridge || this->width == this->height);
|
||||
auto flow = bridge ? Flow::bridging_flow(this->width, 0.f) : Flow(this->width, this->height, 0.f);
|
||||
polygons_append(out, offset(this->polyline, 0.5f * float(flow.scaled_spacing()) + scaled_epsilon));
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,8 @@ struct SurfaceFillParams
|
||||
// coordf_t overlap = 0.;
|
||||
// Angle as provided by the region config, in radians.
|
||||
float angle = 0.f;
|
||||
// Is bridging used for this fill? Bridging parameters may be used even if this->flow.bridge() is not set.
|
||||
bool bridge;
|
||||
// Non-negative for a bridge.
|
||||
float bridge_angle = 0.f;
|
||||
|
||||
@ -42,7 +44,7 @@ struct SurfaceFillParams
|
||||
|
||||
// width, height of extrusion, nozzle diameter, is bridge
|
||||
// For the output, for fill generator.
|
||||
Flow flow = Flow(0.f, 0.f, 0.f, false);
|
||||
Flow flow;
|
||||
|
||||
// For the output
|
||||
ExtrusionRole extrusion_role = ExtrusionRole(0);
|
||||
@ -70,21 +72,22 @@ struct SurfaceFillParams
|
||||
// RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
|
||||
RETURN_COMPARE_NON_EQUAL(anchor_length);
|
||||
RETURN_COMPARE_NON_EQUAL(anchor_length_max);
|
||||
RETURN_COMPARE_NON_EQUAL(flow.width);
|
||||
RETURN_COMPARE_NON_EQUAL(flow.height);
|
||||
RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter);
|
||||
RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, flow.bridge);
|
||||
RETURN_COMPARE_NON_EQUAL(flow.width());
|
||||
RETURN_COMPARE_NON_EQUAL(flow.height());
|
||||
RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter());
|
||||
RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, bridge);
|
||||
RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, extrusion_role);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator==(const SurfaceFillParams &rhs) const {
|
||||
return this->extruder == rhs.extruder &&
|
||||
this->pattern == rhs.pattern &&
|
||||
this->pattern == rhs.pattern &&
|
||||
this->spacing == rhs.spacing &&
|
||||
// this->overlap == rhs.overlap &&
|
||||
this->angle == rhs.angle &&
|
||||
this->bridge == rhs.bridge &&
|
||||
// this->bridge_angle == rhs.bridge_angle &&
|
||||
this->density == rhs.density &&
|
||||
// this->dont_adjust == rhs.dont_adjust &&
|
||||
this->anchor_length == rhs.anchor_length &&
|
||||
@ -128,6 +131,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||
|
||||
if (surface.is_solid()) {
|
||||
params.density = 100.f;
|
||||
//FIXME for non-thick bridges, shall we allow a bottom surface pattern?
|
||||
params.pattern = (surface.is_external() && ! is_bridge) ?
|
||||
(surface.is_top() ? region_config.top_fill_pattern.value : region_config.bottom_fill_pattern.value) :
|
||||
region_config.top_fill_pattern == ipMonotonic ? ipMonotonic : ipRectilinear;
|
||||
@ -143,17 +147,13 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||
params.bridge_angle = float(surface.bridge_angle);
|
||||
params.angle = float(Geometry::deg2rad(region_config.fill_angle.value));
|
||||
|
||||
// calculate the actual flow we'll be using for this infill
|
||||
params.flow = layerm.region()->flow(
|
||||
extrusion_role,
|
||||
(surface.thickness == -1) ? layer.height : surface.thickness, // extrusion height
|
||||
is_bridge || Fill::use_bridge_flow(params.pattern), // bridge flow?
|
||||
layer.id() == 0, // first layer?
|
||||
-1, // auto width
|
||||
*layer.object()
|
||||
);
|
||||
|
||||
// Calculate flow spacing for infill pattern generation.
|
||||
// Calculate the actual flow we'll be using for this infill.
|
||||
params.bridge = is_bridge || Fill::use_bridge_flow(params.pattern);
|
||||
params.flow = params.bridge ?
|
||||
layerm.bridging_flow(extrusion_role) :
|
||||
layerm.flow(extrusion_role, (surface.thickness == -1) ? layer.height : surface.thickness);
|
||||
|
||||
// Calculate flow spacing for infill pattern generation.
|
||||
if (surface.is_solid() || is_bridge) {
|
||||
params.spacing = params.flow.spacing();
|
||||
// Don't limit anchor length for solid or bridging infill.
|
||||
@ -164,14 +164,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||
// for all layers, for avoiding the ugly effect of
|
||||
// misaligned infill on first layer because of different extrusion width and
|
||||
// layer height
|
||||
params.spacing = layerm.region()->flow(
|
||||
frInfill,
|
||||
layer.object()->config().layer_height.value, // TODO: handle infill_every_layers?
|
||||
false, // no bridge
|
||||
false, // no first layer
|
||||
-1, // auto width
|
||||
*layer.object()
|
||||
).spacing();
|
||||
params.spacing = layerm.flow(frInfill, layer.object()->config().layer_height).spacing();
|
||||
// Anchor a sparse infill to inner perimeters with the following anchor length:
|
||||
params.anchor_length = float(region_config.infill_anchor);
|
||||
if (region_config.infill_anchor.percent)
|
||||
@ -278,7 +271,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||
region_id = region_some_infill;
|
||||
const LayerRegion& layerm = *layer.regions()[region_id];
|
||||
for (SurfaceFill &surface_fill : surface_fills)
|
||||
if (surface_fill.surface.surface_type == stInternalSolid && std::abs(layer.height - surface_fill.params.flow.height) < EPSILON) {
|
||||
if (surface_fill.surface.surface_type == stInternalSolid && std::abs(layer.height - surface_fill.params.flow.height()) < EPSILON) {
|
||||
internal_solid_fill = &surface_fill;
|
||||
break;
|
||||
}
|
||||
@ -290,14 +283,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
||||
params.extrusion_role = erInternalInfill;
|
||||
params.angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value));
|
||||
// calculate the actual flow we'll be using for this infill
|
||||
params.flow = layerm.region()->flow(
|
||||
frSolidInfill,
|
||||
layer.height, // extrusion height
|
||||
false, // bridge flow?
|
||||
layer.id() == 0, // first layer?
|
||||
-1, // auto width
|
||||
*layer.object()
|
||||
);
|
||||
params.flow = layerm.flow(frSolidInfill);
|
||||
params.spacing = params.flow.spacing();
|
||||
surface_fills.emplace_back(params);
|
||||
surface_fills.back().surface.surface_type = stInternalSolid;
|
||||
@ -365,9 +351,9 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
|
||||
|
||||
// calculate flow spacing for infill pattern generation
|
||||
bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge;
|
||||
bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge;
|
||||
double link_max_length = 0.;
|
||||
if (! surface_fill.params.flow.bridge) {
|
||||
if (! surface_fill.params.bridge) {
|
||||
#if 0
|
||||
link_max_length = layerm.region()->config().get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing());
|
||||
// printf("flow spacing: %f, is_external: %d, link_max_length: %lf\n", flow.spacing(), int(surface.is_external()), link_max_length);
|
||||
@ -380,7 +366,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
// Maximum length of the perimeter segment linking two infill lines.
|
||||
f->link_max_length = (coord_t)scale_(link_max_length);
|
||||
// Used by the concentric infill pattern to clip the loops to create extrusion paths.
|
||||
f->loop_clipping = coord_t(scale_(surface_fill.params.flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER);
|
||||
f->loop_clipping = coord_t(scale_(surface_fill.params.flow.nozzle_diameter()) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER);
|
||||
|
||||
// apply half spacing using this flow's own spacing and generate infill
|
||||
FillParams params;
|
||||
@ -402,15 +388,15 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
// calculate actual flow from spacing (which might have been adjusted by the infill
|
||||
// pattern generator)
|
||||
double flow_mm3_per_mm = surface_fill.params.flow.mm3_per_mm();
|
||||
double flow_width = surface_fill.params.flow.width;
|
||||
double flow_width = surface_fill.params.flow.width();
|
||||
if (using_internal_flow) {
|
||||
// if we used the internal flow we're not doing a solid infill
|
||||
// so we can safely ignore the slight variation that might have
|
||||
// been applied to f->spacing
|
||||
} else {
|
||||
Flow new_flow = Flow::new_from_spacing(float(f->spacing), surface_fill.params.flow.nozzle_diameter, surface_fill.params.flow.height, surface_fill.params.flow.bridge);
|
||||
Flow new_flow = surface_fill.params.flow.with_spacing(float(f->spacing));
|
||||
flow_mm3_per_mm = new_flow.mm3_per_mm();
|
||||
flow_width = new_flow.width;
|
||||
flow_width = new_flow.width();
|
||||
}
|
||||
// Save into layer.
|
||||
ExtrusionEntityCollection* eec = nullptr;
|
||||
@ -420,7 +406,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
extrusion_entities_append_paths(
|
||||
eec->entities, std::move(polylines),
|
||||
surface_fill.params.extrusion_role,
|
||||
flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height);
|
||||
flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -618,9 +604,9 @@ void Layer::make_ironing()
|
||||
fill.spacing = ironing_params.line_spacing;
|
||||
fill.angle = float(ironing_params.angle + 0.25 * M_PI);
|
||||
fill.link_max_length = (coord_t)scale_(3. * fill.spacing);
|
||||
double height = ironing_params.height * fill.spacing / nozzle_dmr;
|
||||
Flow flow = Flow::new_from_spacing(float(nozzle_dmr), 0., float(height), false);
|
||||
double flow_mm3_per_mm = flow.mm3_per_mm();
|
||||
double extrusion_height = ironing_params.height * fill.spacing / nozzle_dmr;
|
||||
float extrusion_width = Flow::rounded_rectangle_extrusion_width_from_spacing(float(nozzle_dmr), float(extrusion_height));
|
||||
double flow_mm3_per_mm = nozzle_dmr * extrusion_height;
|
||||
Surface surface_fill(stTop, ExPolygon());
|
||||
for (ExPolygon &expoly : ironing_areas) {
|
||||
surface_fill.expolygon = std::move(expoly);
|
||||
@ -638,7 +624,7 @@ void Layer::make_ironing()
|
||||
extrusion_entities_append_paths(
|
||||
eec->entities, std::move(polylines),
|
||||
erIroning,
|
||||
flow_mm3_per_mm, float(flow.width), float(height));
|
||||
flow_mm3_per_mm, extrusion_width, float(extrusion_height));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,20 +122,13 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionResol
|
||||
|
||||
// This constructor builds a Flow object from an extrusion width config setting
|
||||
// and other context properties.
|
||||
Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio)
|
||||
Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height)
|
||||
{
|
||||
// we need layer height unless it's a bridge
|
||||
if (height <= 0 && bridge_flow_ratio == 0)
|
||||
if (height <= 0)
|
||||
throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_config_width()");
|
||||
|
||||
float w;
|
||||
if (bridge_flow_ratio > 0) {
|
||||
// If bridge flow was requested, calculate the bridge width.
|
||||
height = w = (bridge_flow_ratio == 1.) ?
|
||||
// optimization to avoid sqrt()
|
||||
nozzle_diameter :
|
||||
sqrt(bridge_flow_ratio) * nozzle_diameter;
|
||||
} else if (! width.percent && width.value == 0.) {
|
||||
if (! width.percent && width.value == 0.) {
|
||||
// If user left option to 0, calculate a sane default width.
|
||||
w = auto_extrusion_width(role, nozzle_diameter);
|
||||
} else {
|
||||
@ -143,71 +136,89 @@ Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent
|
||||
w = float(width.get_abs_value(height));
|
||||
}
|
||||
|
||||
return Flow(w, height, nozzle_diameter, bridge_flow_ratio > 0);
|
||||
return Flow(w, height, rounded_rectangle_extrusion_spacing(w, height), nozzle_diameter, false);
|
||||
}
|
||||
|
||||
// This constructor builds a Flow object from a given centerline spacing.
|
||||
Flow Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge)
|
||||
// Adjust extrusion flow for new extrusion line spacing, maintaining the old spacing between extrusions.
|
||||
Flow Flow::with_spacing(float new_spacing) const
|
||||
{
|
||||
// we need layer height unless it's a bridge
|
||||
if (height <= 0 && !bridge)
|
||||
throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_spacing()");
|
||||
// Calculate width from spacing.
|
||||
// For normal extrusons, extrusion width is wider than the spacing due to the rounding and squishing of the extrusions.
|
||||
// For bridge extrusions, the extrusions are placed with a tiny BRIDGE_EXTRA_SPACING gaps between the threads.
|
||||
float width = float(bridge ?
|
||||
(spacing - BRIDGE_EXTRA_SPACING) :
|
||||
#ifdef HAS_PERIMETER_LINE_OVERLAP
|
||||
(spacing + PERIMETER_LINE_OVERLAP_FACTOR * height * (1. - 0.25 * PI));
|
||||
#else
|
||||
(spacing + height * (1. - 0.25 * PI)));
|
||||
#endif
|
||||
return Flow(width, bridge ? width : height, nozzle_diameter, bridge);
|
||||
Flow out = *this;
|
||||
if (m_bridge) {
|
||||
// Diameter of the rounded extrusion.
|
||||
assert(m_width == m_height);
|
||||
float gap = m_spacing - m_width;
|
||||
auto new_diameter = new_spacing - gap;
|
||||
out.m_width = out.m_height = new_diameter;
|
||||
} else {
|
||||
assert(m_width >= m_height);
|
||||
out.m_width += new_spacing - m_spacing;
|
||||
if (out.m_width < out.m_height)
|
||||
throw Slic3r::InvalidArgument("Invalid spacing supplied to Flow::with_spacing()");
|
||||
}
|
||||
out.m_spacing = new_spacing;
|
||||
return out;
|
||||
}
|
||||
|
||||
// This method returns the centerline spacing between two adjacent extrusions
|
||||
// having the same extrusion width (and other properties).
|
||||
float Flow::spacing() const
|
||||
// Adjust the width / height of a rounded extrusion model to reach the prescribed cross section area while maintaining extrusion spacing.
|
||||
Flow Flow::with_cross_section(float area_new) const
|
||||
{
|
||||
#ifdef HAS_PERIMETER_LINE_OVERLAP
|
||||
if (this->bridge)
|
||||
return this->width + BRIDGE_EXTRA_SPACING;
|
||||
// rectangle with semicircles at the ends
|
||||
float min_flow_spacing = this->width - this->height * (1. - 0.25 * PI);
|
||||
float res = this->width - PERIMETER_LINE_OVERLAP_FACTOR * (this->width - min_flow_spacing);
|
||||
#else
|
||||
float res = float(this->bridge ? (this->width + BRIDGE_EXTRA_SPACING) : (this->width - this->height * (1. - 0.25 * PI)));
|
||||
#endif
|
||||
// assert(res > 0.f);
|
||||
if (res <= 0.f)
|
||||
throw FlowErrorNegativeSpacing();
|
||||
return res;
|
||||
assert(! m_bridge);
|
||||
assert(m_width >= m_height);
|
||||
|
||||
// Adjust for bridge_flow_ratio, maintain the extrusion spacing.
|
||||
float area = this->mm3_per_mm();
|
||||
if (area_new > area + EPSILON) {
|
||||
// Increasing the flow rate.
|
||||
float new_full_spacing = area_new / m_height;
|
||||
if (new_full_spacing > m_spacing) {
|
||||
// Filling up the spacing without an air gap. Grow the extrusion in height.
|
||||
float height = area_new / m_spacing;
|
||||
return Flow(rounded_rectangle_extrusion_width_from_spacing(m_spacing, height), height, m_spacing, m_nozzle_diameter, false);
|
||||
} else {
|
||||
return this->with_width(rounded_rectangle_extrusion_width_from_spacing(area / m_height, m_height));
|
||||
}
|
||||
} else if (area_new < area - EPSILON) {
|
||||
// Decreasing the flow rate.
|
||||
float width_new = m_width - (area - area_new) / m_height;
|
||||
assert(width_new > 0);
|
||||
if (width_new > m_height) {
|
||||
// Shrink the extrusion width.
|
||||
return this->with_width(width_new);
|
||||
} else {
|
||||
// Create a rounded extrusion.
|
||||
auto dmr = float(sqrt(area_new / M_PI));
|
||||
return Flow(dmr, dmr, m_spacing, m_nozzle_diameter, false);
|
||||
}
|
||||
} else
|
||||
return *this;
|
||||
}
|
||||
|
||||
// This method returns the centerline spacing between an extrusion using this
|
||||
// flow and another one using another flow.
|
||||
// this->spacing(other) shall return the same value as other.spacing(*this)
|
||||
float Flow::spacing(const Flow &other) const
|
||||
float Flow::rounded_rectangle_extrusion_spacing(float width, float height)
|
||||
{
|
||||
assert(this->height == other.height);
|
||||
assert(this->bridge == other.bridge);
|
||||
float res = float(this->bridge ?
|
||||
0.5 * this->width + 0.5 * other.width + BRIDGE_EXTRA_SPACING :
|
||||
0.5 * this->spacing() + 0.5 * other.spacing());
|
||||
// assert(res > 0.f);
|
||||
if (res <= 0.f)
|
||||
throw FlowErrorNegativeSpacing();
|
||||
return res;
|
||||
auto out = width - height * float(1. - 0.25 * PI);
|
||||
if (out <= 0.f)
|
||||
throw FlowErrorNegativeSpacing();
|
||||
return out;
|
||||
}
|
||||
|
||||
float Flow::rounded_rectangle_extrusion_width_from_spacing(float spacing, float height)
|
||||
{
|
||||
return float(spacing + height * (1. - 0.25 * PI));
|
||||
}
|
||||
|
||||
float Flow::bridge_extrusion_spacing(float dmr)
|
||||
{
|
||||
return dmr + BRIDGE_EXTRA_SPACING;
|
||||
}
|
||||
|
||||
// This method returns extrusion volume per head move unit.
|
||||
double Flow::mm3_per_mm() const
|
||||
double Flow::mm3_per_mm() const
|
||||
{
|
||||
float res = this->bridge ?
|
||||
float res = m_bridge ?
|
||||
// Area of a circle with dmr of this->width.
|
||||
float((this->width * this->width) * 0.25 * PI) :
|
||||
float((m_width * m_width) * 0.25 * PI) :
|
||||
// Rectangle with semicircles at the ends. ~ h (w - 0.215 h)
|
||||
float(this->height * (this->width - this->height * (1. - 0.25 * PI)));
|
||||
float(m_height * (m_width - m_height * (1. - 0.25 * PI)));
|
||||
//assert(res > 0.);
|
||||
if (res <= 0.)
|
||||
throw FlowErrorNegativeFlow();
|
||||
@ -222,9 +233,7 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
|
||||
(object->config().support_material_extrusion_width.value > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width,
|
||||
// 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),
|
||||
// bridge_flow_ratio
|
||||
0.f);
|
||||
(layer_height > 0.f) ? layer_height : float(object->config().layer_height.value));
|
||||
}
|
||||
|
||||
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
|
||||
@ -235,9 +244,7 @@ Flow support_material_1st_layer_flow(const PrintObject *object, float layer_heig
|
||||
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
|
||||
(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)),
|
||||
// bridge_flow_ratio
|
||||
0.f);
|
||||
(layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value)));
|
||||
}
|
||||
|
||||
Flow support_material_interface_flow(const PrintObject *object, float layer_height)
|
||||
@ -248,9 +255,7 @@ Flow support_material_interface_flow(const PrintObject *object, float layer_heig
|
||||
(object->config().support_material_extrusion_width > 0) ? object->config().support_material_extrusion_width : object->config().extrusion_width,
|
||||
// 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),
|
||||
// bridge_flow_ratio
|
||||
0.f);
|
||||
(layer_height > 0.f) ? layer_height : float(object->config().layer_height.value));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,11 +13,6 @@ class PrintObject;
|
||||
// Extra spacing of bridge threads, in mm.
|
||||
#define BRIDGE_EXTRA_SPACING 0.05
|
||||
|
||||
// Overlap factor of perimeter lines. Currently no overlap.
|
||||
#ifdef HAS_PERIMETER_LINE_OVERLAP
|
||||
#define PERIMETER_LINE_OVERLAP_FACTOR 1.0
|
||||
#endif
|
||||
|
||||
enum FlowRole {
|
||||
frExternalPerimeter,
|
||||
frPerimeter,
|
||||
@ -56,26 +51,26 @@ public:
|
||||
class Flow
|
||||
{
|
||||
public:
|
||||
Flow() = default;
|
||||
Flow(float width, float height, float nozzle_diameter) :
|
||||
Flow(width, height, rounded_rectangle_extrusion_spacing(width, height), nozzle_diameter, false) {}
|
||||
|
||||
// Non bridging flow: Maximum width of an extrusion with semicircles at the ends.
|
||||
// Bridging flow: Bridge thread diameter.
|
||||
float width;
|
||||
float width() const { return m_width; }
|
||||
coord_t scaled_width() const { return coord_t(scale_(m_width)); }
|
||||
// Non bridging flow: Layer height.
|
||||
// Bridging flow: Bridge thread diameter = layer height.
|
||||
float height;
|
||||
float height() const { return m_height; }
|
||||
// Spacing between the extrusion centerlines.
|
||||
float spacing() const { return m_spacing; }
|
||||
coord_t scaled_spacing() const { return coord_t(scale_(m_spacing)); }
|
||||
// Nozzle diameter.
|
||||
float nozzle_diameter;
|
||||
float nozzle_diameter() const { return m_nozzle_diameter; }
|
||||
// Is it a bridge?
|
||||
bool bridge;
|
||||
|
||||
Flow(float _w, float _h, float _nd, bool _bridge = false) :
|
||||
width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {}
|
||||
|
||||
float spacing() const;
|
||||
float spacing(const Flow &other) const;
|
||||
double mm3_per_mm() const;
|
||||
coord_t scaled_width() const { return coord_t(scale_(this->width)); }
|
||||
coord_t scaled_spacing() const { return coord_t(scale_(this->spacing())); }
|
||||
coord_t scaled_spacing(const Flow &other) const { return coord_t(scale_(this->spacing(other))); }
|
||||
bool bridge() const { return m_bridge; }
|
||||
// Cross section area of the extrusion.
|
||||
double mm3_per_mm() const;
|
||||
|
||||
// Elephant foot compensation spacing to be used to detect narrow parts, where the elephant foot compensation cannot be applied.
|
||||
// To be used on frExternalPerimeter only.
|
||||
@ -83,13 +78,32 @@ public:
|
||||
// Here an overlap of 0.2x external perimeter spacing is allowed for by the elephant foot compensation.
|
||||
coord_t scaled_elephant_foot_spacing() const { return coord_t(0.5f * float(this->scaled_width() + 0.6f * this->scaled_spacing())); }
|
||||
|
||||
bool operator==(const Flow &rhs) const { return this->width == rhs.width && this->height == rhs.height && this->nozzle_diameter == rhs.nozzle_diameter && this->bridge == rhs.bridge; }
|
||||
|
||||
static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio);
|
||||
// Create a flow from the spacing of extrusion lines.
|
||||
// This method is used exclusively to calculate new flow of 100% infill, where the extrusion width was allowed to scale
|
||||
// to fit a region with integer number of lines.
|
||||
static Flow new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge);
|
||||
bool operator==(const Flow &rhs) const { return m_width == rhs.m_width && m_height == rhs.m_height && m_nozzle_diameter == rhs.m_nozzle_diameter && m_bridge == rhs.m_bridge; }
|
||||
|
||||
Flow with_width (float width) const {
|
||||
assert(! m_bridge);
|
||||
return Flow(width, m_height, rounded_rectangle_extrusion_spacing(width, m_height), m_nozzle_diameter, m_bridge);
|
||||
}
|
||||
Flow with_height(float height) const {
|
||||
assert(! m_bridge);
|
||||
return Flow(m_width, height, rounded_rectangle_extrusion_spacing(m_width, height), m_nozzle_diameter, m_bridge);
|
||||
}
|
||||
// Adjust extrusion flow for new extrusion line spacing, maintaining the old spacing between extrusions.
|
||||
Flow with_spacing(float spacing) const;
|
||||
// Adjust the width / height of a rounded extrusion model to reach the prescribed cross section area while maintaining extrusion spacing.
|
||||
Flow with_cross_section(float area) const;
|
||||
Flow with_flow_ratio(double ratio) const { return this->with_cross_section(this->mm3_per_mm() * ratio); }
|
||||
|
||||
static Flow bridging_flow(float dmr, float nozzle_diameter) { return Flow { dmr, dmr, bridge_extrusion_spacing(dmr), nozzle_diameter, true }; }
|
||||
|
||||
static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height);
|
||||
|
||||
// Spacing of extrusions with rounded extrusion model.
|
||||
static float rounded_rectangle_extrusion_spacing(float width, float height);
|
||||
// Width of extrusions with rounded extrusion model.
|
||||
static float rounded_rectangle_extrusion_width_from_spacing(float spacing, float height);
|
||||
// Spacing of round thread extrusions.
|
||||
static float bridge_extrusion_spacing(float dmr);
|
||||
|
||||
// Sane extrusion width defautl based on nozzle diameter.
|
||||
// The defaults were derived from manual Prusa MK3 profiles.
|
||||
@ -100,6 +114,20 @@ public:
|
||||
// on active extruder etc. Therefore the value calculated by this function shall be used as a hint only.
|
||||
static double extrusion_width(const std::string &opt_key, const ConfigOptionFloatOrPercent *opt, const ConfigOptionResolver &config, const unsigned int first_printing_extruder = 0);
|
||||
static double extrusion_width(const std::string &opt_key, const ConfigOptionResolver &config, const unsigned int first_printing_extruder = 0);
|
||||
|
||||
private:
|
||||
Flow(float width, float height, float spacing, float nozzle_diameter, bool bridge) :
|
||||
m_width(width), m_height(height), m_spacing(spacing), m_nozzle_diameter(nozzle_diameter), m_bridge(bridge)
|
||||
{
|
||||
// Gap fill violates this condition.
|
||||
//assert(width >= height);
|
||||
}
|
||||
|
||||
float m_width { 0 };
|
||||
float m_height { 0 };
|
||||
float m_spacing { 0 };
|
||||
float m_nozzle_diameter { 0 };
|
||||
bool m_bridge { false };
|
||||
};
|
||||
|
||||
extern Flow support_material_flow(const PrintObject *object, float layer_height = 0.f);
|
||||
|
@ -2014,9 +2014,10 @@ namespace Slic3r {
|
||||
typedef std::map<int, ObjectData> IdToObjectDataMap;
|
||||
|
||||
bool m_fullpath_sources{ true };
|
||||
bool m_zip64 { true };
|
||||
|
||||
public:
|
||||
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr);
|
||||
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data, bool zip64);
|
||||
|
||||
private:
|
||||
bool _save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data);
|
||||
@ -2036,10 +2037,11 @@ namespace Slic3r {
|
||||
bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config);
|
||||
};
|
||||
|
||||
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data)
|
||||
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data, bool zip64)
|
||||
{
|
||||
clear_errors();
|
||||
m_fullpath_sources = fullpath_sources;
|
||||
m_zip64 = zip64;
|
||||
return _save_model_to_file(filename, model, config, thumbnail_data);
|
||||
}
|
||||
|
||||
@ -2233,9 +2235,13 @@ namespace Slic3r {
|
||||
{
|
||||
mz_zip_writer_staged_context context;
|
||||
if (!mz_zip_writer_add_staged_open(&archive, &context, MODEL_FILE.c_str(),
|
||||
// Maximum expected and allowed 3MF file size is 16GiB.
|
||||
// This switches the ZIP file to a 64bit mode, which adds a tiny bit of overhead to file records.
|
||||
(uint64_t(1) << 30) * 16,
|
||||
m_zip64 ?
|
||||
// Maximum expected and allowed 3MF file size is 16GiB.
|
||||
// This switches the ZIP file to a 64bit mode, which adds a tiny bit of overhead to file records.
|
||||
(uint64_t(1) << 30) * 16 :
|
||||
// Maximum expected 3MF file size is 4GB-1. This is a workaround for interoperability with Windows 10 3D model fixing API, see
|
||||
// GH issue #6193.
|
||||
(uint64_t(1) << 32) - 1,
|
||||
nullptr, nullptr, 0, MZ_DEFAULT_COMPRESSION, nullptr, 0, nullptr, 0)) {
|
||||
add_error("Unable to add model file to archive");
|
||||
return false;
|
||||
@ -2926,13 +2932,13 @@ bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool c
|
||||
return res;
|
||||
}
|
||||
|
||||
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data)
|
||||
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data, bool zip64)
|
||||
{
|
||||
if (path == nullptr || model == nullptr)
|
||||
return false;
|
||||
|
||||
_3MF_Exporter exporter;
|
||||
bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources, thumbnail_data);
|
||||
bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources, thumbnail_data, zip64);
|
||||
if (!res)
|
||||
exporter.log_errors();
|
||||
|
||||
|
@ -33,7 +33,7 @@ namespace Slic3r {
|
||||
|
||||
// Save the given model and the config data contained in the given Print into a 3mf file.
|
||||
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
|
||||
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr);
|
||||
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr, bool zip64 = true);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <chrono>
|
||||
#include <math.h>
|
||||
#include <string_view>
|
||||
|
||||
@ -1113,15 +1114,15 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
||||
const double layer_height = first_object->config().layer_height.value;
|
||||
const double first_layer_height = first_object->config().first_layer_height.get_abs_value(layer_height);
|
||||
for (const PrintRegion* region : print.regions()) {
|
||||
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(frExternalPerimeter, layer_height, false, false, -1., *first_object).width);
|
||||
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(frPerimeter, layer_height, false, false, -1., *first_object).width);
|
||||
_write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(frInfill, layer_height, false, false, -1., *first_object).width);
|
||||
_write_format(file, "; solid infill extrusion width = %.2fmm\n", region->flow(frSolidInfill, layer_height, false, false, -1., *first_object).width);
|
||||
_write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(frTopSolidInfill, layer_height, false, false, -1., *first_object).width);
|
||||
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frExternalPerimeter, layer_height).width());
|
||||
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, layer_height).width());
|
||||
_write_format(file, "; infill extrusion width = %.2fmm\n", region->flow(*first_object, frInfill, layer_height).width());
|
||||
_write_format(file, "; solid infill extrusion width = %.2fmm\n", region->flow(*first_object, frSolidInfill, layer_height).width());
|
||||
_write_format(file, "; top infill extrusion width = %.2fmm\n", region->flow(*first_object, frTopSolidInfill, layer_height).width());
|
||||
if (print.has_support_material())
|
||||
_write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width);
|
||||
_write_format(file, "; support material extrusion width = %.2fmm\n", support_material_flow(first_object).width());
|
||||
if (print.config().first_layer_extrusion_width.value > 0)
|
||||
_write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(frPerimeter, first_layer_height, false, true, -1., *first_object).width);
|
||||
_write_format(file, "; first layer extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, first_layer_height, true).width());
|
||||
_write_format(file, "\n");
|
||||
}
|
||||
print.throw_if_canceled();
|
||||
@ -1137,6 +1138,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
||||
// Prepare the helper object for replacing placeholders in custom G-code and output filename.
|
||||
m_placeholder_parser = print.placeholder_parser();
|
||||
m_placeholder_parser.update_timestamp();
|
||||
m_placeholder_parser_context.rng = std::mt19937(std::chrono::high_resolution_clock::now().time_since_epoch().count());
|
||||
print.update_object_placeholders(m_placeholder_parser.config_writable(), ".gcode");
|
||||
|
||||
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
|
||||
@ -1823,7 +1825,8 @@ namespace Skirt {
|
||||
// Extrude skirt at the print_z of the raft layers and normal object layers
|
||||
// not at the print_z of the interlaced support material layers.
|
||||
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder_out;
|
||||
assert(skirt_done.empty());
|
||||
//For sequential print, the following test may fail when extruding the 2nd and other objects.
|
||||
// assert(skirt_done.empty());
|
||||
if (skirt_done.empty() && print.has_skirt() && ! print.skirt().entities.empty() && layer_tools.has_skirt) {
|
||||
skirt_loops_per_extruder_all_printing(print, layer_tools, skirt_loops_per_extruder_out);
|
||||
skirt_done.emplace_back(layer_tools.print_z);
|
||||
@ -2178,14 +2181,13 @@ void GCode::process_layer(
|
||||
const std::pair<size_t, size_t> loops = loops_it->second;
|
||||
this->set_origin(0., 0.);
|
||||
m_avoid_crossing_perimeters.use_external_mp();
|
||||
Flow layer_skirt_flow(print.skirt_flow());
|
||||
layer_skirt_flow.height = float(m_skirt_done.back() - (m_skirt_done.size() == 1 ? 0. : m_skirt_done[m_skirt_done.size() - 2]));
|
||||
Flow layer_skirt_flow = print.skirt_flow().with_height(float(m_skirt_done.back() - (m_skirt_done.size() == 1 ? 0. : m_skirt_done[m_skirt_done.size() - 2])));
|
||||
double mm3_per_mm = layer_skirt_flow.mm3_per_mm();
|
||||
for (size_t i = loops.first; i < loops.second; ++i) {
|
||||
// Adjust flow according to this layer's layer height.
|
||||
ExtrusionLoop loop = *dynamic_cast<const ExtrusionLoop*>(print.skirt().entities[i]);
|
||||
for (ExtrusionPath &path : loop.paths) {
|
||||
path.height = layer_skirt_flow.height;
|
||||
path.height = layer_skirt_flow.height();
|
||||
path.mm3_per_mm = mm3_per_mm;
|
||||
}
|
||||
//FIXME using the support_material_speed of the 1st object printed.
|
||||
|
@ -59,7 +59,10 @@ public:
|
||||
// (this collection contains only ExtrusionEntityCollection objects)
|
||||
ExtrusionEntityCollection fills;
|
||||
|
||||
Flow flow(FlowRole role, bool bridge = false, double width = -1) const;
|
||||
Flow flow(FlowRole role) const;
|
||||
Flow flow(FlowRole role, double layer_height) const;
|
||||
Flow bridging_flow(FlowRole role) const;
|
||||
|
||||
void slices_to_fill_surfaces_clipped();
|
||||
void prepare_fill_surfaces();
|
||||
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces);
|
||||
|
@ -15,16 +15,31 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const
|
||||
Flow LayerRegion::flow(FlowRole role) const
|
||||
{
|
||||
return m_region->flow(
|
||||
role,
|
||||
m_layer->height,
|
||||
bridge,
|
||||
m_layer->id() == 0,
|
||||
width,
|
||||
*m_layer->object()
|
||||
);
|
||||
return this->flow(role, m_layer->height);
|
||||
}
|
||||
|
||||
Flow LayerRegion::flow(FlowRole role, double layer_height) const
|
||||
{
|
||||
return m_region->flow(*m_layer->object(), role, layer_height, m_layer->id() == 0);
|
||||
}
|
||||
|
||||
Flow LayerRegion::bridging_flow(FlowRole role) const
|
||||
{
|
||||
const PrintRegion ®ion = *this->region();
|
||||
const PrintRegionConfig ®ion_config = region.config();
|
||||
if (this->layer()->object()->config().thick_bridges) {
|
||||
// The old Slic3r way (different from all other slicers): Use rounded extrusions.
|
||||
// Get the configured nozzle_diameter for the extruder associated to the flow role requested.
|
||||
// Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right.
|
||||
auto nozzle_diameter = float(region.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1));
|
||||
// Applies default bridge spacing.
|
||||
return Flow::bridging_flow(float(sqrt(region_config.bridge_flow_ratio)) * nozzle_diameter, nozzle_diameter);
|
||||
} else {
|
||||
// The same way as other slicers: Use normal extrusions. Apply bridge_flow_ratio while maintaining the original spacing.
|
||||
return this->flow(role).with_flow_ratio(region_config.bridge_flow_ratio);
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces.
|
||||
@ -84,7 +99,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
|
||||
|
||||
g.layer_id = (int)this->layer()->id();
|
||||
g.ext_perimeter_flow = this->flow(frExternalPerimeter);
|
||||
g.overhang_flow = this->region()->flow(frPerimeter, -1, true, false, -1, *this->layer()->object());
|
||||
g.overhang_flow = this->bridging_flow(frPerimeter);
|
||||
g.solid_infill_flow = this->flow(frSolidInfill);
|
||||
|
||||
g.process();
|
||||
@ -266,11 +281,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
||||
// would get merged into a single one while they need different directions
|
||||
// also, supply the original expolygon instead of the grown one, because in case
|
||||
// of very thin (but still working) anchors, the grown expolygon would go beyond them
|
||||
BridgeDetector bd(
|
||||
initial,
|
||||
lower_layer->lslices,
|
||||
this->flow(frInfill, true).scaled_width()
|
||||
);
|
||||
BridgeDetector bd(initial, lower_layer->lslices, this->bridging_flow(frInfill).scaled_width());
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf("Processing bridge at layer %zu:\n", this->layer()->id());
|
||||
#endif
|
||||
|
@ -1302,52 +1302,54 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b
|
||||
|
||||
void ModelObject::split(ModelObjectPtrs* new_objects)
|
||||
{
|
||||
if (this->volumes.size() > 1) {
|
||||
// We can't split meshes if there's more than one volume, because
|
||||
// we can't group the resulting meshes by object afterwards
|
||||
new_objects->emplace_back(this);
|
||||
return;
|
||||
}
|
||||
|
||||
ModelVolume* volume = this->volumes.front();
|
||||
TriangleMeshPtrs meshptrs = volume->mesh().split();
|
||||
size_t counter = 1;
|
||||
for (TriangleMesh *mesh : meshptrs) {
|
||||
for (ModelVolume* volume : this->volumes) {
|
||||
if (volume->type() != ModelVolumeType::MODEL_PART)
|
||||
continue;
|
||||
|
||||
// FIXME: crashes if not satisfied
|
||||
if (mesh->facets_count() < 3) continue;
|
||||
TriangleMeshPtrs meshptrs = volume->mesh().split();
|
||||
size_t counter = 1;
|
||||
for (TriangleMesh* mesh : meshptrs) {
|
||||
|
||||
mesh->repair();
|
||||
|
||||
// XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed?
|
||||
ModelObject* new_object = m_model->add_object();
|
||||
new_object->name = this->name + (meshptrs.size() > 1 ? "_" + std::to_string(counter++) : "");
|
||||
// FIXME: crashes if not satisfied
|
||||
if (mesh->facets_count() < 3) continue;
|
||||
|
||||
// Don't copy the config's ID.
|
||||
new_object->config.assign_config(this->config);
|
||||
assert(new_object->config.id().valid());
|
||||
assert(new_object->config.id() != this->config.id());
|
||||
new_object->instances.reserve(this->instances.size());
|
||||
for (const ModelInstance *model_instance : this->instances)
|
||||
new_object->add_instance(*model_instance);
|
||||
ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh));
|
||||
mesh->repair();
|
||||
|
||||
for (ModelInstance* model_instance : new_object->instances)
|
||||
{
|
||||
Vec3d shift = model_instance->get_transformation().get_matrix(true) * new_vol->get_offset();
|
||||
model_instance->set_offset(model_instance->get_offset() + shift);
|
||||
// XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed?
|
||||
ModelObject* new_object = m_model->add_object();
|
||||
if (meshptrs.size() == 1) {
|
||||
new_object->name = volume->name;
|
||||
// Don't copy the config's ID.
|
||||
new_object->config.assign_config(this->config.size() > 0 ? this->config : volume->config);
|
||||
}
|
||||
else {
|
||||
new_object->name = this->name + (meshptrs.size() > 1 ? "_" + std::to_string(counter++) : "");
|
||||
// Don't copy the config's ID.
|
||||
new_object->config.assign_config(this->config);
|
||||
}
|
||||
assert(new_object->config.id().valid());
|
||||
assert(new_object->config.id() != this->config.id());
|
||||
new_object->instances.reserve(this->instances.size());
|
||||
for (const ModelInstance* model_instance : this->instances)
|
||||
new_object->add_instance(*model_instance);
|
||||
ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh));
|
||||
|
||||
for (ModelInstance* model_instance : new_object->instances)
|
||||
{
|
||||
Vec3d shift = model_instance->get_transformation().get_matrix(true) * new_vol->get_offset();
|
||||
model_instance->set_offset(model_instance->get_offset() + shift);
|
||||
}
|
||||
|
||||
new_vol->set_offset(Vec3d::Zero());
|
||||
// reset the source to disable reload from disk
|
||||
new_vol->source = ModelVolume::Source();
|
||||
new_objects->emplace_back(new_object);
|
||||
delete mesh;
|
||||
}
|
||||
|
||||
new_vol->set_offset(Vec3d::Zero());
|
||||
// reset the source to disable reload from disk
|
||||
new_vol->source = ModelVolume::Source();
|
||||
new_objects->emplace_back(new_object);
|
||||
delete mesh;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void ModelObject::merge()
|
||||
{
|
||||
if (this->volumes.size() == 1) {
|
||||
@ -1738,6 +1740,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
|
||||
this->object->volumes[ivolume]->translate(offset);
|
||||
this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1);
|
||||
this->object->volumes[ivolume]->config.set_deserialize("extruder", auto_extruder_id(max_extruders, extruder_counter));
|
||||
this->object->volumes[ivolume]->m_is_splittable = 0;
|
||||
delete mesh;
|
||||
++ idx;
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ static bool clip_narrow_corner(
|
||||
Vec2i64 p2 = it2->cast<int64_t>();
|
||||
Vec2i64 p02;
|
||||
Vec2i64 p22;
|
||||
int64_t dist2_next;
|
||||
int64_t dist2_next = 0;
|
||||
|
||||
// As long as there is at least a single triangle left in the polygon.
|
||||
while (polygon.size() >= 3) {
|
||||
|
@ -22,74 +22,54 @@ namespace Slic3r {
|
||||
class TriangleMeshDataAdapter {
|
||||
public:
|
||||
const TriangleMesh &mesh;
|
||||
|
||||
float voxel_scale;
|
||||
|
||||
size_t polygonCount() const { return mesh.its.indices.size(); }
|
||||
size_t pointCount() const { return mesh.its.vertices.size(); }
|
||||
size_t vertexCount(size_t) const { return 3; }
|
||||
|
||||
|
||||
// Return position pos in local grid index space for polygon n and vertex v
|
||||
void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const;
|
||||
// The actual mesh will appear to openvdb as scaled uniformly by voxel_size
|
||||
// And the voxel count per unit volume can be affected this way.
|
||||
void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const
|
||||
{
|
||||
auto vidx = size_t(mesh.its.indices[n](Eigen::Index(v)));
|
||||
Slic3r::Vec3d p = mesh.its.vertices[vidx].cast<double>() * voxel_scale;
|
||||
pos = {p.x(), p.y(), p.z()};
|
||||
}
|
||||
|
||||
TriangleMeshDataAdapter(const TriangleMesh &m, float voxel_sc = 1.f)
|
||||
: mesh{m}, voxel_scale{voxel_sc} {};
|
||||
};
|
||||
|
||||
class Contour3DDataAdapter {
|
||||
public:
|
||||
const sla::Contour3D &mesh;
|
||||
|
||||
size_t polygonCount() const { return mesh.faces3.size() + mesh.faces4.size(); }
|
||||
size_t pointCount() const { return mesh.points.size(); }
|
||||
size_t vertexCount(size_t n) const { return n < mesh.faces3.size() ? 3 : 4; }
|
||||
|
||||
// Return position pos in local grid index space for polygon n and vertex v
|
||||
void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const;
|
||||
};
|
||||
|
||||
void TriangleMeshDataAdapter::getIndexSpacePoint(size_t n,
|
||||
size_t v,
|
||||
openvdb::Vec3d &pos) const
|
||||
{
|
||||
auto vidx = size_t(mesh.its.indices[n](Eigen::Index(v)));
|
||||
Slic3r::Vec3d p = mesh.its.vertices[vidx].cast<double>();
|
||||
pos = {p.x(), p.y(), p.z()};
|
||||
}
|
||||
|
||||
void Contour3DDataAdapter::getIndexSpacePoint(size_t n,
|
||||
size_t v,
|
||||
openvdb::Vec3d &pos) const
|
||||
{
|
||||
size_t vidx = 0;
|
||||
if (n < mesh.faces3.size()) vidx = size_t(mesh.faces3[n](Eigen::Index(v)));
|
||||
else vidx = size_t(mesh.faces4[n - mesh.faces3.size()](Eigen::Index(v)));
|
||||
|
||||
Slic3r::Vec3d p = mesh.points[vidx];
|
||||
pos = {p.x(), p.y(), p.z()};
|
||||
}
|
||||
|
||||
|
||||
// TODO: Do I need to call initialize? Seems to work without it as well but the
|
||||
// docs say it should be called ones. It does a mutex lock-unlock sequence all
|
||||
// even if was called previously.
|
||||
openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh &mesh,
|
||||
openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh & mesh,
|
||||
const openvdb::math::Transform &tr,
|
||||
float exteriorBandWidth,
|
||||
float interiorBandWidth,
|
||||
int flags)
|
||||
float voxel_scale,
|
||||
float exteriorBandWidth,
|
||||
float interiorBandWidth,
|
||||
int flags)
|
||||
{
|
||||
openvdb::initialize();
|
||||
|
||||
TriangleMeshPtrs meshparts = mesh.split();
|
||||
TriangleMeshPtrs meshparts_raw = mesh.split();
|
||||
auto meshparts = reserve_vector<std::unique_ptr<TriangleMesh>>(meshparts_raw.size());
|
||||
for (auto *p : meshparts_raw)
|
||||
meshparts.emplace_back(p);
|
||||
|
||||
auto it = std::remove_if(meshparts.begin(), meshparts.end(),
|
||||
[](TriangleMesh *m){
|
||||
m->require_shared_vertices();
|
||||
return !m->is_manifold() || m->volume() < EPSILON;
|
||||
});
|
||||
auto it = std::remove_if(meshparts.begin(), meshparts.end(), [](auto &m) {
|
||||
m->require_shared_vertices();
|
||||
return m->volume() < EPSILON;
|
||||
});
|
||||
|
||||
meshparts.erase(it, meshparts.end());
|
||||
|
||||
openvdb::FloatGrid::Ptr grid;
|
||||
for (TriangleMesh *m : meshparts) {
|
||||
for (auto &m : meshparts) {
|
||||
auto subgrid = openvdb::tools::meshToVolume<openvdb::FloatGrid>(
|
||||
TriangleMeshDataAdapter{*m}, tr, exteriorBandWidth,
|
||||
TriangleMeshDataAdapter{*m, voxel_scale}, tr, exteriorBandWidth,
|
||||
interiorBandWidth, flags);
|
||||
|
||||
if (grid && subgrid) openvdb::tools::csgUnion(*grid, *subgrid);
|
||||
@ -106,19 +86,9 @@ openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh &mesh,
|
||||
interiorBandWidth, flags);
|
||||
}
|
||||
|
||||
return grid;
|
||||
}
|
||||
grid->insertMeta("voxel_scale", openvdb::FloatMetadata(voxel_scale));
|
||||
|
||||
openvdb::FloatGrid::Ptr mesh_to_grid(const sla::Contour3D &mesh,
|
||||
const openvdb::math::Transform &tr,
|
||||
float exteriorBandWidth,
|
||||
float interiorBandWidth,
|
||||
int flags)
|
||||
{
|
||||
openvdb::initialize();
|
||||
return openvdb::tools::meshToVolume<openvdb::FloatGrid>(
|
||||
Contour3DDataAdapter{mesh}, tr, exteriorBandWidth, interiorBandWidth,
|
||||
flags);
|
||||
return grid;
|
||||
}
|
||||
|
||||
template<class Grid>
|
||||
@ -128,20 +98,25 @@ sla::Contour3D _volumeToMesh(const Grid &grid,
|
||||
bool relaxDisorientedTriangles)
|
||||
{
|
||||
openvdb::initialize();
|
||||
|
||||
|
||||
std::vector<openvdb::Vec3s> points;
|
||||
std::vector<openvdb::Vec3I> triangles;
|
||||
std::vector<openvdb::Vec4I> quads;
|
||||
|
||||
|
||||
openvdb::tools::volumeToMesh(grid, points, triangles, quads, isovalue,
|
||||
adaptivity, relaxDisorientedTriangles);
|
||||
|
||||
|
||||
float scale = 1.;
|
||||
try {
|
||||
scale = grid.template metaValue<float>("voxel_scale");
|
||||
} catch (...) { }
|
||||
|
||||
sla::Contour3D ret;
|
||||
ret.points.reserve(points.size());
|
||||
ret.faces3.reserve(triangles.size());
|
||||
ret.faces4.reserve(quads.size());
|
||||
|
||||
for (auto &v : points) ret.points.emplace_back(to_vec3d(v));
|
||||
for (auto &v : points) ret.points.emplace_back(to_vec3d(v) / scale);
|
||||
for (auto &v : triangles) ret.faces3.emplace_back(to_vec3i(v));
|
||||
for (auto &v : quads) ret.faces4.emplace_back(to_vec4i(v));
|
||||
|
||||
@ -166,9 +141,18 @@ sla::Contour3D grid_to_contour3d(const openvdb::FloatGrid &grid,
|
||||
relaxDisorientedTriangles);
|
||||
}
|
||||
|
||||
openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid, double iso, double er, double ir)
|
||||
openvdb::FloatGrid::Ptr redistance_grid(const openvdb::FloatGrid &grid,
|
||||
double iso,
|
||||
double er,
|
||||
double ir)
|
||||
{
|
||||
return openvdb::tools::levelSetRebuild(grid, float(iso), float(er), float(ir));
|
||||
auto new_grid = openvdb::tools::levelSetRebuild(grid, float(iso),
|
||||
float(er), float(ir));
|
||||
|
||||
// Copies voxel_scale metadata, if it exists.
|
||||
new_grid->insertMeta(*grid.deepCopyMeta());
|
||||
|
||||
return new_grid;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
@ -21,14 +21,16 @@ inline Vec3d to_vec3d(const openvdb::Vec3s &v) { return to_vec3f(v).cast<double>
|
||||
inline Vec3i to_vec3i(const openvdb::Vec3I &v) { return Vec3i{int(v[0]), int(v[1]), int(v[2])}; }
|
||||
inline Vec4i to_vec4i(const openvdb::Vec4I &v) { return Vec4i{int(v[0]), int(v[1]), int(v[2]), int(v[3])}; }
|
||||
|
||||
// Here voxel_scale defines the scaling of voxels which affects the voxel count.
|
||||
// 1.0 value means a voxel for every unit cube. 2 means the model is scaled to
|
||||
// be 2x larger and the voxel count is increased by the increment in the scaled
|
||||
// volume, thus 4 times. This kind a sampling accuracy selection is not
|
||||
// achievable through the Transform parameter. (TODO: or is it?)
|
||||
// The resulting grid will contain the voxel_scale in its metadata under the
|
||||
// "voxel_scale" key to be used in grid_to_mesh function.
|
||||
openvdb::FloatGrid::Ptr mesh_to_grid(const TriangleMesh & mesh,
|
||||
const openvdb::math::Transform &tr = {},
|
||||
float exteriorBandWidth = 3.0f,
|
||||
float interiorBandWidth = 3.0f,
|
||||
int flags = 0);
|
||||
|
||||
openvdb::FloatGrid::Ptr mesh_to_grid(const sla::Contour3D & mesh,
|
||||
const openvdb::math::Transform &tr = {},
|
||||
float voxel_scale = 1.f,
|
||||
float exteriorBandWidth = 3.0f,
|
||||
float interiorBandWidth = 3.0f,
|
||||
int flags = 0);
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, Flow &flow, const float tolerance)
|
||||
static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, const float tolerance)
|
||||
{
|
||||
ExtrusionPaths paths;
|
||||
ExtrusionPath path(role);
|
||||
@ -62,15 +62,15 @@ static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thi
|
||||
path.polyline.append(line.b);
|
||||
// Convert from spacing to extrusion width based on the extrusion model
|
||||
// of a square extrusion ended with semi circles.
|
||||
flow.width = unscale<float>(w) + flow.height * float(1. - 0.25 * PI);
|
||||
Flow new_flow = flow.with_width(unscale<float>(w) + flow.height() * float(1. - 0.25 * PI));
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf(" filling %f gap\n", flow.width);
|
||||
#endif
|
||||
path.mm3_per_mm = flow.mm3_per_mm();
|
||||
path.width = flow.width;
|
||||
path.height = flow.height;
|
||||
path.mm3_per_mm = new_flow.mm3_per_mm();
|
||||
path.width = new_flow.width();
|
||||
path.height = new_flow.height();
|
||||
} else {
|
||||
thickness_delta = fabs(scale_(flow.width) - w);
|
||||
thickness_delta = fabs(scale_(flow.width()) - w);
|
||||
if (thickness_delta <= tolerance) {
|
||||
// the width difference between this line and the current flow width is
|
||||
// within the accepted tolerance
|
||||
@ -88,7 +88,7 @@ static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thi
|
||||
return paths;
|
||||
}
|
||||
|
||||
static void variable_width(const ThickPolylines& polylines, ExtrusionRole role, Flow flow, std::vector<ExtrusionEntity*> &out)
|
||||
static void variable_width(const ThickPolylines& polylines, ExtrusionRole role, const Flow &flow, std::vector<ExtrusionEntity*> &out)
|
||||
{
|
||||
// This value determines granularity of adaptive width, as G-code does not allow
|
||||
// variable extrusion within a single move; this value shall only affect the amount
|
||||
@ -205,8 +205,8 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
||||
paths,
|
||||
intersection_pl({ polygon }, perimeter_generator.lower_slices_polygons()),
|
||||
role,
|
||||
is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm(),
|
||||
is_external ? perimeter_generator.ext_perimeter_flow.width : perimeter_generator.perimeter_flow.width,
|
||||
is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm(),
|
||||
is_external ? perimeter_generator.ext_perimeter_flow.width() : perimeter_generator.perimeter_flow.width(),
|
||||
(float)perimeter_generator.layer_height);
|
||||
|
||||
// get overhang paths by checking what parts of this loop fall
|
||||
@ -217,8 +217,8 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
||||
diff_pl({ polygon }, perimeter_generator.lower_slices_polygons()),
|
||||
erOverhangPerimeter,
|
||||
perimeter_generator.mm3_per_mm_overhang(),
|
||||
perimeter_generator.overhang_flow.width,
|
||||
perimeter_generator.overhang_flow.height);
|
||||
perimeter_generator.overhang_flow.width(),
|
||||
perimeter_generator.overhang_flow.height());
|
||||
|
||||
// Reapply the nearest point search for starting point.
|
||||
// We allow polyline reversal because Clipper may have randomly reversed polylines during clipping.
|
||||
@ -226,8 +226,8 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
|
||||
} else {
|
||||
ExtrusionPath path(role);
|
||||
path.polyline = polygon.split_at_first_point();
|
||||
path.mm3_per_mm = is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm();
|
||||
path.width = is_external ? perimeter_generator.ext_perimeter_flow.width : perimeter_generator.perimeter_flow.width;
|
||||
path.mm3_per_mm = is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm();
|
||||
path.width = is_external ? perimeter_generator.ext_perimeter_flow.width() : perimeter_generator.perimeter_flow.width();
|
||||
path.height = (float)perimeter_generator.layer_height;
|
||||
paths.push_back(path);
|
||||
}
|
||||
@ -286,7 +286,7 @@ void PerimeterGenerator::process()
|
||||
m_ext_mm3_per_mm = this->ext_perimeter_flow.mm3_per_mm();
|
||||
coord_t ext_perimeter_width = this->ext_perimeter_flow.scaled_width();
|
||||
coord_t ext_perimeter_spacing = this->ext_perimeter_flow.scaled_spacing();
|
||||
coord_t ext_perimeter_spacing2 = this->ext_perimeter_flow.scaled_spacing(this->perimeter_flow);
|
||||
coord_t ext_perimeter_spacing2 = scaled<coord_t>(0.5f * (this->ext_perimeter_flow.spacing() + this->perimeter_flow.spacing()));
|
||||
|
||||
// overhang perimeters
|
||||
m_mm3_per_mm_overhang = this->overhang_flow.mm3_per_mm();
|
||||
@ -346,7 +346,7 @@ void PerimeterGenerator::process()
|
||||
if (this->config->thin_walls) {
|
||||
// the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width
|
||||
// (actually, something larger than that still may exist due to mitering or other causes)
|
||||
coord_t min_width = coord_t(scale_(this->ext_perimeter_flow.nozzle_diameter / 3));
|
||||
coord_t min_width = coord_t(scale_(this->ext_perimeter_flow.nozzle_diameter() / 3));
|
||||
ExPolygons expp = offset2_ex(
|
||||
// medial axis requires non-overlapping geometry
|
||||
diff_ex(to_polygons(last),
|
||||
|
@ -428,9 +428,10 @@ const std::vector<std::string>& Preset::print_options()
|
||||
"min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
|
||||
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
|
||||
"support_material_pattern", "support_material_with_sheath", "support_material_spacing",
|
||||
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers",
|
||||
"support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance",
|
||||
"support_material_buildplate_only", "dont_support_bridges", "notes", "complete_objects", "extruder_clearance_radius",
|
||||
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers",
|
||||
"support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops",
|
||||
"support_material_contact_distance", "support_material_bottom_contact_distance",
|
||||
"support_material_buildplate_only", "dont_support_bridges", "thick_bridges", "notes", "complete_objects", "extruder_clearance_radius",
|
||||
"extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "perimeter_extruder",
|
||||
"infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder",
|
||||
"ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width",
|
||||
|
@ -1326,7 +1326,8 @@ std::string Print::validate(std::string* warning) const
|
||||
return L("The Wipe Tower is only supported for multiple objects if they have equal layer heights");
|
||||
if (slicing_params.raft_layers() != slicing_params0.raft_layers())
|
||||
return L("The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers");
|
||||
if (object->config().support_material_contact_distance != m_objects.front()->config().support_material_contact_distance)
|
||||
if (slicing_params0.gap_object_support != slicing_params.gap_object_support ||
|
||||
slicing_params0.gap_support_object != slicing_params.gap_support_object)
|
||||
return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance");
|
||||
if (! equal_layering(slicing_params, slicing_params0))
|
||||
return L("The Wipe Tower is only supported for multiple objects if they are sliced equally.");
|
||||
@ -1577,9 +1578,7 @@ Flow Print::brim_flow() const
|
||||
frPerimeter,
|
||||
width,
|
||||
(float)m_config.nozzle_diameter.get_at(m_regions.front()->config().perimeter_extruder-1),
|
||||
(float)this->skirt_first_layer_height(),
|
||||
0
|
||||
);
|
||||
(float)this->skirt_first_layer_height());
|
||||
}
|
||||
|
||||
Flow Print::skirt_flow() const
|
||||
@ -1599,9 +1598,7 @@ Flow Print::skirt_flow() const
|
||||
frPerimeter,
|
||||
width,
|
||||
(float)m_config.nozzle_diameter.get_at(m_objects.front()->config().support_material_extruder-1),
|
||||
(float)this->skirt_first_layer_height(),
|
||||
0
|
||||
);
|
||||
(float)this->skirt_first_layer_height());
|
||||
}
|
||||
|
||||
bool Print::has_support_material() const
|
||||
@ -1818,7 +1815,7 @@ void Print::_make_skirt()
|
||||
ExtrusionPath(
|
||||
erSkirt,
|
||||
(float)mm3_per_mm, // this will be overridden at G-code export time
|
||||
flow.width,
|
||||
flow.width(),
|
||||
(float)first_layer_height // this will be overridden at G-code export time
|
||||
)));
|
||||
eloop.paths.back().polyline = loop.split_at_first_point();
|
||||
|
@ -65,7 +65,7 @@ public:
|
||||
const PrintRegionConfig& config() const { return m_config; }
|
||||
// 1-based extruder identifier for this region and role.
|
||||
unsigned int extruder(FlowRole role) const;
|
||||
Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const;
|
||||
Flow flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer = false) 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.
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Config.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include <set>
|
||||
@ -66,7 +67,7 @@ void PrintConfigDef::init_common_params()
|
||||
def->label = L("G-code thumbnails");
|
||||
def->tooltip = L("Picture sizes to be stored into a .gcode and .sl1 files, in the following format: \"XxY, XxY, ...\"");
|
||||
def->mode = comExpert;
|
||||
def->gui_type = "one_string";
|
||||
def->gui_type = ConfigOptionDef::GUIType::one_string;
|
||||
def->set_default_value(new ConfigOptionPoints());
|
||||
|
||||
def = this->add("layer_height", coFloat);
|
||||
@ -116,7 +117,7 @@ void PrintConfigDef::init_common_params()
|
||||
def = this->add("printhost_port", coString);
|
||||
def->label = L("Printer");
|
||||
def->tooltip = L("Name of the printer");
|
||||
def->gui_type = "select_open";
|
||||
def->gui_type = ConfigOptionDef::GUIType::select_open;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionString(""));
|
||||
|
||||
@ -568,7 +569,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
def = this->add("extruder", coInt);
|
||||
def->gui_type = "i_enum_open";
|
||||
def->gui_type = ConfigOptionDef::GUIType::i_enum_open;
|
||||
def->label = L("Extruder");
|
||||
def->category = L("Extruders");
|
||||
def->tooltip = L("The extruder to use (unless more specific extruder settings are specified). "
|
||||
@ -606,7 +607,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def = this->add("extruder_colour", coStrings);
|
||||
def->label = L("Extruder Color");
|
||||
def->tooltip = L("This is only used in the Slic3r interface as a visual help.");
|
||||
def->gui_type = "color";
|
||||
def->gui_type = ConfigOptionDef::GUIType::color;
|
||||
// Empty string means no color assigned yet.
|
||||
def->set_default_value(new ConfigOptionStrings { "" });
|
||||
|
||||
@ -668,7 +669,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def = this->add("filament_colour", coStrings);
|
||||
def->label = L("Color");
|
||||
def->tooltip = L("This is only used in the Slic3r interface as a visual help.");
|
||||
def->gui_type = "color";
|
||||
def->gui_type = ConfigOptionDef::GUIType::color;
|
||||
def->set_default_value(new ConfigOptionStrings { "#29B2B2" });
|
||||
|
||||
def = this->add("filament_notes", coStrings);
|
||||
@ -812,7 +813,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def = this->add("filament_type", coStrings);
|
||||
def->label = L("Filament type");
|
||||
def->tooltip = L("The filament material type for use in custom G-codes.");
|
||||
def->gui_type = "f_enum_open";
|
||||
def->gui_type = ConfigOptionDef::GUIType::f_enum_open;
|
||||
def->gui_flags = "show_value";
|
||||
def->enum_values.push_back("PLA");
|
||||
def->enum_values.push_back("PET");
|
||||
@ -881,7 +882,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->set_default_value(new ConfigOptionFloat(45));
|
||||
|
||||
def = this->add("fill_density", coPercent);
|
||||
def->gui_type = "f_enum_open";
|
||||
def->gui_type = ConfigOptionDef::GUIType::f_enum_open;
|
||||
def->gui_flags = "show_value";
|
||||
def->label = L("Fill density");
|
||||
def->category = L("Infill");
|
||||
@ -1168,7 +1169,7 @@ void PrintConfigDef::init_fff_params()
|
||||
"Set this parameter to zero to disable anchoring perimeters connected to a single infill line.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->ratio_over = "infill_extrusion_width";
|
||||
def->gui_type = "f_enum_open";
|
||||
def->gui_type = ConfigOptionDef::GUIType::f_enum_open;
|
||||
def->enum_values.push_back("0");
|
||||
def->enum_values.push_back("1");
|
||||
def->enum_values.push_back("2");
|
||||
@ -1950,7 +1951,7 @@ void PrintConfigDef::init_fff_params()
|
||||
|
||||
#if 0
|
||||
def = this->add("seam_preferred_direction", coFloat);
|
||||
// def->gui_type = "slider";
|
||||
// def->gui_type = ConfigOptionDef::GUIType::slider;
|
||||
def->label = L("Direction");
|
||||
def->sidetext = L("°");
|
||||
def->full_label = L("Preferred direction of the seam");
|
||||
@ -1960,7 +1961,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("seam_preferred_direction_jitter", coFloat);
|
||||
// def->gui_type = "slider";
|
||||
// def->gui_type = ConfigOptionDef::GUIType::slider;
|
||||
def->label = L("Jitter");
|
||||
def->sidetext = L("°");
|
||||
def->full_label = L("Seam preferred direction jitter");
|
||||
@ -2234,8 +2235,8 @@ void PrintConfigDef::init_fff_params()
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("support_material_contact_distance", coFloat);
|
||||
def->gui_type = "f_enum_open";
|
||||
def->label = L("Contact Z distance");
|
||||
def->gui_type = ConfigOptionDef::GUIType::f_enum_open;
|
||||
def->label = L("Top contact Z distance");
|
||||
def->category = L("Support material");
|
||||
def->tooltip = L("The vertical distance between object and support material interface. "
|
||||
"Setting this to 0 will also prevent Slic3r from using bridge flow and speed "
|
||||
@ -2243,12 +2244,31 @@ void PrintConfigDef::init_fff_params()
|
||||
def->sidetext = L("mm");
|
||||
// def->min = 0;
|
||||
def->enum_values.push_back("0");
|
||||
def->enum_values.push_back("0.1");
|
||||
def->enum_values.push_back("0.2");
|
||||
def->enum_labels.push_back(L("0 (soluble)"));
|
||||
def->enum_labels.push_back(L("0.1 (detachable)"));
|
||||
def->enum_labels.push_back(L("0.2 (detachable)"));
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.2));
|
||||
|
||||
def = this->add("support_material_bottom_contact_distance", coFloat);
|
||||
def->gui_type = ConfigOptionDef::GUIType::f_enum_open;
|
||||
def->label = L("Bottom contact Z distance");
|
||||
def->category = L("Support material");
|
||||
def->tooltip = L("The vertical distance between the object top surface and the support material interface. "
|
||||
"If set to zero, support_material_contact_distance will be used for both top and bottom contact Z distances.");
|
||||
def->sidetext = L("mm");
|
||||
// def->min = 0;
|
||||
def->enum_values.push_back("0");
|
||||
def->enum_values.push_back("0.1");
|
||||
def->enum_values.push_back("0.2");
|
||||
def->enum_labels.push_back(L("same as top"));
|
||||
def->enum_labels.push_back(L("0.1"));
|
||||
def->enum_labels.push_back(L("0.2"));
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0));
|
||||
|
||||
def = this->add("support_material_enforce_layers", coInt);
|
||||
def->label = L("Enforce support for the first");
|
||||
def->category = L("Support material");
|
||||
@ -2298,15 +2318,39 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt(1));
|
||||
|
||||
def = this->add("support_material_interface_layers", coInt);
|
||||
def->label = L("Interface layers");
|
||||
auto support_material_interface_layers = def = this->add("support_material_interface_layers", coInt);
|
||||
def->gui_type = ConfigOptionDef::GUIType::i_enum_open;
|
||||
def->label = L("Top interface layers");
|
||||
def->category = L("Support material");
|
||||
def->tooltip = L("Number of interface layers to insert between the object(s) and support material.");
|
||||
def->sidetext = L("layers");
|
||||
def->min = 0;
|
||||
def->enum_values.push_back("0");
|
||||
def->enum_values.push_back("1");
|
||||
def->enum_values.push_back("2");
|
||||
def->enum_values.push_back("3");
|
||||
def->enum_labels.push_back(L("0 (off)"));
|
||||
def->enum_labels.push_back(L("1 (light)"));
|
||||
def->enum_labels.push_back(L("2 (default)"));
|
||||
def->enum_labels.push_back(L("3 (heavy)"));
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt(3));
|
||||
|
||||
def = this->add("support_material_bottom_interface_layers", coInt);
|
||||
def->gui_type = ConfigOptionDef::GUIType::i_enum_open;
|
||||
def->label = L("Bottom interface layers");
|
||||
def->category = L("Support material");
|
||||
def->tooltip = L("Number of interface layers to insert between the object(s) and support material. "
|
||||
"Set to -1 to use support_material_interface_layers");
|
||||
def->sidetext = L("layers");
|
||||
def->min = -1;
|
||||
def->enum_values.push_back("-1");
|
||||
append(def->enum_values, support_material_interface_layers->enum_values);
|
||||
def->enum_labels.push_back(L("same as top"));
|
||||
append(def->enum_labels, support_material_interface_layers->enum_labels);
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt(-1));
|
||||
|
||||
def = this->add("support_material_interface_spacing", coFloat);
|
||||
def->label = L("Interface pattern spacing");
|
||||
def->category = L("Support material");
|
||||
@ -2415,6 +2459,13 @@ void PrintConfigDef::init_fff_params()
|
||||
def->max = max_temp;
|
||||
def->set_default_value(new ConfigOptionInts { 200 });
|
||||
|
||||
def = this->add("thick_bridges", coBool);
|
||||
def->label = L("Thick bridges");
|
||||
def->category = L("Layers and Perimeters");
|
||||
def->tooltip = L("Print bridges with round extrusions.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
def = this->add("thin_walls", coBool);
|
||||
def->label = L("Detect thin walls");
|
||||
def->category = L("Layers and Perimeters");
|
||||
@ -2823,7 +2874,7 @@ void PrintConfigDef::init_sla_params()
|
||||
def = this->add("material_type", coString);
|
||||
def->label = L("SLA material type");
|
||||
def->tooltip = L("SLA material type");
|
||||
def->gui_type = "f_enum_open"; // TODO: ???
|
||||
def->gui_type = ConfigOptionDef::GUIType::f_enum_open; // TODO: ???
|
||||
def->gui_flags = "show_value";
|
||||
def->enum_values.push_back("Tough");
|
||||
def->enum_values.push_back("Flexible");
|
||||
|
@ -503,12 +503,14 @@ public:
|
||||
ConfigOptionFloat support_material_angle;
|
||||
ConfigOptionBool support_material_buildplate_only;
|
||||
ConfigOptionFloat support_material_contact_distance;
|
||||
ConfigOptionFloat support_material_bottom_contact_distance;
|
||||
ConfigOptionInt support_material_enforce_layers;
|
||||
ConfigOptionInt support_material_extruder;
|
||||
ConfigOptionFloatOrPercent support_material_extrusion_width;
|
||||
ConfigOptionBool support_material_interface_contact_loops;
|
||||
ConfigOptionInt support_material_interface_extruder;
|
||||
ConfigOptionInt support_material_interface_layers;
|
||||
ConfigOptionInt support_material_bottom_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;
|
||||
@ -522,6 +524,7 @@ public:
|
||||
ConfigOptionInt support_material_threshold;
|
||||
ConfigOptionBool support_material_with_sheath;
|
||||
ConfigOptionFloatOrPercent support_material_xy_spacing;
|
||||
ConfigOptionBool thick_bridges;
|
||||
ConfigOptionFloat xy_size_compensation;
|
||||
ConfigOptionBool wipe_into_objects;
|
||||
|
||||
@ -553,12 +556,14 @@ protected:
|
||||
OPT_PTR(support_material_angle);
|
||||
OPT_PTR(support_material_buildplate_only);
|
||||
OPT_PTR(support_material_contact_distance);
|
||||
OPT_PTR(support_material_bottom_contact_distance);
|
||||
OPT_PTR(support_material_enforce_layers);
|
||||
OPT_PTR(support_material_interface_contact_loops);
|
||||
OPT_PTR(support_material_extruder);
|
||||
OPT_PTR(support_material_extrusion_width);
|
||||
OPT_PTR(support_material_interface_extruder);
|
||||
OPT_PTR(support_material_interface_layers);
|
||||
OPT_PTR(support_material_bottom_interface_layers);
|
||||
OPT_PTR(support_material_interface_spacing);
|
||||
OPT_PTR(support_material_interface_speed);
|
||||
OPT_PTR(support_material_pattern);
|
||||
@ -569,6 +574,7 @@ protected:
|
||||
OPT_PTR(support_material_xy_spacing);
|
||||
OPT_PTR(support_material_threshold);
|
||||
OPT_PTR(support_material_with_sheath);
|
||||
OPT_PTR(thick_bridges);
|
||||
OPT_PTR(xy_size_compensation);
|
||||
OPT_PTR(wipe_into_objects);
|
||||
}
|
||||
|
@ -546,14 +546,9 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "extra_perimeters"
|
||||
|| opt_key == "gap_fill_enabled"
|
||||
|| opt_key == "gap_fill_speed"
|
||||
|| opt_key == "overhangs"
|
||||
|| opt_key == "first_layer_extrusion_width"
|
||||
|| opt_key == "fuzzy_skin"
|
||||
|| opt_key == "fuzzy_skin_thickness"
|
||||
|| opt_key == "fuzzy_skin_point_dist"
|
||||
|| opt_key == "perimeter_extrusion_width"
|
||||
|| opt_key == "infill_overlap"
|
||||
|| opt_key == "thin_walls"
|
||||
|| opt_key == "external_perimeters_first") {
|
||||
steps.emplace_back(posPerimeters);
|
||||
} else if (
|
||||
@ -585,7 +580,9 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "support_material_enforce_layers"
|
||||
|| opt_key == "support_material_extruder"
|
||||
|| opt_key == "support_material_extrusion_width"
|
||||
|| opt_key == "support_material_bottom_contact_distance"
|
||||
|| opt_key == "support_material_interface_layers"
|
||||
|| opt_key == "support_material_bottom_interface_layers"
|
||||
|| opt_key == "support_material_interface_pattern"
|
||||
|| opt_key == "support_material_interface_contact_loops"
|
||||
|| opt_key == "support_material_interface_extruder"
|
||||
@ -652,7 +649,13 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
steps.emplace_back(posPrepareInfill);
|
||||
} else if (
|
||||
opt_key == "external_perimeter_extrusion_width"
|
||||
|| opt_key == "perimeter_extruder") {
|
||||
|| opt_key == "perimeter_extruder"
|
||||
|| opt_key == "fuzzy_skin"
|
||||
|| opt_key == "fuzzy_skin_thickness"
|
||||
|| opt_key == "fuzzy_skin_point_dist"
|
||||
|| opt_key == "overhangs"
|
||||
|| opt_key == "thin_walls"
|
||||
|| opt_key == "thick_bridges") {
|
||||
steps.emplace_back(posPerimeters);
|
||||
steps.emplace_back(posSupportMaterial);
|
||||
} else if (opt_key == "bridge_flow_ratio") {
|
||||
@ -1456,26 +1459,18 @@ void PrintObject::bridge_over_infill()
|
||||
const PrintRegion ®ion = *m_print->regions()[region_id];
|
||||
|
||||
// skip bridging in case there are no voids
|
||||
if (region.config().fill_density.value == 100) continue;
|
||||
|
||||
// get bridge flow
|
||||
Flow bridge_flow = region.flow(
|
||||
frSolidInfill,
|
||||
-1, // layer height, not relevant for bridge flow
|
||||
true, // bridge
|
||||
false, // first layer
|
||||
-1, // custom width, not relevant for bridge flow
|
||||
*this
|
||||
);
|
||||
|
||||
if (region.config().fill_density.value == 100)
|
||||
continue;
|
||||
|
||||
for (LayerPtrs::iterator layer_it = m_layers.begin(); layer_it != m_layers.end(); ++ layer_it) {
|
||||
// skip first layer
|
||||
if (layer_it == m_layers.begin())
|
||||
continue;
|
||||
|
||||
Layer* layer = *layer_it;
|
||||
LayerRegion* layerm = layer->m_regions[region_id];
|
||||
|
||||
Layer *layer = *layer_it;
|
||||
LayerRegion *layerm = layer->m_regions[region_id];
|
||||
Flow bridge_flow = layerm->bridging_flow(frSolidInfill);
|
||||
|
||||
// extract the stInternalSolid surfaces that might be transformed into bridges
|
||||
Polygons internal_solid;
|
||||
layerm->fill_surfaces.filter_by_type(stInternalSolid, &internal_solid);
|
||||
@ -1488,7 +1483,7 @@ void PrintObject::bridge_over_infill()
|
||||
Polygons to_bridge_pp = internal_solid;
|
||||
|
||||
// iterate through lower layers spanned by bridge_flow
|
||||
double bottom_z = layer->print_z - bridge_flow.height;
|
||||
double bottom_z = layer->print_z - bridge_flow.height();
|
||||
for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) {
|
||||
const Layer* lower_layer = m_layers[i];
|
||||
|
||||
|
@ -18,31 +18,25 @@ unsigned int PrintRegion::extruder(FlowRole role) const
|
||||
return extruder;
|
||||
}
|
||||
|
||||
Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const
|
||||
Flow PrintRegion::flow(const PrintObject &object, FlowRole role, double layer_height, bool first_layer) const
|
||||
{
|
||||
ConfigOptionFloatOrPercent config_width;
|
||||
if (width != -1) {
|
||||
// use the supplied custom width, if any
|
||||
config_width.value = width;
|
||||
config_width.percent = false;
|
||||
// Get extrusion width from configuration.
|
||||
// (might be an absolute value, or a percent value, or zero for auto)
|
||||
if (first_layer && m_print->config().first_layer_extrusion_width.value > 0) {
|
||||
config_width = m_print->config().first_layer_extrusion_width;
|
||||
} else if (role == frExternalPerimeter) {
|
||||
config_width = m_config.external_perimeter_extrusion_width;
|
||||
} else if (role == frPerimeter) {
|
||||
config_width = m_config.perimeter_extrusion_width;
|
||||
} else if (role == frInfill) {
|
||||
config_width = m_config.infill_extrusion_width;
|
||||
} else if (role == frSolidInfill) {
|
||||
config_width = m_config.solid_infill_extrusion_width;
|
||||
} else if (role == frTopSolidInfill) {
|
||||
config_width = m_config.top_infill_extrusion_width;
|
||||
} else {
|
||||
// otherwise, get extrusion width from configuration
|
||||
// (might be an absolute value, or a percent value, or zero for auto)
|
||||
if (first_layer && m_print->config().first_layer_extrusion_width.value > 0) {
|
||||
config_width = m_print->config().first_layer_extrusion_width;
|
||||
} else if (role == frExternalPerimeter) {
|
||||
config_width = m_config.external_perimeter_extrusion_width;
|
||||
} else if (role == frPerimeter) {
|
||||
config_width = m_config.perimeter_extrusion_width;
|
||||
} else if (role == frInfill) {
|
||||
config_width = m_config.infill_extrusion_width;
|
||||
} else if (role == frSolidInfill) {
|
||||
config_width = m_config.solid_infill_extrusion_width;
|
||||
} else if (role == frTopSolidInfill) {
|
||||
config_width = m_config.top_infill_extrusion_width;
|
||||
} else {
|
||||
throw Slic3r::InvalidArgument("Unknown role");
|
||||
}
|
||||
throw Slic3r::InvalidArgument("Unknown role");
|
||||
}
|
||||
|
||||
if (config_width.value == 0)
|
||||
@ -50,8 +44,8 @@ Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool fir
|
||||
|
||||
// Get the configured nozzle_diameter for the extruder associated to the flow role requested.
|
||||
// Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right.
|
||||
double nozzle_diameter = m_print->config().nozzle_diameter.get_at(this->extruder(role) - 1);
|
||||
return Flow::new_from_config_width(role, config_width, (float)nozzle_diameter, (float)layer_height, bridge ? (float)m_config.bridge_flow_ratio : 0.0f);
|
||||
auto nozzle_diameter = float(m_print->config().nozzle_diameter.get_at(this->extruder(role) - 1));
|
||||
return Flow::new_from_config_width(role, config_width, nozzle_diameter, float(layer_height));
|
||||
}
|
||||
|
||||
coordf_t PrintRegion::nozzle_dmr_avg(const PrintConfig &print_config) const
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <tbb/mutex.h>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_reduce.h>
|
||||
#include <tbb/task_arena.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
@ -76,13 +77,18 @@ template<> struct _ccr<true>
|
||||
from, to, init, std::forward<MergeFn>(mergefn),
|
||||
[](typename I::value_type &i) { return i; }, granularity);
|
||||
}
|
||||
|
||||
static size_t max_concurreny()
|
||||
{
|
||||
return tbb::this_task_arena::max_concurrency();
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct _ccr<false>
|
||||
{
|
||||
private:
|
||||
struct _Mtx { inline void lock() {} inline void unlock() {} };
|
||||
|
||||
|
||||
public:
|
||||
using SpinningMutex = _Mtx;
|
||||
using BlockingMutex = _Mtx;
|
||||
@ -133,6 +139,8 @@ public:
|
||||
return reduce(from, to, init, std::forward<MergeFn>(mergefn),
|
||||
[](typename I::value_type &i) { return i; });
|
||||
}
|
||||
|
||||
static size_t max_concurreny() { return 1; }
|
||||
};
|
||||
|
||||
using ccr = _ccr<USE_FULL_CONCURRENCY>;
|
||||
|
@ -26,64 +26,99 @@ inline void _scale(S s, TriangleMesh &m) { m.scale(float(s)); }
|
||||
template<class S, class = FloatingOnly<S>>
|
||||
inline void _scale(S s, Contour3D &m) { for (auto &p : m.points) p *= s; }
|
||||
|
||||
static TriangleMesh _generate_interior(const TriangleMesh &mesh,
|
||||
const JobController &ctl,
|
||||
double min_thickness,
|
||||
double voxel_scale,
|
||||
double closing_dist)
|
||||
struct Interior {
|
||||
TriangleMesh mesh;
|
||||
openvdb::FloatGrid::Ptr gridptr;
|
||||
mutable std::optional<openvdb::FloatGrid::ConstAccessor> accessor;
|
||||
|
||||
double closing_distance = 0.;
|
||||
double thickness = 0.;
|
||||
double voxel_scale = 1.;
|
||||
double nb_in = 3.; // narrow band width inwards
|
||||
double nb_out = 3.; // narrow band width outwards
|
||||
// Full narrow band is the sum of the two above values.
|
||||
|
||||
void reset_accessor() const // This resets the accessor and its cache
|
||||
// Not a thread safe call!
|
||||
{
|
||||
if (gridptr)
|
||||
accessor = gridptr->getConstAccessor();
|
||||
}
|
||||
};
|
||||
|
||||
void InteriorDeleter::operator()(Interior *p)
|
||||
{
|
||||
delete p;
|
||||
}
|
||||
|
||||
TriangleMesh &get_mesh(Interior &interior)
|
||||
{
|
||||
return interior.mesh;
|
||||
}
|
||||
|
||||
const TriangleMesh &get_mesh(const Interior &interior)
|
||||
{
|
||||
return interior.mesh;
|
||||
}
|
||||
|
||||
static InteriorPtr generate_interior_verbose(const TriangleMesh & mesh,
|
||||
const JobController &ctl,
|
||||
double min_thickness,
|
||||
double voxel_scale,
|
||||
double closing_dist)
|
||||
{
|
||||
TriangleMesh imesh{mesh};
|
||||
|
||||
_scale(voxel_scale, imesh);
|
||||
|
||||
double offset = voxel_scale * min_thickness;
|
||||
double D = voxel_scale * closing_dist;
|
||||
float out_range = 0.1f * float(offset);
|
||||
float in_range = 1.1f * float(offset + D);
|
||||
|
||||
|
||||
if (ctl.stopcondition()) return {};
|
||||
else ctl.statuscb(0, L("Hollowing"));
|
||||
|
||||
auto gridptr = mesh_to_grid(imesh, {}, out_range, in_range);
|
||||
|
||||
|
||||
auto gridptr = mesh_to_grid(mesh, {}, voxel_scale, out_range, in_range);
|
||||
|
||||
assert(gridptr);
|
||||
|
||||
|
||||
if (!gridptr) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Returned OpenVDB grid is NULL";
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
if (ctl.stopcondition()) return {};
|
||||
else ctl.statuscb(30, L("Hollowing"));
|
||||
|
||||
if (closing_dist > .0) {
|
||||
gridptr = redistance_grid(*gridptr, -(offset + D), double(in_range));
|
||||
} else {
|
||||
D = -offset;
|
||||
}
|
||||
|
||||
|
||||
double iso_surface = D;
|
||||
auto narrowb = double(in_range);
|
||||
gridptr = redistance_grid(*gridptr, -(offset + D), narrowb, narrowb);
|
||||
|
||||
if (ctl.stopcondition()) return {};
|
||||
else ctl.statuscb(70, L("Hollowing"));
|
||||
|
||||
double iso_surface = D;
|
||||
|
||||
double adaptivity = 0.;
|
||||
auto omesh = grid_to_mesh(*gridptr, iso_surface, adaptivity);
|
||||
|
||||
_scale(1. / voxel_scale, omesh);
|
||||
|
||||
InteriorPtr interior = InteriorPtr{new Interior{}};
|
||||
|
||||
interior->mesh = grid_to_mesh(*gridptr, iso_surface, adaptivity);
|
||||
interior->gridptr = gridptr;
|
||||
|
||||
if (ctl.stopcondition()) return {};
|
||||
else ctl.statuscb(100, L("Hollowing"));
|
||||
|
||||
return omesh;
|
||||
|
||||
interior->closing_distance = D;
|
||||
interior->thickness = offset;
|
||||
interior->voxel_scale = voxel_scale;
|
||||
interior->nb_in = narrowb;
|
||||
interior->nb_out = narrowb;
|
||||
|
||||
return interior;
|
||||
}
|
||||
|
||||
std::unique_ptr<TriangleMesh> generate_interior(const TriangleMesh & mesh,
|
||||
const HollowingConfig &hc,
|
||||
const JobController & ctl)
|
||||
InteriorPtr generate_interior(const TriangleMesh & mesh,
|
||||
const HollowingConfig &hc,
|
||||
const JobController & ctl)
|
||||
{
|
||||
static const double MIN_OVERSAMPL = 3.;
|
||||
static const double MAX_OVERSAMPL = 8.;
|
||||
|
||||
|
||||
// I can't figure out how to increase the grid resolution through openvdb
|
||||
// API so the model will be scaled up before conversion and the result
|
||||
// scaled down. Voxels have a unit size. If I set voxelSize smaller, it
|
||||
@ -92,26 +127,29 @@ std::unique_ptr<TriangleMesh> generate_interior(const TriangleMesh & mesh,
|
||||
//
|
||||
// max 8x upscale, min is native voxel size
|
||||
auto voxel_scale = MIN_OVERSAMPL + (MAX_OVERSAMPL - MIN_OVERSAMPL) * hc.quality;
|
||||
auto meshptr = std::make_unique<TriangleMesh>(
|
||||
_generate_interior(mesh, ctl, hc.min_thickness, voxel_scale,
|
||||
hc.closing_distance));
|
||||
|
||||
if (meshptr && !meshptr->empty()) {
|
||||
|
||||
|
||||
InteriorPtr interior =
|
||||
generate_interior_verbose(mesh, ctl, hc.min_thickness, voxel_scale,
|
||||
hc.closing_distance);
|
||||
|
||||
if (interior && !interior->mesh.empty()) {
|
||||
|
||||
// This flips the normals to be outward facing...
|
||||
meshptr->require_shared_vertices();
|
||||
indexed_triangle_set its = std::move(meshptr->its);
|
||||
|
||||
interior->mesh.require_shared_vertices();
|
||||
indexed_triangle_set its = std::move(interior->mesh.its);
|
||||
|
||||
Slic3r::simplify_mesh(its);
|
||||
|
||||
|
||||
// flip normals back...
|
||||
for (stl_triangle_vertex_indices &ind : its.indices)
|
||||
std::swap(ind(0), ind(2));
|
||||
|
||||
*meshptr = Slic3r::TriangleMesh{its};
|
||||
|
||||
interior->mesh = Slic3r::TriangleMesh{its};
|
||||
interior->mesh.repaired = true;
|
||||
interior->mesh.require_shared_vertices();
|
||||
}
|
||||
|
||||
return meshptr;
|
||||
|
||||
return interior;
|
||||
}
|
||||
|
||||
Contour3D DrainHole::to_mesh() const
|
||||
@ -273,12 +311,264 @@ void cut_drainholes(std::vector<ExPolygons> & obj_slices,
|
||||
obj_slices[i] = diff_ex(obj_slices[i], hole_slices[i]);
|
||||
}
|
||||
|
||||
void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg)
|
||||
void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg, int flags)
|
||||
{
|
||||
std::unique_ptr<Slic3r::TriangleMesh> inter_ptr =
|
||||
Slic3r::sla::generate_interior(mesh);
|
||||
InteriorPtr interior = generate_interior(mesh, cfg, JobController{});
|
||||
if (!interior) return;
|
||||
|
||||
if (inter_ptr) mesh.merge(*inter_ptr);
|
||||
hollow_mesh(mesh, *interior, flags);
|
||||
}
|
||||
|
||||
void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags)
|
||||
{
|
||||
if (mesh.empty() || interior.mesh.empty()) return;
|
||||
|
||||
if (flags & hfRemoveInsideTriangles && interior.gridptr)
|
||||
remove_inside_triangles(mesh, interior);
|
||||
|
||||
mesh.merge(interior.mesh);
|
||||
mesh.require_shared_vertices();
|
||||
}
|
||||
|
||||
// Get the distance of p to the interior's zero iso_surface. Interior should
|
||||
// have its zero isosurface positioned at offset + closing_distance inwards form
|
||||
// the model surface.
|
||||
static double get_distance_raw(const Vec3f &p, const Interior &interior)
|
||||
{
|
||||
assert(interior.gridptr);
|
||||
|
||||
if (!interior.accessor) interior.reset_accessor();
|
||||
|
||||
auto v = (p * interior.voxel_scale).cast<double>();
|
||||
auto grididx = interior.gridptr->transform().worldToIndexCellCentered(
|
||||
{v.x(), v.y(), v.z()});
|
||||
|
||||
return interior.accessor->getValue(grididx) ;
|
||||
}
|
||||
|
||||
struct TriangleBubble { Vec3f center; double R; };
|
||||
|
||||
// Return the distance of bubble center to the interior boundary or NaN if the
|
||||
// triangle is too big to be measured.
|
||||
static double get_distance(const TriangleBubble &b, const Interior &interior)
|
||||
{
|
||||
double R = b.R * interior.voxel_scale;
|
||||
double D = get_distance_raw(b.center, interior);
|
||||
|
||||
return (D > 0. && R >= interior.nb_out) ||
|
||||
(D < 0. && R >= interior.nb_in) ||
|
||||
((D - R) < 0. && 2 * R > interior.thickness) ?
|
||||
std::nan("") :
|
||||
// FIXME: Adding interior.voxel_scale is a compromise supposed
|
||||
// to prevent the deletion of the triangles forming the interior
|
||||
// itself. This has a side effect that a small portion of the
|
||||
// bad triangles will still be visible.
|
||||
D - interior.closing_distance /*+ 2 * interior.voxel_scale*/;
|
||||
}
|
||||
|
||||
double get_distance(const Vec3f &p, const Interior &interior)
|
||||
{
|
||||
double d = get_distance_raw(p, interior) - interior.closing_distance;
|
||||
return d / interior.voxel_scale;
|
||||
}
|
||||
|
||||
// A face that can be divided. Stores the indices into the original mesh if its
|
||||
// part of that mesh and the vertices it consists of.
|
||||
enum { NEW_FACE = -1};
|
||||
struct DivFace {
|
||||
Vec3i indx;
|
||||
std::array<Vec3f, 3> verts;
|
||||
long faceid = NEW_FACE;
|
||||
long parent = NEW_FACE;
|
||||
};
|
||||
|
||||
// Divide a face recursively and call visitor on all the sub-faces.
|
||||
template<class Fn>
|
||||
void divide_triangle(const DivFace &face, Fn &&visitor)
|
||||
{
|
||||
std::array<Vec3f, 3> edges = {(face.verts[0] - face.verts[1]),
|
||||
(face.verts[1] - face.verts[2]),
|
||||
(face.verts[2] - face.verts[0])};
|
||||
|
||||
std::array<size_t, 3> edgeidx = {0, 1, 2};
|
||||
|
||||
std::sort(edgeidx.begin(), edgeidx.end(), [&edges](size_t e1, size_t e2) {
|
||||
return edges[e1].squaredNorm() > edges[e2].squaredNorm();
|
||||
});
|
||||
|
||||
DivFace child1, child2;
|
||||
|
||||
child1.parent = face.faceid == NEW_FACE ? face.parent : face.faceid;
|
||||
child1.indx(0) = -1;
|
||||
child1.indx(1) = face.indx(edgeidx[1]);
|
||||
child1.indx(2) = face.indx((edgeidx[1] + 1) % 3);
|
||||
child1.verts[0] = (face.verts[edgeidx[0]] + face.verts[(edgeidx[0] + 1) % 3]) / 2.;
|
||||
child1.verts[1] = face.verts[edgeidx[1]];
|
||||
child1.verts[2] = face.verts[(edgeidx[1] + 1) % 3];
|
||||
|
||||
if (visitor(child1))
|
||||
divide_triangle(child1, std::forward<Fn>(visitor));
|
||||
|
||||
child2.parent = face.faceid == NEW_FACE ? face.parent : face.faceid;
|
||||
child2.indx(0) = -1;
|
||||
child2.indx(1) = face.indx(edgeidx[2]);
|
||||
child2.indx(2) = face.indx((edgeidx[2] + 1) % 3);
|
||||
child2.verts[0] = child1.verts[0];
|
||||
child2.verts[1] = face.verts[edgeidx[2]];
|
||||
child2.verts[2] = face.verts[(edgeidx[2] + 1) % 3];
|
||||
|
||||
if (visitor(child2))
|
||||
divide_triangle(child2, std::forward<Fn>(visitor));
|
||||
}
|
||||
|
||||
void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior,
|
||||
const std::vector<bool> &exclude_mask)
|
||||
{
|
||||
enum TrPos { posInside, posTouch, posOutside };
|
||||
|
||||
auto &faces = mesh.its.indices;
|
||||
auto &vertices = mesh.its.vertices;
|
||||
auto bb = mesh.bounding_box();
|
||||
|
||||
bool use_exclude_mask = faces.size() == exclude_mask.size();
|
||||
auto is_excluded = [&exclude_mask, use_exclude_mask](size_t face_id) {
|
||||
return use_exclude_mask && exclude_mask[face_id];
|
||||
};
|
||||
|
||||
// TODO: Parallel mode not working yet
|
||||
using exec_policy = ccr_seq;
|
||||
|
||||
// Info about the needed modifications on the input mesh.
|
||||
struct MeshMods {
|
||||
|
||||
// Just a thread safe wrapper for a vector of triangles.
|
||||
struct {
|
||||
std::vector<std::array<Vec3f, 3>> data;
|
||||
exec_policy::SpinningMutex mutex;
|
||||
|
||||
void emplace_back(const std::array<Vec3f, 3> &pts)
|
||||
{
|
||||
std::lock_guard lk{mutex};
|
||||
data.emplace_back(pts);
|
||||
}
|
||||
|
||||
size_t size() const { return data.size(); }
|
||||
const std::array<Vec3f, 3>& operator[](size_t idx) const
|
||||
{
|
||||
return data[idx];
|
||||
}
|
||||
|
||||
} new_triangles;
|
||||
|
||||
// A vector of bool for all faces signaling if it needs to be removed
|
||||
// or not.
|
||||
std::vector<bool> to_remove;
|
||||
|
||||
MeshMods(const TriangleMesh &mesh):
|
||||
to_remove(mesh.its.indices.size(), false) {}
|
||||
|
||||
// Number of triangles that need to be removed.
|
||||
size_t to_remove_cnt() const
|
||||
{
|
||||
return std::accumulate(to_remove.begin(), to_remove.end(), size_t(0));
|
||||
}
|
||||
|
||||
} mesh_mods{mesh};
|
||||
|
||||
// Must return true if further division of the face is needed.
|
||||
auto divfn = [&interior, bb, &mesh_mods](const DivFace &f) {
|
||||
BoundingBoxf3 facebb { f.verts.begin(), f.verts.end() };
|
||||
|
||||
// Face is certainly outside the cavity
|
||||
if (! facebb.intersects(bb) && f.faceid != NEW_FACE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TriangleBubble bubble{facebb.center().cast<float>(), facebb.radius()};
|
||||
|
||||
double D = get_distance(bubble, interior);
|
||||
double R = bubble.R * interior.voxel_scale;
|
||||
|
||||
if (std::isnan(D)) // The distance cannot be measured, triangle too big
|
||||
return true;
|
||||
|
||||
// Distance of the bubble wall to the interior wall. Negative if the
|
||||
// bubble is overlapping with the interior
|
||||
double bubble_distance = D - R;
|
||||
|
||||
// The face is crossing the interior or inside, it must be removed and
|
||||
// parts of it re-added, that are outside the interior
|
||||
if (bubble_distance < 0.) {
|
||||
if (f.faceid != NEW_FACE)
|
||||
mesh_mods.to_remove[f.faceid] = true;
|
||||
|
||||
if (f.parent != NEW_FACE) // Top parent needs to be removed as well
|
||||
mesh_mods.to_remove[f.parent] = true;
|
||||
|
||||
// If the outside part is between the interior end the exterior
|
||||
// (inside the wall being invisible), no further division is needed.
|
||||
if ((R + D) < interior.thickness)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
} else if (f.faceid == NEW_FACE) {
|
||||
// New face completely outside needs to be re-added.
|
||||
mesh_mods.new_triangles.emplace_back(f.verts);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
interior.reset_accessor();
|
||||
|
||||
exec_policy::for_each(size_t(0), faces.size(), [&] (size_t face_idx) {
|
||||
const Vec3i &face = faces[face_idx];
|
||||
|
||||
// If the triangle is excluded, we need to keep it.
|
||||
if (is_excluded(face_idx))
|
||||
return;
|
||||
|
||||
std::array<Vec3f, 3> pts =
|
||||
{ vertices[face(0)], vertices[face(1)], vertices[face(2)] };
|
||||
|
||||
BoundingBoxf3 facebb { pts.begin(), pts.end() };
|
||||
|
||||
// Face is certainly outside the cavity
|
||||
if (! facebb.intersects(bb)) return;
|
||||
|
||||
DivFace df{face, pts, long(face_idx)};
|
||||
|
||||
if (divfn(df))
|
||||
divide_triangle(df, divfn);
|
||||
|
||||
}, exec_policy::max_concurreny());
|
||||
|
||||
auto new_faces = reserve_vector<Vec3i>(faces.size() +
|
||||
mesh_mods.new_triangles.size());
|
||||
|
||||
for (size_t face_idx = 0; face_idx < faces.size(); ++face_idx) {
|
||||
if (!mesh_mods.to_remove[face_idx])
|
||||
new_faces.emplace_back(faces[face_idx]);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < mesh_mods.new_triangles.size(); ++i) {
|
||||
size_t o = vertices.size();
|
||||
vertices.emplace_back(mesh_mods.new_triangles[i][0]);
|
||||
vertices.emplace_back(mesh_mods.new_triangles[i][1]);
|
||||
vertices.emplace_back(mesh_mods.new_triangles[i][2]);
|
||||
new_faces.emplace_back(int(o), int(o + 1), int(o + 2));
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Trimming: " << mesh_mods.to_remove_cnt() << " triangles removed";
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Trimming: " << mesh_mods.new_triangles.size() << " triangles added";
|
||||
|
||||
faces.swap(new_faces);
|
||||
new_faces = {};
|
||||
|
||||
mesh = TriangleMesh{mesh.its};
|
||||
mesh.repaired = true;
|
||||
mesh.require_shared_vertices();
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,17 @@ struct HollowingConfig
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
enum HollowingFlags { hfRemoveInsideTriangles = 0x1 };
|
||||
|
||||
// All data related to a generated mesh interior. Includes the 3D grid and mesh
|
||||
// and various metadata. No need to manipulate from outside.
|
||||
struct Interior;
|
||||
struct InteriorDeleter { void operator()(Interior *p); };
|
||||
using InteriorPtr = std::unique_ptr<Interior, InteriorDeleter>;
|
||||
|
||||
TriangleMesh & get_mesh(Interior &interior);
|
||||
const TriangleMesh &get_mesh(const Interior &interior);
|
||||
|
||||
struct DrainHole
|
||||
{
|
||||
Vec3f pos;
|
||||
@ -60,11 +71,26 @@ using DrainHoles = std::vector<DrainHole>;
|
||||
|
||||
constexpr float HoleStickOutLength = 1.f;
|
||||
|
||||
std::unique_ptr<TriangleMesh> generate_interior(const TriangleMesh &mesh,
|
||||
const HollowingConfig & = {},
|
||||
const JobController &ctl = {});
|
||||
InteriorPtr generate_interior(const TriangleMesh &mesh,
|
||||
const HollowingConfig & = {},
|
||||
const JobController &ctl = {});
|
||||
|
||||
void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg);
|
||||
// Will do the hollowing
|
||||
void hollow_mesh(TriangleMesh &mesh, const HollowingConfig &cfg, int flags = 0);
|
||||
|
||||
// Hollowing prepared in "interior", merge with original mesh
|
||||
void hollow_mesh(TriangleMesh &mesh, const Interior &interior, int flags = 0);
|
||||
|
||||
void remove_inside_triangles(TriangleMesh &mesh, const Interior &interior,
|
||||
const std::vector<bool> &exclude_mask = {});
|
||||
|
||||
double get_distance(const Vec3f &p, const Interior &interior);
|
||||
|
||||
template<class T>
|
||||
FloatingOnly<T> get_distance(const Vec<3, T> &p, const Interior &interior)
|
||||
{
|
||||
return get_distance(Vec3f(p.template cast<float>()), interior);
|
||||
}
|
||||
|
||||
void cut_drainholes(std::vector<ExPolygons> & obj_slices,
|
||||
const std::vector<float> &slicegrid,
|
||||
|
@ -1120,7 +1120,7 @@ TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const
|
||||
return this->pad_mesh();
|
||||
case slaposDrillHoles:
|
||||
if (m_hollowing_data)
|
||||
return m_hollowing_data->hollow_mesh_with_holes;
|
||||
return get_mesh_to_print();
|
||||
[[fallthrough]];
|
||||
default:
|
||||
return TriangleMesh();
|
||||
@ -1149,8 +1149,9 @@ const TriangleMesh& SLAPrintObject::pad_mesh() const
|
||||
|
||||
const TriangleMesh &SLAPrintObject::hollowed_interior_mesh() const
|
||||
{
|
||||
if (m_hollowing_data && m_config.hollowing_enable.getBool())
|
||||
return m_hollowing_data->interior;
|
||||
if (m_hollowing_data && m_hollowing_data->interior &&
|
||||
m_config.hollowing_enable.getBool())
|
||||
return sla::get_mesh(*m_hollowing_data->interior);
|
||||
|
||||
return EMPTY_MESH;
|
||||
}
|
||||
|
@ -85,6 +85,10 @@ public:
|
||||
// Get the mesh that is going to be printed with all the modifications
|
||||
// like hollowing and drilled holes.
|
||||
const TriangleMesh & get_mesh_to_print() const {
|
||||
return (m_hollowing_data && is_step_done(slaposDrillHoles)) ? m_hollowing_data->hollow_mesh_with_holes_trimmed : transformed_mesh();
|
||||
}
|
||||
|
||||
const TriangleMesh & get_mesh_to_slice() const {
|
||||
return (m_hollowing_data && is_step_done(slaposDrillHoles)) ? m_hollowing_data->hollow_mesh_with_holes : transformed_mesh();
|
||||
}
|
||||
|
||||
@ -327,9 +331,10 @@ private:
|
||||
class HollowingData
|
||||
{
|
||||
public:
|
||||
|
||||
TriangleMesh interior;
|
||||
|
||||
sla::InteriorPtr interior;
|
||||
mutable TriangleMesh hollow_mesh_with_holes; // caching the complete hollowed mesh
|
||||
mutable TriangleMesh hollow_mesh_with_holes_trimmed;
|
||||
};
|
||||
|
||||
std::unique_ptr<HollowingData> m_hollowing_data;
|
||||
|
@ -1,3 +1,5 @@
|
||||
#include <unordered_set>
|
||||
|
||||
#include <libslic3r/Exception.hpp>
|
||||
#include <libslic3r/SLAPrintSteps.hpp>
|
||||
#include <libslic3r/MeshBoolean.hpp>
|
||||
@ -84,17 +86,17 @@ SLAPrint::Steps::Steps(SLAPrint *print)
|
||||
void SLAPrint::Steps::apply_printer_corrections(SLAPrintObject &po, SliceOrigin o)
|
||||
{
|
||||
if (o == soSupport && !po.m_supportdata) return;
|
||||
|
||||
|
||||
auto faded_lyrs = size_t(po.m_config.faded_layers.getInt());
|
||||
double min_w = m_print->m_printer_config.elefant_foot_min_width.getFloat() / 2.;
|
||||
double start_efc = m_print->m_printer_config.elefant_foot_compensation.getFloat();
|
||||
|
||||
|
||||
double doffs = m_print->m_printer_config.absolute_correction.getFloat();
|
||||
coord_t clpr_offs = scaled(doffs);
|
||||
|
||||
|
||||
faded_lyrs = std::min(po.m_slice_index.size(), faded_lyrs);
|
||||
size_t faded_lyrs_efc = std::max(size_t(1), faded_lyrs - 1);
|
||||
|
||||
|
||||
auto efc = [start_efc, faded_lyrs_efc](size_t pos) {
|
||||
return (faded_lyrs_efc - pos) * start_efc / faded_lyrs_efc;
|
||||
};
|
||||
@ -102,13 +104,13 @@ void SLAPrint::Steps::apply_printer_corrections(SLAPrintObject &po, SliceOrigin
|
||||
std::vector<ExPolygons> &slices = o == soModel ?
|
||||
po.m_model_slices :
|
||||
po.m_supportdata->support_slices;
|
||||
|
||||
|
||||
if (clpr_offs != 0) for (size_t i = 0; i < po.m_slice_index.size(); ++i) {
|
||||
size_t idx = po.m_slice_index[i].get_slice_idx(o);
|
||||
if (idx < slices.size())
|
||||
slices[idx] = offset_ex(slices[idx], float(clpr_offs));
|
||||
}
|
||||
|
||||
|
||||
if (start_efc > 0.) for (size_t i = 0; i < faded_lyrs; ++i) {
|
||||
size_t idx = po.m_slice_index[i].get_slice_idx(o);
|
||||
if (idx < slices.size())
|
||||
@ -124,28 +126,157 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po)
|
||||
BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Performing hollowing step!";
|
||||
|
||||
double thickness = po.m_config.hollowing_min_thickness.getFloat();
|
||||
double quality = po.m_config.hollowing_quality.getFloat();
|
||||
double closing_d = po.m_config.hollowing_closing_distance.getFloat();
|
||||
sla::HollowingConfig hlwcfg{thickness, quality, closing_d};
|
||||
auto meshptr = generate_interior(po.transformed_mesh(), hlwcfg);
|
||||
|
||||
if (meshptr->empty())
|
||||
sla::InteriorPtr interior = generate_interior(po.transformed_mesh(), hlwcfg);
|
||||
|
||||
if (!interior || sla::get_mesh(*interior).empty())
|
||||
BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!";
|
||||
else {
|
||||
po.m_hollowing_data.reset(new SLAPrintObject::HollowingData());
|
||||
po.m_hollowing_data->interior = *meshptr;
|
||||
po.m_hollowing_data->interior = std::move(interior);
|
||||
}
|
||||
}
|
||||
|
||||
struct FaceHash {
|
||||
|
||||
// A hash is created for each triangle to be identifiable. The hash uses
|
||||
// only the triangle's geometric traits, not the index in a particular mesh.
|
||||
std::unordered_set<std::string> facehash;
|
||||
|
||||
static std::string facekey(const Vec3i &face,
|
||||
const std::vector<Vec3f> &vertices)
|
||||
{
|
||||
// Scale to integer to avoid floating points
|
||||
std::array<Vec<3, int64_t>, 3> pts = {
|
||||
scaled<int64_t>(vertices[face(0)]),
|
||||
scaled<int64_t>(vertices[face(1)]),
|
||||
scaled<int64_t>(vertices[face(2)])
|
||||
};
|
||||
|
||||
// Get the first two sides of the triangle, do a cross product and move
|
||||
// that vector to the center of the triangle. This encodes all
|
||||
// information to identify an identical triangle at the same position.
|
||||
Vec<3, int64_t> a = pts[0] - pts[2], b = pts[1] - pts[2];
|
||||
Vec<3, int64_t> c = a.cross(b) + (pts[0] + pts[1] + pts[2]) / 3;
|
||||
|
||||
// Return a concatenated string representation of the coordinates
|
||||
return std::to_string(c(0)) + std::to_string(c(1)) + std::to_string(c(2));
|
||||
};
|
||||
|
||||
FaceHash(const indexed_triangle_set &its)
|
||||
{
|
||||
for (const Vec3i &face : its.indices) {
|
||||
std::string keystr = facekey(face, its.vertices);
|
||||
facehash.insert(keystr);
|
||||
}
|
||||
}
|
||||
|
||||
bool find(const std::string &key)
|
||||
{
|
||||
auto it = facehash.find(key);
|
||||
return it != facehash.end();
|
||||
}
|
||||
};
|
||||
|
||||
// Create exclude mask for triangle removal inside hollowed interiors.
|
||||
// This is necessary when the interior is already part of the mesh which was
|
||||
// drilled using CGAL mesh boolean operation. Excluded will be the triangles
|
||||
// originally part of the interior mesh and triangles that make up the drilled
|
||||
// hole walls.
|
||||
static std::vector<bool> create_exclude_mask(
|
||||
const indexed_triangle_set &its,
|
||||
const sla::Interior &interior,
|
||||
const std::vector<sla::DrainHole> &holes)
|
||||
{
|
||||
FaceHash interior_hash{sla::get_mesh(interior).its};
|
||||
|
||||
std::vector<bool> exclude_mask(its.indices.size(), false);
|
||||
|
||||
std::vector< std::vector<size_t> > neighbor_index =
|
||||
create_neighbor_index(its);
|
||||
|
||||
auto exclude_neighbors = [&neighbor_index, &exclude_mask](const Vec3i &face)
|
||||
{
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
const std::vector<size_t> &neighbors = neighbor_index[face(i)];
|
||||
for (size_t fi_n : neighbors) exclude_mask[fi_n] = true;
|
||||
}
|
||||
};
|
||||
|
||||
for (size_t fi = 0; fi < its.indices.size(); ++fi) {
|
||||
auto &face = its.indices[fi];
|
||||
|
||||
if (interior_hash.find(FaceHash::facekey(face, its.vertices))) {
|
||||
exclude_mask[fi] = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (exclude_mask[fi]) {
|
||||
exclude_neighbors(face);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Lets deal with the holes. All the triangles of a hole and all the
|
||||
// neighbors of these triangles need to be kept. The neigbors were
|
||||
// created by CGAL mesh boolean operation that modified the original
|
||||
// interior inside the input mesh to contain the holes.
|
||||
Vec3d tr_center = (
|
||||
its.vertices[face(0)] +
|
||||
its.vertices[face(1)] +
|
||||
its.vertices[face(2)]
|
||||
).cast<double>() / 3.;
|
||||
|
||||
// If the center is more than half a mm inside the interior,
|
||||
// it cannot possibly be part of a hole wall.
|
||||
if (sla::get_distance(tr_center, interior) < -0.5)
|
||||
continue;
|
||||
|
||||
Vec3f U = its.vertices[face(1)] - its.vertices[face(0)];
|
||||
Vec3f V = its.vertices[face(2)] - its.vertices[face(0)];
|
||||
Vec3f C = U.cross(V);
|
||||
Vec3f face_normal = C.normalized();
|
||||
|
||||
for (const sla::DrainHole &dh : holes) {
|
||||
Vec3d dhpos = dh.pos.cast<double>();
|
||||
Vec3d dhend = dhpos + dh.normal.cast<double>() * dh.height;
|
||||
|
||||
Linef3 holeaxis{dhpos, dhend};
|
||||
|
||||
double D_hole_center = line_alg::distance_to(holeaxis, tr_center);
|
||||
double D_hole = std::abs(D_hole_center - dh.radius);
|
||||
float dot = dh.normal.dot(face_normal);
|
||||
|
||||
// Empiric tolerances for center distance and normals angle.
|
||||
// For triangles that are part of a hole wall the angle of
|
||||
// triangle normal and the hole axis is around 90 degrees,
|
||||
// so the dot product is around zero.
|
||||
double D_tol = dh.radius / sla::DrainHole::steps;
|
||||
float normal_angle_tol = 1.f / sla::DrainHole::steps;
|
||||
|
||||
if (D_hole < D_tol && std::abs(dot) < normal_angle_tol) {
|
||||
exclude_mask[fi] = true;
|
||||
exclude_neighbors(face);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return exclude_mask;
|
||||
}
|
||||
|
||||
// Drill holes into the hollowed/original mesh.
|
||||
void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
|
||||
{
|
||||
bool needs_drilling = ! po.m_model_object->sla_drain_holes.empty();
|
||||
bool is_hollowed = (po.m_hollowing_data && ! po.m_hollowing_data->interior.empty());
|
||||
bool is_hollowed =
|
||||
(po.m_hollowing_data && po.m_hollowing_data->interior &&
|
||||
!sla::get_mesh(*po.m_hollowing_data->interior).empty());
|
||||
|
||||
if (! is_hollowed && ! needs_drilling) {
|
||||
// In this case we can dump any data that might have been
|
||||
@ -163,19 +294,25 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
|
||||
// holes that are no longer on the frontend.
|
||||
TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes;
|
||||
hollowed_mesh = po.transformed_mesh();
|
||||
if (! po.m_hollowing_data->interior.empty()) {
|
||||
hollowed_mesh.merge(po.m_hollowing_data->interior);
|
||||
hollowed_mesh.require_shared_vertices();
|
||||
}
|
||||
if (is_hollowed)
|
||||
sla::hollow_mesh(hollowed_mesh, *po.m_hollowing_data->interior);
|
||||
|
||||
TriangleMesh &mesh_view = po.m_hollowing_data->hollow_mesh_with_holes_trimmed;
|
||||
|
||||
if (! needs_drilling) {
|
||||
mesh_view = po.transformed_mesh();
|
||||
|
||||
if (is_hollowed)
|
||||
sla::hollow_mesh(mesh_view, *po.m_hollowing_data->interior,
|
||||
sla::hfRemoveInsideTriangles);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes).";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes.";
|
||||
sla::DrainHoles drainholes = po.transformed_drainhole_points();
|
||||
|
||||
|
||||
std::uniform_real_distribution<float> dist(0., float(EPSILON));
|
||||
auto holes_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal({});
|
||||
for (sla::DrainHole holept : drainholes) {
|
||||
@ -187,15 +324,25 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
|
||||
auto cgal_m = MeshBoolean::cgal::triangle_mesh_to_cgal(m);
|
||||
MeshBoolean::cgal::plus(*holes_mesh_cgal, *cgal_m);
|
||||
}
|
||||
|
||||
|
||||
if (MeshBoolean::cgal::does_self_intersect(*holes_mesh_cgal))
|
||||
throw Slic3r::SlicingError(L("Too many overlapping holes."));
|
||||
|
||||
|
||||
auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh);
|
||||
|
||||
|
||||
try {
|
||||
MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal);
|
||||
hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal);
|
||||
mesh_view = hollowed_mesh;
|
||||
|
||||
if (is_hollowed) {
|
||||
auto &interior = *po.m_hollowing_data->interior;
|
||||
std::vector<bool> exclude_mask =
|
||||
create_exclude_mask(mesh_view.its, interior, drainholes);
|
||||
|
||||
sla::remove_inside_triangles(mesh_view, interior, exclude_mask);
|
||||
}
|
||||
|
||||
} catch (const std::runtime_error &) {
|
||||
throw Slic3r::SlicingError(L(
|
||||
"Drilling holes into the mesh failed. "
|
||||
@ -212,11 +359,11 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
|
||||
// of it. In any case, the model and the supports have to be sliced in the
|
||||
// same imaginary grid (the height vector argument to TriangleMeshSlicer).
|
||||
void SLAPrint::Steps::slice_model(SLAPrintObject &po)
|
||||
{
|
||||
const TriangleMesh &mesh = po.get_mesh_to_print();
|
||||
{
|
||||
const TriangleMesh &mesh = po.get_mesh_to_slice();
|
||||
|
||||
// We need to prepare the slice index...
|
||||
|
||||
|
||||
double lhd = m_print->m_objects.front()->m_config.layer_height.getFloat();
|
||||
float lh = float(lhd);
|
||||
coord_t lhs = scaled(lhd);
|
||||
@ -226,43 +373,49 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
|
||||
auto minZf = float(minZ);
|
||||
coord_t minZs = scaled(minZ);
|
||||
coord_t maxZs = scaled(maxZ);
|
||||
|
||||
|
||||
po.m_slice_index.clear();
|
||||
|
||||
|
||||
size_t cap = size_t(1 + (maxZs - minZs - ilhs) / lhs);
|
||||
po.m_slice_index.reserve(cap);
|
||||
|
||||
|
||||
po.m_slice_index.emplace_back(minZs + ilhs, minZf + ilh / 2.f, ilh);
|
||||
|
||||
|
||||
for(coord_t h = minZs + ilhs + lhs; h <= maxZs; h += lhs)
|
||||
po.m_slice_index.emplace_back(h, unscaled<float>(h) - lh / 2.f, lh);
|
||||
|
||||
|
||||
// Just get the first record that is from the model:
|
||||
auto slindex_it =
|
||||
po.closest_slice_record(po.m_slice_index, float(bb3d.min(Z)));
|
||||
|
||||
|
||||
if(slindex_it == po.m_slice_index.end())
|
||||
//TRN To be shown at the status bar on SLA slicing error.
|
||||
throw Slic3r::RuntimeError(
|
||||
L("Slicing had to be stopped due to an internal error: "
|
||||
"Inconsistent slice index."));
|
||||
|
||||
|
||||
po.m_model_height_levels.clear();
|
||||
po.m_model_height_levels.reserve(po.m_slice_index.size());
|
||||
for(auto it = slindex_it; it != po.m_slice_index.end(); ++it)
|
||||
po.m_model_height_levels.emplace_back(it->slice_level());
|
||||
|
||||
|
||||
TriangleMeshSlicer slicer(&mesh);
|
||||
|
||||
|
||||
po.m_model_slices.clear();
|
||||
float closing_r = float(po.config().slice_closing_radius.value);
|
||||
auto thr = [this]() { m_print->throw_if_canceled(); };
|
||||
auto &slice_grid = po.m_model_height_levels;
|
||||
slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &po.m_model_slices, thr);
|
||||
|
||||
if (po.m_hollowing_data && ! po.m_hollowing_data->interior.empty()) {
|
||||
po.m_hollowing_data->interior.repair(true);
|
||||
TriangleMeshSlicer interior_slicer(&po.m_hollowing_data->interior);
|
||||
|
||||
sla::Interior *interior = po.m_hollowing_data ?
|
||||
po.m_hollowing_data->interior.get() :
|
||||
nullptr;
|
||||
|
||||
if (interior && ! sla::get_mesh(*interior).empty()) {
|
||||
TriangleMesh interiormesh = sla::get_mesh(*interior);
|
||||
interiormesh.repaired = false;
|
||||
interiormesh.repair(true);
|
||||
TriangleMeshSlicer interior_slicer(&interiormesh);
|
||||
std::vector<ExPolygons> interior_slices;
|
||||
interior_slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &interior_slices, thr);
|
||||
|
||||
@ -273,17 +426,17 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po)
|
||||
diff_ex(po.m_model_slices[i], slice);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
auto mit = slindex_it;
|
||||
for (size_t id = 0;
|
||||
id < po.m_model_slices.size() && mit != po.m_slice_index.end();
|
||||
id++) {
|
||||
mit->set_model_slice_idx(po, id); ++mit;
|
||||
}
|
||||
|
||||
|
||||
// We apply the printer correction offset here.
|
||||
apply_printer_corrections(po, soModel);
|
||||
|
||||
|
||||
if(po.m_config.supports_enable.getBool() || po.m_config.pad_enable.getBool())
|
||||
{
|
||||
po.m_supportdata.reset(new SLAPrintObject::SupportData(mesh));
|
||||
@ -296,22 +449,22 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
|
||||
{
|
||||
// If supports are disabled, we can skip the model scan.
|
||||
if(!po.m_config.supports_enable.getBool()) return;
|
||||
|
||||
const TriangleMesh &mesh = po.get_mesh_to_print();
|
||||
|
||||
|
||||
const TriangleMesh &mesh = po.get_mesh_to_slice();
|
||||
|
||||
if (!po.m_supportdata)
|
||||
po.m_supportdata.reset(new SLAPrintObject::SupportData(mesh));
|
||||
|
||||
|
||||
const ModelObject& mo = *po.m_model_object;
|
||||
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Support point count "
|
||||
<< mo.sla_support_points.size();
|
||||
|
||||
|
||||
// Unless the user modified the points or we already did the calculation,
|
||||
// we will do the autoplacement. Otherwise we will just blindly copy the
|
||||
// frontend data into the backend cache.
|
||||
if (mo.sla_points_status != sla::PointsStatus::UserModified) {
|
||||
|
||||
|
||||
// calculate heights of slices (slices are calculated already)
|
||||
const std::vector<float>& heights = po.m_model_height_levels;
|
||||
|
||||
@ -319,27 +472,27 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
|
||||
// calculated on slices, the algorithm then raycasts the points
|
||||
// so they actually lie on the mesh.
|
||||
// po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points());
|
||||
|
||||
|
||||
throw_if_canceled();
|
||||
sla::SupportPointGenerator::Config config;
|
||||
const SLAPrintObjectConfig& cfg = po.config();
|
||||
|
||||
|
||||
// the density config value is in percents:
|
||||
config.density_relative = float(cfg.support_points_density_relative / 100.f);
|
||||
config.minimal_distance = float(cfg.support_points_minimal_distance);
|
||||
config.head_diameter = float(cfg.support_head_front_diameter);
|
||||
|
||||
|
||||
// scaling for the sub operations
|
||||
double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportPoints] / 100.0;
|
||||
double init = current_status();
|
||||
|
||||
|
||||
auto statuscb = [this, d, init](unsigned st)
|
||||
{
|
||||
double current = init + st * d;
|
||||
if(std::round(current_status()) < std::round(current))
|
||||
report_status(current, OBJ_STEP_LABELS(slaposSupportPoints));
|
||||
};
|
||||
|
||||
|
||||
// Construction of this object does the calculation.
|
||||
throw_if_canceled();
|
||||
sla::SupportPointGenerator auto_supports(
|
||||
@ -350,10 +503,10 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
|
||||
const std::vector<sla::SupportPoint>& points = auto_supports.output();
|
||||
throw_if_canceled();
|
||||
po.m_supportdata->pts = points;
|
||||
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Automatic support points: "
|
||||
<< po.m_supportdata->pts.size();
|
||||
|
||||
|
||||
// Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass
|
||||
// the update status to GLGizmoSlaSupports
|
||||
report_status(-1, L("Generating support points"),
|
||||
@ -368,9 +521,9 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
|
||||
void SLAPrint::Steps::support_tree(SLAPrintObject &po)
|
||||
{
|
||||
if(!po.m_supportdata) return;
|
||||
|
||||
|
||||
sla::PadConfig pcfg = make_pad_cfg(po.m_config);
|
||||
|
||||
|
||||
if (pcfg.embed_object)
|
||||
po.m_supportdata->emesh.ground_level_offset(pcfg.wall_thickness_mm);
|
||||
|
||||
@ -380,15 +533,15 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po)
|
||||
remove_bottom_points(po.m_supportdata->pts,
|
||||
float(po.m_supportdata->emesh.ground_level() + EPSILON));
|
||||
}
|
||||
|
||||
|
||||
po.m_supportdata->cfg = make_support_cfg(po.m_config);
|
||||
// po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points());
|
||||
|
||||
|
||||
// scaling for the sub operations
|
||||
double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportTree] / 100.0;
|
||||
double init = current_status();
|
||||
sla::JobController ctl;
|
||||
|
||||
|
||||
ctl.statuscb = [this, d, init](unsigned st, const std::string &logmsg) {
|
||||
double current = init + st * d;
|
||||
if (std::round(current_status()) < std::round(current))
|
||||
@ -397,26 +550,26 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po)
|
||||
};
|
||||
ctl.stopcondition = [this]() { return canceled(); };
|
||||
ctl.cancelfn = [this]() { throw_if_canceled(); };
|
||||
|
||||
|
||||
po.m_supportdata->create_support_tree(ctl);
|
||||
|
||||
|
||||
if (!po.m_config.supports_enable.getBool()) return;
|
||||
|
||||
|
||||
throw_if_canceled();
|
||||
|
||||
|
||||
// Create the unified mesh
|
||||
auto rc = SlicingStatus::RELOAD_SCENE;
|
||||
|
||||
|
||||
// This is to prevent "Done." being displayed during merged_mesh()
|
||||
report_status(-1, L("Visualizing supports"));
|
||||
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Processed support point count "
|
||||
<< po.m_supportdata->pts.size();
|
||||
|
||||
|
||||
// Check the mesh for later troubleshooting.
|
||||
if(po.support_mesh().empty())
|
||||
BOOST_LOG_TRIVIAL(warning) << "Support mesh is empty";
|
||||
|
||||
|
||||
report_status(-1, L("Visualizing supports"), rc);
|
||||
}
|
||||
|
||||
@ -424,15 +577,15 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) {
|
||||
// this step can only go after the support tree has been created
|
||||
// and before the supports had been sliced. (or the slicing has to be
|
||||
// repeated)
|
||||
|
||||
|
||||
if(po.m_config.pad_enable.getBool()) {
|
||||
// Get the distilled pad configuration from the config
|
||||
sla::PadConfig pcfg = make_pad_cfg(po.m_config);
|
||||
|
||||
|
||||
ExPolygons bp; // This will store the base plate of the pad.
|
||||
double pad_h = pcfg.full_height();
|
||||
const TriangleMesh &trmesh = po.transformed_mesh();
|
||||
|
||||
|
||||
if (!po.m_config.supports_enable.getBool() || pcfg.embed_object) {
|
||||
// No support (thus no elevation) or zero elevation mode
|
||||
// we sometimes call it "builtin pad" is enabled so we will
|
||||
@ -442,19 +595,19 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) {
|
||||
float(po.m_config.layer_height.getFloat()),
|
||||
[this](){ throw_if_canceled(); });
|
||||
}
|
||||
|
||||
|
||||
po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg);
|
||||
auto &pad_mesh = po.m_supportdata->support_tree_ptr->retrieve_mesh(sla::MeshType::Pad);
|
||||
|
||||
|
||||
if (!validate_pad(pad_mesh, pcfg))
|
||||
throw Slic3r::SlicingError(
|
||||
L("No pad can be generated for this model with the "
|
||||
"current configuration"));
|
||||
|
||||
|
||||
} else if(po.m_supportdata && po.m_supportdata->support_tree_ptr) {
|
||||
po.m_supportdata->support_tree_ptr->remove_pad();
|
||||
}
|
||||
|
||||
|
||||
throw_if_canceled();
|
||||
report_status(-1, L("Visualizing supports"), SlicingStatus::RELOAD_SCENE);
|
||||
}
|
||||
@ -464,25 +617,25 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) {
|
||||
// be part of the slices)
|
||||
void SLAPrint::Steps::slice_supports(SLAPrintObject &po) {
|
||||
auto& sd = po.m_supportdata;
|
||||
|
||||
|
||||
if(sd) sd->support_slices.clear();
|
||||
|
||||
|
||||
// Don't bother if no supports and no pad is present.
|
||||
if (!po.m_config.supports_enable.getBool() && !po.m_config.pad_enable.getBool())
|
||||
return;
|
||||
|
||||
|
||||
if(sd && sd->support_tree_ptr) {
|
||||
auto heights = reserve_vector<float>(po.m_slice_index.size());
|
||||
|
||||
|
||||
for(auto& rec : po.m_slice_index) heights.emplace_back(rec.slice_level());
|
||||
|
||||
sd->support_slices = sd->support_tree_ptr->slice(
|
||||
heights, float(po.config().slice_closing_radius.value));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sd->support_slices.size() && i < po.m_slice_index.size(); ++i)
|
||||
|
||||
for (size_t i = 0; i < sd->support_slices.size() && i < po.m_slice_index.size(); ++i)
|
||||
po.m_slice_index[i].set_support_slice_idx(po, i);
|
||||
|
||||
|
||||
apply_printer_corrections(po, soSupport);
|
||||
|
||||
// Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update
|
||||
@ -497,37 +650,37 @@ using ClipperPolygons = std::vector<ClipperPolygon>;
|
||||
static ClipperPolygons polyunion(const ClipperPolygons &subjects)
|
||||
{
|
||||
ClipperLib::Clipper clipper;
|
||||
|
||||
|
||||
bool closed = true;
|
||||
|
||||
|
||||
for(auto& path : subjects) {
|
||||
clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
|
||||
clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
|
||||
}
|
||||
|
||||
|
||||
auto mode = ClipperLib::pftPositive;
|
||||
|
||||
|
||||
return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode);
|
||||
}
|
||||
|
||||
static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPolygons& clips)
|
||||
{
|
||||
ClipperLib::Clipper clipper;
|
||||
|
||||
|
||||
bool closed = true;
|
||||
|
||||
|
||||
for(auto& path : subjects) {
|
||||
clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
|
||||
clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
|
||||
}
|
||||
|
||||
|
||||
for(auto& path : clips) {
|
||||
clipper.AddPath(path.Contour, ClipperLib::ptClip, closed);
|
||||
clipper.AddPaths(path.Holes, ClipperLib::ptClip, closed);
|
||||
}
|
||||
|
||||
|
||||
auto mode = ClipperLib::pftPositive;
|
||||
|
||||
|
||||
return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode);
|
||||
}
|
||||
|
||||
@ -535,28 +688,28 @@ static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPo
|
||||
static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o)
|
||||
{
|
||||
namespace sl = libnest2d::sl;
|
||||
|
||||
|
||||
if (!record.print_obj()) return {};
|
||||
|
||||
|
||||
ClipperPolygons polygons;
|
||||
auto &input_polygons = record.get_slice(o);
|
||||
auto &instances = record.print_obj()->instances();
|
||||
bool is_lefthanded = record.print_obj()->is_left_handed();
|
||||
polygons.reserve(input_polygons.size() * instances.size());
|
||||
|
||||
|
||||
for (const ExPolygon& polygon : input_polygons) {
|
||||
if(polygon.contour.empty()) continue;
|
||||
|
||||
|
||||
for (size_t i = 0; i < instances.size(); ++i)
|
||||
{
|
||||
ClipperPolygon poly;
|
||||
|
||||
|
||||
// We need to reverse if is_lefthanded is true but
|
||||
bool needreverse = is_lefthanded;
|
||||
|
||||
|
||||
// should be a move
|
||||
poly.Contour.reserve(polygon.contour.size() + 1);
|
||||
|
||||
|
||||
auto& cntr = polygon.contour.points;
|
||||
if(needreverse)
|
||||
for(auto it = cntr.rbegin(); it != cntr.rend(); ++it)
|
||||
@ -564,12 +717,12 @@ static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o
|
||||
else
|
||||
for(auto& p : cntr)
|
||||
poly.Contour.emplace_back(p.x(), p.y());
|
||||
|
||||
|
||||
for(auto& h : polygon.holes) {
|
||||
poly.Holes.emplace_back();
|
||||
auto& hole = poly.Holes.back();
|
||||
hole.reserve(h.points.size() + 1);
|
||||
|
||||
|
||||
if(needreverse)
|
||||
for(auto it = h.points.rbegin(); it != h.points.rend(); ++it)
|
||||
hole.emplace_back(it->x(), it->y());
|
||||
@ -577,42 +730,42 @@ static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o
|
||||
for(auto& p : h.points)
|
||||
hole.emplace_back(p.x(), p.y());
|
||||
}
|
||||
|
||||
|
||||
if(is_lefthanded) {
|
||||
for(auto& p : poly.Contour) p.X = -p.X;
|
||||
for(auto& h : poly.Holes) for(auto& p : h) p.X = -p.X;
|
||||
}
|
||||
|
||||
|
||||
sl::rotate(poly, double(instances[i].rotation));
|
||||
sl::translate(poly, ClipperPoint{instances[i].shift.x(),
|
||||
instances[i].shift.y()});
|
||||
|
||||
|
||||
polygons.emplace_back(std::move(poly));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return polygons;
|
||||
}
|
||||
|
||||
void SLAPrint::Steps::initialize_printer_input()
|
||||
{
|
||||
auto &printer_input = m_print->m_printer_input;
|
||||
|
||||
|
||||
// clear the rasterizer input
|
||||
printer_input.clear();
|
||||
|
||||
|
||||
size_t mx = 0;
|
||||
for(SLAPrintObject * o : m_print->m_objects) {
|
||||
if(auto m = o->get_slice_index().size() > mx) mx = m;
|
||||
}
|
||||
|
||||
|
||||
printer_input.reserve(mx);
|
||||
|
||||
|
||||
auto eps = coord_t(SCALED_EPSILON);
|
||||
|
||||
|
||||
for(SLAPrintObject * o : m_print->m_objects) {
|
||||
coord_t gndlvl = o->get_slice_index().front().print_level() - ilhs;
|
||||
|
||||
|
||||
for(const SliceRecord& slicerecord : o->get_slice_index()) {
|
||||
if (!slicerecord.is_valid())
|
||||
throw Slic3r::SlicingError(
|
||||
@ -621,7 +774,7 @@ void SLAPrint::Steps::initialize_printer_input()
|
||||
"objects printable."));
|
||||
|
||||
coord_t lvlid = slicerecord.print_level() - gndlvl;
|
||||
|
||||
|
||||
// Neat trick to round the layer levels to the grid.
|
||||
lvlid = eps * (lvlid / eps);
|
||||
|
||||
@ -631,8 +784,8 @@ void SLAPrint::Steps::initialize_printer_input()
|
||||
|
||||
if(it == printer_input.end() || it->level() != lvlid)
|
||||
it = printer_input.insert(it, PrintLayer(lvlid));
|
||||
|
||||
|
||||
|
||||
|
||||
it->add(slicerecord);
|
||||
}
|
||||
}
|
||||
@ -641,53 +794,53 @@ void SLAPrint::Steps::initialize_printer_input()
|
||||
// Merging the slices from all the print objects into one slice grid and
|
||||
// calculating print statistics from the merge result.
|
||||
void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
||||
|
||||
|
||||
initialize_printer_input();
|
||||
|
||||
|
||||
auto &print_statistics = m_print->m_print_statistics;
|
||||
auto &printer_config = m_print->m_printer_config;
|
||||
auto &material_config = m_print->m_material_config;
|
||||
auto &printer_input = m_print->m_printer_input;
|
||||
|
||||
|
||||
print_statistics.clear();
|
||||
|
||||
|
||||
// libnest calculates positive area for clockwise polygons, Slic3r is in counter-clockwise
|
||||
auto areafn = [](const ClipperPolygon& poly) { return - libnest2d::sl::area(poly); };
|
||||
|
||||
|
||||
const double area_fill = printer_config.area_fill.getFloat()*0.01;// 0.5 (50%);
|
||||
const double fast_tilt = printer_config.fast_tilt_time.getFloat();// 5.0;
|
||||
const double slow_tilt = printer_config.slow_tilt_time.getFloat();// 8.0;
|
||||
|
||||
|
||||
const double init_exp_time = material_config.initial_exposure_time.getFloat();
|
||||
const double exp_time = material_config.exposure_time.getFloat();
|
||||
|
||||
|
||||
const int fade_layers_cnt = m_print->m_default_object_config.faded_layers.getInt();// 10 // [3;20]
|
||||
|
||||
|
||||
const auto width = scaled<double>(printer_config.display_width.getFloat());
|
||||
const auto height = scaled<double>(printer_config.display_height.getFloat());
|
||||
const double display_area = width*height;
|
||||
|
||||
|
||||
double supports_volume(0.0);
|
||||
double models_volume(0.0);
|
||||
|
||||
|
||||
double estim_time(0.0);
|
||||
std::vector<double> layers_times;
|
||||
layers_times.reserve(printer_input.size());
|
||||
|
||||
|
||||
size_t slow_layers = 0;
|
||||
size_t fast_layers = 0;
|
||||
|
||||
|
||||
const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1);
|
||||
double fade_layer_time = init_exp_time;
|
||||
|
||||
|
||||
sla::ccr::SpinningMutex mutex;
|
||||
using Lock = std::lock_guard<sla::ccr::SpinningMutex>;
|
||||
|
||||
|
||||
// Going to parallel:
|
||||
auto printlayerfn = [this,
|
||||
// functions and read only vars
|
||||
areafn, area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time,
|
||||
|
||||
|
||||
// write vars
|
||||
&mutex, &models_volume, &supports_volume, &estim_time, &slow_layers,
|
||||
&fast_layers, &fade_layer_time, &layers_times](size_t sliced_layer_cnt)
|
||||
@ -696,87 +849,87 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
||||
|
||||
// vector of slice record references
|
||||
auto& slicerecord_references = layer.slices();
|
||||
|
||||
|
||||
if(slicerecord_references.empty()) return;
|
||||
|
||||
|
||||
// Layer height should match for all object slices for a given level.
|
||||
const auto l_height = double(slicerecord_references.front().get().layer_height());
|
||||
|
||||
|
||||
// Calculation of the consumed material
|
||||
|
||||
|
||||
ClipperPolygons model_polygons;
|
||||
ClipperPolygons supports_polygons;
|
||||
|
||||
|
||||
size_t c = std::accumulate(layer.slices().begin(),
|
||||
layer.slices().end(),
|
||||
size_t(0),
|
||||
[](size_t a, const SliceRecord &sr) {
|
||||
return a + sr.get_slice(soModel).size();
|
||||
});
|
||||
|
||||
|
||||
model_polygons.reserve(c);
|
||||
|
||||
|
||||
c = std::accumulate(layer.slices().begin(),
|
||||
layer.slices().end(),
|
||||
size_t(0),
|
||||
[](size_t a, const SliceRecord &sr) {
|
||||
return a + sr.get_slice(soModel).size();
|
||||
});
|
||||
|
||||
|
||||
supports_polygons.reserve(c);
|
||||
|
||||
|
||||
for(const SliceRecord& record : layer.slices()) {
|
||||
|
||||
|
||||
ClipperPolygons modelslices = get_all_polygons(record, soModel);
|
||||
for(ClipperPolygon& p_tmp : modelslices) model_polygons.emplace_back(std::move(p_tmp));
|
||||
|
||||
|
||||
ClipperPolygons supportslices = get_all_polygons(record, soSupport);
|
||||
for(ClipperPolygon& p_tmp : supportslices) supports_polygons.emplace_back(std::move(p_tmp));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
model_polygons = polyunion(model_polygons);
|
||||
double layer_model_area = 0;
|
||||
for (const ClipperPolygon& polygon : model_polygons)
|
||||
layer_model_area += areafn(polygon);
|
||||
|
||||
|
||||
if (layer_model_area < 0 || layer_model_area > 0) {
|
||||
Lock lck(mutex); models_volume += layer_model_area * l_height;
|
||||
}
|
||||
|
||||
|
||||
if(!supports_polygons.empty()) {
|
||||
if(model_polygons.empty()) supports_polygons = polyunion(supports_polygons);
|
||||
else supports_polygons = polydiff(supports_polygons, model_polygons);
|
||||
// allegedly, union of subject is done withing the diff according to the pftPositive polyFillType
|
||||
}
|
||||
|
||||
|
||||
double layer_support_area = 0;
|
||||
for (const ClipperPolygon& polygon : supports_polygons)
|
||||
layer_support_area += areafn(polygon);
|
||||
|
||||
|
||||
if (layer_support_area < 0 || layer_support_area > 0) {
|
||||
Lock lck(mutex); supports_volume += layer_support_area * l_height;
|
||||
}
|
||||
|
||||
|
||||
// Here we can save the expensively calculated polygons for printing
|
||||
ClipperPolygons trslices;
|
||||
trslices.reserve(model_polygons.size() + supports_polygons.size());
|
||||
for(ClipperPolygon& poly : model_polygons) trslices.emplace_back(std::move(poly));
|
||||
for(ClipperPolygon& poly : supports_polygons) trslices.emplace_back(std::move(poly));
|
||||
|
||||
|
||||
layer.transformed_slices(polyunion(trslices));
|
||||
|
||||
|
||||
// Calculation of the slow and fast layers to the future controlling those values on FW
|
||||
|
||||
|
||||
const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill;
|
||||
const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt;
|
||||
|
||||
|
||||
{ Lock lck(mutex);
|
||||
if (is_fast_layer)
|
||||
fast_layers++;
|
||||
else
|
||||
slow_layers++;
|
||||
|
||||
|
||||
// Calculation of the printing time
|
||||
|
||||
double layer_times = 0.0;
|
||||
@ -794,15 +947,15 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
||||
estim_time += layer_times;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// sequential version for debugging:
|
||||
// for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i);
|
||||
sla::ccr::for_each(size_t(0), printer_input.size(), printlayerfn);
|
||||
|
||||
|
||||
auto SCALING2 = SCALING_FACTOR * SCALING_FACTOR;
|
||||
print_statistics.support_used_material = supports_volume * SCALING2;
|
||||
print_statistics.objects_used_material = models_volume * SCALING2;
|
||||
|
||||
|
||||
// Estimated printing time
|
||||
// A layers count o the highest object
|
||||
if (printer_input.size() == 0)
|
||||
@ -811,10 +964,10 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
||||
print_statistics.estimated_print_time = estim_time;
|
||||
print_statistics.layers_times = layers_times;
|
||||
}
|
||||
|
||||
|
||||
print_statistics.fast_layers_count = fast_layers;
|
||||
print_statistics.slow_layers_count = slow_layers;
|
||||
|
||||
|
||||
report_status(-2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
|
||||
}
|
||||
|
||||
@ -822,23 +975,23 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
||||
void SLAPrint::Steps::rasterize()
|
||||
{
|
||||
if(canceled() || !m_print->m_printer) return;
|
||||
|
||||
|
||||
// coefficient to map the rasterization state (0-99) to the allocated
|
||||
// portion (slot) of the process state
|
||||
double sd = (100 - max_objstatus) / 100.0;
|
||||
|
||||
|
||||
// slot is the portion of 100% that is realted to rasterization
|
||||
unsigned slot = PRINT_STEP_LEVELS[slapsRasterize];
|
||||
|
||||
|
||||
// pst: previous state
|
||||
double pst = current_status();
|
||||
|
||||
|
||||
double increment = (slot * sd) / m_print->m_printer_input.size();
|
||||
double dstatus = current_status();
|
||||
|
||||
|
||||
sla::ccr::SpinningMutex slck;
|
||||
using Lock = std::lock_guard<sla::ccr::SpinningMutex>;
|
||||
|
||||
|
||||
// procedure to process one height level. This will run in parallel
|
||||
auto lvlfn =
|
||||
[this, &slck, increment, &dstatus, &pst]
|
||||
@ -846,10 +999,10 @@ void SLAPrint::Steps::rasterize()
|
||||
{
|
||||
PrintLayer& printlayer = m_print->m_printer_input[idx];
|
||||
if(canceled()) return;
|
||||
|
||||
|
||||
for (const ClipperLib::Polygon& poly : printlayer.transformed_slices())
|
||||
raster.draw(poly);
|
||||
|
||||
|
||||
// Status indication guarded with the spinlock
|
||||
{
|
||||
Lock lck(slck);
|
||||
@ -861,10 +1014,10 @@ void SLAPrint::Steps::rasterize()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// last minute escape
|
||||
if(canceled()) return;
|
||||
|
||||
|
||||
// Print all the layers in parallel
|
||||
m_print->m_printer->draw_layers(m_print->m_printer_input.size(), lvlfn);
|
||||
}
|
||||
|
@ -112,8 +112,10 @@ SlicingParameters SlicingParameters::create_from_config(
|
||||
|
||||
if (! soluble_interface) {
|
||||
params.gap_raft_object = object_config.raft_contact_distance.value;
|
||||
params.gap_object_support = object_config.support_material_contact_distance.value;
|
||||
params.gap_object_support = object_config.support_material_bottom_contact_distance.value;
|
||||
params.gap_support_object = object_config.support_material_contact_distance.value;
|
||||
if (params.gap_object_support <= 0)
|
||||
params.gap_object_support = params.gap_support_object;
|
||||
}
|
||||
|
||||
if (params.base_raft_layers > 0) {
|
||||
|
@ -26,7 +26,7 @@ class DynamicPrintConfig;
|
||||
// (using a normal flow over a soluble support, using a bridging flow over a non-soluble support).
|
||||
struct SlicingParameters
|
||||
{
|
||||
SlicingParameters() { memset(this, 0, sizeof(SlicingParameters)); }
|
||||
SlicingParameters() = default;
|
||||
|
||||
static SlicingParameters create_from_config(
|
||||
const PrintConfig &print_config,
|
||||
@ -44,58 +44,58 @@ struct SlicingParameters
|
||||
// Height of the object to be printed. This value does not contain the raft height.
|
||||
coordf_t object_print_z_height() const { return object_print_z_max - object_print_z_min; }
|
||||
|
||||
bool valid;
|
||||
bool valid { false };
|
||||
|
||||
// Number of raft layers.
|
||||
size_t base_raft_layers;
|
||||
size_t base_raft_layers { 0 };
|
||||
// Number of interface layers including the contact layer.
|
||||
size_t interface_raft_layers;
|
||||
size_t interface_raft_layers { 0 };
|
||||
|
||||
// Layer heights of the raft (base, interface and a contact layer).
|
||||
coordf_t base_raft_layer_height;
|
||||
coordf_t interface_raft_layer_height;
|
||||
coordf_t contact_raft_layer_height;
|
||||
coordf_t base_raft_layer_height { 0 };
|
||||
coordf_t interface_raft_layer_height { 0 };
|
||||
coordf_t contact_raft_layer_height { 0 };
|
||||
|
||||
// The regular layer height, applied for all but the first layer, if not overridden by layer ranges
|
||||
// or by the variable layer thickness table.
|
||||
coordf_t layer_height;
|
||||
coordf_t layer_height { 0 };
|
||||
// Minimum / maximum layer height, to be used for the automatic adaptive layer height algorithm,
|
||||
// or by an interactive layer height editor.
|
||||
coordf_t min_layer_height;
|
||||
coordf_t max_layer_height;
|
||||
coordf_t max_suport_layer_height;
|
||||
coordf_t min_layer_height { 0 };
|
||||
coordf_t max_layer_height { 0 };
|
||||
coordf_t max_suport_layer_height { 0 };
|
||||
|
||||
// First layer height of the print, this may be used for the first layer of the raft
|
||||
// or for the first layer of the print.
|
||||
coordf_t first_print_layer_height;
|
||||
coordf_t first_print_layer_height { 0 };
|
||||
|
||||
// Thickness of the first layer. This is either the first print layer thickness if printed without a raft,
|
||||
// or a bridging flow thickness if printed over a non-soluble raft,
|
||||
// or a normal layer height if printed over a soluble raft.
|
||||
coordf_t first_object_layer_height;
|
||||
coordf_t first_object_layer_height { 0 };
|
||||
|
||||
// If the object is printed over a non-soluble raft, the first layer may be printed with a briding flow.
|
||||
bool first_object_layer_bridging;
|
||||
bool first_object_layer_bridging { false };
|
||||
|
||||
// Soluble interface? (PLA soluble in water, HIPS soluble in lemonen)
|
||||
// otherwise the interface must be broken off.
|
||||
bool soluble_interface;
|
||||
bool soluble_interface { false };
|
||||
// Gap when placing object over raft.
|
||||
coordf_t gap_raft_object;
|
||||
coordf_t gap_raft_object { 0 };
|
||||
// Gap when placing support over object.
|
||||
coordf_t gap_object_support;
|
||||
coordf_t gap_object_support { 0 };
|
||||
// Gap when placing object over support.
|
||||
coordf_t gap_support_object;
|
||||
coordf_t gap_support_object { 0 };
|
||||
|
||||
// Bottom and top of the printed object.
|
||||
// If printed without a raft, object_print_z_min = 0 and object_print_z_max = object height.
|
||||
// Otherwise object_print_z_min is equal to the raft height.
|
||||
coordf_t raft_base_top_z;
|
||||
coordf_t raft_interface_top_z;
|
||||
coordf_t raft_contact_top_z;
|
||||
coordf_t raft_base_top_z { 0 };
|
||||
coordf_t raft_interface_top_z { 0 };
|
||||
coordf_t raft_contact_top_z { 0 };
|
||||
// In case of a soluble interface, object_print_z_min == raft_contact_top_z, otherwise there is a gap between the raft and the 1st object layer.
|
||||
coordf_t object_print_z_min;
|
||||
coordf_t object_print_z_max;
|
||||
coordf_t object_print_z_min { 0 };
|
||||
coordf_t object_print_z_max { 0 };
|
||||
};
|
||||
static_assert(IsTriviallyCopyable<SlicingParameters>::value, "SlicingParameters class is not POD (and it should be - see constructor).");
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/atomic.h>
|
||||
@ -334,7 +335,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object
|
||||
for (auto lh : m_print_config->min_layer_height.values)
|
||||
m_support_layer_height_min = std::min(m_support_layer_height_min, std::max(0.01, lh));
|
||||
|
||||
if (m_object_config->support_material_interface_layers.value == 0) {
|
||||
if (m_slicing_params.soluble_interface) {
|
||||
// No interface layers allowed, print everything with the base support pattern.
|
||||
m_support_material_interface_flow = m_support_material_flow;
|
||||
}
|
||||
@ -342,11 +343,21 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object
|
||||
// Evaluate the XY gap between the object outer perimeters and the support structures.
|
||||
// Evaluate the XY gap between the object outer perimeters and the support structures.
|
||||
coordf_t external_perimeter_width = 0.;
|
||||
size_t num_nonempty_regions = 0;
|
||||
coordf_t bridge_flow_ratio = 0;
|
||||
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id)
|
||||
if (! object->region_volumes[region_id].empty())
|
||||
external_perimeter_width = std::max(external_perimeter_width,
|
||||
(coordf_t)object->print()->get_region(region_id)->flow(frExternalPerimeter, slicing_params.layer_height, false, false, -1, *object).width);
|
||||
if (! object->region_volumes[region_id].empty()) {
|
||||
++ num_nonempty_regions;
|
||||
const PrintRegion ®ion = *object->print()->get_region(region_id);
|
||||
external_perimeter_width = std::max(external_perimeter_width, coordf_t(region.flow(*object, frExternalPerimeter, slicing_params.layer_height).width()));
|
||||
bridge_flow_ratio += region.config().bridge_flow_ratio;
|
||||
}
|
||||
m_gap_xy = m_object_config->support_material_xy_spacing.get_abs_value(external_perimeter_width);
|
||||
bridge_flow_ratio /= num_nonempty_regions;
|
||||
|
||||
m_support_material_bottom_interface_flow = m_slicing_params.soluble_interface || ! m_object_config->thick_bridges ?
|
||||
m_support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) :
|
||||
Flow::bridging_flow(bridge_flow_ratio * m_support_material_interface_flow.nozzle_diameter(), m_support_material_interface_flow.nozzle_diameter());
|
||||
|
||||
m_can_merge_support_regions = m_object_config->support_material_extruder.value == m_object_config->support_material_interface_extruder.value;
|
||||
if (! m_can_merge_support_regions && (m_object_config->support_material_extruder.value == 0 || m_object_config->support_material_interface_extruder.value == 0)) {
|
||||
@ -387,14 +398,6 @@ inline void layers_append(PrintObjectSupportMaterial::MyLayersPtr &dst, const Pr
|
||||
dst.insert(dst.end(), src.begin(), src.end());
|
||||
}
|
||||
|
||||
// Compare layers lexicographically.
|
||||
struct MyLayersPtrCompare
|
||||
{
|
||||
bool operator()(const PrintObjectSupportMaterial::MyLayer* layer1, const PrintObjectSupportMaterial::MyLayer* layer2) const {
|
||||
return *layer1 < *layer2;
|
||||
}
|
||||
};
|
||||
|
||||
void PrintObjectSupportMaterial::generate(PrintObject &object)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Support generator - Start";
|
||||
@ -457,10 +460,7 @@ 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_object_config->support_material_contact_distance.value,
|
||||
m_slicing_params.soluble_interface ? 0. : m_object_config->support_material_contact_distance.value, m_gap_xy);
|
||||
this->trim_support_layers_by_object(object, top_contacts, m_slicing_params.gap_support_object, m_slicing_params.gap_object_support, m_gap_xy);
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
for (const MyLayer *layer : top_contacts)
|
||||
@ -542,7 +542,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
|
||||
layers_append(layers_sorted, interface_layers);
|
||||
layers_append(layers_sorted, base_interface_layers);
|
||||
// Sort the layers lexicographically by a raising print_z and a decreasing height.
|
||||
std::sort(layers_sorted.begin(), layers_sorted.end(), MyLayersPtrCompare());
|
||||
std::sort(layers_sorted.begin(), layers_sorted.end(), [](auto *l1, auto *l2) { return *l1 < *l2; });
|
||||
int layer_id = 0;
|
||||
assert(object.support_layers().empty());
|
||||
for (size_t i = 0; i < layers_sorted.size();) {
|
||||
@ -1231,8 +1231,8 @@ namespace SupportMaterialInternal {
|
||||
// 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()));
|
||||
Flow perimeter_bridge_flow = layerm->bridging_flow(frPerimeter);
|
||||
float w = float(std::max(perimeter_bridge_flow.scaled_width(), perimeter_bridge_flow.scaled_spacing()));
|
||||
for (Polyline &polyline : overhang_perimeters)
|
||||
if (polyline.is_straight()) {
|
||||
// This is a bridge
|
||||
@ -1542,16 +1542,20 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
||||
}
|
||||
}
|
||||
// Offset the contact polygons outside.
|
||||
#if 0
|
||||
for (size_t i = 0; i < NUM_MARGIN_STEPS; ++ i) {
|
||||
diff_polygons = diff(
|
||||
offset(
|
||||
diff_polygons,
|
||||
SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS,
|
||||
scaled<float>(SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS),
|
||||
ClipperLib::jtRound,
|
||||
// round mitter limit
|
||||
scale_(0.05)),
|
||||
slices_margin_cached);
|
||||
}
|
||||
#else
|
||||
diff_polygons = diff(diff_polygons, slices_margin_cached);
|
||||
#endif
|
||||
}
|
||||
polygons_append(contact_polygons, diff_polygons);
|
||||
} // for each layer.region
|
||||
@ -1571,11 +1575,11 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
||||
} else if (m_slicing_params.soluble_interface) {
|
||||
// Align the contact surface height with a layer immediately below the supported layer.
|
||||
// Interface layer will be synchronized with the object.
|
||||
new_layer.print_z = layer.print_z - layer.height;
|
||||
new_layer.print_z = layer.bottom_z();
|
||||
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 {
|
||||
new_layer.print_z = layer.print_z - layer.height - m_object_config->support_material_contact_distance;
|
||||
new_layer.print_z = layer.bottom_z() - m_slicing_params.gap_object_support;
|
||||
new_layer.bottom_z = new_layer.print_z;
|
||||
new_layer.height = 0.;
|
||||
// Ignore this contact area if it's too low.
|
||||
@ -1597,12 +1601,12 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
||||
|
||||
// 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)) {
|
||||
if (m_object_config->thick_bridges && 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;
|
||||
coordf_t bridging_print_z = layer.print_z - bridging_height - m_slicing_params.gap_support_object;
|
||||
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) {
|
||||
@ -1875,16 +1879,13 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
|
||||
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;
|
||||
// Place a bridge flow interface layer or the normal flow interface layer over the top surface.
|
||||
m_support_material_bottom_interface_flow.height();
|
||||
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.print_z + layer_new.height + m_slicing_params.gap_object_support;
|
||||
layer_new.bottom_z = layer.print_z;
|
||||
layer_new.idx_object_layer_below = layer_id;
|
||||
layer_new.bridging = ! m_slicing_params.soluble_interface;
|
||||
layer_new.bridging = ! m_slicing_params.soluble_interface && m_object_config->thick_bridges;
|
||||
//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);
|
||||
@ -2028,11 +2029,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
|
||||
task_group.wait();
|
||||
}
|
||||
std::reverse(bottom_contacts.begin(), bottom_contacts.end());
|
||||
// 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);
|
||||
|
||||
trim_support_layers_by_object(object, bottom_contacts, m_slicing_params.gap_support_object, m_slicing_params.gap_object_support, m_gap_xy);
|
||||
} // ! top_contacts.empty()
|
||||
|
||||
return bottom_contacts;
|
||||
@ -2462,10 +2459,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
|
||||
++ iRun;
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
|
||||
// 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);
|
||||
this->trim_support_layers_by_object(object, intermediate_layers, m_slicing_params.gap_support_object, m_slicing_params.gap_object_support, m_gap_xy);
|
||||
}
|
||||
|
||||
void PrintObjectSupportMaterial::trim_support_layers_by_object(
|
||||
@ -2499,7 +2493,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
|
||||
// BOOST_LOG_TRIVIAL(trace) << "Support generator - trim_support_layers_by_object - trimmming non-empty layer " << idx_layer << " of " << nonempty_layers.size();
|
||||
assert(! support_layer.polygons.empty() && support_layer.print_z >= m_slicing_params.raft_contact_top_z + EPSILON);
|
||||
// Find the overlapping object layers including the extra above / below gap.
|
||||
coordf_t z_threshold = support_layer.print_z - support_layer.height - gap_extra_below + EPSILON;
|
||||
coordf_t z_threshold = support_layer.bottom_print_z() - gap_extra_below + EPSILON;
|
||||
idx_object_layer_overlapping = idx_higher_or_equal(
|
||||
object.layers().begin(), object.layers().end(), idx_object_layer_overlapping,
|
||||
[z_threshold](const Layer *layer){ return layer->print_z >= z_threshold; });
|
||||
@ -2508,11 +2502,11 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
|
||||
size_t i = idx_object_layer_overlapping;
|
||||
for (; i < object.layers().size(); ++ i) {
|
||||
const Layer &object_layer = *object.layers()[i];
|
||||
if (object_layer.print_z - object_layer.height > support_layer.print_z + gap_extra_above - EPSILON)
|
||||
if (object_layer.bottom_z() > support_layer.print_z + gap_extra_above - EPSILON)
|
||||
break;
|
||||
polygons_append(polygons_trimming, offset(object_layer.lslices, gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
}
|
||||
if (! m_slicing_params.soluble_interface) {
|
||||
if (! m_slicing_params.soluble_interface && m_object_config->thick_bridges) {
|
||||
// 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];
|
||||
@ -2526,6 +2520,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
|
||||
offset(to_expolygons(region->fill_surfaces.filter_by_type(stBottomBridge)),
|
||||
gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
if (region->region()->config().overhangs.value)
|
||||
// Add bridging perimeters.
|
||||
SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming);
|
||||
}
|
||||
if (! some_region_overlaps)
|
||||
@ -2709,17 +2704,23 @@ std::pair<PrintObjectSupportMaterial::MyLayersPtr, PrintObjectSupportMaterial::M
|
||||
m_object_config->support_material_interface_extruder.value > 0 && m_print_config->filament_soluble.get_at(m_object_config->support_material_interface_extruder.value - 1) &&
|
||||
// Base extruder: Either "print with active extruder" not soluble.
|
||||
(m_object_config->support_material_extruder.value == 0 || ! m_print_config->filament_soluble.get_at(m_object_config->support_material_extruder.value - 1));
|
||||
int num_interface_layers = m_object_config->support_material_interface_layers.value;
|
||||
int num_base_interface_layers = soluble_interface_non_soluble_base ? std::min(num_interface_layers / 2, 2) : 0;
|
||||
int num_interface_layers_top = m_object_config->support_material_interface_layers;
|
||||
int num_interface_layers_bottom = m_object_config->support_material_bottom_interface_layers;
|
||||
if (num_interface_layers_bottom < 0)
|
||||
num_interface_layers_bottom = num_interface_layers_top;
|
||||
int num_base_interface_layers_top = soluble_interface_non_soluble_base ? std::min(num_interface_layers_top / 2, 2) : 0;
|
||||
int num_base_interface_layers_bottom = soluble_interface_non_soluble_base ? std::min(num_interface_layers_bottom / 2, 2) : 0;
|
||||
|
||||
if (! intermediate_layers.empty() && num_interface_layers > 1) {
|
||||
if (! intermediate_layers.empty() && (num_interface_layers_top > 1 || num_interface_layers_bottom > 1)) {
|
||||
// For all intermediate layers, collect top contact surfaces, which are not further than support_material_interface_layers.
|
||||
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start";
|
||||
// Since the intermediate layer index starts at zero the number of interface layer needs to be reduced by 1.
|
||||
-- num_interface_layers;
|
||||
int num_interface_layers_only = num_interface_layers - num_base_interface_layers;
|
||||
-- num_interface_layers_top;
|
||||
-- num_interface_layers_bottom;
|
||||
int num_interface_layers_only_top = num_interface_layers_top - num_base_interface_layers_top;
|
||||
int num_interface_layers_only_bottom = num_interface_layers_bottom - num_base_interface_layers_bottom;
|
||||
interface_layers.assign(intermediate_layers.size(), nullptr);
|
||||
if (num_base_interface_layers)
|
||||
if (num_base_interface_layers_top || num_base_interface_layers_bottom)
|
||||
base_interface_layers.assign(intermediate_layers.size(), nullptr);
|
||||
tbb::spin_mutex layer_storage_mutex;
|
||||
// Insert a new layer into base_interface_layers, if intersection with base exists.
|
||||
@ -2743,7 +2744,8 @@ std::pair<PrintObjectSupportMaterial::MyLayersPtr, PrintObjectSupportMaterial::M
|
||||
return &layer_new;
|
||||
};
|
||||
tbb::parallel_for(tbb::blocked_range<int>(0, int(intermediate_layers.size())),
|
||||
[&bottom_contacts, &top_contacts, &intermediate_layers, &insert_layer, num_interface_layers, num_base_interface_layers, num_interface_layers_only,
|
||||
[&bottom_contacts, &top_contacts, &intermediate_layers, &insert_layer,
|
||||
num_interface_layers_top, num_interface_layers_bottom, num_base_interface_layers_top, num_base_interface_layers_bottom, num_interface_layers_only_top, num_interface_layers_only_bottom,
|
||||
&interface_layers, &base_interface_layers](const tbb::blocked_range<int>& range) {
|
||||
// Gather the top / bottom contact layers intersecting with num_interface_layers resp. num_interface_layers_only intermediate layers above / below
|
||||
// this intermediate layer.
|
||||
@ -2754,45 +2756,51 @@ std::pair<PrintObjectSupportMaterial::MyLayersPtr, PrintObjectSupportMaterial::M
|
||||
auto num_intermediate = int(intermediate_layers.size());
|
||||
for (int idx_intermediate_layer = range.begin(); idx_intermediate_layer < range.end(); ++ idx_intermediate_layer) {
|
||||
MyLayer &intermediate_layer = *intermediate_layers[idx_intermediate_layer];
|
||||
// Top / bottom Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces
|
||||
coordf_t top_z = intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers - 1)]->print_z;
|
||||
coordf_t top_inteface_z = std::numeric_limits<coordf_t>::max();
|
||||
coordf_t bottom_z = intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers + 1)]->bottom_z;
|
||||
coordf_t bottom_interface_z = - std::numeric_limits<coordf_t>::max();
|
||||
if (num_base_interface_layers > 0) {
|
||||
// Some base interface layers will be generated.
|
||||
if (num_interface_layers_only == 0)
|
||||
// Only base interface layers to generate.
|
||||
std::swap(top_inteface_z, bottom_interface_z);
|
||||
else {
|
||||
top_inteface_z = intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers_only - 1)]->print_z;
|
||||
bottom_interface_z = intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_only)]->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; }); // - EPSILON
|
||||
// Collect the top contact areas above this intermediate layer, below top_z.
|
||||
Polygons polygons_top_contact_projected_interface;
|
||||
Polygons polygons_top_contact_projected_base;
|
||||
for (int idx_top_contact = idx_top_contact_first; idx_top_contact < int(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(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface, top_contact_layer.polygons);
|
||||
}
|
||||
// Move idx_bottom_contact_first up until touching bottom_z.
|
||||
idx_bottom_contact_first = idx_higher_or_equal(bottom_contacts, idx_bottom_contact_first, [bottom_z](const MyLayer *layer){ return layer->print_z >= bottom_z - EPSILON; });
|
||||
// Collect the top contact areas above this intermediate layer, below top_z.
|
||||
Polygons polygons_bottom_contact_projected_interface;
|
||||
Polygons polygons_bottom_contact_projected_base;
|
||||
for (int idx_bottom_contact = idx_bottom_contact_first; idx_bottom_contact < int(bottom_contacts.size()); ++ idx_bottom_contact) {
|
||||
const MyLayer &bottom_contact_layer = *bottom_contacts[idx_bottom_contact];
|
||||
if (bottom_contact_layer.print_z - EPSILON > intermediate_layer.bottom_z)
|
||||
break;
|
||||
polygons_append(bottom_contact_layer.print_z - EPSILON > bottom_interface_z ? polygons_bottom_contact_projected_interface : polygons_bottom_contact_projected_base, bottom_contact_layer.polygons);
|
||||
if (num_interface_layers_top > 0) {
|
||||
// Top Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces
|
||||
coordf_t top_z = intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers_top - 1)]->print_z;
|
||||
coordf_t top_inteface_z = std::numeric_limits<coordf_t>::max();
|
||||
if (num_base_interface_layers_top > 0)
|
||||
// Some top base interface layers will be generated.
|
||||
top_inteface_z = num_interface_layers_only_top == 0 ?
|
||||
// Only base interface layers to generate.
|
||||
- std::numeric_limits<coordf_t>::max() :
|
||||
intermediate_layers[std::min(num_intermediate - 1, idx_intermediate_layer + num_interface_layers_only_top - 1)]->print_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; }); // - EPSILON
|
||||
// Collect the top contact areas above this intermediate layer, below top_z.
|
||||
for (int idx_top_contact = idx_top_contact_first; idx_top_contact < int(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(top_contact_layer.bottom_z - EPSILON > top_inteface_z ? polygons_top_contact_projected_base : polygons_top_contact_projected_interface, top_contact_layer.polygons);
|
||||
}
|
||||
}
|
||||
if (num_interface_layers_bottom > 0) {
|
||||
// Bottom Z coordinate of a slab, over which we are collecting the top / bottom contact surfaces
|
||||
coordf_t bottom_z = intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_bottom + 1)]->bottom_z;
|
||||
coordf_t bottom_interface_z = - std::numeric_limits<coordf_t>::max();
|
||||
if (num_base_interface_layers_bottom > 0)
|
||||
// Some bottom base interface layers will be generated.
|
||||
bottom_interface_z = num_interface_layers_only_bottom == 0 ?
|
||||
// Only base interface layers to generate.
|
||||
std::numeric_limits<coordf_t>::max() :
|
||||
intermediate_layers[std::max(0, idx_intermediate_layer - num_interface_layers_only_bottom)]->bottom_z;
|
||||
// Move idx_bottom_contact_first up until touching bottom_z.
|
||||
idx_bottom_contact_first = idx_higher_or_equal(bottom_contacts, idx_bottom_contact_first, [bottom_z](const MyLayer *layer){ return layer->print_z >= bottom_z - EPSILON; });
|
||||
// Collect the top contact areas above this intermediate layer, below top_z.
|
||||
for (int idx_bottom_contact = idx_bottom_contact_first; idx_bottom_contact < int(bottom_contacts.size()); ++ idx_bottom_contact) {
|
||||
const MyLayer &bottom_contact_layer = *bottom_contacts[idx_bottom_contact];
|
||||
if (bottom_contact_layer.print_z - EPSILON > intermediate_layer.bottom_z)
|
||||
break;
|
||||
polygons_append(bottom_contact_layer.print_z - EPSILON > bottom_interface_z ? polygons_bottom_contact_projected_interface : polygons_bottom_contact_projected_base, bottom_contact_layer.polygons);
|
||||
}
|
||||
}
|
||||
|
||||
MyLayer *interface_layer = nullptr;
|
||||
if (! polygons_bottom_contact_projected_interface.empty() || ! polygons_top_contact_projected_interface.empty()) {
|
||||
interface_layer = insert_layer(
|
||||
@ -2810,7 +2818,7 @@ std::pair<PrintObjectSupportMaterial::MyLayersPtr, PrintObjectSupportMaterial::M
|
||||
// Compress contact_out, remove the nullptr items.
|
||||
remove_nulls(interface_layers);
|
||||
remove_nulls(base_interface_layers);
|
||||
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - start";
|
||||
BOOST_LOG_TRIVIAL(debug) << "PrintObjectSupportMaterial::generate_interface_layers() in parallel - end";
|
||||
}
|
||||
|
||||
return base_and_interface_layers;
|
||||
@ -2835,7 +2843,7 @@ static inline void fill_expolygon_generate_paths(
|
||||
dst,
|
||||
std::move(polylines),
|
||||
role,
|
||||
flow.mm3_per_mm(), flow.width, flow.height);
|
||||
flow.mm3_per_mm(), flow.width(), flow.height());
|
||||
}
|
||||
|
||||
static inline void fill_expolygons_generate_paths(
|
||||
@ -2872,7 +2880,8 @@ static inline void fill_expolygons_with_sheath_generate_paths(
|
||||
float density,
|
||||
ExtrusionRole role,
|
||||
const Flow &flow,
|
||||
bool with_sheath)
|
||||
bool with_sheath,
|
||||
bool no_sort)
|
||||
{
|
||||
if (polygons.empty())
|
||||
return;
|
||||
@ -2892,8 +2901,12 @@ static inline void fill_expolygons_with_sheath_generate_paths(
|
||||
|
||||
for (ExPolygon &expoly : offset2_ex(polygons, float(SCALED_EPSILON), float(- SCALED_EPSILON - 0.5*flow.scaled_width()))) {
|
||||
// Don't reorder the skirt and its infills.
|
||||
auto eec = std::make_unique<ExtrusionEntityCollection>();
|
||||
eec->no_sort = true;
|
||||
std::unique_ptr<ExtrusionEntityCollection> eec;
|
||||
if (no_sort) {
|
||||
eec = std::make_unique<ExtrusionEntityCollection>();
|
||||
eec->no_sort = true;
|
||||
}
|
||||
ExtrusionEntitiesPtr &out = no_sort ? eec->entities : dst;
|
||||
// Draw the perimeters.
|
||||
Polylines polylines;
|
||||
polylines.reserve(expoly.holes.size() + 1);
|
||||
@ -2903,10 +2916,11 @@ static inline void fill_expolygons_with_sheath_generate_paths(
|
||||
pl.clip_end(clip_length);
|
||||
polylines.emplace_back(std::move(pl));
|
||||
}
|
||||
extrusion_entities_append_paths(eec->entities, polylines, erSupportMaterial, flow.mm3_per_mm(), flow.width, flow.height);
|
||||
extrusion_entities_append_paths(out, polylines, erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height());
|
||||
// Fill in the rest.
|
||||
fill_expolygons_generate_paths(eec->entities, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, density, role, flow);
|
||||
dst.emplace_back(eec.release());
|
||||
fill_expolygons_generate_paths(out, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, density, role, flow);
|
||||
if (no_sort)
|
||||
dst.emplace_back(eec.release());
|
||||
}
|
||||
}
|
||||
|
||||
@ -3011,8 +3025,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
|
||||
if (n_contact_loops == 0 || top_contact_layer.empty())
|
||||
return;
|
||||
|
||||
Flow flow = interface_flow_src;
|
||||
flow.height = float(top_contact_layer.layer->height);
|
||||
Flow flow = interface_flow_src.with_height(top_contact_layer.layer->height);
|
||||
|
||||
Polygons overhang_polygons;
|
||||
if (top_contact_layer.layer->overhang_polygons != nullptr)
|
||||
@ -3209,7 +3222,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
|
||||
extrusion_entities_append_paths(
|
||||
top_contact_layer.extrusions,
|
||||
std::move(loop_lines),
|
||||
erSupportMaterialInterface, flow.mm3_per_mm(), flow.width, flow.height);
|
||||
erSupportMaterialInterface, flow.mm3_per_mm(), flow.width(), flow.height());
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
@ -3232,6 +3245,9 @@ static std::string dbg_index_to_color(int idx)
|
||||
// Therefore the bottom interface spots are expanded a bit. The expanded regions may overlap with another bottom interface layers,
|
||||
// leading to over extrusion, where they overlap. The over extrusion is better avoided as it often makes the interface layers
|
||||
// to stick too firmly to the object.
|
||||
//
|
||||
// Modulate thickness (increase bottom_z) of extrusions_in_out generated for this_layer
|
||||
// if they overlap with overlapping_layers, whose print_z is above this_layer.bottom_z() and below this_layer.print_z.
|
||||
void modulate_extrusion_by_overlapping_layers(
|
||||
// Extrusions generated for this_layer.
|
||||
ExtrusionEntitiesPtr &extrusions_in_out,
|
||||
@ -3320,8 +3336,8 @@ void modulate_extrusion_by_overlapping_layers(
|
||||
// Collect the paths of this_layer.
|
||||
{
|
||||
Polylines &polylines = path_fragments.back().polylines;
|
||||
for (ExtrusionEntitiesPtr::const_iterator it = extrusions_in_out.begin(); it != extrusions_in_out.end(); ++ it) {
|
||||
ExtrusionPath *path = dynamic_cast<ExtrusionPath*>(*it);
|
||||
for (ExtrusionEntity *ee : extrusions_in_out) {
|
||||
ExtrusionPath *path = dynamic_cast<ExtrusionPath*>(ee);
|
||||
assert(path != nullptr);
|
||||
polylines.emplace_back(Polyline(std::move(path->polyline)));
|
||||
path_ends.emplace_back(std::pair<Point, Point>(polylines.back().points.front(), polylines.back().points.back()));
|
||||
@ -3342,7 +3358,7 @@ void modulate_extrusion_by_overlapping_layers(
|
||||
// Adjust the extrusion parameters for a reduced layer height and a non-bridging flow (nozzle_dmr = -1, does not matter).
|
||||
assert(this_layer.print_z > overlapping_layer.print_z);
|
||||
frag.height = float(this_layer.print_z - overlapping_layer.print_z);
|
||||
frag.mm3_per_mm = Flow(frag.width, frag.height, -1.f, false).mm3_per_mm();
|
||||
frag.mm3_per_mm = Flow(frag.width, frag.height, -1.f).mm3_per_mm();
|
||||
#ifdef SLIC3R_DEBUG
|
||||
svg.draw(frag.polylines, dbg_index_to_color(i_overlapping_layer), scale_(0.1));
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
@ -3481,7 +3497,6 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
const MyLayersPtr &interface_layers,
|
||||
const MyLayersPtr &base_interface_layers) const
|
||||
{
|
||||
// Slic3r::debugf "Generating patterns\n";
|
||||
// loop_interface_processor with a given circle radius.
|
||||
LoopInterfaceProcessor loop_interface_processor(1.5 * m_support_material_interface_flow.scaled_width());
|
||||
loop_interface_processor.n_contact_loops = this->has_contact_loops() ? 1 : 0;
|
||||
@ -3492,7 +3507,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
coordf_t interface_density = std::min(1., m_support_material_interface_flow.spacing() / interface_spacing);
|
||||
coordf_t support_spacing = m_object_config->support_material_spacing.value + m_support_material_flow.spacing();
|
||||
coordf_t support_density = std::min(1., m_support_material_flow.spacing() / support_spacing);
|
||||
if (m_object_config->support_material_interface_layers.value == 0) {
|
||||
if (m_slicing_params.soluble_interface) {
|
||||
// No interface layers allowed, print everything with the base support pattern.
|
||||
interface_spacing = support_spacing;
|
||||
interface_density = support_density;
|
||||
@ -3565,7 +3580,8 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
//FIXME misusing contact_polygons for support columns.
|
||||
((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons);
|
||||
if (! to_infill_polygons.empty()) {
|
||||
Flow flow(float(m_support_material_flow.width), float(raft_layer.height), m_support_material_flow.nozzle_diameter, raft_layer.bridging);
|
||||
assert(! raft_layer.bridging);
|
||||
Flow flow(float(m_support_material_flow.width()), float(raft_layer.height), m_support_material_flow.nozzle_diameter());
|
||||
Fill * filler = filler_support.get();
|
||||
filler->angle = raft_angle_base;
|
||||
filler->spacing = m_support_material_flow.spacing();
|
||||
@ -3579,7 +3595,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
filler, float(support_density),
|
||||
// Extrusion parameters
|
||||
erSupportMaterial, flow,
|
||||
with_sheath);
|
||||
with_sheath, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3596,7 +3612,8 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
// We don't use $base_flow->spacing because we need a constant spacing
|
||||
// value that guarantees that all layers are correctly aligned.
|
||||
filler->spacing = m_support_material_flow.spacing();
|
||||
flow = Flow(float(m_support_material_interface_flow.width), float(raft_layer.height), m_support_material_flow.nozzle_diameter, raft_layer.bridging);
|
||||
assert(! raft_layer.bridging);
|
||||
flow = Flow(float(m_support_material_interface_flow.width()), float(raft_layer.height), m_support_material_flow.nozzle_diameter());
|
||||
density = float(interface_density);
|
||||
} else
|
||||
continue;
|
||||
@ -3611,7 +3628,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
// Extrusion parameters
|
||||
(support_layer_id < m_slicing_params.base_raft_layers) ? erSupportMaterial : erSupportMaterialInterface, flow,
|
||||
// sheath at first layer
|
||||
support_layer_id == 0);
|
||||
support_layer_id == 0, support_layer_id == 0);
|
||||
}
|
||||
});
|
||||
|
||||
@ -3621,12 +3638,20 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
std::vector<MyLayer*> overlapping;
|
||||
};
|
||||
struct LayerCache {
|
||||
MyLayerExtruded bottom_contact_layer;
|
||||
MyLayerExtruded top_contact_layer;
|
||||
MyLayerExtruded base_layer;
|
||||
MyLayerExtruded interface_layer;
|
||||
MyLayerExtruded base_interface_layer;
|
||||
std::vector<LayerCacheItem> overlaps;
|
||||
MyLayerExtruded bottom_contact_layer;
|
||||
MyLayerExtruded top_contact_layer;
|
||||
MyLayerExtruded base_layer;
|
||||
MyLayerExtruded interface_layer;
|
||||
MyLayerExtruded base_interface_layer;
|
||||
boost::container::static_vector<LayerCacheItem, 5> nonempty;
|
||||
|
||||
void add_nonempty_and_sort() {
|
||||
for (MyLayerExtruded *item : { &bottom_contact_layer, &top_contact_layer, &interface_layer, &base_interface_layer, &base_layer })
|
||||
if (! item->empty())
|
||||
this->nonempty.emplace_back(item);
|
||||
// Sort the layers with the same print_z coordinate by their heights, thickest first.
|
||||
std::stable_sort(this->nonempty.begin(), this->nonempty.end(), [](const LayerCacheItem &lc1, const LayerCacheItem &lc2) { return lc1.layer_extruded->layer->height > lc2.layer_extruded->layer->height; });
|
||||
}
|
||||
};
|
||||
std::vector<LayerCache> layer_caches(support_layers.size(), LayerCache());
|
||||
|
||||
@ -3700,10 +3725,6 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
base_layer.merge(std::move(top_contact_layer));
|
||||
else if (base_layer.empty() && !top_contact_layer.empty() && !top_contact_layer.layer->bridging)
|
||||
std::swap(base_layer, top_contact_layer);
|
||||
if (base_layer.could_merge(bottom_contact_layer))
|
||||
base_layer.merge(std::move(bottom_contact_layer));
|
||||
else if (base_layer.empty() && !bottom_contact_layer.empty() && !bottom_contact_layer.layer->bridging)
|
||||
std::swap(base_layer, bottom_contact_layer);
|
||||
}
|
||||
} else {
|
||||
loop_interface_processor.generate(top_contact_layer, m_support_material_interface_flow);
|
||||
@ -3713,6 +3734,12 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
if (top_contact_layer.could_merge(interface_layer))
|
||||
top_contact_layer.merge(std::move(interface_layer));
|
||||
}
|
||||
if ((m_object_config->support_material_interface_layers == 0 || m_object_config->support_material_bottom_interface_layers == 0) && m_can_merge_support_regions) {
|
||||
if (base_layer.could_merge(bottom_contact_layer))
|
||||
base_layer.merge(std::move(bottom_contact_layer));
|
||||
else if (base_layer.empty() && !bottom_contact_layer.empty() && !bottom_contact_layer.layer->bridging)
|
||||
std::swap(base_layer, bottom_contact_layer);
|
||||
}
|
||||
|
||||
#if 0
|
||||
if ( ! interface_layer.empty() && ! base_layer.empty()) {
|
||||
@ -3731,14 +3758,12 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
MyLayerExtruded &layer_ex = (i == 0) ? top_contact_layer : (i == 1 ? bottom_contact_layer : interface_layer);
|
||||
if (layer_ex.empty() || layer_ex.polygons_to_extrude().empty())
|
||||
continue;
|
||||
bool interface_as_base = (&layer_ex == &interface_layer) && m_object_config->support_material_interface_layers.value == 0;
|
||||
bool interface_as_base = (&layer_ex == &interface_layer) && m_slicing_params.soluble_interface;
|
||||
//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),
|
||||
m_support_material_interface_flow.nozzle_diameter,
|
||||
layer_ex.layer->bridging);
|
||||
auto interface_flow = layer_ex.layer->bridging ?
|
||||
Flow::bridging_flow(layer_ex.layer->height, m_support_material_bottom_interface_flow.nozzle_diameter()) :
|
||||
(interface_as_base ? &m_support_material_flow : &m_support_material_interface_flow)->with_height(float(layer_ex.layer->height));
|
||||
filler_interface->angle = interface_as_base ?
|
||||
// If zero interface layers are configured, use the same angle as for the base layers.
|
||||
angles[support_layer_id % angles.size()] :
|
||||
@ -3762,11 +3787,8 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
Fill *filler = filler_base_interface.get();
|
||||
//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(base_interface_layer.layer->bridging ? base_interface_layer.layer->height : m_support_material_flow.width), // m_support_material_interface_flow.width)),
|
||||
float(base_interface_layer.layer->height),
|
||||
m_support_material_flow.nozzle_diameter,
|
||||
base_interface_layer.layer->bridging);
|
||||
assert(! base_interface_layer.layer->bridging);
|
||||
Flow interface_flow = m_support_material_flow.with_height(float(base_interface_layer.layer->height));
|
||||
filler->angle = interface_angle;
|
||||
filler->spacing = m_support_material_interface_flow.spacing();
|
||||
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / interface_density));
|
||||
@ -3788,15 +3810,13 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
filler->angle = angles[support_layer_id % angles.size()];
|
||||
// We don't use $base_flow->spacing because we need a constant spacing
|
||||
// value that guarantees that all layers are correctly aligned.
|
||||
Flow flow(
|
||||
float(base_layer.layer->bridging ? base_layer.layer->height : m_support_material_flow.width),
|
||||
float(base_layer.layer->height),
|
||||
m_support_material_flow.nozzle_diameter,
|
||||
base_layer.layer->bridging);
|
||||
assert(! base_layer.layer->bridging);
|
||||
auto flow = m_support_material_flow.with_height(float(base_layer.layer->height));
|
||||
filler->spacing = m_support_material_flow.spacing();
|
||||
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_density));
|
||||
float density = float(support_density);
|
||||
bool sheath = with_sheath;
|
||||
bool no_sort = false;
|
||||
if (base_layer.layer->bottom_z < EPSILON) {
|
||||
// Base flange (the 1st layer).
|
||||
filler = filler_first_layer;
|
||||
@ -3808,7 +3828,8 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
//FIXME When paralellizing, each thread shall have its own copy of the fillers.
|
||||
filler->spacing = flow.spacing();
|
||||
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density));
|
||||
sheath = true;
|
||||
sheath = true;
|
||||
no_sort = true;
|
||||
}
|
||||
fill_expolygons_with_sheath_generate_paths(
|
||||
// Destination
|
||||
@ -3819,7 +3840,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
filler, density,
|
||||
// Extrusion parameters
|
||||
erSupportMaterial, flow,
|
||||
sheath);
|
||||
sheath, no_sort);
|
||||
|
||||
}
|
||||
|
||||
@ -3828,24 +3849,13 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
base_layer.could_merge(base_interface_layer))
|
||||
base_layer.merge(std::move(base_interface_layer));
|
||||
|
||||
layer_cache.overlaps.reserve(5);
|
||||
if (! bottom_contact_layer.empty())
|
||||
layer_cache.overlaps.push_back(&bottom_contact_layer);
|
||||
if (! top_contact_layer.empty())
|
||||
layer_cache.overlaps.push_back(&top_contact_layer);
|
||||
if (! interface_layer.empty())
|
||||
layer_cache.overlaps.push_back(&interface_layer);
|
||||
if (! base_interface_layer.empty())
|
||||
layer_cache.overlaps.push_back(&base_interface_layer);
|
||||
if (! base_layer.empty())
|
||||
layer_cache.overlaps.push_back(&base_layer);
|
||||
// Sort the layers with the same print_z coordinate by their heights, thickest first.
|
||||
std::sort(layer_cache.overlaps.begin(), layer_cache.overlaps.end(), [](const LayerCacheItem &lc1, const LayerCacheItem &lc2) { return lc1.layer_extruded->layer->height > lc2.layer_extruded->layer->height; });
|
||||
layer_cache.add_nonempty_and_sort();
|
||||
|
||||
// Collect the support areas with this print_z into islands, as there is no need
|
||||
// for retraction over these islands.
|
||||
Polygons polys;
|
||||
// Collect the extrusions, sorted by the bottom extrusion height.
|
||||
for (LayerCacheItem &layer_cache_item : layer_cache.overlaps) {
|
||||
for (LayerCacheItem &layer_cache_item : layer_cache.nonempty) {
|
||||
// Collect islands to polys.
|
||||
layer_cache_item.layer_extruded->polygons_append(polys);
|
||||
// The print_z of the top contact surfaces and bottom_z of the bottom contact surfaces are "free"
|
||||
@ -3859,32 +3869,22 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
// Collect overlapping top/bottom surfaces.
|
||||
layer_cache_item.overlapping.reserve(20);
|
||||
coordf_t bottom_z = layer_cache_item.layer_extruded->layer->bottom_print_z() + EPSILON;
|
||||
for (int i = int(idx_layer_bottom_contact) - 1; i >= 0 && bottom_contacts[i]->print_z > bottom_z; -- i)
|
||||
layer_cache_item.overlapping.push_back(bottom_contacts[i]);
|
||||
for (int i = int(idx_layer_top_contact) - 1; i >= 0 && top_contacts[i]->print_z > bottom_z; -- i)
|
||||
layer_cache_item.overlapping.push_back(top_contacts[i]);
|
||||
auto add_overlapping = [&layer_cache_item, bottom_z](const MyLayersPtr &layers, size_t idx_top) {
|
||||
for (int i = int(idx_top) - 1; i >= 0 && layers[i]->print_z > bottom_z; -- i)
|
||||
layer_cache_item.overlapping.push_back(layers[i]);
|
||||
};
|
||||
add_overlapping(top_contacts, idx_layer_top_contact);
|
||||
if (layer_cache_item.layer_extruded->layer->layer_type == sltBottomContact) {
|
||||
// Bottom contact layer may overlap with a base layer, which may be changed to interface layer.
|
||||
for (int i = int(idx_layer_intermediate) - 1; i >= 0 && intermediate_layers[i]->print_z > bottom_z; -- i)
|
||||
layer_cache_item.overlapping.push_back(intermediate_layers[i]);
|
||||
for (int i = int(idx_layer_interface) - 1; i >= 0 && interface_layers[i]->print_z > bottom_z; -- i)
|
||||
layer_cache_item.overlapping.push_back(interface_layers[i]);
|
||||
for (int i = int(idx_layer_base_interface) - 1; i >= 0 && base_interface_layers[i]->print_z > bottom_z; -- i)
|
||||
layer_cache_item.overlapping.push_back(base_interface_layers[i]);
|
||||
add_overlapping(intermediate_layers, idx_layer_intermediate);
|
||||
add_overlapping(interface_layers, idx_layer_interface);
|
||||
add_overlapping(base_interface_layers, idx_layer_base_interface);
|
||||
}
|
||||
std::sort(layer_cache_item.overlapping.begin(), layer_cache_item.overlapping.end(), MyLayersPtrCompare());
|
||||
// Order the layers by lexicographically by an increasing print_z and a decreasing layer height.
|
||||
std::stable_sort(layer_cache_item.overlapping.begin(), layer_cache_item.overlapping.end(), [](auto *l1, auto *l2) { return *l1 < *l2; });
|
||||
}
|
||||
if (! polys.empty())
|
||||
expolygons_append(support_layer.support_islands.expolygons, union_ex(polys));
|
||||
/* {
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output("islands_" . $z . ".svg",
|
||||
red_expolygons => union_ex($contact),
|
||||
green_expolygons => union_ex($interface),
|
||||
green_polylines => [ map $_->unpack->polyline, @{$layer->support_contact_fills} ],
|
||||
polylines => [ map $_->unpack->polyline, @{$layer->support_fills} ],
|
||||
);
|
||||
} */
|
||||
} // for each support_layer_id
|
||||
});
|
||||
|
||||
@ -3895,7 +3895,9 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
||||
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) {
|
||||
SupportLayer &support_layer = *support_layers[support_layer_id];
|
||||
LayerCache &layer_cache = layer_caches[support_layer_id];
|
||||
for (LayerCacheItem &layer_cache_item : layer_cache.overlaps) {
|
||||
// For all extrusion types at this print_z, ordered by decreasing layer height:
|
||||
for (LayerCacheItem &layer_cache_item : layer_cache.nonempty) {
|
||||
// Trim the extrusion height from the bottom by the overlapping layers.
|
||||
modulate_extrusion_by_overlapping_layers(layer_cache_item.layer_extruded->extrusions, *layer_cache_item.layer_extruded->layer, layer_cache_item.overlapping);
|
||||
support_layer.support_fills.append(std::move(layer_cache_item.layer_extruded->extrusions));
|
||||
}
|
||||
|
@ -244,6 +244,7 @@ private:
|
||||
Flow m_first_layer_flow;
|
||||
Flow m_support_material_flow;
|
||||
Flow m_support_material_interface_flow;
|
||||
Flow m_support_material_bottom_interface_flow;
|
||||
// Is merging of regions allowed? Could the interface & base support regions be printed with the same extruder?
|
||||
bool m_can_merge_support_regions;
|
||||
|
||||
|
@ -2063,4 +2063,22 @@ TriangleMesh make_sphere(double radius, double fa)
|
||||
return mesh;
|
||||
}
|
||||
|
||||
std::vector<std::vector<size_t> > create_neighbor_index(const indexed_triangle_set &its)
|
||||
{
|
||||
if (its.vertices.empty()) return {};
|
||||
|
||||
size_t res = its.indices.size() / its.vertices.size();
|
||||
std::vector< std::vector<size_t> > index(its.vertices.size(),
|
||||
reserve_vector<size_t>(res));
|
||||
|
||||
for (size_t fi = 0; fi < its.indices.size(); ++fi) {
|
||||
auto &face = its.indices[fi];
|
||||
index[face(0)].emplace_back(fi);
|
||||
index[face(1)].emplace_back(fi);
|
||||
index[face(2)].emplace_back(fi);
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -89,6 +89,12 @@ private:
|
||||
std::deque<uint32_t> find_unvisited_neighbors(std::vector<unsigned char> &facet_visited) const;
|
||||
};
|
||||
|
||||
// Create an index of faces belonging to each vertex. The returned vector can
|
||||
// be indexed with vertex indices and contains a list of face indices for each
|
||||
// vertex.
|
||||
std::vector< std::vector<size_t> >
|
||||
create_neighbor_index(const indexed_triangle_set &its);
|
||||
|
||||
enum FacetEdgeType {
|
||||
// A general case, the cutting plane intersect a face at two different edges.
|
||||
feGeneral,
|
||||
|
@ -93,6 +93,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/SavePresetDialog.cpp
|
||||
GUI/PhysicalPrinterDialog.hpp
|
||||
GUI/PhysicalPrinterDialog.cpp
|
||||
GUI/GUI_Factories.cpp
|
||||
GUI/GUI_Factories.hpp
|
||||
GUI/GUI_ObjectList.cpp
|
||||
GUI/GUI_ObjectList.hpp
|
||||
GUI/GUI_ObjectManipulation.cpp
|
||||
@ -207,6 +209,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
Utils/Bonjour.hpp
|
||||
Utils/PresetUpdater.cpp
|
||||
Utils/PresetUpdater.hpp
|
||||
Utils/Platform.cpp
|
||||
Utils/Platform.hpp
|
||||
Utils/Process.cpp
|
||||
Utils/Process.hpp
|
||||
Utils/Profile.hpp
|
||||
|
@ -845,6 +845,57 @@ bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, M
|
||||
return contained_min_one;
|
||||
}
|
||||
|
||||
bool GLVolumeCollection::check_outside_state(const DynamicPrintConfig* config, bool& partlyOut, bool& fullyOut)
|
||||
{
|
||||
if (config == nullptr)
|
||||
return false;
|
||||
|
||||
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config->option("bed_shape"));
|
||||
if (opt == nullptr)
|
||||
return false;
|
||||
|
||||
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
|
||||
BoundingBoxf3 print_volume(Vec3d(unscale<double>(bed_box_2D.min(0)), unscale<double>(bed_box_2D.min(1)), 0.0), Vec3d(unscale<double>(bed_box_2D.max(0)), unscale<double>(bed_box_2D.max(1)), config->opt_float("max_print_height")));
|
||||
// Allow the objects to protrude below the print bed
|
||||
print_volume.min(2) = -1e10;
|
||||
print_volume.min(0) -= BedEpsilon;
|
||||
print_volume.min(1) -= BedEpsilon;
|
||||
print_volume.max(0) += BedEpsilon;
|
||||
print_volume.max(1) += BedEpsilon;
|
||||
|
||||
bool contained_min_one = false;
|
||||
|
||||
partlyOut = false;
|
||||
fullyOut = false;
|
||||
for (GLVolume* volume : this->volumes)
|
||||
{
|
||||
if ((volume == nullptr) || volume->is_modifier || (volume->is_wipe_tower && !volume->shader_outside_printer_detection_enabled) || ((volume->composite_id.volume_id < 0) && !volume->shader_outside_printer_detection_enabled))
|
||||
continue;
|
||||
|
||||
const BoundingBoxf3& bb = volume->transformed_convex_hull_bounding_box();
|
||||
bool contained = print_volume.contains(bb);
|
||||
|
||||
volume->is_outside = !contained;
|
||||
if (!volume->printable)
|
||||
continue;
|
||||
|
||||
if (contained)
|
||||
contained_min_one = true;
|
||||
|
||||
if (volume->is_outside) {
|
||||
if (print_volume.intersects(bb))
|
||||
partlyOut = true;
|
||||
else
|
||||
fullyOut = true;
|
||||
}
|
||||
}
|
||||
/*
|
||||
if (out_state != nullptr)
|
||||
*out_state = state;
|
||||
*/
|
||||
return contained_min_one;
|
||||
}
|
||||
|
||||
void GLVolumeCollection::reset_outside_state()
|
||||
{
|
||||
for (GLVolume* volume : this->volumes)
|
||||
|
@ -569,6 +569,7 @@ public:
|
||||
// returns true if all the volumes are completely contained in the print volume
|
||||
// returns the containment state in the given out_state, if non-null
|
||||
bool check_outside_state(const DynamicPrintConfig* config, ModelInstanceEPrintVolumeState* out_state);
|
||||
bool check_outside_state(const DynamicPrintConfig* config, bool& partlyOut, bool& fullyOut);
|
||||
void reset_outside_state();
|
||||
|
||||
void update_colors_by_extruder(const DynamicPrintConfig* config);
|
||||
|
@ -285,8 +285,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
|
||||
"support_material_xy_spacing" })
|
||||
toggle_field(el, have_support_material);
|
||||
toggle_field("support_material_threshold", have_support_material_auto);
|
||||
toggle_field("support_material_bottom_contact_distance", have_support_material && ! have_support_soluble);
|
||||
|
||||
for (auto el : { "support_material_interface_spacing", "support_material_interface_extruder",
|
||||
for (auto el : { "support_material_bottom_interface_layers", "support_material_interface_spacing", "support_material_interface_extruder",
|
||||
"support_material_interface_speed", "support_material_interface_contact_loops" })
|
||||
toggle_field(el, have_support_material && have_support_interface);
|
||||
toggle_field("support_material_synchronize_layers", have_support_soluble);
|
||||
|
@ -1317,7 +1317,12 @@ wxString Control::get_tooltip(int tick/*=-1*/)
|
||||
"This code won't be processed during G-code generation.");
|
||||
|
||||
// Show custom Gcode as a first string of tooltop
|
||||
tooltip = " ";
|
||||
std::string space = " ";
|
||||
tooltip = space;
|
||||
auto format_gcode = [space](std::string gcode) {
|
||||
boost::replace_all(gcode, "\n", "\n" + space);
|
||||
return gcode;
|
||||
};
|
||||
tooltip +=
|
||||
tick_code_it->type == ColorChange ?
|
||||
(m_mode == SingleExtruder ?
|
||||
@ -1329,7 +1334,7 @@ wxString Control::get_tooltip(int tick/*=-1*/)
|
||||
format_wxstr(_L("Custom template (\"%1%\")"), gcode(Template)) :
|
||||
tick_code_it->type == ToolChange ?
|
||||
format_wxstr(_L("Extruder (tool) is changed to Extruder \"%1%\""), tick_code_it->extruder) :
|
||||
from_u8(tick_code_it->extra);// tick_code_it->type == Custom
|
||||
from_u8(format_gcode(tick_code_it->extra));// tick_code_it->type == Custom
|
||||
|
||||
// If tick is marked as a conflict (exclamation icon),
|
||||
// we should to explain why
|
||||
@ -1925,6 +1930,8 @@ void Control::auto_color_change()
|
||||
double delta_area = scale_(scale_(25)); // equal to 25 mm2
|
||||
|
||||
for (auto object : print.objects()) {
|
||||
if (object->layer_count() == 0)
|
||||
continue;
|
||||
double prev_area = area(object->get_layer(0)->lslices);
|
||||
|
||||
for (size_t i = 1; i < object->layers().size(); i++) {
|
||||
|
@ -902,7 +902,7 @@ void Choice::BUILD() {
|
||||
if (m_opt.width >= 0) size.SetWidth(m_opt.width*m_em_unit);
|
||||
|
||||
choice_ctrl* temp;
|
||||
if (!m_opt.gui_type.empty() && m_opt.gui_type.compare("select_open") != 0) {
|
||||
if (m_opt.gui_type != ConfigOptionDef::GUIType::undefined && m_opt.gui_type != ConfigOptionDef::GUIType::select_open) {
|
||||
m_is_editable = true;
|
||||
temp = new choice_ctrl(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size);
|
||||
}
|
||||
@ -965,15 +965,27 @@ void Choice::BUILD() {
|
||||
}
|
||||
|
||||
if (is_defined_input_value<choice_ctrl>(window, m_opt.type)) {
|
||||
if (m_opt.type == coFloatOrPercent) {
|
||||
switch (m_opt.type) {
|
||||
case coFloatOrPercent:
|
||||
{
|
||||
std::string old_val = !m_value.empty() ? boost::any_cast<std::string>(m_value) : "";
|
||||
if (old_val == boost::any_cast<std::string>(get_value()))
|
||||
return;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
double old_val = !m_value.empty() ? boost::any_cast<double>(m_value) : -99999;
|
||||
if (fabs(old_val - boost::any_cast<double>(get_value())) <= 0.0001)
|
||||
case coInt:
|
||||
{
|
||||
int old_val = !m_value.empty() ? boost::any_cast<int>(m_value) : 0;
|
||||
if (old_val == boost::any_cast<int>(get_value()))
|
||||
return;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
double old_val = !m_value.empty() ? boost::any_cast<double>(m_value) : -99999;
|
||||
if (fabs(old_val - boost::any_cast<double>(get_value())) <= 0.0001)
|
||||
return;
|
||||
}
|
||||
}
|
||||
on_change_field();
|
||||
}
|
||||
@ -1225,7 +1237,7 @@ boost::any& Choice::get_value()
|
||||
else if (m_opt_id == "brim_type")
|
||||
m_value = static_cast<BrimType>(ret_enum);
|
||||
}
|
||||
else if (m_opt.gui_type == "f_enum_open") {
|
||||
else if (m_opt.gui_type == ConfigOptionDef::GUIType::f_enum_open || m_opt.gui_type == ConfigOptionDef::GUIType::i_enum_open) {
|
||||
const int ret_enum = field->GetSelection();
|
||||
if (ret_enum < 0 || m_opt.enum_values.empty() || m_opt.type == coStrings ||
|
||||
(ret_str != m_opt.enum_values[ret_enum] && ret_str != _(m_opt.enum_labels[ret_enum])))
|
||||
@ -1233,6 +1245,8 @@ boost::any& Choice::get_value()
|
||||
get_value_by_opt_type(ret_str);
|
||||
else if (m_opt.type == coFloatOrPercent)
|
||||
m_value = m_opt.enum_values[ret_enum];
|
||||
else if (m_opt.type == coInt)
|
||||
m_value = atoi(m_opt.enum_values[ret_enum].c_str());
|
||||
else
|
||||
m_value = atof(m_opt.enum_values[ret_enum].c_str());
|
||||
}
|
||||
|
@ -641,6 +641,7 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool
|
||||
error = true;
|
||||
break;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(error) << state << " : " << text ;
|
||||
auto ¬ification_manager = *wxGetApp().plater()->get_notification_manager();
|
||||
if (state) {
|
||||
if(error)
|
||||
@ -1620,9 +1621,6 @@ void GLCanvas3D::render()
|
||||
wxGetApp().plater()->init_environment_texture();
|
||||
#endif // ENABLE_ENVIRONMENT_MAP
|
||||
|
||||
m_render_timer.Stop();
|
||||
m_extra_frame_requested_delayed = std::numeric_limits<int>::max();
|
||||
|
||||
const Size& cnv_size = get_canvas_size();
|
||||
// Probably due to different order of events on Linux/GTK2, when one switched from 3D scene
|
||||
// to preview, this was called before canvas had its final size. It reported zero width
|
||||
@ -1754,7 +1752,8 @@ void GLCanvas3D::render()
|
||||
m_tooltip.render(m_mouse.position, *this);
|
||||
|
||||
wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(*this);
|
||||
wxGetApp().plater()->get_notification_manager()->render_notifications(get_overlay_window_width());
|
||||
|
||||
wxGetApp().plater()->get_notification_manager()->render_notifications(*this, get_overlay_window_width());
|
||||
|
||||
wxGetApp().imgui()->render();
|
||||
|
||||
@ -2238,24 +2237,24 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
|
||||
// checks for geometry outside the print volume to render it accordingly
|
||||
if (!m_volumes.empty()) {
|
||||
ModelInstanceEPrintVolumeState state;
|
||||
|
||||
const bool contained_min_one = m_volumes.check_outside_state(m_config, &state);
|
||||
bool partlyOut = false;
|
||||
bool fullyOut = false;
|
||||
const bool contained_min_one = m_volumes.check_outside_state(m_config, partlyOut, fullyOut);
|
||||
|
||||
#if ENABLE_WARNING_TEXTURE_REMOVAL
|
||||
_set_warning_notification(EWarning::ObjectClashed, state == ModelInstancePVS_Partly_Outside);
|
||||
_set_warning_notification(EWarning::ObjectOutside, state == ModelInstancePVS_Fully_Outside);
|
||||
if (printer_technology != ptSLA || state == ModelInstancePVS_Inside)
|
||||
_set_warning_notification(EWarning::ObjectClashed, partlyOut);
|
||||
_set_warning_notification(EWarning::ObjectOutside, fullyOut);
|
||||
if (printer_technology != ptSLA || !contained_min_one)
|
||||
_set_warning_notification(EWarning::SlaSupportsOutside, false);
|
||||
#else
|
||||
_set_warning_texture(WarningTexture::ObjectClashed, state == ModelInstancePVS_Partly_Outside);
|
||||
_set_warning_texture(WarningTexture::ObjectOutside, state == ModelInstancePVS_Fully_Outside);
|
||||
if(printer_technology != ptSLA || state == ModelInstancePVS_Inside)
|
||||
_set_warning_texture(WarningTexture::ObjectClashed, partlyOut);
|
||||
_set_warning_texture(WarningTexture::ObjectOutside, fullyOut);
|
||||
if(printer_technology != ptSLA || !contained_min_one)
|
||||
_set_warning_texture(WarningTexture::SlaSupportsOutside, false);
|
||||
#endif // ENABLE_WARNING_TEXTURE_REMOVAL
|
||||
|
||||
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS,
|
||||
contained_min_one && !m_model->objects.empty() && state != ModelInstancePVS_Partly_Outside));
|
||||
contained_min_one && !m_model->objects.empty() && !partlyOut));
|
||||
}
|
||||
else {
|
||||
#if ENABLE_WARNING_TEXTURE_REMOVAL
|
||||
@ -2442,13 +2441,13 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
||||
if (!m_initialized)
|
||||
return;
|
||||
|
||||
// FIXME
|
||||
m_dirty |= m_main_toolbar.update_items_state();
|
||||
m_dirty |= m_undoredo_toolbar.update_items_state();
|
||||
m_dirty |= wxGetApp().plater()->get_view_toolbar().update_items_state();
|
||||
m_dirty |= wxGetApp().plater()->get_collapse_toolbar().update_items_state();
|
||||
bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(wxGetApp().plater()->get_camera());
|
||||
m_dirty |= mouse3d_controller_applied;
|
||||
m_dirty |= wxGetApp().plater()->get_notification_manager()->update_notifications(*this);
|
||||
|
||||
if (!m_dirty)
|
||||
return;
|
||||
@ -2986,30 +2985,39 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt)
|
||||
|
||||
void GLCanvas3D::on_render_timer(wxTimerEvent& evt)
|
||||
{
|
||||
// If slicer is not top window -> restart timer with one second to try again
|
||||
wxWindow* p = dynamic_cast<wxWindow*>(wxGetApp().plater());
|
||||
while (p->GetParent() != nullptr)
|
||||
p = p->GetParent();
|
||||
wxTopLevelWindow* top_level_wnd = dynamic_cast<wxTopLevelWindow*>(p);
|
||||
if (!top_level_wnd->IsActive()) {
|
||||
request_extra_frame_delayed(1000);
|
||||
return;
|
||||
}
|
||||
//render();
|
||||
m_dirty = true;
|
||||
wxWakeUpIdle();
|
||||
// no need to do anything here
|
||||
// right after this event is recieved, idle event is fired
|
||||
|
||||
//m_dirty = true;
|
||||
//wxWakeUpIdle();
|
||||
}
|
||||
|
||||
void GLCanvas3D::request_extra_frame_delayed(int miliseconds)
|
||||
|
||||
void GLCanvas3D::schedule_extra_frame(int miliseconds)
|
||||
{
|
||||
// Schedule idle event right now
|
||||
if (miliseconds == 0)
|
||||
{
|
||||
// We want to wakeup idle evnt but most likely this is call inside render cycle so we need to wait
|
||||
if (m_in_render)
|
||||
miliseconds = 33;
|
||||
else {
|
||||
m_dirty = true;
|
||||
wxWakeUpIdle();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Start timer
|
||||
int64_t now = timestamp_now();
|
||||
// Timer is not running
|
||||
if (! m_render_timer.IsRunning()) {
|
||||
m_extra_frame_requested_delayed = miliseconds;
|
||||
m_render_timer.StartOnce(miliseconds);
|
||||
m_render_timer_start = now;
|
||||
// Timer is running - restart only if new period is shorter than remaning period
|
||||
} else {
|
||||
const int64_t remaining_time = (m_render_timer_start + m_extra_frame_requested_delayed) - now;
|
||||
if (miliseconds < remaining_time) {
|
||||
if (miliseconds + 20 < remaining_time) {
|
||||
m_render_timer.Stop();
|
||||
m_extra_frame_requested_delayed = miliseconds;
|
||||
m_render_timer.StartOnce(miliseconds);
|
||||
|
@ -743,7 +743,8 @@ public:
|
||||
void msw_rescale();
|
||||
|
||||
void request_extra_frame() { m_extra_frame_requested = true; }
|
||||
void request_extra_frame_delayed(int miliseconds);
|
||||
|
||||
void schedule_extra_frame(int miliseconds);
|
||||
|
||||
int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); }
|
||||
void force_main_toolbar_left_action(int item_id) { m_main_toolbar.force_left_action(item_id, *this); }
|
||||
|
@ -1978,6 +1978,11 @@ wxNotebook* GUI_App::tab_panel() const
|
||||
return mainframe->m_tabpanel;
|
||||
}
|
||||
|
||||
NotificationManager* GUI_App::notification_manager()
|
||||
{
|
||||
return plater_->get_notification_manager();
|
||||
}
|
||||
|
||||
// extruders count from selected printer preset
|
||||
int GUI_App::extruders_cnt() const
|
||||
{
|
||||
|
@ -43,6 +43,7 @@ class ObjectSettings;
|
||||
class ObjectList;
|
||||
class ObjectLayers;
|
||||
class Plater;
|
||||
class NotificationManager;
|
||||
struct GUI_InitParams;
|
||||
|
||||
|
||||
@ -226,14 +227,14 @@ public:
|
||||
void MacOpenFiles(const wxArrayString &fileNames) override;
|
||||
#endif /* __APPLE */
|
||||
|
||||
Sidebar& sidebar();
|
||||
ObjectManipulation* obj_manipul();
|
||||
ObjectSettings* obj_settings();
|
||||
ObjectList* obj_list();
|
||||
ObjectLayers* obj_layers();
|
||||
Plater* plater();
|
||||
Model& model();
|
||||
|
||||
Sidebar& sidebar();
|
||||
ObjectManipulation* obj_manipul();
|
||||
ObjectSettings* obj_settings();
|
||||
ObjectList* obj_list();
|
||||
ObjectLayers* obj_layers();
|
||||
Plater* plater();
|
||||
Model& model();
|
||||
NotificationManager* notification_manager();
|
||||
|
||||
// Parameters extracted from the command line to be passed to GUI after initialization.
|
||||
const GUI_InitParams* init_params { nullptr };
|
||||
|
1027
src/slic3r/GUI/GUI_Factories.cpp
Normal file
1027
src/slic3r/GUI/GUI_Factories.cpp
Normal file
File diff suppressed because it is too large
Load Diff
104
src/slic3r/GUI/GUI_Factories.hpp
Normal file
104
src/slic3r/GUI/GUI_Factories.hpp
Normal file
@ -0,0 +1,104 @@
|
||||
#ifndef slic3r_GUI_Factories_hpp_
|
||||
#define slic3r_GUI_Factories_hpp_
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
#include <wx/bitmap.h>
|
||||
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
class wxMenu;
|
||||
class wxMenuItem;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum class ModelVolumeType : int;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
struct SettingsFactory
|
||||
{
|
||||
// category -> vector ( option )
|
||||
typedef std::map<std::string, std::vector<std::string>> Bundle;
|
||||
static std::map<std::string, std::string> CATEGORY_ICON;
|
||||
|
||||
static wxBitmap get_category_bitmap(const std::string& category_name);
|
||||
static Bundle get_bundle(const DynamicPrintConfig* config, bool is_object_settings);
|
||||
static std::vector<std::string> get_options(bool is_part);
|
||||
};
|
||||
|
||||
class MenuFactory
|
||||
{
|
||||
public:
|
||||
static std::vector<std::pair<std::string, std::string>> ADD_VOLUME_MENU_ITEMS;
|
||||
static std::vector<wxBitmap> get_volume_bitmaps();
|
||||
|
||||
MenuFactory();
|
||||
~MenuFactory() = default;
|
||||
|
||||
void init(wxWindow* parent);
|
||||
void update_object_menu();
|
||||
void msw_rescale();
|
||||
|
||||
wxMenu* default_menu();
|
||||
wxMenu* object_menu();
|
||||
wxMenu* sla_object_menu();
|
||||
wxMenu* part_menu();
|
||||
wxMenu* instance_menu();
|
||||
wxMenu* layer_menu();
|
||||
wxMenu* multi_selection_menu();
|
||||
|
||||
private:
|
||||
enum MenuType {
|
||||
mtObjectFFF = 0,
|
||||
mtObjectSLA,
|
||||
mtCount
|
||||
};
|
||||
|
||||
wxWindow* m_parent {nullptr};
|
||||
|
||||
MenuWithSeparators m_object_menu;
|
||||
MenuWithSeparators m_part_menu;
|
||||
MenuWithSeparators m_sla_object_menu;
|
||||
MenuWithSeparators m_default_menu;
|
||||
MenuWithSeparators m_instance_menu;
|
||||
|
||||
// Removed/Prepended Items according to the view mode
|
||||
std::array<wxMenuItem*, mtCount> items_increase;
|
||||
std::array<wxMenuItem*, mtCount> items_decrease;
|
||||
std::array<wxMenuItem*, mtCount> items_set_number_of_copies;
|
||||
|
||||
void create_default_menu();
|
||||
void create_common_object_menu(wxMenu *menu);
|
||||
void create_object_menu();
|
||||
void create_sla_object_menu();
|
||||
void create_part_menu();
|
||||
|
||||
wxMenu* append_submenu_add_generic(wxMenu* menu, ModelVolumeType type);
|
||||
void append_menu_items_add_volume(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_layers_editing(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_settings(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_change_type(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_printable(wxMenu* menu);
|
||||
void append_menu_items_osx(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu);
|
||||
void append_menu_item_export_stl(wxMenu* menu);
|
||||
void append_menu_item_reload_from_disk(wxMenu* menu);
|
||||
void append_menu_item_change_extruder(wxMenu* menu);
|
||||
void append_menu_item_delete(wxMenu* menu);
|
||||
void append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu);
|
||||
void append_menu_items_convert_unit(wxMenu* menu, int insert_pos = 1); // Add "Conver/Revert..." menu items (from/to inches/meters) after "Reload From Disk"
|
||||
void append_menu_item_merge_to_multipart_object(wxMenu *menu);
|
||||
// void append_menu_item_merge_to_single_object(wxMenu *menu);
|
||||
void append_menu_items_mirror(wxMenu *menu);
|
||||
void append_menu_items_instance_manipulation(wxMenu *menu);
|
||||
void update_menu_items_instance_manipulation(MenuType type);
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif //slic3r_GUI_Factories_hpp_
|
@ -9,6 +9,7 @@
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/Utils/Platform.hpp"
|
||||
|
||||
// To show a message box if GUI initialization ends up with an exception thrown.
|
||||
#include <wx/msgdlg.h>
|
||||
@ -36,6 +37,8 @@ int GUI_Run(GUI_InitParams ¶ms)
|
||||
signal(SIGCHLD, SIG_DFL);
|
||||
#endif // __APPLE__
|
||||
|
||||
detect_platform();
|
||||
|
||||
try {
|
||||
GUI::GUI_App* gui = new GUI::GUI_App(params.start_as_gcodeviewer ? GUI::GUI_App::EAppMode::GCodeViewer : GUI::GUI_App::EAppMode::Editor);
|
||||
if (gui->get_app_mode() != GUI::GUI_App::EAppMode::GCodeViewer) {
|
||||
|
@ -260,16 +260,15 @@ void ObjectLayers::msw_rescale()
|
||||
editor->msw_rescale();
|
||||
}
|
||||
|
||||
const std::vector<size_t> btns = {2, 3}; // del_btn, add_btn
|
||||
for (auto btn : btns)
|
||||
{
|
||||
wxSizerItem* b_item = item->GetSizer()->GetItem(btn);
|
||||
if (b_item->IsWindow()) {
|
||||
auto button = dynamic_cast<PlusMinusButton*>(b_item->GetWindow());
|
||||
if (button != nullptr)
|
||||
button->msw_rescale();
|
||||
}
|
||||
}
|
||||
if (item->GetSizer()->GetItemCount() > 2) // if there are Add/Del buttons
|
||||
for (size_t btn : {2, 3}) { // del_btn, add_btn
|
||||
wxSizerItem* b_item = item->GetSizer()->GetItem(btn);
|
||||
if (b_item->IsWindow()) {
|
||||
auto button = dynamic_cast<PlusMinusButton*>(b_item->GetWindow());
|
||||
if (button != nullptr)
|
||||
button->msw_rescale();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_grid_sizer->Layout();
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -31,18 +31,11 @@ class TriangleMesh;
|
||||
enum class ModelVolumeType : int;
|
||||
|
||||
// FIXME: broken build on mac os because of this is missing:
|
||||
typedef std::vector<std::string> t_config_option_keys;
|
||||
|
||||
typedef std::map<std::string, std::vector<std::string>> SettingsBundle;
|
||||
|
||||
// category -> vector ( option ; label )
|
||||
typedef std::map< std::string, std::vector< std::pair<std::string, std::string> > > settings_menu_hierarchy;
|
||||
|
||||
typedef std::vector<ModelVolume*> ModelVolumePtrs;
|
||||
|
||||
typedef double coordf_t;
|
||||
typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
|
||||
typedef std::map<t_layer_height_range, ModelConfig> t_layer_config_ranges;
|
||||
typedef std::vector<std::string> t_config_option_keys;
|
||||
typedef std::vector<ModelVolume*> ModelVolumePtrs;
|
||||
typedef double coordf_t;
|
||||
typedef std::pair<coordf_t, coordf_t> t_layer_height_range;
|
||||
typedef std::map<t_layer_height_range, ModelConfig> t_layer_config_ranges;
|
||||
|
||||
namespace GUI {
|
||||
|
||||
@ -106,7 +99,7 @@ public:
|
||||
|
||||
private:
|
||||
SELECTION_MODE m_selection_mode {smUndef};
|
||||
int m_selected_layers_range_idx;
|
||||
int m_selected_layers_range_idx {-1};
|
||||
|
||||
Clipboard m_clipboard;
|
||||
|
||||
@ -147,23 +140,6 @@ private:
|
||||
} m_dragged_data;
|
||||
|
||||
wxBoxSizer *m_sizer {nullptr};
|
||||
wxWindow *m_parent {nullptr};
|
||||
|
||||
ScalableBitmap m_bmp_modifiermesh;
|
||||
ScalableBitmap m_bmp_solidmesh;
|
||||
ScalableBitmap m_bmp_support_enforcer;
|
||||
ScalableBitmap m_bmp_support_blocker;
|
||||
ScalableBitmap m_bmp_manifold_warning;
|
||||
ScalableBitmap m_bmp_cog;
|
||||
|
||||
MenuWithSeparators m_menu_object;
|
||||
MenuWithSeparators m_menu_part;
|
||||
MenuWithSeparators m_menu_sla_object;
|
||||
MenuWithSeparators m_menu_instance;
|
||||
MenuWithSeparators m_menu_layer;
|
||||
MenuWithSeparators m_menu_default;
|
||||
wxMenuItem* m_menu_item_settings { nullptr };
|
||||
wxMenuItem* m_menu_item_split_instances { nullptr };
|
||||
|
||||
ObjectDataViewModel *m_objects_model{ nullptr };
|
||||
ModelConfig *m_config {nullptr};
|
||||
@ -185,7 +161,6 @@ private:
|
||||
// update_settings_items - updating canvas selection is undesirable,
|
||||
// because it would turn off the gizmos (mainly a problem for the SLA gizmo)
|
||||
|
||||
int m_selected_row = 0;
|
||||
wxDataViewItem m_last_selected_item {nullptr};
|
||||
#ifdef __WXMSW__
|
||||
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.
|
||||
@ -193,8 +168,8 @@ private:
|
||||
#endif /* __MSW__ */
|
||||
|
||||
#if 0
|
||||
SettingsBundle m_freq_settings_fff;
|
||||
SettingsBundle m_freq_settings_sla;
|
||||
SettingsFactory::Bundle m_freq_settings_fff;
|
||||
SettingsFactory::Bundle m_freq_settings_sla;
|
||||
#endif
|
||||
|
||||
size_t m_items_count { size_t(-1) };
|
||||
@ -212,8 +187,6 @@ public:
|
||||
void set_min_height();
|
||||
void update_min_height();
|
||||
|
||||
std::map<std::string, wxBitmap> CATEGORY_ICON;
|
||||
|
||||
ObjectDataViewModel* GetModel() const { return m_objects_model; }
|
||||
ModelConfig* config() const { return m_config; }
|
||||
std::vector<ModelObject*>* objects() const { return m_objects; }
|
||||
@ -221,7 +194,6 @@ public:
|
||||
ModelObject* object(const int obj_idx) const ;
|
||||
|
||||
void create_objects_ctrl();
|
||||
void create_popup_menus();
|
||||
void update_objects_list_extruder_column(size_t extruders_count);
|
||||
void update_extruder_colors();
|
||||
// show/hide "Extruder" column for Objects List
|
||||
@ -232,9 +204,6 @@ public:
|
||||
void update_name_in_model(const wxDataViewItem& item) const;
|
||||
void update_extruder_values_for_items(const size_t max_extruder);
|
||||
|
||||
void init_icons();
|
||||
void msw_rescale_icons();
|
||||
|
||||
// Get obj_idx and vol_idx values for the selected (by default) or an adjusted item
|
||||
void get_selected_item_indexes(int& obj_idx, int& vol_idx, const wxDataViewItem& item = wxDataViewItem(0));
|
||||
void get_selection_indexes(std::vector<int>& obj_idxs, std::vector<int>& vol_idxs);
|
||||
@ -264,39 +233,11 @@ public:
|
||||
void increase_instances();
|
||||
void decrease_instances();
|
||||
|
||||
void get_settings_choice(const wxString& category_name);
|
||||
void get_freq_settings_choice(const wxString& bundle_name);
|
||||
void add_category_to_settings_from_selection(const std::vector< std::pair<std::string, bool> >& category_options, wxDataViewItem item);
|
||||
void add_category_to_settings_from_frequent(const std::vector<std::string>& category_options, wxDataViewItem item);
|
||||
void show_settings(const wxDataViewItem settings_item);
|
||||
bool is_instance_or_object_selected();
|
||||
|
||||
wxMenu* append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type);
|
||||
void append_menu_items_add_volume(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_split(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_layers_editing(wxMenu* menu, wxWindow* parent);
|
||||
wxMenuItem* append_menu_item_settings(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_change_type(wxMenu* menu, wxWindow* parent = nullptr);
|
||||
wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu, wxWindow* parent);
|
||||
wxMenuItem* append_menu_item_printable(wxMenu* menu, wxWindow* parent);
|
||||
void append_menu_items_osx(wxMenu* menu);
|
||||
wxMenuItem* append_menu_item_fix_through_netfabb(wxMenu* menu);
|
||||
void append_menu_item_export_stl(wxMenu* menu) const;
|
||||
void append_menu_item_reload_from_disk(wxMenu* menu) const;
|
||||
void append_menu_item_change_extruder(wxMenu* menu);
|
||||
void append_menu_item_delete(wxMenu* menu);
|
||||
void append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu);
|
||||
void append_menu_items_convert_unit(wxMenu* menu, int insert_pos = 1); // Add "Conver/Revert..." menu items (from/to inches/meters) after "Reload From Disk"
|
||||
void append_menu_item_merge_to_multipart_object(wxMenu *menu);
|
||||
void append_menu_item_merge_to_single_object(wxMenu *menu);
|
||||
void create_object_popupmenu(wxMenu *menu);
|
||||
void create_sla_object_popupmenu(wxMenu*menu);
|
||||
void create_part_popupmenu(wxMenu*menu);
|
||||
void create_instance_popupmenu(wxMenu*menu);
|
||||
void create_default_popupmenu(wxMenu *menu);
|
||||
wxMenu* create_settings_popupmenu(wxMenu *parent_menu);
|
||||
void create_freq_settings_popupmenu(wxMenu *parent_menu, const bool is_object_settings = true);
|
||||
|
||||
void update_opt_keys(t_config_option_keys& t_optopt_keys, const bool is_object);
|
||||
|
||||
void load_subobject(ModelVolumeType type);
|
||||
void load_part(ModelObject* model_object, std::vector<std::pair<wxString, bool>> &volumes_info, ModelVolumeType type);
|
||||
void load_generic_subobject(const std::string& type_name, const ModelVolumeType type);
|
||||
@ -318,7 +259,7 @@ public:
|
||||
|
||||
DynamicPrintConfig get_default_layer_config(const int obj_idx);
|
||||
bool get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume);
|
||||
bool is_splittable();
|
||||
bool is_splittable(bool to_objects);
|
||||
bool selected_instances_of_same_object();
|
||||
bool can_split_instances();
|
||||
bool can_merge_to_multipart_object() const;
|
||||
@ -328,7 +269,6 @@ public:
|
||||
wxBoxSizer* get_sizer() {return m_sizer;}
|
||||
int get_selected_obj_idx() const;
|
||||
ModelConfig& get_item_config(const wxDataViewItem& item) const;
|
||||
SettingsBundle get_item_settings_bundle(const DynamicPrintConfig* config, const bool is_object_settings);
|
||||
|
||||
void changed_object(const int obj_idx = -1) const;
|
||||
void part_selection_changed();
|
||||
@ -404,11 +344,9 @@ public:
|
||||
void change_part_type();
|
||||
|
||||
void last_volume_is_deleted(const int obj_idx);
|
||||
void update_settings_items();
|
||||
void update_and_show_object_settings_item();
|
||||
void update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections);
|
||||
void update_object_list_by_printer_technology();
|
||||
void update_object_menu();
|
||||
|
||||
void instances_to_separated_object(const int obj_idx, const std::set<int>& inst_idx);
|
||||
void instances_to_separated_objects(const int obj_idx);
|
||||
@ -433,7 +371,7 @@ public:
|
||||
void update_printable_state(int obj_idx, int instance_idx);
|
||||
void toggle_printable_state(wxDataViewItem item);
|
||||
|
||||
void show_multi_selection_menu();
|
||||
void set_extruder_for_selected_items(const int extruder) const ;
|
||||
|
||||
private:
|
||||
#ifdef __WXOSX__
|
||||
@ -454,11 +392,6 @@ private:
|
||||
#endif /* __WXMSW__ */
|
||||
void OnEditingDone(wxDataViewEvent &event);
|
||||
void extruder_selection();
|
||||
void set_extruder_for_selected_items(const int extruder) const ;
|
||||
|
||||
std::vector<std::string> get_options(const bool is_part);
|
||||
const std::vector<std::string>& get_options_for_bundle(const wxString& bundle_name);
|
||||
void get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part);
|
||||
};
|
||||
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "GUI_ObjectSettings.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "GUI_Factories.hpp"
|
||||
|
||||
#include "OptionsGroup.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
@ -83,7 +84,7 @@ bool ObjectSettings::update_settings_list()
|
||||
return false;
|
||||
|
||||
const bool is_object_settings = objects_model->GetItemType(objects_model->GetParent(item)) == itObject;
|
||||
SettingsBundle cat_options = objects_ctrl->get_item_settings_bundle(&config->get(), is_object_settings);
|
||||
SettingsFactory::Bundle cat_options = SettingsFactory::get_bundle(&config->get(), is_object_settings);
|
||||
|
||||
if (!cat_options.empty())
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Layer.hpp"
|
||||
#include "GUI_Preview.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI.hpp"
|
||||
@ -24,6 +25,7 @@
|
||||
// this include must follow the wxWidgets ones or it won't compile on Windows -> see http://trac.wxwidgets.org/ticket/2421
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
#include "NotificationManager.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
@ -639,6 +641,53 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
|
||||
else
|
||||
m_layers_slider->SetLayersTimes(m_gcode_result->time_statistics.modes.front().layers_times);
|
||||
|
||||
// Suggest the auto color change, if model looks like sign
|
||||
if (ticks_info_from_model.gcodes.empty())
|
||||
{
|
||||
NotificationManager* notif_mngr = wxGetApp().plater()->get_notification_manager();
|
||||
notif_mngr->close_notification_of_type(NotificationType::SignDetected);
|
||||
|
||||
const Print& print = wxGetApp().plater()->fff_print();
|
||||
double delta_area = scale_(scale_(25)); // equal to 25 mm2
|
||||
|
||||
//bool is_possible_auto_color_change = false;
|
||||
for (auto object : print.objects()) {
|
||||
double height = object->height();
|
||||
coord_t longer_side = std::max(object->size().x(), object->size().y());
|
||||
if (height / longer_side > 0.3)
|
||||
continue;
|
||||
|
||||
const ExPolygons& bottom = object->get_layer(0)->lslices;
|
||||
if (bottom.size() > 1 || !bottom[0].holes.empty())
|
||||
continue;
|
||||
|
||||
double bottom_area = area(bottom);
|
||||
int i;
|
||||
for (i = 1; i < int(0.3 * object->layers().size()); i++)
|
||||
if (area(object->get_layer(1)->lslices) != bottom_area)
|
||||
break;
|
||||
if (i < int(0.3 * object->layers().size()))
|
||||
continue;
|
||||
|
||||
double top_area = area(object->get_layer(int(object->layers().size()) - 1)->lslices);
|
||||
if( bottom_area - top_area > delta_area) {
|
||||
notif_mngr->push_notification(
|
||||
NotificationType::SignDetected, NotificationManager::NotificationLevel::RegularNotification,
|
||||
_u8L("NOTE:") + "\n" + _u8L("Sliced object looks like the sign") + "\n",
|
||||
_u8L("Apply auto color change to print"),
|
||||
[this, notif_mngr](wxEvtHandler*) {
|
||||
notif_mngr->close_notification_of_type(NotificationType::SignDetected);
|
||||
m_layers_slider->auto_color_change();
|
||||
return true;
|
||||
});
|
||||
|
||||
notif_mngr->set_in_preview(true);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_layers_slider_sizer->Show((size_t)0);
|
||||
Layout();
|
||||
}
|
||||
|
@ -200,12 +200,20 @@ void HollowedMesh::on_update()
|
||||
if (print_object->is_step_done(slaposDrillHoles) && print_object->has_mesh(slaposDrillHoles)) {
|
||||
size_t timestamp = print_object->step_state_with_timestamp(slaposDrillHoles).timestamp;
|
||||
if (timestamp > m_old_hollowing_timestamp) {
|
||||
const TriangleMesh& backend_mesh = print_object->get_mesh_to_print();
|
||||
const TriangleMesh& backend_mesh = print_object->get_mesh_to_slice();
|
||||
if (! backend_mesh.empty()) {
|
||||
m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh));
|
||||
Transform3d trafo_inv = canvas->sla_print()->sla_trafo(*mo).inverse();
|
||||
m_hollowed_mesh_transformed->transform(trafo_inv);
|
||||
m_old_hollowing_timestamp = timestamp;
|
||||
|
||||
const TriangleMesh &interior = print_object->hollowed_interior_mesh();
|
||||
if (!interior.empty()) {
|
||||
m_hollowed_interior_transformed = std::make_unique<TriangleMesh>(interior);
|
||||
m_hollowed_interior_transformed->repaired = false;
|
||||
m_hollowed_interior_transformed->repair(true);
|
||||
m_hollowed_interior_transformed->transform(trafo_inv);
|
||||
}
|
||||
}
|
||||
else
|
||||
m_hollowed_mesh_transformed.reset(nullptr);
|
||||
@ -230,6 +238,10 @@ const TriangleMesh* HollowedMesh::get_hollowed_mesh() const
|
||||
return m_hollowed_mesh_transformed.get();
|
||||
}
|
||||
|
||||
const TriangleMesh* HollowedMesh::get_hollowed_interior() const
|
||||
{
|
||||
return m_hollowed_interior_transformed.get();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -306,6 +318,10 @@ void ObjectClipper::on_update()
|
||||
m_clippers.back()->set_mesh(*mesh);
|
||||
}
|
||||
m_old_meshes = meshes;
|
||||
|
||||
if (has_hollowed)
|
||||
m_clippers.front()->set_negative_mesh(*get_pool()->hollowed_mesh()->get_hollowed_interior());
|
||||
|
||||
m_active_inst_bb_radius =
|
||||
mo->instance_bounding_box(get_pool()->selection_info()->get_active_instance()).radius();
|
||||
//if (has_hollowed && m_clp_ratio != 0.)
|
||||
|
@ -199,6 +199,7 @@ public:
|
||||
#endif // NDEBUG
|
||||
|
||||
const TriangleMesh* get_hollowed_mesh() const;
|
||||
const TriangleMesh* get_hollowed_interior() const;
|
||||
|
||||
protected:
|
||||
void on_update() override;
|
||||
@ -206,6 +207,7 @@ protected:
|
||||
|
||||
private:
|
||||
std::unique_ptr<TriangleMesh> m_hollowed_mesh_transformed;
|
||||
std::unique_ptr<TriangleMesh> m_hollowed_interior_transformed;
|
||||
size_t m_old_hollowing_timestamp = 0;
|
||||
int m_print_object_idx = -1;
|
||||
int m_print_objects_count = 0;
|
||||
|
@ -51,7 +51,9 @@ static const std::map<const char, std::string> font_icons_large = {
|
||||
{ImGui::EjectButton , "notification_eject_sd" },
|
||||
{ImGui::EjectHoverButton , "notification_eject_sd_hover" },
|
||||
{ImGui::WarningMarker , "notification_warning" },
|
||||
{ImGui::ErrorMarker , "notification_error" }
|
||||
{ImGui::ErrorMarker , "notification_error" },
|
||||
{ImGui::CancelButton , "notification_cancel" },
|
||||
{ImGui::CancelHoverButton , "notification_cancel_hover" },
|
||||
};
|
||||
|
||||
const ImVec4 ImGuiWrapper::COL_GREY_DARK = { 0.333f, 0.333f, 0.333f, 1.0f };
|
||||
|
@ -562,8 +562,6 @@ void MainFrame::init_tabpanel()
|
||||
|
||||
wxGetApp().plater_ = m_plater;
|
||||
|
||||
wxGetApp().obj_list()->create_popup_menus();
|
||||
|
||||
if (wxGetApp().is_editor())
|
||||
create_preset_tabs();
|
||||
|
||||
@ -1219,6 +1217,9 @@ void MainFrame::init_menubar_as_editor()
|
||||
append_menu_check_item(viewMenu, wxID_ANY, _L("&Collapse sidebar") + sep + "Shift+" + sep_space + "Tab", _L("Collapse sidebar"),
|
||||
[this](wxCommandEvent&) { m_plater->collapse_sidebar(!m_plater->is_sidebar_collapsed()); }, this,
|
||||
[]() { return true; }, [this]() { return m_plater->is_sidebar_collapsed(); }, this);
|
||||
append_menu_check_item(viewMenu, wxID_ANY, _L("&Full screen") + "\t" + "F11", _L("Full screen"),
|
||||
[this](wxCommandEvent&) { this->ShowFullScreen(!this->IsFullScreen()); }, this,
|
||||
[]() { return true; }, [this]() { return this->IsFullScreen(); }, this);
|
||||
}
|
||||
|
||||
// Help menu
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "libslic3r/Tesselate.hpp"
|
||||
#include "libslic3r/TriangleMesh.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
|
||||
#include "slic3r/GUI/Camera.hpp"
|
||||
|
||||
@ -31,6 +32,15 @@ void MeshClipper::set_mesh(const TriangleMesh& mesh)
|
||||
}
|
||||
}
|
||||
|
||||
void MeshClipper::set_negative_mesh(const TriangleMesh& mesh)
|
||||
{
|
||||
if (m_negative_mesh != &mesh) {
|
||||
m_negative_mesh = &mesh;
|
||||
m_triangles_valid = false;
|
||||
m_triangles2d.resize(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void MeshClipper::set_transformation(const Geometry::Transformation& trafo)
|
||||
@ -74,6 +84,15 @@ void MeshClipper::recalculate_triangles()
|
||||
std::vector<ExPolygons> list_of_expolys;
|
||||
m_tms->set_up_direction(up.cast<float>());
|
||||
m_tms->slice(std::vector<float>{height_mesh}, SlicingMode::Regular, 0.f, &list_of_expolys, [](){});
|
||||
|
||||
if (m_negative_mesh && !m_negative_mesh->empty()) {
|
||||
TriangleMeshSlicer negative_tms{m_negative_mesh};
|
||||
negative_tms.set_up_direction(up.cast<float>());
|
||||
|
||||
std::vector<ExPolygons> neg_polys;
|
||||
negative_tms.slice(std::vector<float>{height_mesh}, SlicingMode::Regular, 0.f, &neg_polys, [](){});
|
||||
list_of_expolys.front() = diff_ex(list_of_expolys.front(), neg_polys.front());
|
||||
}
|
||||
m_triangles2d = triangulate_expolygons_2f(list_of_expolys[0], m_trafo.get_matrix().matrix().determinant() < 0.);
|
||||
|
||||
// Rotate the cut into world coords:
|
||||
|
@ -78,6 +78,8 @@ public:
|
||||
// must make sure that it stays valid.
|
||||
void set_mesh(const TriangleMesh& mesh);
|
||||
|
||||
void set_negative_mesh(const TriangleMesh &mesh);
|
||||
|
||||
// Inform the MeshClipper about the transformation that transforms the mesh
|
||||
// into world coordinates.
|
||||
void set_transformation(const Geometry::Transformation& trafo);
|
||||
@ -91,6 +93,7 @@ private:
|
||||
|
||||
Geometry::Transformation m_trafo;
|
||||
const TriangleMesh* m_mesh = nullptr;
|
||||
const TriangleMesh* m_negative_mesh = nullptr;
|
||||
ClippingPlane m_plane;
|
||||
std::vector<Vec2f> m_triangles2d;
|
||||
GLIndexedVertexArray m_vertex_array;
|
||||
|
@ -1,11 +1,9 @@
|
||||
#include "NotificationManager.hpp"
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "GLCanvas3D.hpp"
|
||||
#include "ImGuiWrapper.hpp"
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include "ImGuiWrapper.hpp"
|
||||
#include "PrintHostDialogs.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
@ -22,9 +20,6 @@ static constexpr float SPACE_RIGHT_PANEL = 10.0f;
|
||||
static constexpr float FADING_OUT_DURATION = 2.0f;
|
||||
// Time in Miliseconds after next render when fading out is requested
|
||||
static constexpr int FADING_OUT_TIMEOUT = 100;
|
||||
// If timeout is changed to higher than 1 second, substract_time call should be revorked
|
||||
//static constexpr int MAX_TIMEOUT_MILISECONDS = 1000;
|
||||
//static constexpr int MAX_TIMEOUT_SECONDS = 1;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
@ -131,35 +126,35 @@ void NotificationManager::NotificationIDProvider::release_id(int) {}
|
||||
NotificationManager::PopNotification::PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler) :
|
||||
m_data (n)
|
||||
, m_id_provider (id_provider)
|
||||
, m_remaining_time (n.duration)
|
||||
, m_last_remaining_time (n.duration)
|
||||
, m_counting_down (n.duration != 0)
|
||||
, m_text1 (n.text1)
|
||||
, m_hypertext (n.hypertext)
|
||||
, m_text2 (n.text2)
|
||||
, m_evt_handler (evt_handler)
|
||||
, m_notification_start (GLCanvas3D::timestamp_now())
|
||||
{
|
||||
//init();
|
||||
}
|
||||
{}
|
||||
|
||||
void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width)
|
||||
{
|
||||
if (!m_initialized)
|
||||
|
||||
if (m_state == EState::Unknown)
|
||||
init();
|
||||
|
||||
if (m_hidden) {
|
||||
if (m_state == EState::Hidden) {
|
||||
m_top_y = initial_y - GAP_WIDTH;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_fading_out)
|
||||
m_last_render_fading = GLCanvas3D::timestamp_now();
|
||||
if (m_state == EState::ClosePending || m_state == EState::Finished)
|
||||
{
|
||||
m_state = EState::Finished;
|
||||
return;
|
||||
}
|
||||
|
||||
Size cnv_size = canvas.get_canvas_size();
|
||||
Size cnv_size = canvas.get_canvas_size();
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
ImVec2 mouse_pos = ImGui::GetMousePos();
|
||||
float right_gap = SPACE_RIGHT_PANEL + (move_from_overlay ? overlay_width + m_line_height * 5 : 0);
|
||||
ImVec2 mouse_pos = ImGui::GetMousePos();
|
||||
float right_gap = SPACE_RIGHT_PANEL + (move_from_overlay ? overlay_width + m_line_height * 5 : 0);
|
||||
bool fading_pop = false;
|
||||
|
||||
if (m_line_height != ImGui::CalcTextSize("A").y)
|
||||
init();
|
||||
@ -174,54 +169,46 @@ void NotificationManager::PopNotification::render(GLCanvas3D& canvas, float init
|
||||
imgui.set_next_window_size(m_window_width, m_window_height, ImGuiCond_Always);
|
||||
|
||||
// find if hovered
|
||||
m_hovered = false;
|
||||
if (m_state == EState::Hovered)
|
||||
m_state = EState::Shown;
|
||||
|
||||
if (mouse_pos.x < win_pos.x && mouse_pos.x > win_pos.x - m_window_width && mouse_pos.y > win_pos.y && mouse_pos.y < win_pos.y + m_window_height) {
|
||||
ImGui::SetNextWindowFocus();
|
||||
m_hovered = true;
|
||||
m_state = EState::Hovered;
|
||||
}
|
||||
|
||||
|
||||
// color change based on fading out
|
||||
bool fading_pop = false;
|
||||
if (m_fading_out) {
|
||||
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), m_fading_out, m_current_fade_opacity);
|
||||
if (m_state == EState::FadingOut) {
|
||||
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), true, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_Text, ImGui::GetStyleColorVec4(ImGuiCol_Text), true, m_current_fade_opacity);
|
||||
fading_pop = true;
|
||||
}
|
||||
|
||||
|
||||
// background color
|
||||
if (m_is_gray) {
|
||||
ImVec4 backcolor(0.7f, 0.7f, 0.7f, 0.5f);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
}
|
||||
else if (m_data.level == NotificationLevel::ErrorNotification) {
|
||||
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
|
||||
backcolor.x += 0.3f;
|
||||
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
}
|
||||
else if (m_data.level == NotificationLevel::WarningNotification) {
|
||||
ImVec4 backcolor = ImGui::GetStyleColorVec4(ImGuiCol_WindowBg);
|
||||
backcolor.x += 0.3f;
|
||||
backcolor.y += 0.15f;
|
||||
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_fading_out, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_WindowBg, backcolor, m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
}
|
||||
|
||||
// name of window - probably indentifies window and is shown so last_end add whitespaces according to id
|
||||
|
||||
// name of window indentifies window - has to be unique string
|
||||
if (m_id == 0)
|
||||
m_id = m_id_provider.allocate_id();
|
||||
std::string name = "!!Ntfctn" + std::to_string(m_id);
|
||||
|
||||
if (imgui.begin(name, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoScrollbar)) {
|
||||
ImVec2 win_size = ImGui::GetWindowSize();
|
||||
|
||||
//FIXME: dont forget to us this for texts
|
||||
//GUI::format(_utf8(L()));
|
||||
|
||||
/*
|
||||
//countdown numbers
|
||||
ImGui::SetCursorPosX(15);
|
||||
ImGui::SetCursorPosY(15);
|
||||
imgui.text(std::to_string(m_remaining_time).c_str());
|
||||
*/
|
||||
|
||||
render_left_sign(imgui);
|
||||
render_text(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y);
|
||||
render_close_button(imgui, win_size.x, win_size.y, win_pos.x, win_pos.y);
|
||||
@ -253,9 +240,14 @@ void NotificationManager::PopNotification::count_spaces()
|
||||
m_window_width_offset = m_left_indentation + m_line_height * 3.f;
|
||||
m_window_width = m_line_height * 25;
|
||||
}
|
||||
|
||||
void NotificationManager::PopNotification::init()
|
||||
{
|
||||
std::string text = m_text1 + " " + m_hypertext;
|
||||
// Do not init closing notification
|
||||
if (is_finished())
|
||||
return;
|
||||
|
||||
std::string text = m_text1 + " " + m_hypertext;
|
||||
size_t last_end = 0;
|
||||
m_lines_count = 0;
|
||||
|
||||
@ -306,7 +298,9 @@ void NotificationManager::PopNotification::init()
|
||||
}
|
||||
if (m_lines_count == 3)
|
||||
m_multiline = true;
|
||||
m_initialized = true;
|
||||
m_notification_start = GLCanvas3D::timestamp_now();
|
||||
//if (m_state != EState::Hidden)
|
||||
// m_state = EState::Shown;
|
||||
}
|
||||
void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& imgui)
|
||||
{
|
||||
@ -375,12 +369,14 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons
|
||||
ImGui::SetCursorPosY(win_size.y / 2 - win_size.y / 6 - m_line_height / 2);
|
||||
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
|
||||
// line2
|
||||
std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
|
||||
cursor_y = win_size.y / 2 + win_size.y / 6 - m_line_height / 2;
|
||||
ImGui::SetCursorPosX(x_offset);
|
||||
ImGui::SetCursorPosY(cursor_y);
|
||||
imgui.text(line.c_str());
|
||||
cursor_x = x_offset + ImGui::CalcTextSize(line.c_str()).x;
|
||||
if (m_text1.length() > m_endlines[0]) {
|
||||
std::string line = m_text1.substr(m_endlines[0] + (m_text1[m_endlines[0]] == '\n' || m_text1[m_endlines[0]] == ' ' ? 1 : 0));
|
||||
cursor_y = win_size.y / 2 + win_size.y / 6 - m_line_height / 2;
|
||||
ImGui::SetCursorPosX(x_offset);
|
||||
ImGui::SetCursorPosY(cursor_y);
|
||||
imgui.text(line.c_str());
|
||||
cursor_x = x_offset + ImGui::CalcTextSize(line.c_str()).x;
|
||||
}
|
||||
} else {
|
||||
ImGui::SetCursorPosX(x_offset);
|
||||
ImGui::SetCursorPosY(cursor_y);
|
||||
@ -423,8 +419,8 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
|
||||
m_multiline = true;
|
||||
set_next_window_size(imgui);
|
||||
}
|
||||
else {
|
||||
m_close_pending = on_text_click();
|
||||
else if (on_text_click()) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
@ -432,12 +428,12 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
//hover color
|
||||
ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f);//ImGui::GetStyleColorVec4(ImGuiCol_Button);
|
||||
ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f);
|
||||
if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly))
|
||||
orange_color.y += 0.2f;
|
||||
|
||||
//text
|
||||
Notifications_Internal::push_style_color(ImGuiCol_Text, orange_color, m_fading_out, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_Text, orange_color, m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
ImGui::SetCursorPosX(text_x);
|
||||
ImGui::SetCursorPosY(text_y);
|
||||
imgui.text(text.c_str());
|
||||
@ -448,7 +444,7 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
|
||||
lineEnd.y -= 2;
|
||||
ImVec2 lineStart = lineEnd;
|
||||
lineStart.x = ImGui::GetItemRectMin().x;
|
||||
ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (int)(orange_color.w * 255.f * (m_fading_out ? m_current_fade_opacity : 1.f))));
|
||||
ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (int)(orange_color.w * 255.f * (m_state == EState::FadingOut ? m_current_fade_opacity : 1.f))));
|
||||
|
||||
}
|
||||
|
||||
@ -458,12 +454,11 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
|
||||
ImVec2 win_pos(win_pos_x, win_pos_y);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||
|
||||
|
||||
//button - if part if treggered
|
||||
std::string button_text;
|
||||
button_text = ImGui::CloseNotifButton;
|
||||
|
||||
@ -479,7 +474,7 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
|
||||
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
|
||||
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
|
||||
{
|
||||
m_close_pending = true;
|
||||
close();
|
||||
}
|
||||
|
||||
//invisible large button
|
||||
@ -487,7 +482,7 @@ void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& img
|
||||
ImGui::SetCursorPosY(0);
|
||||
if (imgui.button(" ", m_line_height * 2.125, win_size.y - ( m_minimize_b_visible ? 2 * m_line_height : 0)))
|
||||
{
|
||||
m_close_pending = true;
|
||||
close();
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
@ -510,9 +505,9 @@ void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper&
|
||||
{
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||
Notifications_Internal::push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_fading_out, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_ButtonActive, ImGui::GetStyleColorVec4(ImGuiCol_WindowBg), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
|
||||
|
||||
//button - if part if treggered
|
||||
@ -564,71 +559,62 @@ bool NotificationManager::PopNotification::compare_text(const std::string& text)
|
||||
return false;
|
||||
}
|
||||
|
||||
void NotificationManager::PopNotification::update_state()
|
||||
bool NotificationManager::PopNotification::update_state(bool paused, const int64_t delta)
|
||||
{
|
||||
if (!m_initialized)
|
||||
init();
|
||||
|
||||
m_next_render = std::numeric_limits<int64_t>::max();
|
||||
|
||||
if (m_hidden) {
|
||||
m_state = EState::Hidden;
|
||||
return;
|
||||
if (m_state == EState::Unknown) {
|
||||
init();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_state == EState::Hidden) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t now = GLCanvas3D::timestamp_now();
|
||||
|
||||
if (m_hovered) {
|
||||
// reset fading
|
||||
m_fading_out = false;
|
||||
// reset timers - hovered state is set in render
|
||||
if (m_state == EState::Hovered) {
|
||||
m_current_fade_opacity = 1.0f;
|
||||
m_remaining_time = m_data.duration;
|
||||
m_notification_start = now;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (m_counting_down) {
|
||||
// Timers when not fading
|
||||
} else if (m_state != EState::FadingOut && m_data.duration != 0 && !paused) {
|
||||
int64_t up_time = now - m_notification_start;
|
||||
if (up_time >= m_data.duration * 1000) {
|
||||
m_state = EState::FadingOut;
|
||||
m_fading_start = now;
|
||||
} else {
|
||||
m_next_render = m_data.duration * 1000 - up_time;
|
||||
}
|
||||
}
|
||||
// Timers when fading
|
||||
if (m_state == EState::FadingOut && !paused) {
|
||||
int64_t curr_time = now - m_fading_start;
|
||||
int64_t next_render = FADING_OUT_TIMEOUT - delta;
|
||||
m_current_fade_opacity = std::clamp(1.0f - 0.001f * static_cast<float>(curr_time) / FADING_OUT_DURATION, 0.0f, 1.0f);
|
||||
if (m_current_fade_opacity <= 0.0f) {
|
||||
m_state = EState::Finished;
|
||||
return true;
|
||||
} else if (next_render <= 20) {
|
||||
m_next_render = FADING_OUT_TIMEOUT;
|
||||
return true;
|
||||
} else {
|
||||
m_next_render = next_render;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_fading_out && m_current_fade_opacity <= 0.0f)
|
||||
m_finished = true;
|
||||
else if (!m_fading_out && /*m_remaining_time <=0*/up_time >= m_data.duration * 1000) {
|
||||
m_fading_out = true;
|
||||
m_fading_start = now;
|
||||
m_last_render_fading = now;
|
||||
} else if (!m_fading_out) {
|
||||
m_next_render = m_data.duration * 1000 - up_time;//std::min<int64_t>(/*m_data.duration * 1000 - up_time*/m_remaining_time * 1000, MAX_TIMEOUT_MILISECONDS);
|
||||
}
|
||||
|
||||
if (m_state == EState::Finished) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_finished) {
|
||||
|
||||
if (m_state == EState::ClosePending) {
|
||||
m_state = EState::Finished;
|
||||
m_next_render = 0;
|
||||
return;
|
||||
}
|
||||
if (m_close_pending) {
|
||||
m_finished = true;
|
||||
m_state = EState::ClosePending;
|
||||
m_next_render = 0;
|
||||
return;
|
||||
}
|
||||
if (m_fading_out) {
|
||||
if (!m_paused) {
|
||||
m_state = EState::FadingOutStatic;
|
||||
int64_t curr_time = now - m_fading_start;
|
||||
int64_t no_render_time = now - m_last_render_fading;
|
||||
m_current_fade_opacity = std::clamp(1.0f - 0.001f * static_cast<float>(curr_time) / FADING_OUT_DURATION, 0.0f, 1.0f);
|
||||
auto next_render = FADING_OUT_TIMEOUT - no_render_time;
|
||||
if (next_render <= 0) {
|
||||
//m_last_render_fading = GLCanvas3D::timestamp_now();
|
||||
m_state = EState::FadingOutRender;
|
||||
m_next_render = 0;
|
||||
} else
|
||||
m_next_render = next_render;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
NotificationManager::SlicingCompleteLargeNotification::SlicingCompleteLargeNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool large) :
|
||||
@ -672,9 +658,11 @@ void NotificationManager::SlicingCompleteLargeNotification::set_print_info(const
|
||||
void NotificationManager::SlicingCompleteLargeNotification::set_large(bool l)
|
||||
{
|
||||
m_is_large = l;
|
||||
m_counting_down = !l;
|
||||
//FIXME this information should not be lost (change m_data?)
|
||||
// m_counting_down = !l;
|
||||
m_hypertext = l ? _u8L("Export G-Code.") : std::string();
|
||||
m_hidden = !l;
|
||||
m_state = l ? EState::Shown : EState::Hidden;
|
||||
init();
|
||||
}
|
||||
//---------------ExportFinishedNotification-----------
|
||||
void NotificationManager::ExportFinishedNotification::count_spaces()
|
||||
@ -733,8 +721,8 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
|
||||
ImVec2 win_pos(win_pos_x, win_pos_y);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_fading_out, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_fading_out, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||
|
||||
std::string button_text;
|
||||
@ -768,7 +756,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
|
||||
assert(m_evt_handler != nullptr);
|
||||
if (m_evt_handler != nullptr)
|
||||
wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED));
|
||||
m_close_pending = true;
|
||||
close();
|
||||
}
|
||||
|
||||
//invisible large button
|
||||
@ -779,7 +767,7 @@ void NotificationManager::ExportFinishedNotification::render_eject_button(ImGuiW
|
||||
assert(m_evt_handler != nullptr);
|
||||
if (m_evt_handler != nullptr)
|
||||
wxPostEvent(m_evt_handler, EjectDriveNotificationClickedEvent(EVT_EJECT_DRIVE_NOTIFICAION_CLICKED));
|
||||
m_close_pending = true;
|
||||
close();
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
@ -799,30 +787,157 @@ void NotificationManager::ProgressBarNotification::init()
|
||||
m_lines_count++;
|
||||
m_endlines.push_back(m_endlines.back());
|
||||
}
|
||||
void NotificationManager::ProgressBarNotification::count_spaces()
|
||||
{
|
||||
//determine line width
|
||||
m_line_height = ImGui::CalcTextSize("A").y;
|
||||
|
||||
m_left_indentation = m_line_height;
|
||||
if (m_data.level == NotificationLevel::ErrorNotification || m_data.level == NotificationLevel::WarningNotification) {
|
||||
std::string text;
|
||||
text = (m_data.level == NotificationLevel::ErrorNotification ? ImGui::ErrorMarker : ImGui::WarningMarker);
|
||||
float picture_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
m_left_indentation = picture_width + m_line_height / 2;
|
||||
}
|
||||
m_window_width_offset = m_line_height * (m_has_cancel_button ? 6 : 4);
|
||||
m_window_width = m_line_height * 25;
|
||||
}
|
||||
|
||||
void NotificationManager::ProgressBarNotification::render_text(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
PopNotification::render_text(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
// line1 - we do not print any more text than what fits on line 1. Line 2 is bar.
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(win_size_y / 2 - win_size_y / 6 - m_line_height / 2);
|
||||
imgui.text(m_text1.substr(0, m_endlines[0]).c_str());
|
||||
if (m_has_cancel_button)
|
||||
render_cancel_button(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
|
||||
}
|
||||
void NotificationManager::ProgressBarNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f);
|
||||
float invisible_length = 0;//((float)(m_data.duration - m_remaining_time) / (float)m_data.duration * win_size_x);
|
||||
//invisible_length -= win_size_x / ((float)m_data.duration * 60.f) * (60 - m_countdown_frame);
|
||||
ImVec2 lineEnd = ImVec2(win_pos_x - invisible_length - m_window_width_offset, win_pos_y + win_size_y/2 + m_line_height / 2);
|
||||
ImVec2 lineStart = ImVec2(win_pos_x - win_size_x + m_left_indentation, win_pos_y + win_size_y/2 + m_line_height / 2);
|
||||
ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (1.0f * 255.f)), m_line_height * 0.7f);
|
||||
/*
|
||||
//countdown line
|
||||
ImVec4 orange_color = ImGui::GetStyleColorVec4(ImGuiCol_Button);
|
||||
float invisible_length = ((float)(m_data.duration - m_remaining_time) / (float)m_data.duration * win_size_x);
|
||||
invisible_length -= win_size_x / ((float)m_data.duration * 60.f) * (60 - m_countdown_frame);
|
||||
ImVec2 lineEnd = ImVec2(win_pos_x - invisible_length, win_pos_y + win_size_y - 5);
|
||||
ImVec2 lineStart = ImVec2(win_pos_x - win_size_x, win_pos_y + win_size_y - 5);
|
||||
ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (int)(orange_color.picture_width * 255.f * (m_fading_out ? m_current_fade_opacity : 1.f))), 2.f);
|
||||
if (!m_paused)
|
||||
m_countdown_frame++;
|
||||
*/
|
||||
ImVec4 orange_color = ImVec4(.99f, .313f, .0f, 1.0f);
|
||||
ImVec4 gray_color = ImVec4(.34f, .34f, .34f, 1.0f);
|
||||
ImVec2 lineEnd = ImVec2(win_pos_x - m_window_width_offset, win_pos_y + win_size_y / 2 + m_line_height / 4);
|
||||
ImVec2 lineStart = ImVec2(win_pos_x - win_size_x + m_left_indentation, win_pos_y + win_size_y / 2 + m_line_height / 4);
|
||||
ImVec2 midPoint = ImVec2(lineStart.x + (lineEnd.x - lineStart.x) * m_percentage, lineStart.y);
|
||||
ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, IM_COL32((int)(gray_color.x * 255), (int)(gray_color.y * 255), (int)(gray_color.z * 255), (1.0f * 255.f)), m_line_height * 0.2f);
|
||||
ImGui::GetWindowDrawList()->AddLine(lineStart, midPoint, IM_COL32((int)(orange_color.x * 255), (int)(orange_color.y * 255), (int)(orange_color.z * 255), (1.0f * 255.f)), m_line_height * 0.2f);
|
||||
}
|
||||
//------PrintHostUploadNotification----------------
|
||||
void NotificationManager::PrintHostUploadNotification::set_percentage(float percent)
|
||||
{
|
||||
if (m_uj_state == UploadJobState::PB_CANCELLED)
|
||||
return;
|
||||
m_percentage = percent;
|
||||
if (percent >= 1.0f) {
|
||||
m_uj_state = UploadJobState::PB_COMPLETED;
|
||||
m_has_cancel_button = false;
|
||||
} else if (percent < 0.0f) {
|
||||
error();
|
||||
} else {
|
||||
m_uj_state = UploadJobState::PB_PROGRESS;
|
||||
m_has_cancel_button = true;
|
||||
}
|
||||
}
|
||||
void NotificationManager::PrintHostUploadNotification::render_bar(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
std::string text;
|
||||
switch (m_uj_state) {
|
||||
case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_PROGRESS:
|
||||
{
|
||||
ProgressBarNotification::render_bar(imgui, win_size_x, win_size_y, win_pos_x, win_pos_y);
|
||||
float uploaded = m_file_size / 100 * m_percentage;
|
||||
std::stringstream stream;
|
||||
stream << std::fixed << std::setprecision(2) << (int)(m_percentage * 100) << "% - " << uploaded << " of " << m_file_size << "MB uploaded";
|
||||
text = stream.str();
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 /*- m_line_height / 4 * 3*/);
|
||||
break;
|
||||
}
|
||||
case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_ERROR:
|
||||
text = _u8L("ERROR");
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2);
|
||||
break;
|
||||
case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_CANCELLED:
|
||||
text = _u8L("CANCELED");
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2);
|
||||
break;
|
||||
case Slic3r::GUI::NotificationManager::PrintHostUploadNotification::UploadJobState::PB_COMPLETED:
|
||||
text = _u8L("COMPLETED");
|
||||
ImGui::SetCursorPosX(m_left_indentation);
|
||||
ImGui::SetCursorPosY(win_size_y / 2 + win_size_y / 6 - m_line_height / 2);
|
||||
break;
|
||||
}
|
||||
|
||||
imgui.text(text.c_str());
|
||||
|
||||
}
|
||||
void NotificationManager::PrintHostUploadNotification::render_cancel_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
ImVec2 win_size(win_size_x, win_size_y);
|
||||
ImVec2 win_pos(win_pos_x, win_pos_y);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
|
||||
Notifications_Internal::push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
Notifications_Internal::push_style_color(ImGuiCol_TextSelectedBg, ImVec4(0, .75f, .75f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
|
||||
|
||||
std::string button_text;
|
||||
button_text = ImGui::CancelButton;
|
||||
|
||||
if (ImGui::IsMouseHoveringRect(ImVec2(win_pos.x - m_line_height * 5.f, win_pos.y),
|
||||
ImVec2(win_pos.x - m_line_height * 2.5f, win_pos.y + win_size.y),
|
||||
true))
|
||||
{
|
||||
button_text = ImGui::CancelHoverButton;
|
||||
// tooltip
|
||||
long time_now = wxGetLocalTime();
|
||||
if (m_hover_time > 0 && m_hover_time < time_now) {
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND);
|
||||
ImGui::BeginTooltip();
|
||||
imgui.text(_u8L("Cancel upload") + " " + GUI::shortkey_ctrl_prefix() + "T");
|
||||
ImGui::EndTooltip();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
if (m_hover_time == 0)
|
||||
m_hover_time = time_now;
|
||||
}
|
||||
else
|
||||
m_hover_time = 0;
|
||||
|
||||
ImVec2 button_pic_size = ImGui::CalcTextSize(button_text.c_str());
|
||||
ImVec2 button_size(button_pic_size.x * 1.25f, button_pic_size.y * 1.25f);
|
||||
ImGui::SetCursorPosX(win_size.x - m_line_height * 5.0f);
|
||||
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y);
|
||||
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
|
||||
{
|
||||
assert(m_evt_handler != nullptr);
|
||||
if (m_evt_handler != nullptr) {
|
||||
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_CANCEL, m_job_id, m_job_id);
|
||||
wxQueueEvent(m_evt_handler, evt);
|
||||
}
|
||||
}
|
||||
|
||||
//invisible large button
|
||||
ImGui::SetCursorPosX(win_size.x - m_line_height * 4.625f);
|
||||
ImGui::SetCursorPosY(0);
|
||||
if (imgui.button(" ", m_line_height * 2.f, win_size.y))
|
||||
{
|
||||
assert(m_evt_handler != nullptr);
|
||||
if (m_evt_handler != nullptr) {
|
||||
auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_CANCEL, m_job_id, m_job_id);
|
||||
wxQueueEvent(m_evt_handler, evt);
|
||||
}
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
}
|
||||
//------NotificationManager--------
|
||||
NotificationManager::NotificationManager(wxEvtHandler* evt_handler) :
|
||||
@ -881,12 +996,7 @@ void NotificationManager::push_plater_error_notification(const std::string& text
|
||||
{
|
||||
push_notification_data({ NotificationType::PlaterError, NotificationLevel::ErrorNotification, 0, _u8L("ERROR:") + "\n" + text }, 0);
|
||||
}
|
||||
void NotificationManager::push_plater_warning_notification(const std::string& text)
|
||||
{
|
||||
push_notification_data({ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text }, 0);
|
||||
// dissaper if in preview
|
||||
set_in_preview(m_in_preview);
|
||||
}
|
||||
|
||||
void NotificationManager::close_plater_error_notification(const std::string& text)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification> ¬ification : m_pop_notifications) {
|
||||
@ -895,11 +1005,32 @@ void NotificationManager::close_plater_error_notification(const std::string& tex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::push_plater_warning_notification(const std::string& text)
|
||||
{
|
||||
// Find if was not hidden
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::PlaterWarning && notification->compare_text(_u8L("WARNING:") + "\n" + text)) {
|
||||
if (notification->get_state() == PopNotification::EState::Hidden) {
|
||||
//dynamic_cast<PlaterWarningNotification*>(notification.get())->show();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NotificationData data{ NotificationType::PlaterWarning, NotificationLevel::WarningNotification, 0, _u8L("WARNING:") + "\n" + text };
|
||||
|
||||
auto notification = std::make_unique<NotificationManager::PlaterWarningNotification>(data, m_id_provider, m_evt_handler);
|
||||
push_notification_data(std::move(notification), 0);
|
||||
// dissaper if in preview
|
||||
set_in_preview(m_in_preview);
|
||||
}
|
||||
|
||||
void NotificationManager::close_plater_warning_notification(const std::string& text)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification> ¬ification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::PlaterWarning && notification->compare_text(_u8L("WARNING:") + "\n" + text)) {
|
||||
notification->close();
|
||||
dynamic_cast<PlaterWarningNotification*>(notification.get())->real_close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -997,23 +1128,50 @@ void NotificationManager::push_exporting_finished_notification(const std::string
|
||||
NotificationData data{ NotificationType::ExportFinished, NotificationLevel::RegularNotification, on_removable ? 0 : 20, _u8L("Exporting finished.") + "\n" + path };
|
||||
push_notification_data(std::make_unique<NotificationManager::ExportFinishedNotification>(data, m_id_provider, m_evt_handler, on_removable, path, dir_path), 0);
|
||||
}
|
||||
void NotificationManager::push_progress_bar_notification(const std::string& text, float percentage)
|
||||
|
||||
void NotificationManager::push_upload_job_notification(wxEvtHandler* evt_handler, int id, float filesize, const std::string& filename, const std::string& host, float percentage)
|
||||
{
|
||||
NotificationData data{ NotificationType::ProgressBar, NotificationLevel::ProgressBarNotification, 0, text };
|
||||
push_notification_data(std::make_unique<NotificationManager::ProgressBarNotification>(data, m_id_provider, m_evt_handler, 0), 0);
|
||||
std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
|
||||
NotificationData data{ NotificationType::PrintHostUpload, NotificationLevel::ProgressBarNotification, 0, text };
|
||||
push_notification_data(std::make_unique<NotificationManager::PrintHostUploadNotification>(data, m_id_provider, evt_handler, 0, id, filesize), 0);
|
||||
}
|
||||
void NotificationManager::set_progress_bar_percentage(const std::string& text, float percentage)
|
||||
void NotificationManager::set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage)
|
||||
{
|
||||
bool found = false;
|
||||
std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
|
||||
// bool found = false;
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::ProgressBar && notification->compare_text(text)) {
|
||||
dynamic_cast<ProgressBarNotification*>(notification.get())->set_percentage(percentage);
|
||||
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame();
|
||||
found = true;
|
||||
dynamic_cast<PrintHostUploadNotification*>(notification.get())->set_percentage(percentage);
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
// found = true;
|
||||
}
|
||||
}
|
||||
/*
|
||||
if (!found) {
|
||||
push_progress_bar_notification(text, percentage);
|
||||
push_upload_job_notification(id, filename, host, percentage);
|
||||
}
|
||||
*/
|
||||
}
|
||||
void NotificationManager::upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host)
|
||||
{
|
||||
std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) {
|
||||
dynamic_cast<PrintHostUploadNotification*>(notification.get())->cancel();
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void NotificationManager::upload_job_notification_show_error(int id, const std::string& filename, const std::string& host)
|
||||
{
|
||||
std::string text = PrintHostUploadNotification::get_upload_job_text(id, filename, host);
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::PrintHostUpload && notification->compare_text(text)) {
|
||||
dynamic_cast<PrintHostUploadNotification*>(notification.get())->error();
|
||||
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool NotificationManager::push_notification_data(const NotificationData& notification_data, int timestamp)
|
||||
@ -1035,20 +1193,19 @@ bool NotificationManager::push_notification_data(std::unique_ptr<NotificationMan
|
||||
|
||||
if (this->activate_existing(notification.get())) {
|
||||
m_pop_notifications.back()->update(notification->get_data());
|
||||
canvas.request_extra_frame_delayed(33);
|
||||
canvas.schedule_extra_frame(0);
|
||||
return false;
|
||||
} else {
|
||||
m_pop_notifications.emplace_back(std::move(notification));
|
||||
canvas.request_extra_frame_delayed(33);
|
||||
canvas.schedule_extra_frame(0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::render_notifications(float overlay_width)
|
||||
void NotificationManager::render_notifications(GLCanvas3D& canvas, float overlay_width)
|
||||
{
|
||||
sort_notifications();
|
||||
|
||||
GLCanvas3D& canvas = *wxGetApp().plater()->get_current_canvas3D();
|
||||
|
||||
float last_y = 0.0f;
|
||||
|
||||
for (const auto& notification : m_pop_notifications) {
|
||||
@ -1059,7 +1216,49 @@ void NotificationManager::render_notifications(float overlay_width)
|
||||
}
|
||||
|
||||
}
|
||||
update_notifications();
|
||||
m_last_render = GLCanvas3D::timestamp_now();
|
||||
}
|
||||
|
||||
bool NotificationManager::update_notifications(GLCanvas3D& canvas)
|
||||
{
|
||||
// no update if not top window
|
||||
wxWindow* p = dynamic_cast<wxWindow*>(wxGetApp().plater());
|
||||
while (p->GetParent() != nullptr)
|
||||
p = p->GetParent();
|
||||
wxTopLevelWindow* top_level_wnd = dynamic_cast<wxTopLevelWindow*>(p);
|
||||
if (!top_level_wnd->IsActive())
|
||||
return false;
|
||||
|
||||
// next_render() returns numeric_limits::max if no need for frame
|
||||
const int64_t max = std::numeric_limits<int64_t>::max();
|
||||
int64_t next_render = max;
|
||||
const int64_t time_since_render = GLCanvas3D::timestamp_now() - m_last_render;
|
||||
bool request_render = false;
|
||||
// During render, each notification detects if its currently hovered and changes its state to EState::Hovered
|
||||
// If any notification is hovered, all restarts its countdown
|
||||
bool hover = false;
|
||||
for (const std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->is_hovered()) {
|
||||
hover = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// update state of all notif and erase finished
|
||||
for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) {
|
||||
std::unique_ptr<PopNotification>& notification = *it;
|
||||
request_render |= notification->update_state(hover, time_since_render);
|
||||
next_render = std::min<int64_t>(next_render, notification->next_render());
|
||||
if (notification->get_state() == PopNotification::EState::Finished)
|
||||
it = m_pop_notifications.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
// request next frame in future
|
||||
if (next_render < max)
|
||||
canvas.schedule_extra_frame(int(next_render));
|
||||
|
||||
return request_render;
|
||||
}
|
||||
|
||||
void NotificationManager::sort_notifications()
|
||||
@ -1080,9 +1279,11 @@ bool NotificationManager::activate_existing(const NotificationManager::PopNotifi
|
||||
const std::string &new_text = notification->get_data().text1;
|
||||
for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end(); ++it) {
|
||||
if ((*it)->get_type() == new_type && !(*it)->is_finished()) {
|
||||
if (new_type == NotificationType::CustomNotification || new_type == NotificationType::PlaterWarning) {
|
||||
if (!(*it)->compare_text(new_text))
|
||||
if (std::find(m_multiple_types.begin(), m_multiple_types.end(), new_type) != m_multiple_types.end()) {
|
||||
// If found same type and same text, return true - update will be performed on the old notif
|
||||
if ((*it)->compare_text(new_text) == false) {
|
||||
continue;
|
||||
}
|
||||
} else if (new_type == NotificationType::SlicingWarning) {
|
||||
auto w1 = dynamic_cast<const SlicingWarningNotification*>(notification);
|
||||
auto w2 = dynamic_cast<const SlicingWarningNotification*>(it->get());
|
||||
@ -1094,7 +1295,6 @@ bool NotificationManager::activate_existing(const NotificationManager::PopNotifi
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (it != m_pop_notifications.end() - 1)
|
||||
std::rotate(it, it + 1, m_pop_notifications.end());
|
||||
return true;
|
||||
@ -1108,107 +1308,12 @@ void NotificationManager::set_in_preview(bool preview)
|
||||
m_in_preview = preview;
|
||||
for (std::unique_ptr<PopNotification> ¬ification : m_pop_notifications) {
|
||||
if (notification->get_type() == NotificationType::PlaterWarning)
|
||||
notification->hide(preview);
|
||||
notification->hide(preview);
|
||||
if (notification->get_type() == NotificationType::SignDetected)
|
||||
notification->hide(!preview);
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::update_notifications()
|
||||
{
|
||||
// no update if not top window
|
||||
wxWindow* p = dynamic_cast<wxWindow*>(wxGetApp().plater());
|
||||
while (p->GetParent() != nullptr)
|
||||
p = p->GetParent();
|
||||
wxTopLevelWindow* top_level_wnd = dynamic_cast<wxTopLevelWindow*>(p);
|
||||
if (!top_level_wnd->IsActive())
|
||||
return;
|
||||
|
||||
//static size_t last_size = m_pop_notifications.size();
|
||||
|
||||
//request frames
|
||||
int64_t next_render = std::numeric_limits<int64_t>::max();
|
||||
for (auto it = m_pop_notifications.begin(); it != m_pop_notifications.end();) {
|
||||
std::unique_ptr<PopNotification>& notification = *it;
|
||||
notification->set_paused(m_hovered);
|
||||
notification->update_state();
|
||||
next_render = std::min<int64_t>(next_render, notification->next_render());
|
||||
if (notification->get_state() == PopNotification::EState::Finished)
|
||||
it = m_pop_notifications.erase(it);
|
||||
else {
|
||||
|
||||
++it;
|
||||
}
|
||||
}
|
||||
/*
|
||||
m_requires_update = false;
|
||||
for (const std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->requires_update()) {
|
||||
m_requires_update = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
// update hovering state
|
||||
m_hovered = false;
|
||||
for (const std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->is_hovered()) {
|
||||
m_hovered = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Reuire render if some notification was just deleted.
|
||||
size_t curr_size = m_pop_notifications.size();
|
||||
m_requires_render = m_hovered || (last_size != curr_size);
|
||||
last_size = curr_size;
|
||||
|
||||
// Ask notification if it needs render
|
||||
if (!m_requires_render) {
|
||||
for (const std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
|
||||
if (notification->requires_render()) {
|
||||
m_requires_render = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Make sure there will be update after last notification erased
|
||||
if (m_requires_render)
|
||||
m_requires_update = true;
|
||||
*/
|
||||
|
||||
|
||||
if (next_render == 0)
|
||||
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame_delayed(33); //few milliseconds to get from GLCanvas::render
|
||||
else if (next_render < std::numeric_limits<int64_t>::max())
|
||||
wxGetApp().plater()->get_current_canvas3D()->request_extra_frame_delayed(int(next_render));
|
||||
|
||||
/*
|
||||
// actualizate timers
|
||||
wxWindow* p = dynamic_cast<wxWindow*>(wxGetApp().plater());
|
||||
while (p->GetParent() != nullptr)
|
||||
p = p->GetParent();
|
||||
wxTopLevelWindow* top_level_wnd = dynamic_cast<wxTopLevelWindow*>(p);
|
||||
if (!top_level_wnd->IsActive())
|
||||
return;
|
||||
|
||||
{
|
||||
// Control the fade-out.
|
||||
// time in seconds
|
||||
long now = wxGetLocalTime();
|
||||
// Pausing fade-out when the mouse is over some notification.
|
||||
if (!m_hovered && m_last_time < now) {
|
||||
if (now - m_last_time >= MAX_TIMEOUT_SECONDS) {
|
||||
for (auto& notification : m_pop_notifications) {
|
||||
//if (notification->get_state() != PopNotification::EState::Static)
|
||||
notification->substract_remaining_time(MAX_TIMEOUT_SECONDS);
|
||||
}
|
||||
m_last_time = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
bool NotificationManager::has_slicing_error_notification()
|
||||
{
|
||||
return std::any_of(m_pop_notifications.begin(), m_pop_notifications.end(), [](auto &n) {
|
||||
|
@ -1,6 +1,9 @@
|
||||
#ifndef slic3r_GUI_NotificationManager_hpp_
|
||||
#define slic3r_GUI_NotificationManager_hpp_
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "GLCanvas3D.hpp"
|
||||
#include "Event.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
@ -66,12 +69,16 @@ enum class NotificationType
|
||||
PlaterWarning,
|
||||
// Progress bar instead of text.
|
||||
ProgressBar,
|
||||
// Progress bar with info from Print Host Upload Queue dialog.
|
||||
PrintHostUpload,
|
||||
// Notification, when Color Change G-code is empty and user try to add color change on DoubleSlider.
|
||||
EmptyColorChangeCode,
|
||||
// Notification that custom supports/seams were deleted after mesh repair.
|
||||
CustomSupportsAndSeamRemovedAfterRepair,
|
||||
// Notification that auto adding of color changes is impossible
|
||||
EmptyAutoColorChange,
|
||||
// Notification about detected sign
|
||||
SignDetected,
|
||||
// Notification emitted by Print::validate
|
||||
PrintValidateWarning,
|
||||
// Notification telling user to quit SLA supports manual editing
|
||||
@ -140,21 +147,24 @@ public:
|
||||
// Exporting finished, show this information with path, button to open containing folder and if ejectable - eject button
|
||||
void push_exporting_finished_notification(const std::string& path, const std::string& dir_path, bool on_removable);
|
||||
// notification with progress bar
|
||||
void push_progress_bar_notification(const std::string& text, float percentage = 0);
|
||||
void set_progress_bar_percentage(const std::string& text, float percentage);
|
||||
void push_upload_job_notification(wxEvtHandler* evt_handler, int id, float filesize, const std::string& filename, const std::string& host, float percentage = 0);
|
||||
void set_upload_job_notification_percentage(int id, const std::string& filename, const std::string& host, float percentage);
|
||||
void upload_job_notification_show_canceled(int id, const std::string& filename, const std::string& host);
|
||||
void upload_job_notification_show_error(int id, const std::string& filename, const std::string& host);
|
||||
// Close old notification ExportFinished.
|
||||
void new_export_began(bool on_removable);
|
||||
// finds ExportFinished notification and closes it if it was to removable device
|
||||
void device_ejected();
|
||||
// renders notifications in queue and deletes expired ones
|
||||
void render_notifications(float overlay_width);
|
||||
void render_notifications(GLCanvas3D& canvas, float overlay_width);
|
||||
// finds and closes all notifications of given type
|
||||
void close_notification_of_type(const NotificationType type);
|
||||
// Which view is active? Plater or G-code preview? Hide warnings in G-code preview.
|
||||
void set_in_preview(bool preview);
|
||||
// Move to left to avoid colision with variable layer height gizmo.
|
||||
void set_move_from_overlay(bool move) { m_move_from_overlay = move; }
|
||||
|
||||
// perform update_state on each notification and ask for more frames if needed, return true for render needed
|
||||
bool update_notifications(GLCanvas3D& canvas);
|
||||
private:
|
||||
// duration 0 means not disapearing
|
||||
struct NotificationData {
|
||||
@ -192,23 +202,24 @@ private:
|
||||
|
||||
enum class EState
|
||||
{
|
||||
Unknown,
|
||||
Unknown, // NOT initialized
|
||||
Hidden,
|
||||
FadingOutRender, // Requesting Render
|
||||
FadingOutStatic,
|
||||
Shown, // Requesting Render at some time if duration != 0
|
||||
FadingOut, // Requesting Render at some time
|
||||
ClosePending, // Requesting Render
|
||||
Finished, // Requesting Render
|
||||
Hovered, // Followed by Shown
|
||||
Paused
|
||||
};
|
||||
|
||||
PopNotification(const NotificationData &n, NotificationIDProvider &id_provider, wxEvtHandler* evt_handler);
|
||||
virtual ~PopNotification() { if (m_id) m_id_provider.release_id(m_id); }
|
||||
void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width);
|
||||
virtual void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width);
|
||||
// close will dissapear notification on next render
|
||||
void close() { m_close_pending = true; }
|
||||
virtual void close() { m_state = EState::ClosePending; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0);}
|
||||
// data from newer notification of same type
|
||||
void update(const NotificationData& n);
|
||||
bool is_finished() const { return m_finished || m_close_pending; }
|
||||
bool is_hovered() const { return m_hovered; }
|
||||
bool is_finished() const { return m_state == EState::ClosePending || m_state == EState::Finished; }
|
||||
// returns top after movement
|
||||
float get_top() const { return m_top_y; }
|
||||
//returns top in actual frame
|
||||
@ -216,23 +227,17 @@ private:
|
||||
const NotificationType get_type() const { return m_data.type; }
|
||||
const NotificationData get_data() const { return m_data; }
|
||||
const bool is_gray() const { return m_is_gray; }
|
||||
// Call equals one second down
|
||||
void substract_remaining_time(int seconds) { m_remaining_time -= seconds; }
|
||||
void set_gray(bool g) { m_is_gray = g; }
|
||||
void set_paused(bool p) { m_paused = p; }
|
||||
bool compare_text(const std::string& text);
|
||||
void hide(bool h) { m_hidden = h; }
|
||||
// sets m_next_render with time of next mandatory rendering
|
||||
void update_state();
|
||||
int64_t next_render() const { return m_next_render; }
|
||||
/*
|
||||
bool requires_render() const { return m_state == EState::FadingOutRender || m_state == EState::ClosePending || m_state == EState::Finished; }
|
||||
bool requires_update() const { return m_state != EState::Hidden; }
|
||||
*/
|
||||
EState get_state() const { return m_state; }
|
||||
protected:
|
||||
void hide(bool h) { if (is_finished()) return; m_state = h ? EState::Hidden : EState::Unknown; }
|
||||
// sets m_next_render with time of next mandatory rendering. Delta is time since last render.
|
||||
bool update_state(bool paused, const int64_t delta);
|
||||
int64_t next_render() const { return is_finished() ? 0 : m_next_render; }
|
||||
EState get_state() const { return m_state; }
|
||||
bool is_hovered() const { return m_state == EState::Hovered; }
|
||||
|
||||
// Call after every size change
|
||||
void init();
|
||||
virtual void init();
|
||||
// Part of init()
|
||||
virtual void count_spaces();
|
||||
// Calculetes correct size but not se it in imgui!
|
||||
@ -254,45 +259,36 @@ private:
|
||||
// Hypertext action, returns true if notification should close.
|
||||
// Action is stored in NotificationData::callback as std::function<bool(wxEvtHandler*)>
|
||||
virtual bool on_text_click();
|
||||
|
||||
protected:
|
||||
const NotificationData m_data;
|
||||
|
||||
// For reusing ImGUI windows.
|
||||
NotificationIDProvider &m_id_provider;
|
||||
int m_id{ 0 };
|
||||
|
||||
// State for rendering
|
||||
EState m_state { EState::Unknown };
|
||||
|
||||
int m_id { 0 };
|
||||
bool m_initialized { false };
|
||||
// Time values for rendering fade-out
|
||||
|
||||
int64_t m_fading_start{ 0LL };
|
||||
|
||||
// first appereance of notification or last hover;
|
||||
int64_t m_notification_start;
|
||||
// time to next must-do render
|
||||
int64_t m_next_render{ std::numeric_limits<int64_t>::max() };
|
||||
float m_current_fade_opacity{ 1.0f };
|
||||
|
||||
// Notification data
|
||||
|
||||
// Main text
|
||||
std::string m_text1;
|
||||
// Clickable text
|
||||
std::string m_hypertext;
|
||||
// Aditional text after hypertext - currently not used
|
||||
std::string m_text2;
|
||||
// Countdown variables
|
||||
long m_remaining_time;
|
||||
bool m_counting_down;
|
||||
long m_last_remaining_time;
|
||||
bool m_paused { false };
|
||||
int m_countdown_frame { 0 };
|
||||
bool m_fading_out { false };
|
||||
int64_t m_fading_start { 0LL };
|
||||
// time of last done render when fading
|
||||
int64_t m_last_render_fading { 0LL };
|
||||
// first appereance of notification or last hover;
|
||||
int64_t m_notification_start;
|
||||
// time to next must-do render
|
||||
int64_t m_next_render { std::numeric_limits<int64_t>::max() };
|
||||
float m_current_fade_opacity { 1.0f };
|
||||
// If hidden the notif is alive but not visible to user
|
||||
bool m_hidden { false };
|
||||
// m_finished = true - does not render, marked to delete
|
||||
bool m_finished { false };
|
||||
// Will go to m_finished next render
|
||||
bool m_close_pending { false };
|
||||
bool m_hovered { false };
|
||||
// variables to count positions correctly
|
||||
|
||||
// inner variables to position notification window, texts and buttons correctly
|
||||
|
||||
// all space without text
|
||||
float m_window_width_offset;
|
||||
// Space on left side without text
|
||||
@ -302,9 +298,7 @@ private:
|
||||
float m_window_width { 450.0f };
|
||||
//Distance from bottom of notifications to top of this notification
|
||||
float m_top_y { 0.0f };
|
||||
|
||||
// Height of text
|
||||
// Used as basic scaling unit!
|
||||
// Height of text - Used as basic scaling unit!
|
||||
float m_line_height;
|
||||
std::vector<size_t> m_endlines;
|
||||
// Gray are f.e. eorrors when its uknown if they are still valid
|
||||
@ -322,10 +316,16 @@ private:
|
||||
{
|
||||
public:
|
||||
SlicingCompleteLargeNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, bool largeds);
|
||||
void set_large(bool l);
|
||||
bool get_large() { return m_is_large; }
|
||||
|
||||
void set_print_info(const std::string &info);
|
||||
void set_large(bool l);
|
||||
bool get_large() { return m_is_large; }
|
||||
void set_print_info(const std::string &info);
|
||||
virtual void render(GLCanvas3D& canvas, float initial_y, bool move_from_overlay, float overlay_width)
|
||||
{
|
||||
// This notification is always hidden if !large (means side bar is collapsed)
|
||||
if (!get_large() && !is_finished())
|
||||
m_state = EState::Hidden;
|
||||
PopNotification::render(canvas, initial_y, move_from_overlay, overlay_width);
|
||||
}
|
||||
protected:
|
||||
virtual void render_text(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
@ -344,21 +344,78 @@ private:
|
||||
int warning_step;
|
||||
};
|
||||
|
||||
class PlaterWarningNotification : public PopNotification
|
||||
{
|
||||
public:
|
||||
PlaterWarningNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) : PopNotification(n, id_provider, evt_handler) {}
|
||||
virtual void close() { if(is_finished()) return; m_state = EState::Hidden; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); }
|
||||
void real_close() { m_state = EState::ClosePending; wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(0); }
|
||||
void show() { m_state = EState::Unknown; }
|
||||
};
|
||||
|
||||
|
||||
class ProgressBarNotification : public PopNotification
|
||||
{
|
||||
public:
|
||||
|
||||
ProgressBarNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage) : PopNotification(n, id_provider, evt_handler) { set_percentage(percentage); }
|
||||
void set_percentage(float percent) { m_percentage = percent; if (percent >= 1.0f) m_progress_complete = true; else m_progress_complete = false; }
|
||||
virtual void set_percentage(float percent) { m_percentage = percent; }
|
||||
protected:
|
||||
virtual void init();
|
||||
virtual void init() override;
|
||||
virtual void count_spaces() override;
|
||||
virtual void render_text(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
void render_bar(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
bool m_progress_complete{ false };
|
||||
float m_percentage;
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
virtual void render_bar(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
virtual void render_cancel_button(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y)
|
||||
{}
|
||||
float m_percentage;
|
||||
|
||||
bool m_has_cancel_button {false};
|
||||
// local time of last hover for showing tooltip
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class PrintHostUploadNotification : public ProgressBarNotification
|
||||
{
|
||||
public:
|
||||
enum class UploadJobState
|
||||
{
|
||||
PB_PROGRESS,
|
||||
PB_ERROR,
|
||||
PB_CANCELLED,
|
||||
PB_COMPLETED
|
||||
};
|
||||
PrintHostUploadNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler, float percentage, int job_id, float filesize)
|
||||
:ProgressBarNotification(n, id_provider, evt_handler, percentage)
|
||||
, m_job_id(job_id)
|
||||
, m_file_size(filesize)
|
||||
{
|
||||
m_has_cancel_button = true;
|
||||
}
|
||||
static std::string get_upload_job_text(int id, const std::string& filename, const std::string& host) { return "[" + std::to_string(id) + "] " + filename + " -> " + host; }
|
||||
virtual void set_percentage(float percent);
|
||||
void cancel() { m_uj_state = UploadJobState::PB_CANCELLED; m_has_cancel_button = false; }
|
||||
void error() { m_uj_state = UploadJobState::PB_ERROR; m_has_cancel_button = false; }
|
||||
protected:
|
||||
virtual void render_bar(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
virtual void render_cancel_button(ImGuiWrapper& imgui,
|
||||
const float win_size_x, const float win_size_y,
|
||||
const float win_pos_x, const float win_pos_y);
|
||||
// Identifies job in cancel callback
|
||||
int m_job_id;
|
||||
// Size of uploaded size to be displayed in MB
|
||||
float m_file_size;
|
||||
long m_hover_time{ 0 };
|
||||
UploadJobState m_uj_state{ UploadJobState::PB_PROGRESS };
|
||||
};
|
||||
|
||||
class ExportFinishedNotification : public PopNotification
|
||||
@ -405,32 +462,25 @@ private:
|
||||
void sort_notifications();
|
||||
// If there is some error notification active, then the "Export G-code" notification after the slicing is finished is suppressed.
|
||||
bool has_slicing_error_notification();
|
||||
// perform update_state on each notification and ask for more frames if needed
|
||||
void update_notifications();
|
||||
|
||||
|
||||
// Target for wxWidgets events sent by clicking on the hyperlink available at some notifications.
|
||||
wxEvtHandler* m_evt_handler;
|
||||
// Cache of IDs to identify and reuse ImGUI windows.
|
||||
NotificationIDProvider m_id_provider;
|
||||
std::deque<std::unique_ptr<PopNotification>> m_pop_notifications;
|
||||
// Last render time in seconds for fade out control.
|
||||
long m_last_time { 0 };
|
||||
// When mouse hovers over some notification, the fade-out of all notifications is suppressed.
|
||||
bool m_hovered { false };
|
||||
//timestamps used for slicing finished - notification could be gone so it needs to be stored here
|
||||
std::unordered_set<int> m_used_timestamps;
|
||||
// True if G-code preview is active. False if the Plater is active.
|
||||
bool m_in_preview { false };
|
||||
// True if the layer editing is enabled in Plater, so that the notifications are shifted left of it.
|
||||
bool m_move_from_overlay { false };
|
||||
|
||||
// Timestamp of last rendering
|
||||
int64_t m_last_render { 0LL };
|
||||
// Notification types that can be shown multiple types at once (compared by text)
|
||||
const std::vector<NotificationType> m_multiple_types = { NotificationType::CustomNotification, NotificationType::PlaterWarning, NotificationType::ProgressBar, NotificationType::PrintHostUpload };
|
||||
//prepared (basic) notifications
|
||||
const std::vector<NotificationData> basic_notifications = {
|
||||
// {NotificationType::SlicingNotPossible, NotificationLevel::RegularNotification, 10, _u8L("Slicing is not possible.")},
|
||||
// {NotificationType::ExportToRemovableFinished, NotificationLevel::ImportantNotification, 0, _u8L("Exporting finished."), _u8L("Eject drive.") },
|
||||
{NotificationType::Mouse3dDisconnected, NotificationLevel::RegularNotification, 10, _u8L("3D Mouse disconnected.") },
|
||||
// {NotificationType::Mouse3dConnected, NotificationLevel::RegularNotification, 5, _u8L("3D Mouse connected.") },
|
||||
// {NotificationType::NewPresetsAviable, NotificationLevel::ImportantNotification, 20, _u8L("New Presets are available."), _u8L("See here.") },
|
||||
{NotificationType::PresetUpdateAvailable, NotificationLevel::ImportantNotification, 20, _u8L("Configuration update is available."), _u8L("See more."),
|
||||
[](wxEvtHandler* evnthndlr) {
|
||||
if (evnthndlr != nullptr)
|
||||
|
@ -91,7 +91,7 @@ void OG_CustomCtrl::init_ctrl_lines()
|
||||
height = m_bmp_blinking_sz.GetHeight() + m_v_gap;
|
||||
ctrl_lines.emplace_back(CtrlLine(height, this, line, true));
|
||||
}
|
||||
else if (opt_group->label_width != 0 && (!line.label.IsEmpty() || option_set.front().opt.gui_type == "legend") )
|
||||
else if (opt_group->label_width != 0 && (!line.label.IsEmpty() || option_set.front().opt.gui_type == ConfigOptionDef::GUIType::legend) )
|
||||
{
|
||||
wxSize label_sz = GetTextExtent(line.label);
|
||||
height = label_sz.y * (label_sz.GetWidth() > int(opt_group->label_width * m_em_unit) ? 2 : 1) + m_v_gap;
|
||||
@ -186,11 +186,11 @@ wxPoint OG_CustomCtrl::get_pos(const Line& line, Field* field_in/* = nullptr*/)
|
||||
#endif //__WXMSW__
|
||||
h_pos += label_w + 1 + m_h_gap;
|
||||
}
|
||||
h_pos += (opt.opt.gui_type == "legend" ? 1 : 3) * blinking_button_width;
|
||||
h_pos += (opt.opt.gui_type == ConfigOptionDef::GUIType::legend ? 1 : 3) * blinking_button_width;
|
||||
|
||||
if (field == field_in)
|
||||
break;
|
||||
if (opt.opt.gui_type == "legend")
|
||||
if (opt.opt.gui_type == ConfigOptionDef::GUIType::legend)
|
||||
h_pos += 2 * blinking_button_width;
|
||||
|
||||
h_pos += field->getWindow()->GetSize().x;
|
||||
@ -580,7 +580,7 @@ wxCoord OG_CustomCtrl::CtrlLine::draw_mode_bmp(wxDC& dc, wxCoord v_pos)
|
||||
wxBitmap bmp = create_scaled_bitmap(bmp_name, ctrl, wxOSX ? 10 : 12);
|
||||
wxCoord y_draw = v_pos + lround((height - get_bitmap_size(bmp).GetHeight()) / 2);
|
||||
|
||||
if (og_line.get_options().front().opt.gui_type != "legend")
|
||||
if (og_line.get_options().front().opt.gui_type != ConfigOptionDef::GUIType::legend)
|
||||
dc.DrawBitmap(bmp, 0, y_draw);
|
||||
|
||||
return get_bitmap_size(bmp).GetWidth() + ctrl->m_h_gap;
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "wxExtensions.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "GUI_Factories.hpp"
|
||||
#include "I18N.hpp"
|
||||
|
||||
#include "libslic3r/Model.hpp"
|
||||
@ -44,8 +44,9 @@ void ObjectDataViewModelNode::init_container()
|
||||
#endif //__WXGTK__
|
||||
}
|
||||
|
||||
#define LAYER_ROOT_ICON "edit_layers_all"
|
||||
#define LAYER_ICON "edit_layers_some"
|
||||
static constexpr char LayerRootIcon[] = "edit_layers_all";
|
||||
static constexpr char LayerIcon[] = "edit_layers_some";
|
||||
static constexpr char WarningIcon[] = "exclamation";
|
||||
|
||||
ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent, const ItemType type) :
|
||||
m_parent(parent),
|
||||
@ -65,7 +66,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent
|
||||
}
|
||||
else if (type == itLayerRoot)
|
||||
{
|
||||
m_bmp = create_scaled_bitmap(LAYER_ROOT_ICON); // FIXME: pass window ptr
|
||||
m_bmp = create_scaled_bitmap(LayerRootIcon); // FIXME: pass window ptr
|
||||
m_name = _(L("Layers"));
|
||||
}
|
||||
|
||||
@ -94,7 +95,7 @@ ObjectDataViewModelNode::ObjectDataViewModelNode(ObjectDataViewModelNode* parent
|
||||
}
|
||||
const std::string label_range = (boost::format(" %.2f-%.2f ") % layer_range.first % layer_range.second).str();
|
||||
m_name = _(L("Range")) + label_range + "(" + _(L("mm")) + ")";
|
||||
m_bmp = create_scaled_bitmap(LAYER_ICON); // FIXME: pass window ptr
|
||||
m_bmp = create_scaled_bitmap(LayerIcon); // FIXME: pass window ptr
|
||||
|
||||
set_action_and_extruder_icons();
|
||||
init_container();
|
||||
@ -140,17 +141,14 @@ void ObjectDataViewModelNode::update_settings_digest_bitmaps()
|
||||
{
|
||||
m_bmp = m_empty_bmp;
|
||||
|
||||
std::map<std::string, wxBitmap>& categories_icon = Slic3r::GUI::wxGetApp().obj_list()->CATEGORY_ICON;
|
||||
|
||||
std::string scaled_bitmap_name = m_name.ToUTF8().data();
|
||||
scaled_bitmap_name += "-em" + std::to_string(wxGetApp().em_unit()) + (wxGetApp().dark_mode() ? "-dm" : "");
|
||||
|
||||
wxBitmap *bmp = m_bitmap_cache->find(scaled_bitmap_name);
|
||||
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));
|
||||
for (auto& category : m_opt_categories)
|
||||
bmps.emplace_back(SettingsFactory::get_category_bitmap(category));
|
||||
bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps);
|
||||
}
|
||||
|
||||
@ -249,6 +247,9 @@ static int get_root_idx(ObjectDataViewModelNode *parent_node, const ItemType roo
|
||||
ObjectDataViewModel::ObjectDataViewModel()
|
||||
{
|
||||
m_bitmap_cache = new Slic3r::GUI::BitmapCache;
|
||||
|
||||
m_volume_bmps = MenuFactory::get_volume_bitmaps();
|
||||
m_warning_bmp = create_scaled_bitmap(WarningIcon);
|
||||
}
|
||||
|
||||
ObjectDataViewModel::~ObjectDataViewModel()
|
||||
@ -267,7 +268,7 @@ wxDataViewItem ObjectDataViewModel::Add(const wxString &name,
|
||||
auto root = new ObjectDataViewModelNode(name, extruder_str);
|
||||
// Add error icon if detected auto-repaire
|
||||
if (has_errors)
|
||||
root->m_bmp = *m_warning_bmp;
|
||||
root->m_bmp = m_warning_bmp;
|
||||
|
||||
m_objects.push_back(root);
|
||||
// notify control
|
||||
@ -317,7 +318,7 @@ wxDataViewItem ObjectDataViewModel::AddVolumeChild( const wxDataViewItem &parent
|
||||
|
||||
// if part with errors is added, but object wasn't marked, then mark it
|
||||
if (!obj_errors && has_errors)
|
||||
root->SetBitmap(*m_warning_bmp);
|
||||
root->SetBitmap(m_warning_bmp);
|
||||
|
||||
// notify control
|
||||
const wxDataViewItem child((void*)node);
|
||||
@ -1434,10 +1435,20 @@ void ObjectDataViewModel::SetVolumeType(const wxDataViewItem &item, const Slic3r
|
||||
return;
|
||||
|
||||
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
|
||||
node->SetBitmap(*m_volume_bmps[int(type)]);
|
||||
node->SetVolumeType(type);
|
||||
node->SetBitmap(m_volume_bmps[int(type)]);
|
||||
ItemChanged(item);
|
||||
}
|
||||
|
||||
ModelVolumeType ObjectDataViewModel::GetVolumeType(const wxDataViewItem& item)
|
||||
{
|
||||
if (!item.IsOk() || GetItemType(item) != itVolume)
|
||||
return ModelVolumeType::INVALID;
|
||||
|
||||
ObjectDataViewModelNode *node = static_cast<ObjectDataViewModelNode*>(item.GetID());
|
||||
return node->GetVolumeType();
|
||||
}
|
||||
|
||||
wxDataViewItem ObjectDataViewModel::SetPrintableState(
|
||||
PrintIndicator printable,
|
||||
int obj_idx,
|
||||
@ -1480,6 +1491,9 @@ wxDataViewItem ObjectDataViewModel::SetObjectPrintableState(
|
||||
|
||||
void ObjectDataViewModel::Rescale()
|
||||
{
|
||||
m_volume_bmps = MenuFactory::get_volume_bitmaps();
|
||||
m_warning_bmp = create_scaled_bitmap(WarningIcon);
|
||||
|
||||
wxDataViewItemArray all_items;
|
||||
GetAllChildren(wxDataViewItem(0), all_items);
|
||||
|
||||
@ -1494,15 +1508,15 @@ void ObjectDataViewModel::Rescale()
|
||||
switch (node->m_type)
|
||||
{
|
||||
case itObject:
|
||||
if (node->m_bmp.IsOk()) node->m_bmp = *m_warning_bmp;
|
||||
if (node->m_bmp.IsOk()) node->m_bmp = m_warning_bmp;
|
||||
break;
|
||||
case itVolume:
|
||||
node->m_bmp = GetVolumeIcon(node->m_volume_type, node->m_bmp.GetWidth() != node->m_bmp.GetHeight());
|
||||
break;
|
||||
case itLayerRoot:
|
||||
node->m_bmp = create_scaled_bitmap(LAYER_ROOT_ICON);
|
||||
node->m_bmp = create_scaled_bitmap(LayerRootIcon);
|
||||
case itLayer:
|
||||
node->m_bmp = create_scaled_bitmap(LAYER_ICON);
|
||||
node->m_bmp = create_scaled_bitmap(LayerIcon);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
@ -1514,7 +1528,7 @@ void ObjectDataViewModel::Rescale()
|
||||
wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_type, const bool is_marked/* = false*/)
|
||||
{
|
||||
if (!is_marked)
|
||||
return *m_volume_bmps[static_cast<int>(vol_type)];
|
||||
return m_volume_bmps[static_cast<int>(vol_type)];
|
||||
|
||||
std::string scaled_bitmap_name = "warning" + std::to_string(static_cast<int>(vol_type));
|
||||
scaled_bitmap_name += "-em" + std::to_string(Slic3r::GUI::wxGetApp().em_unit());
|
||||
@ -1523,8 +1537,8 @@ wxBitmap ObjectDataViewModel::GetVolumeIcon(const Slic3r::ModelVolumeType vol_ty
|
||||
if (bmp == nullptr) {
|
||||
std::vector<wxBitmap> bmps;
|
||||
|
||||
bmps.emplace_back(*m_warning_bmp);
|
||||
bmps.emplace_back(*m_volume_bmps[static_cast<int>(vol_type)]);
|
||||
bmps.emplace_back(m_warning_bmp);
|
||||
bmps.emplace_back(m_volume_bmps[static_cast<int>(vol_type)]);
|
||||
|
||||
bmp = m_bitmap_cache->insert(scaled_bitmap_name, bmps);
|
||||
}
|
||||
@ -1543,7 +1557,7 @@ void ObjectDataViewModel::DeleteWarningIcon(const wxDataViewItem& item, const bo
|
||||
return;
|
||||
|
||||
if (node->GetType() & itVolume) {
|
||||
node->SetBitmap(*m_volume_bmps[static_cast<int>(node->volume_type())]);
|
||||
node->SetBitmap(m_volume_bmps[static_cast<int>(node->volume_type())]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -171,13 +171,14 @@ public:
|
||||
}
|
||||
|
||||
bool SetValue(const wxVariant &variant, unsigned int col);
|
||||
|
||||
void SetVolumeType(ModelVolumeType type) { m_volume_type = type; }
|
||||
void SetBitmap(const wxBitmap &icon) { m_bmp = icon; }
|
||||
const wxBitmap& GetBitmap() const { return m_bmp; }
|
||||
const wxString& GetName() const { return m_name; }
|
||||
ItemType GetType() const { return m_type; }
|
||||
void SetIdx(const int& idx);
|
||||
int GetIdx() const { return m_idx; }
|
||||
ModelVolumeType GetVolumeType() { return m_volume_type; }
|
||||
t_layer_height_range GetLayerRange() const { return m_layer_range; }
|
||||
PrintIndicator IsPrintable() const { return m_printable; }
|
||||
|
||||
@ -241,8 +242,8 @@ wxDECLARE_EVENT(wxCUSTOMEVT_LAST_VOLUME_IS_DELETED, wxCommandEvent);
|
||||
class ObjectDataViewModel :public wxDataViewModel
|
||||
{
|
||||
std::vector<ObjectDataViewModelNode*> m_objects;
|
||||
std::vector<wxBitmap*> m_volume_bmps;
|
||||
wxBitmap* m_warning_bmp { nullptr };
|
||||
std::vector<wxBitmap> m_volume_bmps;
|
||||
wxBitmap m_warning_bmp;
|
||||
|
||||
wxDataViewCtrl* m_ctrl { nullptr };
|
||||
|
||||
@ -348,9 +349,8 @@ public:
|
||||
void UpdateObjectPrintable(wxDataViewItem parent_item);
|
||||
void UpdateInstancesPrintable(wxDataViewItem parent_item);
|
||||
|
||||
void SetVolumeBitmaps(const std::vector<wxBitmap*>& volume_bmps) { m_volume_bmps = volume_bmps; }
|
||||
void SetWarningBitmap(wxBitmap* bitmap) { m_warning_bmp = bitmap; }
|
||||
void SetVolumeType(const wxDataViewItem &item, const Slic3r::ModelVolumeType type);
|
||||
ModelVolumeType GetVolumeType(const wxDataViewItem &item);
|
||||
wxDataViewItem SetPrintableState( PrintIndicator printable, int obj_idx,
|
||||
int subobj_idx = -1,
|
||||
ItemType subobj_type = itInstance);
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "GUI.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "3DScene.hpp"
|
||||
#include "slic3r/Utils/Platform.hpp"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
@ -319,7 +320,13 @@ void OpenGLManager::detect_multisample(int* attribList)
|
||||
{
|
||||
int wxVersion = wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + wxRELEASE_NUMBER;
|
||||
bool enable_multisample = wxVersion >= 30003;
|
||||
s_multisample = (enable_multisample && wxGLCanvas::IsDisplaySupported(attribList)) ? EMultisampleState::Enabled : EMultisampleState::Disabled;
|
||||
s_multisample =
|
||||
enable_multisample &&
|
||||
// Disable multi-sampling on ChromeOS, as the OpenGL virtualization swaps Red/Blue channels with multi-sampling enabled,
|
||||
// at least on some platforms.
|
||||
(platform() != Platform::Linux || platform_flavor() != PlatformFlavor::LinuxOnChromium) &&
|
||||
wxGLCanvas::IsDisplaySupported(attribList)
|
||||
? EMultisampleState::Enabled : EMultisampleState::Disabled;
|
||||
// Alternative method: it was working on previous version of wxWidgets but not with the latest, at least on Windows
|
||||
// s_multisample = enable_multisample && wxGLCanvas::IsExtensionSupported("WGL_ARB_multisample");
|
||||
}
|
||||
|
@ -25,23 +25,27 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id) {
|
||||
const t_field& OptionsGroup::build_field(const t_config_option_key& id, const ConfigOptionDef& opt) {
|
||||
// Check the gui_type field first, fall through
|
||||
// is the normal type.
|
||||
if (opt.gui_type == "select") {
|
||||
} else if (opt.gui_type == "select_open") {
|
||||
switch (opt.gui_type) {
|
||||
case ConfigOptionDef::GUIType::select_open:
|
||||
m_fields.emplace(id, Choice::Create<Choice>(this->ctrl_parent(), opt, id));
|
||||
} else if (opt.gui_type == "color") {
|
||||
break;
|
||||
case ConfigOptionDef::GUIType::color:
|
||||
m_fields.emplace(id, ColourPicker::Create<ColourPicker>(this->ctrl_parent(), opt, id));
|
||||
} else if (opt.gui_type == "f_enum_open" ||
|
||||
opt.gui_type == "i_enum_open" ||
|
||||
opt.gui_type == "i_enum_closed") {
|
||||
break;
|
||||
case ConfigOptionDef::GUIType::f_enum_open:
|
||||
case ConfigOptionDef::GUIType::i_enum_open:
|
||||
m_fields.emplace(id, Choice::Create<Choice>(this->ctrl_parent(), opt, id));
|
||||
} else if (opt.gui_type == "slider") {
|
||||
break;
|
||||
case ConfigOptionDef::GUIType::slider:
|
||||
m_fields.emplace(id, SliderCtrl::Create<SliderCtrl>(this->ctrl_parent(), opt, id));
|
||||
} else if (opt.gui_type == "i_spin") { // Spinctrl
|
||||
} else if (opt.gui_type == "legend") { // StaticText
|
||||
break;
|
||||
case ConfigOptionDef::GUIType::legend: // StaticText
|
||||
m_fields.emplace(id, StaticText::Create<StaticText>(this->ctrl_parent(), opt, id));
|
||||
} else if (opt.gui_type == "one_string") {
|
||||
break;
|
||||
case ConfigOptionDef::GUIType::one_string:
|
||||
m_fields.emplace(id, TextCtrl::Create<TextCtrl>(this->ctrl_parent(), opt, id));
|
||||
} else {
|
||||
break;
|
||||
default:
|
||||
switch (opt.type) {
|
||||
case coFloatOrPercent:
|
||||
case coFloat:
|
||||
@ -122,7 +126,7 @@ bool OptionsGroup::is_legend_line()
|
||||
{
|
||||
if (m_lines.size() == 1) {
|
||||
const std::vector<Option>& option_set = m_lines.front().get_options();
|
||||
return !option_set.empty() && option_set.front().opt.gui_type == "legend";
|
||||
return !option_set.empty() && option_set.front().opt.gui_type == ConfigOptionDef::GUIType::legend;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -213,7 +217,7 @@ void OptionsGroup::activate_line(Line& line)
|
||||
}
|
||||
|
||||
auto option_set = line.get_options();
|
||||
bool is_legend_line = option_set.front().opt.gui_type == "legend";
|
||||
bool is_legend_line = option_set.front().opt.gui_type == ConfigOptionDef::GUIType::legend;
|
||||
|
||||
if (!custom_ctrl && m_use_custom_ctrl) {
|
||||
custom_ctrl = new OG_CustomCtrl(is_legend_line || !staticbox ? this->parent() : static_cast<wxWindow*>(this->stb), this);
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include "GUI_ObjectManipulation.hpp"
|
||||
#include "GUI_ObjectLayers.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "GUI_Factories.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "format.hpp"
|
||||
@ -75,6 +76,7 @@
|
||||
#include "../Utils/FixModelByWin10.hpp"
|
||||
#include "../Utils/UndoRedo.hpp"
|
||||
#include "../Utils/PresetUpdater.hpp"
|
||||
#include "../Utils/Platform.hpp"
|
||||
#include "../Utils/Process.hpp"
|
||||
#include "RemovableDriveManager.hpp"
|
||||
#include "InstanceCheck.hpp"
|
||||
@ -356,7 +358,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) :
|
||||
ConfigOptionDef support_def;
|
||||
support_def.label = L("Supports");
|
||||
support_def.type = coStrings;
|
||||
support_def.gui_type = "select_open";
|
||||
support_def.gui_type = ConfigOptionDef::GUIType::select_open;
|
||||
support_def.tooltip = L("Select what kind of support do you need");
|
||||
support_def.enum_labels.push_back(L("None"));
|
||||
support_def.enum_labels.push_back(L("Support on build plate only"));
|
||||
@ -396,7 +398,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) :
|
||||
def.label = L("Brim");
|
||||
def.type = coBool;
|
||||
def.tooltip = L("This flag enables the brim that will be printed around each object on the first layer.");
|
||||
def.gui_type = "";
|
||||
def.gui_type = ConfigOptionDef::GUIType::undefined;
|
||||
def.set_default_value(new ConfigOptionBool{ m_brim_width > 0.0 ? true : false });
|
||||
option = Option(def, "brim");
|
||||
option.opt.sidetext = "";
|
||||
@ -499,7 +501,7 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) :
|
||||
ConfigOptionDef pad_def;
|
||||
pad_def.label = L("Pad");
|
||||
pad_def.type = coStrings;
|
||||
pad_def.gui_type = "select_open";
|
||||
pad_def.gui_type = ConfigOptionDef::GUIType::select_open;
|
||||
pad_def.tooltip = L("Select what kind of pad do you need");
|
||||
pad_def.enum_labels.push_back(L("None"));
|
||||
pad_def.enum_labels.push_back(L("Below object"));
|
||||
@ -1322,7 +1324,7 @@ void Sidebar::update_mode()
|
||||
|
||||
p->object_list->unselect_objects();
|
||||
p->object_list->update_selections();
|
||||
p->object_list->update_object_menu();
|
||||
// p->object_list->update_object_menu();
|
||||
|
||||
Layout();
|
||||
}
|
||||
@ -1404,23 +1406,7 @@ struct Plater::priv
|
||||
Plater *q;
|
||||
MainFrame *main_frame;
|
||||
|
||||
// Object popup menu
|
||||
MenuWithSeparators object_menu;
|
||||
// Part popup menu
|
||||
MenuWithSeparators part_menu;
|
||||
// SLA-Object popup menu
|
||||
MenuWithSeparators sla_object_menu;
|
||||
// Default popup menu (when nothing is selected on 3DScene)
|
||||
MenuWithSeparators default_menu;
|
||||
|
||||
// Removed/Prepended Items according to the view mode
|
||||
std::vector<wxMenuItem*> items_increase;
|
||||
std::vector<wxMenuItem*> items_decrease;
|
||||
std::vector<wxMenuItem*> items_set_number_of_copies;
|
||||
enum MenuIdentifier {
|
||||
miObjectFFF=0,
|
||||
miObjectSLA
|
||||
};
|
||||
MenuFactory menus;
|
||||
|
||||
// Data
|
||||
Slic3r::DynamicPrintConfig *config; // FIXME: leak?
|
||||
@ -1669,7 +1655,6 @@ struct Plater::priv
|
||||
void on_update_geometry(Vec3dsEvent<2>&);
|
||||
void on_3dcanvas_mouse_dragging_finished(SimpleEvent&);
|
||||
|
||||
void update_object_menu();
|
||||
void show_action_buttons(const bool is_ready_to_slice) const;
|
||||
|
||||
// Set the bed shape to a single closed 2D polygon(array of two element arrays),
|
||||
@ -1690,12 +1675,11 @@ struct Plater::priv
|
||||
bool can_set_instance_to_object() const;
|
||||
bool can_mirror() const;
|
||||
bool can_reload_from_disk() const;
|
||||
bool can_split(bool to_objects) const;
|
||||
|
||||
void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
|
||||
void generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
|
||||
|
||||
void msw_rescale_object_menu();
|
||||
|
||||
void bring_instance_forward() const;
|
||||
|
||||
// returns the path to project file with the given extension (none if extension == wxEmptyString)
|
||||
@ -1712,13 +1696,6 @@ struct Plater::priv
|
||||
bool inside_snapshot_capture() { return m_prevent_snapshots != 0; }
|
||||
bool process_completed_with_error { false };
|
||||
private:
|
||||
bool init_object_menu();
|
||||
bool init_common_menu(wxMenu* menu, const bool is_part = false);
|
||||
bool complit_init_object_menu();
|
||||
bool complit_init_sla_object_menu();
|
||||
bool complit_init_part_menu();
|
||||
|
||||
bool can_split() const;
|
||||
bool layers_height_allowed() const;
|
||||
|
||||
void update_fff_scene();
|
||||
@ -1762,7 +1739,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
|
||||
"layer_height", "first_layer_height", "min_layer_height", "max_layer_height",
|
||||
"brim_width", "perimeters", "perimeter_extruder", "fill_density", "infill_extruder", "top_solid_layers",
|
||||
"support_material", "support_material_extruder", "support_material_interface_extruder", "support_material_contact_distance", "raft_layers"
|
||||
"support_material", "support_material_extruder", "support_material_interface_extruder",
|
||||
"support_material_contact_distance", "support_material_bottom_contact_distance", "raft_layers"
|
||||
}))
|
||||
, sidebar(new Sidebar(q))
|
||||
, m_ui_jobs(this)
|
||||
@ -1827,7 +1805,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
hsizer->Add(sidebar, 0, wxEXPAND | wxLEFT | wxRIGHT, 0);
|
||||
q->SetSizer(hsizer);
|
||||
|
||||
init_object_menu();
|
||||
menus.init(q);
|
||||
|
||||
// Events:
|
||||
|
||||
@ -2723,19 +2701,19 @@ void Plater::priv::split_object()
|
||||
Model new_model = model;
|
||||
ModelObject* current_model_object = new_model.objects[obj_idx];
|
||||
|
||||
if (current_model_object->volumes.size() > 1)
|
||||
{
|
||||
Slic3r::GUI::warning_catcher(q, _L("The selected object can't be split because it contains more than one volume/material."));
|
||||
return;
|
||||
}
|
||||
|
||||
wxBusyCursor wait;
|
||||
ModelObjectPtrs new_objects;
|
||||
current_model_object->split(&new_objects);
|
||||
if (new_objects.size() == 1)
|
||||
Slic3r::GUI::warning_catcher(q, _L("The selected object couldn't be split because it contains only one part."));
|
||||
// #ysFIXME use notification
|
||||
Slic3r::GUI::warning_catcher(q, _L("The selected object couldn't be split because it contains only one solid part."));
|
||||
else
|
||||
{
|
||||
if (current_model_object->volumes.size() != new_objects.size())
|
||||
notification_manager->push_notification(NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotification,
|
||||
_u8L("All non-solid parts (modifiers) was deleted"));
|
||||
|
||||
Plater::TakeSnapshot snapshot(q, _L("Split to Objects"));
|
||||
|
||||
remove(obj_idx);
|
||||
@ -3563,6 +3541,7 @@ void Plater::priv::on_slicing_began()
|
||||
{
|
||||
clear_warnings();
|
||||
notification_manager->close_notification_of_type(NotificationType::SlicingComplete);
|
||||
notification_manager->close_notification_of_type(NotificationType::SignDetected);
|
||||
}
|
||||
void Plater::priv::add_warning(const Slic3r::PrintStateBase::Warning& warning, size_t oid)
|
||||
{
|
||||
@ -3679,7 +3658,9 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
||||
// If writing to removable drive was scheduled, show notification with eject button
|
||||
if (exporting_status == ExportingStatus::EXPORTING_TO_REMOVABLE && !has_error) {
|
||||
show_action_buttons(false);
|
||||
notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, true);
|
||||
notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path,
|
||||
// Don't offer the "Eject" button on ChromeOS, the Linux side has no control over it.
|
||||
platform() != Platform::Linux || platform_flavor() != PlatformFlavor::LinuxOnChromium);
|
||||
wxGetApp().removable_drive_manager()->set_exporting_finished(true);
|
||||
}else if (exporting_status == ExportingStatus::EXPORTING_TO_LOCAL && !has_error)
|
||||
notification_manager->push_exporting_finished_notification(last_output_path, last_output_dir_path, false);
|
||||
@ -3727,70 +3708,25 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
|
||||
|
||||
wxMenu* menu = nullptr;
|
||||
|
||||
if (obj_idx == -1) // no one or several object are selected
|
||||
{
|
||||
if (obj_idx == -1) { // no one or several object are selected
|
||||
if (evt.data.second) // right button was clicked on empty space
|
||||
menu = &default_menu;
|
||||
menu = menus.default_menu();
|
||||
else
|
||||
{
|
||||
sidebar->obj_list()->show_multi_selection_menu();
|
||||
return;
|
||||
}
|
||||
menu = menus.multi_selection_menu();
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
// If in 3DScene is(are) selected volume(s), but right button was clicked on empty space
|
||||
if (evt.data.second)
|
||||
return;
|
||||
|
||||
int menu_item_convert_unit_position = 11;
|
||||
return;
|
||||
|
||||
if (printer_technology == ptSLA)
|
||||
menu = &sla_object_menu;
|
||||
else
|
||||
{
|
||||
menu = menus.sla_object_menu();
|
||||
else {
|
||||
// show "Object menu" for each one or several FullInstance instead of FullObject
|
||||
const bool is_some_full_instances = get_selection().is_single_full_instance() ||
|
||||
get_selection().is_single_full_object() ||
|
||||
get_selection().is_multiple_full_instance();
|
||||
menu = is_some_full_instances ? &object_menu : &part_menu;
|
||||
if (!is_some_full_instances)
|
||||
menu_item_convert_unit_position = 2;
|
||||
}
|
||||
|
||||
sidebar->obj_list()->append_menu_items_convert_unit(menu, menu_item_convert_unit_position);
|
||||
sidebar->obj_list()->append_menu_item_settings(menu);
|
||||
|
||||
if (printer_technology != ptSLA)
|
||||
sidebar->obj_list()->append_menu_item_change_extruder(menu);
|
||||
|
||||
if (menu != &part_menu)
|
||||
{
|
||||
/* Remove/Prepend "increase/decrease instances" menu items according to the view mode.
|
||||
* Suppress to show those items for a Simple mode
|
||||
*/
|
||||
const MenuIdentifier id = printer_technology == ptSLA ? miObjectSLA : miObjectFFF;
|
||||
if (wxGetApp().get_mode() == comSimple) {
|
||||
if (menu->FindItem(_L("Add instance")) != wxNOT_FOUND)
|
||||
{
|
||||
/* Detach an items from the menu, but don't delete them
|
||||
* so that they can be added back later
|
||||
* (after switching to the Advanced/Expert mode)
|
||||
*/
|
||||
menu->Remove(items_increase[id]);
|
||||
menu->Remove(items_decrease[id]);
|
||||
menu->Remove(items_set_number_of_copies[id]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (menu->FindItem(_L("Add instance")) == wxNOT_FOUND)
|
||||
{
|
||||
// Prepend items to the menu, if those aren't not there
|
||||
menu->Prepend(items_set_number_of_copies[id]);
|
||||
menu->Prepend(items_decrease[id]);
|
||||
menu->Prepend(items_increase[id]);
|
||||
}
|
||||
}
|
||||
menu = is_some_full_instances ? menus.object_menu() : menus.part_menu();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3837,26 +3773,6 @@ void Plater::priv::on_3dcanvas_mouse_dragging_finished(SimpleEvent&)
|
||||
}
|
||||
}
|
||||
|
||||
bool Plater::priv::init_object_menu()
|
||||
{
|
||||
items_increase.reserve(2);
|
||||
items_decrease.reserve(2);
|
||||
items_set_number_of_copies.reserve(2);
|
||||
|
||||
init_common_menu(&object_menu);
|
||||
complit_init_object_menu();
|
||||
|
||||
init_common_menu(&sla_object_menu);
|
||||
complit_init_sla_object_menu();
|
||||
|
||||
init_common_menu(&part_menu, true);
|
||||
complit_init_part_menu();
|
||||
|
||||
sidebar->obj_list()->create_default_popupmenu(&default_menu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Plater::priv::generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
|
||||
{
|
||||
view3D->get_canvas3d()->render_thumbnail(data, w, h, printable_only, parts_only, show_bed, transparent_background);
|
||||
@ -3875,12 +3791,6 @@ void Plater::priv::generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds&
|
||||
}
|
||||
}
|
||||
|
||||
void Plater::priv::msw_rescale_object_menu()
|
||||
{
|
||||
for (MenuWithSeparators* menu : { &object_menu, &sla_object_menu, &part_menu, &default_menu })
|
||||
msw_rescale_menu(dynamic_cast<wxMenu*>(menu));
|
||||
}
|
||||
|
||||
wxString Plater::priv::get_project_filename(const wxString& extension) const
|
||||
{
|
||||
return m_project_filename.empty() ? "" : m_project_filename + extension;
|
||||
@ -3909,144 +3819,6 @@ void Plater::priv::set_project_filename(const wxString& filename)
|
||||
wxGetApp().mainframe->add_to_recent_projects(filename);
|
||||
}
|
||||
|
||||
bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/)
|
||||
{
|
||||
if (is_part) {
|
||||
append_menu_item(menu, wxID_ANY, _L("Delete") + "\tDel", _L("Remove the selected object"),
|
||||
[this](wxCommandEvent&) { q->remove_selected(); }, "delete", nullptr, [this]() { return can_delete(); }, q);
|
||||
|
||||
append_menu_item(menu, wxID_ANY, _L("Reload from disk"), _L("Reload the selected volumes from disk"),
|
||||
[this](wxCommandEvent&) { q->reload_from_disk(); }, "", menu, [this]() { return can_reload_from_disk(); }, q);
|
||||
|
||||
sidebar->obj_list()->append_menu_item_export_stl(menu);
|
||||
}
|
||||
else {
|
||||
wxMenuItem* item_increase = append_menu_item(menu, wxID_ANY, _L("Add instance") + "\t+", _L("Add one more instance of the selected object"),
|
||||
[this](wxCommandEvent&) { q->increase_instances(); }, "add_copies", nullptr, [this]() { return can_increase_instances(); }, q);
|
||||
wxMenuItem* item_decrease = append_menu_item(menu, wxID_ANY, _L("Remove instance") + "\t-", _L("Remove one instance of the selected object"),
|
||||
[this](wxCommandEvent&) { q->decrease_instances(); }, "remove_copies", nullptr, [this]() { return can_decrease_instances(); }, q);
|
||||
wxMenuItem* item_set_number_of_copies = append_menu_item(menu, wxID_ANY, _L("Set number of instances") + dots, _L("Change the number of instances of the selected object"),
|
||||
[this](wxCommandEvent&) { q->set_number_of_copies(); }, "number_of_copies", nullptr, [this]() { return can_increase_instances(); }, q);
|
||||
append_menu_item(menu, wxID_ANY, _L("Fill bed with instances") + dots, _L("Fill the remaining area of bed with instances of the selected object"),
|
||||
[this](wxCommandEvent&) { q->fill_bed_with_instances(); }, "", nullptr, [this]() { return can_increase_instances(); }, q);
|
||||
|
||||
|
||||
items_increase.push_back(item_increase);
|
||||
items_decrease.push_back(item_decrease);
|
||||
items_set_number_of_copies.push_back(item_set_number_of_copies);
|
||||
|
||||
// Delete menu was moved to be after +/- instace to make it more difficult to be selected by mistake.
|
||||
append_menu_item(menu, wxID_ANY, _L("Delete") + "\tDel", _L("Remove the selected object"),
|
||||
[this](wxCommandEvent&) { q->remove_selected(); }, "delete", nullptr, [this]() { return can_delete(); }, q);
|
||||
|
||||
menu->AppendSeparator();
|
||||
sidebar->obj_list()->append_menu_item_instance_to_object(menu, q);
|
||||
menu->AppendSeparator();
|
||||
|
||||
wxMenuItem* menu_item_printable = sidebar->obj_list()->append_menu_item_printable(menu, q);
|
||||
menu->AppendSeparator();
|
||||
|
||||
append_menu_item(menu, wxID_ANY, _L("Reload from disk"), _L("Reload the selected object from disk"),
|
||||
[this](wxCommandEvent&) { reload_from_disk(); }, "", nullptr, [this]() { return can_reload_from_disk(); }, q);
|
||||
|
||||
append_menu_item(menu, wxID_ANY, _L("Export as STL") + dots, _L("Export the selected object as STL file"),
|
||||
[this](wxCommandEvent&) { q->export_stl(false, true); }, "", nullptr,
|
||||
[this]() {
|
||||
const Selection& selection = get_selection();
|
||||
return selection.is_single_full_instance() || selection.is_single_full_object();
|
||||
}, q);
|
||||
|
||||
menu->AppendSeparator();
|
||||
|
||||
// "Scale to print volume" makes a sense just for whole object
|
||||
sidebar->obj_list()->append_menu_item_scale_selection_to_fit_print_volume(menu);
|
||||
|
||||
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) {
|
||||
const Selection& selection = get_selection();
|
||||
int instance_idx = selection.get_instance_idx();
|
||||
evt.Enable(selection.is_single_full_instance() || selection.is_single_full_object());
|
||||
if (instance_idx != -1)
|
||||
{
|
||||
evt.Check(model.objects[selection.get_object_idx()]->instances[instance_idx]->printable);
|
||||
view3D->set_as_dirty();
|
||||
}
|
||||
}, menu_item_printable->GetId());
|
||||
}
|
||||
|
||||
sidebar->obj_list()->append_menu_item_fix_through_netfabb(menu);
|
||||
|
||||
wxMenu* mirror_menu = new wxMenu();
|
||||
if (mirror_menu == nullptr)
|
||||
return false;
|
||||
|
||||
append_menu_item(mirror_menu, wxID_ANY, _L("Along X axis"), _L("Mirror the selected object along the X axis"),
|
||||
[this](wxCommandEvent&) { mirror(X); }, "mark_X", menu);
|
||||
append_menu_item(mirror_menu, wxID_ANY, _L("Along Y axis"), _L("Mirror the selected object along the Y axis"),
|
||||
[this](wxCommandEvent&) { mirror(Y); }, "mark_Y", menu);
|
||||
append_menu_item(mirror_menu, wxID_ANY, _L("Along Z axis"), _L("Mirror the selected object along the Z axis"),
|
||||
[this](wxCommandEvent&) { mirror(Z); }, "mark_Z", menu);
|
||||
|
||||
append_submenu(menu, mirror_menu, wxID_ANY, _L("Mirror"), _L("Mirror the selected object"), "",
|
||||
[this]() { return can_mirror(); }, q);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Plater::priv::complit_init_object_menu()
|
||||
{
|
||||
wxMenu* split_menu = new wxMenu();
|
||||
if (split_menu == nullptr)
|
||||
return false;
|
||||
|
||||
append_menu_item(split_menu, wxID_ANY, _L("To objects"), _L("Split the selected object into individual objects"),
|
||||
[this](wxCommandEvent&) { split_object(); }, "split_object_SMALL", &object_menu, [this]() { return can_split(); }, q);
|
||||
append_menu_item(split_menu, wxID_ANY, _L("To parts"), _L("Split the selected object into individual sub-parts"),
|
||||
[this](wxCommandEvent&) { split_volume(); }, "split_parts_SMALL", &object_menu, [this]() { return can_split(); }, q);
|
||||
|
||||
append_submenu(&object_menu, split_menu, wxID_ANY, _L("Split"), _L("Split the selected object"), "",
|
||||
[this]() { return can_split() && wxGetApp().get_mode() > comSimple; }, q);
|
||||
object_menu.AppendSeparator();
|
||||
|
||||
// Layers Editing for object
|
||||
sidebar->obj_list()->append_menu_item_layers_editing(&object_menu, q);
|
||||
object_menu.AppendSeparator();
|
||||
|
||||
// "Add (volumes)" popupmenu will be added later in append_menu_items_add_volume()
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Plater::priv::complit_init_sla_object_menu()
|
||||
{
|
||||
append_menu_item(&sla_object_menu, wxID_ANY, _L("Split"), _L("Split the selected object into individual objects"),
|
||||
[this](wxCommandEvent&) { split_object(); }, "split_object_SMALL", nullptr, [this]() { return can_split(); }, q);
|
||||
|
||||
sla_object_menu.AppendSeparator();
|
||||
|
||||
// Add the automatic rotation sub-menu
|
||||
append_menu_item(
|
||||
&sla_object_menu, wxID_ANY, _(L("Optimize orientation")),
|
||||
_(L("Optimize the rotation of the object for better print results.")),
|
||||
[this](wxCommandEvent &) {
|
||||
m_ui_jobs.optimize_rotation();
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Plater::priv::complit_init_part_menu()
|
||||
{
|
||||
append_menu_item(&part_menu, wxID_ANY, _L("Split"), _L("Split the selected object into individual sub-parts"),
|
||||
[this](wxCommandEvent&) { split_volume(); }, "split_parts_SMALL", nullptr, [this]() { return can_split(); }, q);
|
||||
|
||||
part_menu.AppendSeparator();
|
||||
|
||||
auto obj_list = sidebar->obj_list();
|
||||
obj_list->append_menu_item_change_type(&part_menu, q);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Plater::priv::set_current_canvas_as_dirty()
|
||||
{
|
||||
if (current_panel == view3D)
|
||||
@ -4196,9 +3968,9 @@ bool Plater::priv::can_set_instance_to_object() const
|
||||
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && (model.objects[obj_idx]->instances.size() > 1);
|
||||
}
|
||||
|
||||
bool Plater::priv::can_split() const
|
||||
bool Plater::priv::can_split(bool to_objects) const
|
||||
{
|
||||
return sidebar->obj_list()->is_splittable();
|
||||
return sidebar->obj_list()->is_splittable(to_objects);
|
||||
}
|
||||
|
||||
bool Plater::priv::layers_height_allowed() const
|
||||
@ -4314,12 +4086,12 @@ bool Plater::priv::can_decrease_instances() const
|
||||
|
||||
bool Plater::priv::can_split_to_objects() const
|
||||
{
|
||||
return can_split();
|
||||
return q->can_split(true);
|
||||
}
|
||||
|
||||
bool Plater::priv::can_split_to_volumes() const
|
||||
{
|
||||
return (printer_technology != ptSLA) && can_split();
|
||||
return (printer_technology != ptSLA) && q->can_split(false);
|
||||
}
|
||||
|
||||
bool Plater::priv::can_arrange() const
|
||||
@ -4332,11 +4104,6 @@ bool Plater::priv::can_layers_editing() const
|
||||
return layers_height_allowed();
|
||||
}
|
||||
|
||||
void Plater::priv::update_object_menu()
|
||||
{
|
||||
sidebar->obj_list()->append_menu_items_add_volume(&object_menu);
|
||||
}
|
||||
|
||||
void Plater::priv::show_action_buttons(const bool ready_to_slice) const
|
||||
{
|
||||
// Cache this value, so that the callbacks from the RemovableDriveManager may repeat that value when calling show_action_buttons().
|
||||
@ -5363,7 +5130,7 @@ void Plater::export_stl(bool extended, bool selection_only)
|
||||
inst_mesh.merge(inst_supports_mesh);
|
||||
}
|
||||
|
||||
TriangleMesh inst_object_mesh = object->get_mesh_to_print();
|
||||
TriangleMesh inst_object_mesh = object->get_mesh_to_slice();
|
||||
inst_object_mesh.transform(mesh_trafo_inv);
|
||||
inst_object_mesh.transform(inst_transform, is_left_handed);
|
||||
|
||||
@ -6056,9 +5823,12 @@ void Plater::suppress_background_process(const bool stop_background_process)
|
||||
}
|
||||
|
||||
void Plater::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) { p->fix_through_netfabb(obj_idx, vol_idx); }
|
||||
|
||||
void Plater::update_object_menu() { p->update_object_menu(); }
|
||||
void Plater::show_action_buttons(const bool ready_to_slice) const { p->show_action_buttons(ready_to_slice); }
|
||||
void Plater::mirror(Axis axis) { p->mirror(axis); }
|
||||
void Plater::split_object() { p->split_object(); }
|
||||
void Plater::split_volume() { p->split_volume(); }
|
||||
void Plater::optimize_rotation() { p->m_ui_jobs.optimize_rotation();}
|
||||
void Plater::update_object_menu() { p->menus.update_object_menu(); }
|
||||
void Plater::show_action_buttons(const bool ready_to_slice) const { p->show_action_buttons(ready_to_slice); }
|
||||
|
||||
void Plater::copy_selection_to_clipboard()
|
||||
{
|
||||
@ -6115,7 +5885,7 @@ void Plater::msw_rescale()
|
||||
|
||||
p->sidebar->msw_rescale();
|
||||
|
||||
p->msw_rescale_object_menu();
|
||||
p->menus.msw_rescale();
|
||||
|
||||
Layout();
|
||||
GetParent()->Layout();
|
||||
@ -6127,7 +5897,7 @@ void Plater::sys_color_changed()
|
||||
p->sidebar->sys_color_changed();
|
||||
|
||||
// msw_rescale_menu updates just icons, so use it
|
||||
p->msw_rescale_object_menu();
|
||||
p->menus.msw_rescale();
|
||||
|
||||
Layout();
|
||||
GetParent()->Layout();
|
||||
@ -6292,6 +6062,8 @@ bool Plater::can_copy_to_clipboard() const
|
||||
bool Plater::can_undo() const { return p->undo_redo_stack().has_undo_snapshot(); }
|
||||
bool Plater::can_redo() const { return p->undo_redo_stack().has_redo_snapshot(); }
|
||||
bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); }
|
||||
bool Plater::can_mirror() const { return p->can_mirror(); }
|
||||
bool Plater::can_split(bool to_objects) const { return p->can_split(to_objects); }
|
||||
const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); }
|
||||
void Plater::clear_undo_redo_stack_main() { p->undo_redo_stack_main().clear(); }
|
||||
void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); }
|
||||
@ -6333,6 +6105,15 @@ void Plater::bring_instance_forward()
|
||||
p->bring_instance_forward();
|
||||
}
|
||||
|
||||
wxMenu* Plater::object_menu() { return p->menus.object_menu(); }
|
||||
wxMenu* Plater::part_menu() { return p->menus.part_menu(); }
|
||||
wxMenu* Plater::sla_object_menu() { return p->menus.sla_object_menu(); }
|
||||
wxMenu* Plater::default_menu() { return p->menus.default_menu(); }
|
||||
wxMenu* Plater::instance_menu() { return p->menus.instance_menu(); }
|
||||
wxMenu* Plater::layer_menu() { return p->menus.layer_menu(); }
|
||||
wxMenu* Plater::multi_selection_menu() { return p->menus.multi_selection_menu(); }
|
||||
|
||||
|
||||
SuppressBackgroundProcessingUpdate::SuppressBackgroundProcessingUpdate() :
|
||||
m_was_scheduled(wxGetApp().plater()->is_background_process_update_scheduled())
|
||||
{
|
||||
|
@ -271,6 +271,10 @@ public:
|
||||
void copy_selection_to_clipboard();
|
||||
void paste_from_clipboard();
|
||||
void search(bool plater_is_active);
|
||||
void mirror(Axis axis);
|
||||
void split_object();
|
||||
void split_volume();
|
||||
void optimize_rotation();
|
||||
|
||||
bool can_delete() const;
|
||||
bool can_delete_all() const;
|
||||
@ -287,6 +291,8 @@ public:
|
||||
bool can_undo() const;
|
||||
bool can_redo() const;
|
||||
bool can_reload_from_disk() const;
|
||||
bool can_mirror() const;
|
||||
bool can_split(bool to_objects) const;
|
||||
|
||||
void msw_rescale();
|
||||
void sys_color_changed();
|
||||
@ -375,6 +381,15 @@ public:
|
||||
bool PopupMenu(wxMenu *menu, const wxPoint& pos = wxDefaultPosition);
|
||||
bool PopupMenu(wxMenu *menu, int x, int y) { return this->PopupMenu(menu, wxPoint(x, y)); }
|
||||
|
||||
// get same Plater/ObjectList menus
|
||||
wxMenu* object_menu();
|
||||
wxMenu* part_menu();
|
||||
wxMenu* sla_object_menu();
|
||||
wxMenu* default_menu();
|
||||
wxMenu* instance_menu();
|
||||
wxMenu* layer_menu();
|
||||
wxMenu* multi_selection_menu();
|
||||
|
||||
private:
|
||||
struct priv;
|
||||
std::unique_ptr<priv> p;
|
||||
|
@ -148,74 +148,33 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
|
||||
speed_normal = first_layer_speed.get_abs_value(speed_normal);
|
||||
return (speed_normal > 0.) ? speed_normal : speed_max;
|
||||
};
|
||||
auto test_flow =
|
||||
[first_layer_extrusion_width_ptr, extrusion_width, nozzle_diameter, lh, bridging, bridge_speed, bridge_flow_ratio, limit_by_first_layer_speed, max_print_speed, &max_flow, &max_flow_extrusion_type]
|
||||
(FlowRole flow_role, const ConfigOptionFloatOrPercent &this_extrusion_width, double speed, const char *err_msg) {
|
||||
Flow flow = bridging ?
|
||||
Flow::new_from_config_width(flow_role, first_positive(first_layer_extrusion_width_ptr, this_extrusion_width, extrusion_width), nozzle_diameter, lh) :
|
||||
Flow::bridging_flow(nozzle_diameter * bridge_flow_ratio, nozzle_diameter);
|
||||
double volumetric_flow = flow.mm3_per_mm() * (bridging ? bridge_speed : limit_by_first_layer_speed(speed, max_print_speed));
|
||||
if (max_flow < volumetric_flow) {
|
||||
max_flow = volumetric_flow;
|
||||
max_flow_extrusion_type = _utf8(err_msg);
|
||||
}
|
||||
};
|
||||
if (perimeter_extruder_active) {
|
||||
double external_perimeter_rate = Flow::new_from_config_width(frExternalPerimeter,
|
||||
first_positive(first_layer_extrusion_width_ptr, external_perimeter_extrusion_width, extrusion_width),
|
||||
nozzle_diameter, lh, bfr).mm3_per_mm() *
|
||||
(bridging ? bridge_speed :
|
||||
limit_by_first_layer_speed(std::max(external_perimeter_speed, small_perimeter_speed), max_print_speed));
|
||||
if (max_flow < external_perimeter_rate) {
|
||||
max_flow = external_perimeter_rate;
|
||||
max_flow_extrusion_type = _utf8(L("external perimeters"));
|
||||
}
|
||||
double perimeter_rate = Flow::new_from_config_width(frPerimeter,
|
||||
first_positive(first_layer_extrusion_width_ptr, perimeter_extrusion_width, extrusion_width),
|
||||
nozzle_diameter, lh, bfr).mm3_per_mm() *
|
||||
(bridging ? bridge_speed :
|
||||
limit_by_first_layer_speed(std::max(perimeter_speed, small_perimeter_speed), max_print_speed));
|
||||
if (max_flow < perimeter_rate) {
|
||||
max_flow = perimeter_rate;
|
||||
max_flow_extrusion_type = _utf8(L("perimeters"));
|
||||
}
|
||||
}
|
||||
if (! bridging && infill_extruder_active) {
|
||||
double infill_rate = Flow::new_from_config_width(frInfill,
|
||||
first_positive(first_layer_extrusion_width_ptr, infill_extrusion_width, extrusion_width),
|
||||
nozzle_diameter, lh, bfr).mm3_per_mm() * limit_by_first_layer_speed(infill_speed, max_print_speed);
|
||||
if (max_flow < infill_rate) {
|
||||
max_flow = infill_rate;
|
||||
max_flow_extrusion_type = _utf8(L("infill"));
|
||||
}
|
||||
test_flow(frExternalPerimeter, external_perimeter_extrusion_width, std::max(external_perimeter_speed, small_perimeter_speed), L("external perimeters"));
|
||||
test_flow(frPerimeter, perimeter_extrusion_width, std::max(perimeter_speed, small_perimeter_speed), L("perimeters"));
|
||||
}
|
||||
if (! bridging && infill_extruder_active)
|
||||
test_flow(frInfill, infill_extrusion_width, infill_speed, L("infill"));
|
||||
if (solid_infill_extruder_active) {
|
||||
double solid_infill_rate = Flow::new_from_config_width(frInfill,
|
||||
first_positive(first_layer_extrusion_width_ptr, solid_infill_extrusion_width, extrusion_width),
|
||||
nozzle_diameter, lh, 0).mm3_per_mm() *
|
||||
(bridging ? bridge_speed : limit_by_first_layer_speed(solid_infill_speed, max_print_speed));
|
||||
if (max_flow < solid_infill_rate) {
|
||||
max_flow = solid_infill_rate;
|
||||
max_flow_extrusion_type = _utf8(L("solid infill"));
|
||||
}
|
||||
if (! bridging) {
|
||||
double top_solid_infill_rate = Flow::new_from_config_width(frInfill,
|
||||
first_positive(first_layer_extrusion_width_ptr, top_infill_extrusion_width, extrusion_width),
|
||||
nozzle_diameter, lh, bfr).mm3_per_mm() * limit_by_first_layer_speed(top_solid_infill_speed, max_print_speed);
|
||||
if (max_flow < top_solid_infill_rate) {
|
||||
max_flow = top_solid_infill_rate;
|
||||
max_flow_extrusion_type = _utf8(L("top solid infill"));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (support_material_extruder_active) {
|
||||
double support_material_rate = Flow::new_from_config_width(frSupportMaterial,
|
||||
first_positive(first_layer_extrusion_width_ptr, support_material_extrusion_width, extrusion_width),
|
||||
nozzle_diameter, lh, bfr).mm3_per_mm() *
|
||||
(bridging ? bridge_speed : limit_by_first_layer_speed(support_material_speed, max_print_speed));
|
||||
if (max_flow < support_material_rate) {
|
||||
max_flow = support_material_rate;
|
||||
max_flow_extrusion_type = _utf8(L("support"));
|
||||
}
|
||||
}
|
||||
if (support_material_interface_extruder_active) {
|
||||
double support_material_interface_rate = Flow::new_from_config_width(frSupportMaterialInterface,
|
||||
first_positive(first_layer_extrusion_width_ptr, support_material_extrusion_width, extrusion_width),
|
||||
nozzle_diameter, lh, bfr).mm3_per_mm() *
|
||||
(bridging ? bridge_speed : limit_by_first_layer_speed(support_material_interface_speed, max_print_speed));
|
||||
if (max_flow < support_material_interface_rate) {
|
||||
max_flow = support_material_interface_rate;
|
||||
max_flow_extrusion_type = _utf8(L("support interface"));
|
||||
}
|
||||
test_flow(frInfill, solid_infill_extrusion_width, solid_infill_speed, L("solid infill"));
|
||||
if (! bridging)
|
||||
test_flow(frInfill, top_infill_extrusion_width, top_solid_infill_speed, L("top solid infill"));
|
||||
}
|
||||
if (! bridging && support_material_extruder_active)
|
||||
test_flow(frSupportMaterial, support_material_extrusion_width, support_material_speed, L("support"));
|
||||
if (support_material_interface_extruder_active)
|
||||
test_flow(frSupportMaterialInterface, support_material_extrusion_width, support_material_interface_speed, L("support interface"));
|
||||
//FIXME handle gap_fill_speed
|
||||
if (! out.empty())
|
||||
out += "\n";
|
||||
@ -254,11 +213,11 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre
|
||||
Flow external_perimeter_flow = Flow::new_from_config_width(
|
||||
frExternalPerimeter,
|
||||
*print_config.opt<ConfigOptionFloatOrPercent>("external_perimeter_extrusion_width"),
|
||||
nozzle_diameter, layer_height, false);
|
||||
nozzle_diameter, layer_height);
|
||||
Flow perimeter_flow = Flow::new_from_config_width(
|
||||
frPerimeter,
|
||||
*print_config.opt<ConfigOptionFloatOrPercent>("perimeter_extrusion_width"),
|
||||
nozzle_diameter, layer_height, false);
|
||||
nozzle_diameter, layer_height);
|
||||
|
||||
|
||||
if (num_perimeters > 0) {
|
||||
@ -266,7 +225,7 @@ std::string PresetHints::recommended_thin_wall_thickness(const PresetBundle &pre
|
||||
out += (boost::format(_utf8(L("Recommended object thin wall thickness for layer height %.2f and"))) % layer_height).str() + " ";
|
||||
// Start with the width of two closely spaced
|
||||
try {
|
||||
double width = external_perimeter_flow.width + external_perimeter_flow.spacing();
|
||||
double width = external_perimeter_flow.width() + external_perimeter_flow.spacing();
|
||||
for (int i = 2; i <= num_lines; thin_walls ? ++ i : i += 2) {
|
||||
if (i > 2)
|
||||
out += ", ";
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "PrintHostDialogs.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
|
||||
#include <wx/frame.h>
|
||||
#include <wx/progdlg.h>
|
||||
@ -13,14 +14,18 @@
|
||||
#include <wx/wupdlock.h>
|
||||
#include <wx/debug.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "../Utils/PrintHost.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "NotificationManager.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
@ -182,15 +187,24 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
||||
|
||||
auto *topsizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
std::vector<int> widths;
|
||||
widths.reserve(6);
|
||||
if (!load_user_data(UDT_COLS, widths)) {
|
||||
widths.clear();
|
||||
for (size_t i = 0; i < 6; i++)
|
||||
widths.push_back(-1);
|
||||
}
|
||||
|
||||
job_list = new wxDataViewListCtrl(this, wxID_ANY);
|
||||
// Note: Keep these in sync with Column
|
||||
job_list->AppendTextColumn(_L("ID"), wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendProgressColumn(_L("Progress"), wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendTextColumn(_L("Status"), wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendTextColumn(_L("Host"), wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendTextColumn(_L("Filename"), wxDATAVIEW_CELL_INERT);
|
||||
job_list->AppendTextColumn(_L("ID"), wxDATAVIEW_CELL_INERT, widths[0], wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE);
|
||||
job_list->AppendProgressColumn(_L("Progress"), wxDATAVIEW_CELL_INERT, widths[1], wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE);
|
||||
job_list->AppendTextColumn(_L("Status"), wxDATAVIEW_CELL_INERT, widths[2], wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE);
|
||||
job_list->AppendTextColumn(_L("Host"), wxDATAVIEW_CELL_INERT, widths[3], wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE);
|
||||
job_list->AppendTextColumn(_CTX_utf8(L_CONTEXT("Size", "OfFile"), "OfFile"), wxDATAVIEW_CELL_INERT, widths[4], wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE);
|
||||
job_list->AppendTextColumn(_L("Filename"), wxDATAVIEW_CELL_INERT, widths[5], wxALIGN_LEFT, wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_SORTABLE);
|
||||
job_list->AppendTextColumn(_L("Error Message"), wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER, wxDATAVIEW_COL_HIDDEN);
|
||||
|
||||
|
||||
auto *btnsizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
btn_cancel = new wxButton(this, wxID_DELETE, _L("Cancel selected"));
|
||||
btn_cancel->Disable();
|
||||
@ -207,7 +221,21 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent)
|
||||
topsizer->Add(btnsizer, 0, wxEXPAND);
|
||||
SetSizer(topsizer);
|
||||
|
||||
SetSize(wxSize(HEIGHT * em, WIDTH * em));
|
||||
std::vector<int> size;
|
||||
SetSize(load_user_data(UDT_SIZE, size) ? wxSize(size[0] * em, size[1] * em) : wxSize(HEIGHT * em, WIDTH * em));
|
||||
|
||||
Bind(wxEVT_SIZE, [this, em](wxSizeEvent& evt) {
|
||||
OnSize(evt);
|
||||
save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS);
|
||||
});
|
||||
|
||||
std::vector<int> pos;
|
||||
if (load_user_data(UDT_POSITION, pos))
|
||||
SetPosition(wxPoint(pos[0], pos[1]));
|
||||
|
||||
Bind(wxEVT_MOVE, [this, em](wxMoveEvent& evt) {
|
||||
save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS);
|
||||
});
|
||||
|
||||
job_list->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent&) { on_list_select(); });
|
||||
|
||||
@ -238,11 +266,23 @@ void PrintHostQueueDialog::append_job(const PrintHostJob &job)
|
||||
fields.push_back(wxVariant(0));
|
||||
fields.push_back(wxVariant(_L("Enqueued")));
|
||||
fields.push_back(wxVariant(job.printhost->get_host()));
|
||||
boost::system::error_code ec;
|
||||
boost::uintmax_t size_i = boost::filesystem::file_size(job.upload_data.source_path, ec);
|
||||
std::stringstream stream;
|
||||
if (ec) {
|
||||
stream << "unknown";
|
||||
size_i = 0;
|
||||
BOOST_LOG_TRIVIAL(error) << ec.message();
|
||||
} else
|
||||
stream << std::fixed << std::setprecision(2) << ((float)size_i / 1024 / 1024) << "MB";
|
||||
fields.push_back(wxVariant(stream.str()));
|
||||
fields.push_back(wxVariant(job.upload_data.upload_path.string()));
|
||||
fields.push_back(wxVariant(""));
|
||||
job_list->AppendItem(fields, static_cast<wxUIntPtr>(ST_NEW));
|
||||
// Both strings are UTF-8 encoded.
|
||||
upload_names.emplace_back(job.printhost->get_host(), job.upload_data.upload_path.string());
|
||||
|
||||
//wxGetApp().notification_manager()->push_upload_job_notification(this, job_list->GetItemCount(), 0, job.upload_data.upload_path.string(), job.printhost->get_host());
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::on_dpi_changed(const wxRect &suggested_rect)
|
||||
@ -255,6 +295,8 @@ void PrintHostQueueDialog::on_dpi_changed(const wxRect &suggested_rect)
|
||||
|
||||
Fit();
|
||||
Refresh();
|
||||
|
||||
save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS);
|
||||
}
|
||||
|
||||
PrintHostQueueDialog::JobState PrintHostQueueDialog::get_state(int idx)
|
||||
@ -276,6 +318,8 @@ void PrintHostQueueDialog::set_state(int idx, JobState state)
|
||||
case ST_CANCELLED: job_list->SetValue(_L("Cancelled"), idx, COL_STATUS); break;
|
||||
case ST_COMPLETED: job_list->SetValue(_L("Completed"), idx, COL_STATUS); break;
|
||||
}
|
||||
// This might be ambigous call, but user data needs to be saved time to time
|
||||
save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS);
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::on_list_select()
|
||||
@ -304,6 +348,14 @@ void PrintHostQueueDialog::on_progress(Event &evt)
|
||||
}
|
||||
|
||||
on_list_select();
|
||||
|
||||
if (evt.progress > 0)
|
||||
{
|
||||
wxVariant nm, hst;
|
||||
job_list->GetValue(nm, evt.job_id, COL_FILENAME);
|
||||
job_list->GetValue(hst, evt.job_id, COL_HOST);
|
||||
wxGetApp().notification_manager()->set_upload_job_notification_percentage(evt.job_id + 1, boost::nowide::narrow(nm.GetString()), boost::nowide::narrow(hst.GetString()), 100 / evt.progress);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::on_error(Event &evt)
|
||||
@ -319,6 +371,11 @@ void PrintHostQueueDialog::on_error(Event &evt)
|
||||
on_list_select();
|
||||
|
||||
GUI::show_error(nullptr, errormsg);
|
||||
|
||||
wxVariant nm, hst;
|
||||
job_list->GetValue(nm, evt.job_id, COL_FILENAME);
|
||||
job_list->GetValue(hst, evt.job_id, COL_HOST);
|
||||
wxGetApp().notification_manager()->upload_job_notification_show_error(evt.job_id + 1, boost::nowide::narrow(nm.GetString()), boost::nowide::narrow(hst.GetString()));
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::on_cancel(Event &evt)
|
||||
@ -329,7 +386,13 @@ void PrintHostQueueDialog::on_cancel(Event &evt)
|
||||
job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS);
|
||||
|
||||
on_list_select();
|
||||
|
||||
wxVariant nm, hst;
|
||||
job_list->GetValue(nm, evt.job_id, COL_FILENAME);
|
||||
job_list->GetValue(hst, evt.job_id, COL_HOST);
|
||||
wxGetApp().notification_manager()->upload_job_notification_show_canceled(evt.job_id + 1, boost::nowide::narrow(nm.GetString()), boost::nowide::narrow(hst.GetString()));
|
||||
}
|
||||
|
||||
void PrintHostQueueDialog::get_active_jobs(std::vector<std::pair<std::string, std::string>>& ret)
|
||||
{
|
||||
int ic = job_list->GetItemCount();
|
||||
@ -343,4 +406,60 @@ void PrintHostQueueDialog::get_active_jobs(std::vector<std::pair<std::string, st
|
||||
}
|
||||
//job_list->data
|
||||
}
|
||||
void PrintHostQueueDialog::save_user_data(int udt)
|
||||
{
|
||||
const auto em = GetTextExtent("m").x;
|
||||
BOOST_LOG_TRIVIAL(error) << "save" << this->GetSize().x / em << " " << this->GetSize().y / em << " " << this->GetPosition().x << " " << this->GetPosition().y;
|
||||
auto *app_config = wxGetApp().app_config;
|
||||
if (udt & UserDataType::UDT_SIZE) {
|
||||
|
||||
app_config->set("print_host_queue_dialog_height", std::to_string(this->GetSize().x / em));
|
||||
app_config->set("print_host_queue_dialog_width", std::to_string(this->GetSize().y / em));
|
||||
}
|
||||
if (udt & UserDataType::UDT_POSITION)
|
||||
{
|
||||
app_config->set("print_host_queue_dialog_x", std::to_string(this->GetPosition().x));
|
||||
app_config->set("print_host_queue_dialog_y", std::to_string(this->GetPosition().y));
|
||||
}
|
||||
if (udt & UserDataType::UDT_COLS)
|
||||
{
|
||||
for (size_t i = 0; i < job_list->GetColumnCount() - 1; i++)
|
||||
{
|
||||
app_config->set("print_host_queue_dialog_column_" + std::to_string(i), std::to_string(job_list->GetColumn(i)->GetWidth()));
|
||||
}
|
||||
}
|
||||
}
|
||||
bool PrintHostQueueDialog::load_user_data(int udt, std::vector<int>& vector)
|
||||
{
|
||||
auto* app_config = wxGetApp().app_config;
|
||||
auto hasget = [app_config](const std::string& name, std::vector<int>& vector)->bool {
|
||||
if (app_config->has(name)) {
|
||||
vector.push_back(std::stoi(app_config->get(name)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (udt & UserDataType::UDT_SIZE) {
|
||||
if (!hasget("print_host_queue_dialog_height",vector))
|
||||
return false;
|
||||
if (!hasget("print_host_queue_dialog_width", vector))
|
||||
return false;
|
||||
}
|
||||
if (udt & UserDataType::UDT_POSITION)
|
||||
{
|
||||
if (!hasget("print_host_queue_dialog_x", vector))
|
||||
return false;
|
||||
if (!hasget("print_host_queue_dialog_y", vector))
|
||||
return false;
|
||||
}
|
||||
if (udt & UserDataType::UDT_COLS)
|
||||
{
|
||||
for (size_t i = 0; i < 6; i++)
|
||||
{
|
||||
if (!hasget("print_host_queue_dialog_column_" + std::to_string(i), vector))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}}
|
||||
|
@ -8,10 +8,8 @@
|
||||
#include <wx/event.h>
|
||||
#include <wx/dialog.h>
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "../Utils/PrintHost.hpp"
|
||||
|
||||
class wxButton;
|
||||
class wxTextCtrl;
|
||||
@ -65,6 +63,13 @@ public:
|
||||
|
||||
void append_job(const PrintHostJob &job);
|
||||
void get_active_jobs(std::vector<std::pair<std::string, std::string>>& ret);
|
||||
|
||||
virtual bool Show(bool show = true) override
|
||||
{
|
||||
if(!show)
|
||||
save_user_data(UDT_SIZE | UDT_POSITION | UDT_COLS);
|
||||
return DPIDialog::Show(show);
|
||||
}
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
|
||||
@ -74,8 +79,9 @@ private:
|
||||
COL_PROGRESS,
|
||||
COL_STATUS,
|
||||
COL_HOST,
|
||||
COL_SIZE,
|
||||
COL_FILENAME,
|
||||
COL_ERRORMSG,
|
||||
COL_ERRORMSG
|
||||
};
|
||||
|
||||
enum JobState {
|
||||
@ -89,6 +95,12 @@ private:
|
||||
|
||||
enum { HEIGHT = 60, WIDTH = 30, SPACING = 5 };
|
||||
|
||||
enum UserDataType{
|
||||
UDT_SIZE = 1,
|
||||
UDT_POSITION = 2,
|
||||
UDT_COLS = 4
|
||||
};
|
||||
|
||||
wxButton *btn_cancel;
|
||||
wxButton *btn_error;
|
||||
wxDataViewListCtrl *job_list;
|
||||
@ -105,6 +117,8 @@ private:
|
||||
void on_cancel(Event&);
|
||||
// This vector keep adress and filename of uploads. It is used when checking for running uploads during exit.
|
||||
std::vector<std::pair<std::string, std::string>> upload_names;
|
||||
void save_user_data(int);
|
||||
bool load_user_data(int, std::vector<int>&);
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event);
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "RemovableDriveManager.hpp"
|
||||
#include "slic3r/Utils/Platform.hpp"
|
||||
#include <libslic3r/libslic3r.h>
|
||||
|
||||
#include <boost/nowide/convert.hpp>
|
||||
@ -231,22 +232,28 @@ std::vector<DriveData> RemovableDriveManager::search_for_removable_drives() cons
|
||||
|
||||
#else
|
||||
|
||||
//search /media/* folder
|
||||
search_for_drives_internal::search_path("/media/*", "/media", current_drives);
|
||||
if (platform() == Platform::Linux && platform_flavor() == PlatformFlavor::LinuxOnChromium) {
|
||||
// ChromeOS specific: search /mnt/chromeos/removable/* folder
|
||||
search_for_drives_internal::search_path("/mnt/chromeos/removable/*", "/mnt/chromeos/removable", current_drives);
|
||||
} else {
|
||||
//search /media/* folder
|
||||
search_for_drives_internal::search_path("/media/*", "/media", current_drives);
|
||||
|
||||
//search_path("/Volumes/*", "/Volumes");
|
||||
std::string path(std::getenv("USER"));
|
||||
std::string pp(path);
|
||||
//search_path("/Volumes/*", "/Volumes");
|
||||
std::string path(std::getenv("USER"));
|
||||
std::string pp(path);
|
||||
|
||||
//search /media/USERNAME/* folder
|
||||
pp = "/media/"+pp;
|
||||
path = "/media/" + path + "/*";
|
||||
search_for_drives_internal::search_path(path, pp, current_drives);
|
||||
//search /media/USERNAME/* folder
|
||||
pp = "/media/"+pp;
|
||||
path = "/media/" + path + "/*";
|
||||
search_for_drives_internal::search_path(path, pp, current_drives);
|
||||
|
||||
//search /run/media/USERNAME/* folder
|
||||
path = "/run" + path;
|
||||
pp = "/run"+pp;
|
||||
search_for_drives_internal::search_path(path, pp, current_drives);
|
||||
}
|
||||
|
||||
//search /run/media/USERNAME/* folder
|
||||
path = "/run" + path;
|
||||
pp = "/run"+pp;
|
||||
search_for_drives_internal::search_path(path, pp, current_drives);
|
||||
#endif
|
||||
|
||||
return current_drives;
|
||||
@ -443,7 +450,10 @@ RemovableDriveManager::RemovableDrivesStatus RemovableDriveManager::status()
|
||||
RemovableDriveManager::RemovableDrivesStatus out;
|
||||
{
|
||||
tbb::mutex::scoped_lock lock(m_drives_mutex);
|
||||
out.has_eject = this->find_last_save_path_drive_data() != m_current_drives.end();
|
||||
out.has_eject =
|
||||
// Cannot control eject on Chromium.
|
||||
(platform() != Platform::Linux || platform_flavor() != PlatformFlavor::LinuxOnChromium) &&
|
||||
this->find_last_save_path_drive_data() != m_current_drives.end();
|
||||
out.has_removable_drives = ! m_current_drives.empty();
|
||||
}
|
||||
if (! out.has_eject)
|
||||
|
@ -1433,6 +1433,7 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("avoid_crossing_perimeters", category_path + "avoid-crossing-perimeters");
|
||||
optgroup->append_single_option_line("avoid_crossing_perimeters_max_detour", category_path + "avoid_crossing_perimeters_max_detour");
|
||||
optgroup->append_single_option_line("thin_walls", category_path + "detect-thin-walls");
|
||||
optgroup->append_single_option_line("thick_bridges", category_path + "thick_bridges");
|
||||
optgroup->append_single_option_line("overhangs", category_path + "detect-bridging-perimeters");
|
||||
|
||||
optgroup = page->new_optgroup(L("Advanced"));
|
||||
@ -1506,11 +1507,13 @@ void TabPrint::build()
|
||||
|
||||
optgroup = page->new_optgroup(L("Options for support material and raft"));
|
||||
optgroup->append_single_option_line("support_material_contact_distance", category_path + "contact-z-distance");
|
||||
optgroup->append_single_option_line("support_material_bottom_contact_distance", category_path + "contact-z-distance");
|
||||
optgroup->append_single_option_line("support_material_pattern", category_path + "pattern");
|
||||
optgroup->append_single_option_line("support_material_with_sheath", category_path + "with-sheath-around-the-support");
|
||||
optgroup->append_single_option_line("support_material_spacing", category_path + "pattern-spacing-0-inf");
|
||||
optgroup->append_single_option_line("support_material_angle", category_path + "pattern-angle");
|
||||
optgroup->append_single_option_line("support_material_interface_layers", category_path + "interface-layers");
|
||||
optgroup->append_single_option_line("support_material_bottom_interface_layers", category_path + "interface-layers");
|
||||
optgroup->append_single_option_line("support_material_interface_pattern", category_path + "interface-pattern");
|
||||
optgroup->append_single_option_line("support_material_interface_spacing", category_path + "interface-pattern-spacing");
|
||||
optgroup->append_single_option_line("support_material_interface_contact_loops", category_path + "interface-loops");
|
||||
@ -2528,7 +2531,7 @@ PageShp TabPrinter::build_kinematics_page()
|
||||
ConfigOptionDef def;
|
||||
def.type = coString;
|
||||
def.width = Field::def_width();
|
||||
def.gui_type = "legend";
|
||||
def.gui_type = ConfigOptionDef::GUIType::legend;
|
||||
def.mode = comAdvanced;
|
||||
def.tooltip = L("Values in this column are for Normal mode");
|
||||
def.set_default_value(new ConfigOptionString{ _(L("Normal")).ToUTF8().data() });
|
||||
|
@ -363,7 +363,7 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx)
|
||||
ModelObject *model_object = model.add_object();
|
||||
model_object->add_volume(*volumes[ivolume]);
|
||||
model_object->add_instance();
|
||||
if (!Slic3r::store_3mf(path_src.string().c_str(), &model, nullptr, false)) {
|
||||
if (!Slic3r::store_3mf(path_src.string().c_str(), &model, nullptr, false, nullptr, false)) {
|
||||
boost::filesystem::remove(path_src);
|
||||
throw Slic3r::RuntimeError(L("Export of a temporary 3mf file failed"));
|
||||
}
|
||||
|
78
src/slic3r/Utils/Platform.cpp
Normal file
78
src/slic3r/Utils/Platform.cpp
Normal file
@ -0,0 +1,78 @@
|
||||
#include "Platform.hpp"
|
||||
|
||||
|
||||
// For starting another PrusaSlicer instance on OSX.
|
||||
// Fails to compile on Windows on the build server.
|
||||
|
||||
#include <wx/stdpaths.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
static auto s_platform = Platform::Uninitialized;
|
||||
static auto s_platform_flavor = PlatformFlavor::Uninitialized;
|
||||
|
||||
void detect_platform()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
BOOST_LOG_TRIVIAL(info) << "Platform: Windows";
|
||||
s_platform = Platform::Windows;
|
||||
s_platform_flavor = PlatformFlavor::Generic;
|
||||
#elif defined(__APPLE__)
|
||||
BOOST_LOG_TRIVIAL(info) << "Platform: OSX";
|
||||
s_platform = Platform::OSX;
|
||||
s_platform_flavor = PlatformFlavor::Generic;
|
||||
#elif defined(__linux__)
|
||||
BOOST_LOG_TRIVIAL(info) << "Platform: Linux";
|
||||
s_platform = Platform::Linux;
|
||||
s_platform_flavor = PlatformFlavor::GenericLinux;
|
||||
// Test for Chromium.
|
||||
{
|
||||
FILE *f = ::fopen("/proc/version", "rt");
|
||||
if (f) {
|
||||
char buf[4096];
|
||||
// Read the 1st line.
|
||||
if (::fgets(buf, 4096, f)) {
|
||||
if (strstr(buf, "Chromium OS") != nullptr) {
|
||||
s_platform_flavor = PlatformFlavor::LinuxOnChromium;
|
||||
BOOST_LOG_TRIVIAL(info) << "Platform flavor: LinuxOnChromium";
|
||||
} else if (strstr(buf, "microsoft") != nullptr || strstr(buf, "Microsoft") != nullptr) {
|
||||
if (boost::filesystem::exists("/run/WSL") && getenv("WSL_INTEROP") != nullptr) {
|
||||
BOOST_LOG_TRIVIAL(info) << "Platform flavor: WSL2";
|
||||
s_platform_flavor = PlatformFlavor::WSL2;
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(info) << "Platform flavor: WSL";
|
||||
s_platform_flavor = PlatformFlavor::WSL;
|
||||
}
|
||||
}
|
||||
}
|
||||
::fclose(f);
|
||||
}
|
||||
}
|
||||
#elif defined(__OpenBSD__)
|
||||
BOOST_LOG_TRIVIAL(info) << "Platform: OpenBSD";
|
||||
s_platform = Platform::BSDUnix;
|
||||
s_platform_flavor = PlatformFlavor::OpenBSD;
|
||||
#else
|
||||
// This should not happen.
|
||||
BOOST_LOG_TRIVIAL(info) << "Platform: Unknown";
|
||||
static_assert(false, "Unknown platform detected");
|
||||
s_platform = Platform::Unknown;
|
||||
s_platform_flavor = PlatformFlavor::Unknown;
|
||||
#endif
|
||||
}
|
||||
|
||||
Platform platform()
|
||||
{
|
||||
return s_platform;
|
||||
}
|
||||
|
||||
PlatformFlavor platform_flavor()
|
||||
{
|
||||
return s_platform_flavor;
|
||||
}
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
44
src/slic3r/Utils/Platform.hpp
Normal file
44
src/slic3r/Utils/Platform.hpp
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef SLIC3R_GUI_Utils_Platform_HPP
|
||||
#define SLIC3R_GUI_Utils_Platform_HPP
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
enum class Platform
|
||||
{
|
||||
Uninitialized,
|
||||
Unknown,
|
||||
Windows,
|
||||
OSX,
|
||||
Linux,
|
||||
BSDUnix,
|
||||
};
|
||||
|
||||
enum class PlatformFlavor
|
||||
{
|
||||
Uninitialized,
|
||||
Unknown,
|
||||
// For Windows and OSX, until we need to be more specific.
|
||||
Generic,
|
||||
// For Platform::Linux
|
||||
GenericLinux,
|
||||
LinuxOnChromium,
|
||||
// Microsoft's Windows on Linux (Linux kernel simulated on NTFS kernel)
|
||||
WSL,
|
||||
// Microsoft's Windows on Linux, version 2 (virtual machine)
|
||||
WSL2,
|
||||
// For Platform::BSDUnix
|
||||
OpenBSD,
|
||||
};
|
||||
|
||||
// To be called on program start-up.
|
||||
void detect_platform();
|
||||
|
||||
Platform platform();
|
||||
PlatformFlavor platform_flavor();
|
||||
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // SLIC3R_GUI_Utils_Platform_HPP
|
@ -31,7 +31,6 @@ use Slic3r::Test;
|
||||
role => FLOW_ROLE_SUPPORT_MATERIAL,
|
||||
nozzle_diameter => $print->config->nozzle_diameter->[$object_config->support_material_extruder-1] // $print->config->nozzle_diameter->[0],
|
||||
layer_height => $object_config->layer_height,
|
||||
bridge_flow_ratio => 0,
|
||||
);
|
||||
my $support = Slic3r::Print::SupportMaterial->new(
|
||||
object_config => $print->print->objects->[0]->config,
|
||||
|
@ -96,43 +96,41 @@ SCENARIO("Flow: Flow math for non-bridges", "[Flow]") {
|
||||
GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") {
|
||||
ConfigOptionFloatOrPercent width(1.0, false);
|
||||
float nozzle_diameter = 0.4f;
|
||||
float bridge_flow = 0.f;
|
||||
float layer_height = 0.5f;
|
||||
float layer_height = 0.4f;
|
||||
|
||||
// Spacing for non-bridges is has some overlap
|
||||
THEN("External perimeter flow has spacing fixed to 1.125 * nozzle_diameter") {
|
||||
auto flow = Flow::new_from_config_width(frExternalPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, bridge_flow);
|
||||
auto flow = Flow::new_from_config_width(frExternalPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height);
|
||||
REQUIRE(flow.spacing() == Approx(1.125 * nozzle_diameter - layer_height * (1.0 - PI / 4.0)));
|
||||
}
|
||||
|
||||
THEN("Internal perimeter flow has spacing fixed to 1.125 * nozzle_diameter") {
|
||||
auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, bridge_flow);
|
||||
auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height);
|
||||
REQUIRE(flow.spacing() == Approx(1.125 *nozzle_diameter - layer_height * (1.0 - PI / 4.0)));
|
||||
}
|
||||
THEN("Spacing for supplied width is 0.8927f") {
|
||||
auto flow = Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height, bridge_flow);
|
||||
auto flow = Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height);
|
||||
REQUIRE(flow.spacing() == Approx(width.value - layer_height * (1.0 - PI / 4.0)));
|
||||
flow = Flow::new_from_config_width(frPerimeter, width, nozzle_diameter, layer_height, bridge_flow);
|
||||
flow = Flow::new_from_config_width(frPerimeter, width, nozzle_diameter, layer_height);
|
||||
REQUIRE(flow.spacing() == Approx(width.value - layer_height * (1.0 - PI / 4.0)));
|
||||
}
|
||||
}
|
||||
/// Check the min/max
|
||||
GIVEN("Nozzle Diameter of 0.25") {
|
||||
float nozzle_diameter = 0.25f;
|
||||
float bridge_flow = 0.f;
|
||||
float layer_height = 0.5f;
|
||||
WHEN("layer height is set to 0.2") {
|
||||
layer_height = 0.15f;
|
||||
THEN("Max width is set.") {
|
||||
auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, bridge_flow);
|
||||
REQUIRE(flow.width == Approx(1.125 * nozzle_diameter));
|
||||
auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height);
|
||||
REQUIRE(flow.width() == Approx(1.125 * nozzle_diameter));
|
||||
}
|
||||
}
|
||||
WHEN("Layer height is set to 0.2") {
|
||||
layer_height = 0.3f;
|
||||
WHEN("Layer height is set to 0.25") {
|
||||
layer_height = 0.25f;
|
||||
THEN("Min width is set.") {
|
||||
auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height, bridge_flow);
|
||||
REQUIRE(flow.width == Approx(1.125 * nozzle_diameter));
|
||||
auto flow = Flow::new_from_config_width(frPerimeter, ConfigOptionFloatOrPercent(0, false), nozzle_diameter, layer_height);
|
||||
REQUIRE(flow.width() == Approx(1.125 * nozzle_diameter));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -143,7 +141,7 @@ SCENARIO("Flow: Flow math for non-bridges", "[Flow]") {
|
||||
GIVEN("Input spacing of 0.414159 and a total width of 2") {
|
||||
double in_spacing = 0.414159;
|
||||
double total_width = 2.0;
|
||||
auto flow = Flow::new_from_spacing(1.0, 0.4, 0.3, false);
|
||||
auto flow = Flow::new_from_spacing(1.0, 0.4, 0.3);
|
||||
WHEN("solid_spacing() is called") {
|
||||
double result = flow.solid_spacing(total_width, in_spacing);
|
||||
THEN("Yielded spacing is greater than 0") {
|
||||
@ -158,41 +156,12 @@ SCENARIO("Flow: Flow math for non-bridges", "[Flow]") {
|
||||
/// Spacing, width calculation for bridge extrusions
|
||||
SCENARIO("Flow: Flow math for bridges", "[Flow]") {
|
||||
GIVEN("Nozzle Diameter of 0.4, a desired width of 1mm and layer height of 0.5") {
|
||||
auto width = ConfigOptionFloatOrPercent(1.0, false);
|
||||
float nozzle_diameter = 0.4f;
|
||||
float bridge_flow = 1.0f;
|
||||
float layer_height = 0.5f;
|
||||
WHEN("Flow role is frExternalPerimeter") {
|
||||
auto flow = Flow::new_from_config_width(frExternalPerimeter, width, nozzle_diameter, layer_height, bridge_flow);
|
||||
auto flow = Flow::bridging_flow(nozzle_diameter * sqrt(bridge_flow), nozzle_diameter);
|
||||
THEN("Bridge width is same as nozzle diameter") {
|
||||
REQUIRE(flow.width == Approx(nozzle_diameter));
|
||||
}
|
||||
THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") {
|
||||
REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING));
|
||||
}
|
||||
}
|
||||
WHEN("Flow role is frInfill") {
|
||||
auto flow = Flow::new_from_config_width(frInfill, width, nozzle_diameter, layer_height, bridge_flow);
|
||||
THEN("Bridge width is same as nozzle diameter") {
|
||||
REQUIRE(flow.width == Approx(nozzle_diameter));
|
||||
}
|
||||
THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") {
|
||||
REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING));
|
||||
}
|
||||
}
|
||||
WHEN("Flow role is frPerimeter") {
|
||||
auto flow = Flow::new_from_config_width(frPerimeter, width, nozzle_diameter, layer_height, bridge_flow);
|
||||
THEN("Bridge width is same as nozzle diameter") {
|
||||
REQUIRE(flow.width == Approx(nozzle_diameter));
|
||||
}
|
||||
THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") {
|
||||
REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING));
|
||||
}
|
||||
}
|
||||
WHEN("Flow role is frSupportMaterial") {
|
||||
auto flow = Flow::new_from_config_width(frSupportMaterial, width, nozzle_diameter, layer_height, bridge_flow);
|
||||
THEN("Bridge width is same as nozzle diameter") {
|
||||
REQUIRE(flow.width == Approx(nozzle_diameter));
|
||||
REQUIRE(flow.width() == Approx(nozzle_diameter));
|
||||
}
|
||||
THEN("Bridge spacing is same as nozzle diameter + BRIDGE_EXTRA_SPACING") {
|
||||
REQUIRE(flow.spacing() == Approx(nozzle_diameter + BRIDGE_EXTRA_SPACING));
|
||||
|
@ -53,8 +53,8 @@ SCENARIO("Print: Changing number of solid surfaces does not cause all surfaces t
|
||||
config.set_deserialize({
|
||||
{ "top_solid_layers", 2 },
|
||||
{ "bottom_solid_layers", 1 },
|
||||
{ "layer_height", 0.5 }, // get a known number of layers
|
||||
{ "first_layer_height", 0.5 }
|
||||
{ "layer_height", 0.25 }, // get a known number of layers
|
||||
{ "first_layer_height", 0.25 }
|
||||
});
|
||||
Slic3r::Print print;
|
||||
Slic3r::Model model;
|
||||
@ -72,8 +72,8 @@ SCENARIO("Print: Changing number of solid surfaces does not cause all surfaces t
|
||||
};
|
||||
print.process();
|
||||
test_is_solid_infill(0, 0); // should be solid
|
||||
test_is_solid_infill(0, 39); // should be solid
|
||||
test_is_solid_infill(0, 38); // should be solid
|
||||
test_is_solid_infill(0, 79); // should be solid
|
||||
test_is_solid_infill(0, 78); // should be solid
|
||||
WHEN("Model is re-sliced with top_solid_layers == 3") {
|
||||
config.set("top_solid_layers", 3);
|
||||
print.apply(model, config);
|
||||
@ -82,9 +82,9 @@ SCENARIO("Print: Changing number of solid surfaces does not cause all surfaces t
|
||||
test_is_solid_infill(0, 0);
|
||||
}
|
||||
AND_THEN("Print object has 3 top solid layers") {
|
||||
test_is_solid_infill(0, 39);
|
||||
test_is_solid_infill(0, 38);
|
||||
test_is_solid_infill(0, 37);
|
||||
test_is_solid_infill(0, 79);
|
||||
test_is_solid_infill(0, 78);
|
||||
test_is_solid_infill(0, 77);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -244,23 +244,24 @@ SCENARIO( "PrintGCode basic functionality", "[PrintGCode]") {
|
||||
{ "complete_objects", true },
|
||||
{ "gcode_comments", true },
|
||||
{ "layer_gcode", ";Layer:[layer_num] ([layer_z] mm)" },
|
||||
{ "layer_height", 1.0 },
|
||||
{ "first_layer_height", 1.0 }
|
||||
{ "layer_height", 0.1 },
|
||||
{ "first_layer_height", 0.1 }
|
||||
});
|
||||
// End of the 1st object.
|
||||
size_t pos = gcode.find(";Layer:19 ");
|
||||
std::string token = ";Layer:199 ";
|
||||
size_t pos = gcode.find(token);
|
||||
THEN("First and second object last layer is emitted") {
|
||||
// First object
|
||||
REQUIRE(pos != std::string::npos);
|
||||
pos += 10;
|
||||
pos += token.size();
|
||||
REQUIRE(pos < gcode.size());
|
||||
double z = 0;
|
||||
REQUIRE((sscanf(gcode.data() + pos, "(%lf mm)", &z) == 1));
|
||||
REQUIRE(z == Approx(20.));
|
||||
// Second object
|
||||
pos = gcode.find(";Layer:39 ", pos);
|
||||
pos = gcode.find(";Layer:399 ", pos);
|
||||
REQUIRE(pos != std::string::npos);
|
||||
pos += 10;
|
||||
pos += token.size();
|
||||
REQUIRE(pos < gcode.size());
|
||||
REQUIRE((sscanf(gcode.data() + pos, "(%lf mm)", &z) == 1));
|
||||
REQUIRE(z == Approx(20.));
|
||||
|
@ -434,7 +434,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") {
|
||||
ExPolygon expoly = contour_with_hole();
|
||||
WHEN("Compensated") {
|
||||
// Elephant foot compensation shall not pinch off bits from this contour.
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.2f);
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f), 0.2f);
|
||||
#ifdef TESTS_EXPORT_SVGS
|
||||
SVG::export_expolygons(debug_out_path("elephant_foot_compensation_with_hole.svg").c_str(),
|
||||
{ { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } },
|
||||
@ -449,7 +449,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") {
|
||||
GIVEN("Tiny contour") {
|
||||
ExPolygon expoly({ { 133382606, 94912473 }, { 134232493, 95001115 }, { 133783926, 95159440 }, { 133441897, 95180666 }, { 133408242, 95191984 }, { 133339012, 95166830 }, { 132991642, 95011087 }, { 133206549, 94908304 } });
|
||||
WHEN("Compensated") {
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.2f);
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f), 0.2f);
|
||||
#ifdef TESTS_EXPORT_SVGS
|
||||
SVG::export_expolygons(debug_out_path("elephant_foot_compensation_tiny.svg").c_str(),
|
||||
{ { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } },
|
||||
@ -464,7 +464,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") {
|
||||
GIVEN("Large box") {
|
||||
ExPolygon expoly( { {50000000, 50000000 }, { 0, 50000000 }, { 0, 0 }, { 50000000, 0 } } );
|
||||
WHEN("Compensated") {
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.21f);
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f), 0.21f);
|
||||
#ifdef TESTS_EXPORT_SVGS
|
||||
SVG::export_expolygons(debug_out_path("elephant_foot_compensation_large_box.svg").c_str(),
|
||||
{ { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } },
|
||||
@ -479,7 +479,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") {
|
||||
GIVEN("Thin ring (GH issue #2085)") {
|
||||
ExPolygon expoly = thin_ring();
|
||||
WHEN("Compensated") {
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.25f);
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f), 0.25f);
|
||||
#ifdef TESTS_EXPORT_SVGS
|
||||
SVG::export_expolygons(debug_out_path("elephant_foot_compensation_thin_ring.svg").c_str(),
|
||||
{ { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } },
|
||||
@ -532,7 +532,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") {
|
||||
expoly = union_ex({ expoly, expoly2 }).front();
|
||||
|
||||
WHEN("Partially compensated") {
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f, false), 0.25f);
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f), 0.25f);
|
||||
#ifdef TESTS_EXPORT_SVGS
|
||||
SVG::export_expolygons(debug_out_path("elephant_foot_compensation_0.svg").c_str(),
|
||||
{ { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } },
|
||||
@ -543,7 +543,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") {
|
||||
}
|
||||
}
|
||||
WHEN("Fully compensated") {
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.35f, 0.2f, 0.4f, false), 0.17f);
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.35f, 0.2f, 0.4f), 0.17f);
|
||||
#ifdef TESTS_EXPORT_SVGS
|
||||
SVG::export_expolygons(debug_out_path("elephant_foot_compensation_1.svg").c_str(),
|
||||
{ { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } },
|
||||
@ -558,7 +558,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") {
|
||||
GIVEN("Box with hole close to wall (GH issue #2998)") {
|
||||
ExPolygon expoly = box_with_hole_close_to_wall();
|
||||
WHEN("Compensated") {
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.25f);
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f), 0.25f);
|
||||
#ifdef TESTS_EXPORT_SVGS
|
||||
SVG::export_expolygons(debug_out_path("elephant_foot_compensation_2.svg").c_str(),
|
||||
{ { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } },
|
||||
@ -575,7 +575,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") {
|
||||
ExPolygon expoly = spirograph_gear_1mm();
|
||||
|
||||
WHEN("Partially compensated") {
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f, false), 0.25f);
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f), 0.25f);
|
||||
#ifdef TESTS_EXPORT_SVGS
|
||||
SVG::export_expolygons(debug_out_path("elephant_foot_compensation_2.svg").c_str(),
|
||||
{ { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } },
|
||||
@ -586,7 +586,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") {
|
||||
}
|
||||
}
|
||||
WHEN("Fully compensated") {
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.35f, 0.2f, 0.4f, false), 0.17f);
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.35f, 0.2f, 0.4f), 0.17f);
|
||||
#ifdef TESTS_EXPORT_SVGS
|
||||
SVG::export_expolygons(debug_out_path("elephant_foot_compensation_3.svg").c_str(),
|
||||
{ { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } },
|
||||
@ -597,7 +597,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") {
|
||||
}
|
||||
}
|
||||
WHEN("Brutally compensated") {
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f, false), 0.6f);
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.45f, 0.2f, 0.4f), 0.6f);
|
||||
#ifdef TESTS_EXPORT_SVGS
|
||||
SVG::export_expolygons(debug_out_path("elephant_foot_compensation_4.svg").c_str(),
|
||||
{ { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } },
|
||||
@ -612,7 +612,7 @@ SCENARIO("Elephant foot compensation", "[ElephantFoot]") {
|
||||
GIVEN("Vase with fins") {
|
||||
ExPolygon expoly = vase_with_fins();
|
||||
WHEN("Compensated") {
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f, false), 0.41f);
|
||||
ExPolygon expoly_compensated = elephant_foot_compensation(expoly, Flow(0.419999987f, 0.2f, 0.4f), 0.41f);
|
||||
#ifdef TESTS_EXPORT_SVGS
|
||||
SVG::export_expolygons(debug_out_path("elephant_foot_compensation_vase_with_fins.svg").c_str(),
|
||||
{ { { expoly }, { "gray", "black", "blue", coord_t(scale_(0.02)), 0.5f, "black", coord_t(scale_(0.05)) } },
|
||||
|
@ -2,45 +2,21 @@
|
||||
#include <fstream>
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
#include <libslic3r/TriangleMesh.hpp>
|
||||
#include "libslic3r/SLA/Hollowing.hpp"
|
||||
#include <openvdb/tools/Filter.h>
|
||||
#include "libslic3r/Format/OBJ.hpp"
|
||||
|
||||
#include <libnest2d/tools/benchmark.h>
|
||||
TEST_CASE("Hollow two overlapping spheres") {
|
||||
using namespace Slic3r;
|
||||
|
||||
#include <libslic3r/SimplifyMesh.hpp>
|
||||
TriangleMesh sphere1 = make_sphere(10., 2 * PI / 20.), sphere2 = sphere1;
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
#define PATH_SEPARATOR R"(\)"
|
||||
#else
|
||||
#define PATH_SEPARATOR R"(/)"
|
||||
#endif
|
||||
sphere1.translate(-5.f, 0.f, 0.f);
|
||||
sphere2.translate( 5.f, 0.f, 0.f);
|
||||
|
||||
static Slic3r::TriangleMesh load_model(const std::string &obj_filename)
|
||||
{
|
||||
Slic3r::TriangleMesh mesh;
|
||||
auto fpath = TEST_DATA_DIR PATH_SEPARATOR + obj_filename;
|
||||
Slic3r::load_obj(fpath.c_str(), &mesh);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("Negative 3D offset should produce smaller object.", "[Hollowing]")
|
||||
{
|
||||
Slic3r::TriangleMesh in_mesh = load_model("20mm_cube.obj");
|
||||
Benchmark bench;
|
||||
bench.start();
|
||||
|
||||
std::unique_ptr<Slic3r::TriangleMesh> out_mesh_ptr =
|
||||
Slic3r::sla::generate_interior(in_mesh);
|
||||
|
||||
bench.stop();
|
||||
|
||||
std::cout << "Elapsed processing time: " << bench.getElapsedSec() << std::endl;
|
||||
|
||||
if (out_mesh_ptr) in_mesh.merge(*out_mesh_ptr);
|
||||
in_mesh.require_shared_vertices();
|
||||
in_mesh.WriteOBJFile("merged_out.obj");
|
||||
sphere1.merge(sphere2);
|
||||
sphere1.require_shared_vertices();
|
||||
|
||||
sla::hollow_mesh(sphere1, sla::HollowingConfig{}, sla::HollowingFlags::hfRemoveInsideTriangles);
|
||||
|
||||
sphere1.WriteOBJFile("twospheres.obj");
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,6 @@ SCENARIO("Iterators", "[MutablePolygon]") {
|
||||
}
|
||||
WHEN("Deleting 1st point") {
|
||||
auto it_2nd = p.begin().next();
|
||||
auto it_3rd = p.end();
|
||||
auto it = p.begin().remove();
|
||||
THEN("Size is 2") {
|
||||
REQUIRE(p.size() == 2);
|
||||
@ -59,7 +58,6 @@ SCENARIO("Iterators", "[MutablePolygon]") {
|
||||
WHEN("Deleting 2nd point") {
|
||||
auto it_1st = p.begin();
|
||||
auto it_2nd = it_1st.next();
|
||||
auto it_3rd = p.end();
|
||||
auto it = it_2nd.remove();
|
||||
THEN("Size is 2") {
|
||||
REQUIRE(p.size() == 2);
|
||||
|
@ -88,9 +88,9 @@ void test_supports(const std::string &obj_filename,
|
||||
REQUIRE_FALSE(mesh.empty());
|
||||
|
||||
if (hollowingcfg.enabled) {
|
||||
auto inside = sla::generate_interior(mesh, hollowingcfg);
|
||||
REQUIRE(inside);
|
||||
mesh.merge(*inside);
|
||||
sla::InteriorPtr interior = sla::generate_interior(mesh, hollowingcfg);
|
||||
REQUIRE(interior);
|
||||
mesh.merge(sla::get_mesh(*interior));
|
||||
mesh.require_shared_vertices();
|
||||
}
|
||||
|
||||
|
@ -158,7 +158,6 @@ sub new {
|
||||
my $self = $class->_new(
|
||||
@args{qw(width height nozzle_diameter)},
|
||||
);
|
||||
$self->set_bridge($args{bridge} // 0);
|
||||
return $self;
|
||||
}
|
||||
|
||||
@ -166,7 +165,7 @@ sub new_from_width {
|
||||
my ($class, %args) = @_;
|
||||
|
||||
return $class->_new_from_width(
|
||||
@args{qw(role width nozzle_diameter layer_height bridge_flow_ratio)},
|
||||
@args{qw(role width nozzle_diameter layer_height)},
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -170,19 +170,6 @@ print_config_def()
|
||||
throw "Unknown option type";
|
||||
}
|
||||
(void)hv_stores( hv, "type", newSVpv(opt_type, 0) );
|
||||
(void)hv_stores( hv, "gui_type", newSVpvn(optdef->gui_type.c_str(), optdef->gui_type.length()) );
|
||||
(void)hv_stores( hv, "gui_flags", newSVpvn(optdef->gui_flags.c_str(), optdef->gui_flags.length()) );
|
||||
(void)hv_stores( hv, "label", newSVpvn_utf8(optdef->label.c_str(), optdef->label.length(), true) );
|
||||
if (!optdef->full_label.empty())
|
||||
(void)hv_stores( hv, "full_label", newSVpvn_utf8(optdef->full_label.c_str(), optdef->full_label.length(), true) );
|
||||
(void)hv_stores( hv, "category", newSVpvn_utf8(optdef->category.c_str(), optdef->category.length(), true) );
|
||||
(void)hv_stores( hv, "tooltip", newSVpvn_utf8(optdef->tooltip.c_str(), optdef->tooltip.length(), true) );
|
||||
(void)hv_stores( hv, "sidetext", newSVpvn_utf8(optdef->sidetext.c_str(), optdef->sidetext.length(), true) );
|
||||
(void)hv_stores( hv, "cli", newSVpvn(optdef->cli.c_str(), optdef->cli.length()) );
|
||||
(void)hv_stores( hv, "ratio_over", newSVpvn(optdef->ratio_over.c_str(), optdef->ratio_over.length()) );
|
||||
(void)hv_stores( hv, "multiline", newSViv(optdef->multiline ? 1 : 0) );
|
||||
(void)hv_stores( hv, "full_width", newSViv(optdef->full_width ? 1 : 0) );
|
||||
(void)hv_stores( hv, "readonly", newSViv(optdef->readonly ? 1 : 0) );
|
||||
(void)hv_stores( hv, "height", newSViv(optdef->height) );
|
||||
(void)hv_stores( hv, "width", newSViv(optdef->width) );
|
||||
(void)hv_stores( hv, "min", newSViv(optdef->min) );
|
||||
|
@ -8,41 +8,30 @@
|
||||
%name{Slic3r::Flow} class Flow {
|
||||
~Flow();
|
||||
%name{_new} Flow(float width, float height, float nozzle_diameter);
|
||||
void set_height(float height)
|
||||
%code{% THIS->height = height; %};
|
||||
void set_bridge(bool bridge)
|
||||
%code{% THIS->bridge = bridge; %};
|
||||
Clone<Flow> clone()
|
||||
%code{% RETVAL = THIS; %};
|
||||
|
||||
float width()
|
||||
%code{% RETVAL = THIS->width; %};
|
||||
float height()
|
||||
%code{% RETVAL = THIS->height; %};
|
||||
float nozzle_diameter()
|
||||
%code{% RETVAL = THIS->nozzle_diameter; %};
|
||||
bool bridge()
|
||||
%code{% RETVAL = THIS->bridge; %};
|
||||
float width();
|
||||
float height();
|
||||
float nozzle_diameter();
|
||||
bool bridge();
|
||||
float spacing();
|
||||
float spacing_to(Flow* other)
|
||||
%code{% RETVAL = THIS->spacing(*other); %};
|
||||
int scaled_width();
|
||||
int scaled_spacing();
|
||||
double mm3_per_mm();
|
||||
%{
|
||||
|
||||
Flow*
|
||||
_new_from_width(CLASS, role, width, nozzle_diameter, height, bridge_flow_ratio)
|
||||
_new_from_width(CLASS, role, width, nozzle_diameter, height)
|
||||
char* CLASS;
|
||||
FlowRole role;
|
||||
std::string width;
|
||||
float nozzle_diameter;
|
||||
float height;
|
||||
float bridge_flow_ratio;
|
||||
CODE:
|
||||
ConfigOptionFloatOrPercent optwidth;
|
||||
optwidth.deserialize(width);
|
||||
RETVAL = new Flow(Flow::new_from_config_width(role, optwidth, nozzle_diameter, height, bridge_flow_ratio));
|
||||
RETVAL = new Flow(Flow::new_from_config_width(role, optwidth, nozzle_diameter, height));
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
|
@ -23,8 +23,8 @@
|
||||
Ref<ExtrusionEntityCollection> fills()
|
||||
%code%{ RETVAL = &THIS->fills; %};
|
||||
|
||||
Clone<Flow> flow(FlowRole role, bool bridge = false, double width = -1)
|
||||
%code%{ RETVAL = THIS->flow(role, bridge, width); %};
|
||||
Clone<Flow> flow(FlowRole role)
|
||||
%code%{ RETVAL = THIS->flow(role); %};
|
||||
void prepare_fill_surfaces();
|
||||
void make_perimeters(SurfaceCollection* slices, SurfaceCollection* fill_surfaces)
|
||||
%code%{ THIS->make_perimeters(*slices, fill_surfaces); %};
|
||||
|
@ -33,9 +33,6 @@ _constant()
|
||||
Ref<StaticPrintConfig> config()
|
||||
%code%{ RETVAL = &THIS->config(); %};
|
||||
Ref<Print> print();
|
||||
|
||||
Clone<Flow> flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, PrintObject* object)
|
||||
%code%{ RETVAL = THIS->flow(role, layer_height, bridge, first_layer, width, *object); %};
|
||||
};
|
||||
|
||||
%name{Slic3r::Print::Object} class PrintObject {
|
||||
|
Loading…
Reference in New Issue
Block a user