Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_preview_layout
This commit is contained in:
commit
b08c3248b1
43 changed files with 1006 additions and 306 deletions
|
@ -48,6 +48,7 @@
|
|||
# enabled_tags = ...
|
||||
# disabled_tags = ...
|
||||
# supported tags are: simple; advanced; expert; FFF; MMU; SLA; Windows; Linux; OSX;
|
||||
# and all filament types: PLA; PET; ABS; ASA; FLEX; HIPS; EDGE; NGEN; NYLON; PVA; PC; PP; PEI; PEEK; PEKK; POM; PSU; PVDF; SCAFF;
|
||||
# Tags are case sensitive.
|
||||
# FFF is affirmative for both one or more extruder printers.
|
||||
# Algorithm shows hint only if ALL enabled tags are affirmative. (so never do enabled_tags = FFF; SLA;)
|
||||
|
|
67
resources/icons/notification_info.svg
Normal file
67
resources/icons/notification_info.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.0"
|
||||
id="warning"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 200 200"
|
||||
enable-background="new 0 0 100 100"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="notification_info.svg"
|
||||
width="200"
|
||||
height="200"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)"><metadata
|
||||
id="metadata19"><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="defs17" /><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="2560"
|
||||
inkscape:window-height="1377"
|
||||
id="namedview15"
|
||||
showgrid="false"
|
||||
inkscape:zoom="5.04"
|
||||
inkscape:cx="108.69674"
|
||||
inkscape:cy="117.65848"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="warning" />
|
||||
|
||||
|
||||
|
||||
<g
|
||||
style="fill:#ed6b21;fill-opacity:1"
|
||||
transform="matrix(2,0,0,2,0.17117674,0.68464711)"
|
||||
id="g8"><path
|
||||
style="fill:#ed6b21;fill-opacity:1"
|
||||
class="st1"
|
||||
d="m 40.1,43.5 c 4.5,-1.2 8.2,-1.7 11,-1.7 1.7,0 3,0.3 4,0.9 0.9,0.6 1.4,1.5 1.4,2.8 0,0.5 -0.1,1.1 -0.2,1.6 l -5,23.9 c -0.1,0.7 -0.2,1.1 -0.2,1.4 0,1 0.4,1.7 1.3,2 0.8,0.3 2.4,0.5 4.6,0.5 l -0.2,1.8 c -4.3,1.1 -7.8,1.6 -10.5,1.6 -1.9,0 -3.3,-0.4 -4.4,-1.1 -1,-0.7 -1.6,-1.8 -1.6,-3.3 0,-0.7 0.1,-1.6 0.3,-2.7 l 4.6,-22 c 0.2,-1 0.3,-1.7 0.3,-2 0,-0.7 -0.3,-1.2 -0.9,-1.4 C 44,45.5 42.4,45.4 39.7,45.4 Z M 59.2,28.4 c 0,1.7 -0.5,3.1 -1.6,4.2 -1.1,1.1 -2.4,1.6 -4,1.6 -1.5,0 -2.8,-0.5 -3.8,-1.5 -1,-1 -1.5,-2.3 -1.5,-3.9 0,-1.8 0.5,-3.2 1.6,-4.2 1.1,-1 2.4,-1.5 4,-1.5 1.5,0 2.8,0.5 3.8,1.4 1,0.9 1.5,2.2 1.5,3.9 z"
|
||||
id="path6" /></g><path
|
||||
style="opacity:0.999;fill:#ed6b21;fill-opacity:1;stroke-width:0.198413"
|
||||
d="m 16.939336,52.048564 c 0.24576,-0.15442 19.08533,-11.001046 41.86572,-24.103596 L 100.22396,4.1221459 142.03917,28.183626 183.85441,52.245107 V 100.734 149.2229 l -41.81468,24.06212 -41.81467,24.06214 -41.816284,-24.06216 -41.81626,-24.06216 -0.05,-48.44676 -0.05,-48.446739 z M 63.764716,163.5414 c 19.88816,11.44426 36.295164,20.80774 36.460014,20.80774 0.16481,0 16.57129,-9.36332 36.45875,-20.80738 l 36.15903,-20.80738 V 100.73468 58.734992 L 136.68178,37.925746 C 116.79341,26.480662 100.38715,17.116638 100.22347,17.116804 100.05968,17.116972 83.653536,26.48106 63.765126,37.925887 l -36.16072,20.808776 v 41.999497 41.9995 z"
|
||||
id="path853" /><path
|
||||
style="opacity:0.999;fill:#ed6b21;fill-opacity:1;stroke-width:0.198413"
|
||||
d="m 89.952923,157.02042 c -4.242772,-0.58044 -7.113036,-2.32486 -8.380448,-5.09326 -0.626294,-1.368 -0.807444,-3.89074 -0.464458,-6.46814 0.1277,-0.95968 2.419926,-12.20538 5.093816,-24.99044 2.67389,-12.78508 4.908156,-23.917618 4.965016,-24.738958 0.115,-1.6621 -0.189601,-2.54084 -1.114721,-3.21606 -0.792772,-0.57856 -2.966462,-0.9222 -6.787666,-1.07302 -3.481347,-0.1374 -3.555832,-0.1494 -3.458944,-0.5573 0.05446,-0.22926 0.206042,-0.97334 0.336845,-1.6535 0.1308,-0.68016 0.338118,-1.31832 0.4607,-1.41814 0.28243,-0.22998 6.4844,-1.66782 9.46857,-2.19514 5.863452,-1.0361 12.531417,-1.47854 15.428597,-1.02374 5.36116,0.84161 7.82828,3.38728 7.50956,7.74872 -0.0602,0.82388 -2.44364,12.614038 -5.2965,26.200338 -3.08108,14.67312 -5.23336,25.36606 -5.30112,26.33702 -0.24186,3.46504 0.94802,4.64856 5.32416,5.29562 1.15984,0.1716 3.06554,0.31182 4.23488,0.31182 h 2.1261 l -0.1262,1.04166 c -0.0694,0.57292 -0.174,1.37074 -0.23232,1.77292 l -0.1062,0.73126 -2.51448,0.59446 c -8.31236,1.96515 -17.063901,2.95498 -21.165277,2.39389 z"
|
||||
id="path855" /><path
|
||||
id="path859"
|
||||
d="m 103.80449,68.371429 c -4.85388,-1.747057 -7.401434,-6.318219 -6.705139,-12.031262 0.68816,-5.646301 5.025269,-9.262423 11.052899,-9.215502 4.20991,0.03277 7.4957,1.946636 9.16606,5.33893 0.89045,1.808402 0.93519,2.048665 0.92966,4.992684 -0.007,3.886952 -0.60744,5.58627 -2.81498,7.970671 -2.18267,2.35755 -4.19782,3.227419 -7.70164,3.324532 -1.90628,0.05284 -3.02496,-0.05544 -3.92686,-0.380053 z"
|
||||
style="opacity:0.999;fill:#ed6b21;fill-opacity:1;stroke-width:0.280598" /><path
|
||||
id="path861"
|
||||
d="m 88.159837,156.51971 c -4.689261,-1.3224 -6.765722,-3.65088 -6.999725,-7.84929 -0.143463,-2.57397 -0.367006,-1.35766 5.236874,-28.49422 2.02374,-9.79989 3.976706,-19.42817 4.339925,-21.396181 1.189159,-6.443166 0.608576,-7.052275 -7.07692,-7.424629 -1.896078,-0.09186 -3.506416,-0.262489 -3.578529,-0.37917 -0.07211,-0.116682 0.01749,-0.855929 0.199113,-1.642772 0.380336,-1.647704 6.64e-4,-1.47536 6.265635,-2.844151 5.6684,-1.238451 9.941962,-1.750196 14.87169,-1.780835 6.11275,-0.03799 8.21381,0.630882 10.35608,3.296884 0.84162,1.047378 0.86697,1.161816 0.83925,3.788072 -0.0245,2.316986 -0.5437,5.179915 -3.59555,19.824542 -7.12659,34.19777 -7.24867,34.91174 -6.23405,36.46024 0.25629,0.39114 0.76211,0.90521 1.12406,1.14236 1.05449,0.69093 4.30303,1.25008 7.29332,1.25534 l 2.76816,0.005 -0.20111,1.61344 c -0.11061,0.88739 -0.33162,1.69456 -0.49112,1.79371 -0.42321,0.26308 -8.03018,1.8551 -11.29784,2.36446 -4.118692,0.64201 -11.953193,0.79357 -13.819263,0.26733 z"
|
||||
style="opacity:0.999;fill:#ed6b21;fill-opacity:1;stroke-width:0.280598" /></svg>
|
After Width: | Height: | Size: 5.7 KiB |
|
@ -65,7 +65,7 @@ void CSGDisplay::render_scene()
|
|||
glFlush();
|
||||
}
|
||||
|
||||
void Scene::set_print(uqptr<SLAPrint> &&print)
|
||||
void Scene::set_print(std::unique_ptr<SLAPrint> &&print)
|
||||
{
|
||||
m_print = std::move(print);
|
||||
|
||||
|
@ -85,7 +85,7 @@ void CSGDisplay::SceneCache::clear()
|
|||
primitives.clear();
|
||||
}
|
||||
|
||||
shptr<Primitive> CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh)
|
||||
std::shared_ptr<Primitive> CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh)
|
||||
{
|
||||
auto p = std::make_shared<Primitive>();
|
||||
p->load_mesh(mesh);
|
||||
|
@ -94,7 +94,7 @@ shptr<Primitive> CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh)
|
|||
return p;
|
||||
}
|
||||
|
||||
shptr<Primitive> CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh,
|
||||
std::shared_ptr<Primitive> CSGDisplay::SceneCache::add_mesh(const TriangleMesh &mesh,
|
||||
OpenCSG::Operation o,
|
||||
unsigned c)
|
||||
{
|
||||
|
|
|
@ -17,11 +17,6 @@ class SLAPrint;
|
|||
|
||||
namespace GL {
|
||||
|
||||
// Simple shorthands for smart pointers
|
||||
template<class T> using shptr = std::shared_ptr<T>;
|
||||
template<class T> using uqptr = std::unique_ptr<T>;
|
||||
template<class T> using wkptr = std::weak_ptr<T>;
|
||||
|
||||
template<class T, class A = std::allocator<T>> using vector = std::vector<T, A>;
|
||||
|
||||
// remove empty weak pointers from a vector
|
||||
|
@ -61,7 +56,7 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
vector<wkptr<Listener>> m_listeners;
|
||||
vector<std::weak_ptr<Listener>> m_listeners;
|
||||
|
||||
public:
|
||||
virtual ~MouseInput() = default;
|
||||
|
@ -95,7 +90,7 @@ public:
|
|||
call(&Listener::on_moved_to, m_listeners, x, y);
|
||||
}
|
||||
|
||||
void add_listener(shptr<Listener> listener)
|
||||
void add_listener(std::shared_ptr<Listener> listener)
|
||||
{
|
||||
m_listeners.emplace_back(listener);
|
||||
cleanup(m_listeners);
|
||||
|
@ -322,7 +317,7 @@ public:
|
|||
// The scene is a wrapper around SLAPrint which holds the data to be visualized.
|
||||
class Scene
|
||||
{
|
||||
uqptr<SLAPrint> m_print;
|
||||
std::unique_ptr<SLAPrint> m_print;
|
||||
public:
|
||||
|
||||
// Subscribers will be notified if the model is changed. This might be a
|
||||
|
@ -340,19 +335,19 @@ public:
|
|||
Scene();
|
||||
~Scene();
|
||||
|
||||
void set_print(uqptr<SLAPrint> &&print);
|
||||
void set_print(std::unique_ptr<SLAPrint> &&print);
|
||||
const SLAPrint * get_print() const { return m_print.get(); }
|
||||
|
||||
BoundingBoxf3 get_bounding_box() const;
|
||||
|
||||
void add_listener(shptr<Listener> listener)
|
||||
void add_listener(std::shared_ptr<Listener> listener)
|
||||
{
|
||||
m_listeners.emplace_back(listener);
|
||||
cleanup(m_listeners);
|
||||
}
|
||||
|
||||
private:
|
||||
vector<wkptr<Listener>> m_listeners;
|
||||
vector<std::weak_ptr<Listener>> m_listeners;
|
||||
};
|
||||
|
||||
// The basic Display. This is almost just an interface but will do all the
|
||||
|
@ -366,20 +361,20 @@ protected:
|
|||
Vec2i m_size;
|
||||
bool m_initialized = false;
|
||||
|
||||
shptr<Camera> m_camera;
|
||||
std::shared_ptr<Camera> m_camera;
|
||||
FpsCounter m_fps_counter;
|
||||
|
||||
public:
|
||||
|
||||
explicit Display(shptr<Camera> camera = nullptr)
|
||||
explicit Display(std::shared_ptr<Camera> camera = nullptr)
|
||||
: m_camera(camera ? camera : std::make_shared<PerspectiveCamera>())
|
||||
{}
|
||||
|
||||
~Display() override;
|
||||
|
||||
shptr<const Camera> get_camera() const { return m_camera; }
|
||||
shptr<Camera> get_camera() { return m_camera; }
|
||||
void set_camera(shptr<Camera> cam) { m_camera = cam; }
|
||||
std::shared_ptr<const Camera> get_camera() const { return m_camera; }
|
||||
std::shared_ptr<Camera> get_camera() { return m_camera; }
|
||||
void set_camera(std::shared_ptr<Camera> cam) { m_camera = cam; }
|
||||
|
||||
virtual void swap_buffers() = 0;
|
||||
virtual void set_active(long width, long height);
|
||||
|
@ -410,14 +405,14 @@ protected:
|
|||
// Cache the renderable primitives. These will be fetched when the scene
|
||||
// is modified.
|
||||
struct SceneCache {
|
||||
vector<shptr<Primitive>> primitives;
|
||||
vector<std::shared_ptr<Primitive>> primitives;
|
||||
vector<Primitive *> primitives_free;
|
||||
vector<OpenCSG::Primitive *> primitives_csg;
|
||||
|
||||
void clear();
|
||||
|
||||
shptr<Primitive> add_mesh(const TriangleMesh &mesh);
|
||||
shptr<Primitive> add_mesh(const TriangleMesh &mesh,
|
||||
std::shared_ptr<Primitive> add_mesh(const TriangleMesh &mesh);
|
||||
std::shared_ptr<Primitive> add_mesh(const TriangleMesh &mesh,
|
||||
OpenCSG::Operation op,
|
||||
unsigned covexity);
|
||||
} m_scene_cache;
|
||||
|
@ -446,13 +441,13 @@ class Controller : public std::enable_shared_from_this<Controller>,
|
|||
Vec2i m_mouse_pos, m_mouse_pos_rprev, m_mouse_pos_lprev;
|
||||
bool m_left_btn = false, m_right_btn = false;
|
||||
|
||||
shptr<Scene> m_scene;
|
||||
vector<wkptr<Display>> m_displays;
|
||||
std::shared_ptr<Scene> m_scene;
|
||||
vector<std::weak_ptr<Display>> m_displays;
|
||||
|
||||
// Call a method of Camera on all the cameras of the attached displays
|
||||
template<class F, class...Args>
|
||||
void call_cameras(F &&f, Args&&... args) {
|
||||
for (wkptr<Display> &l : m_displays)
|
||||
for (std::weak_ptr<Display> &l : m_displays)
|
||||
if (auto disp = l.lock()) if (auto cam = disp->get_camera())
|
||||
(cam.get()->*f)(std::forward<Args>(args)...);
|
||||
}
|
||||
|
@ -460,7 +455,7 @@ class Controller : public std::enable_shared_from_this<Controller>,
|
|||
public:
|
||||
|
||||
// Set the scene that will be controlled.
|
||||
void set_scene(shptr<Scene> scene)
|
||||
void set_scene(std::shared_ptr<Scene> scene)
|
||||
{
|
||||
m_scene = scene;
|
||||
m_scene->add_listener(shared_from_this());
|
||||
|
@ -468,7 +463,7 @@ public:
|
|||
|
||||
const Scene * get_scene() const { return m_scene.get(); }
|
||||
|
||||
void add_display(shptr<Display> disp)
|
||||
void add_display(std::shared_ptr<Display> disp)
|
||||
{
|
||||
m_displays.emplace_back(disp);
|
||||
cleanup(m_displays);
|
||||
|
|
|
@ -12,7 +12,7 @@ class CSGVolume: public Volume
|
|||
|
||||
class ShaderCSGDisplay: public Display {
|
||||
protected:
|
||||
vector<shptr<CSGVolume>> m_volumes;
|
||||
vector<std::shared_ptr<CSGVolume>> m_volumes;
|
||||
|
||||
void add_mesh(const TriangleMesh &mesh);
|
||||
public:
|
||||
|
|
|
@ -34,7 +34,7 @@ using namespace Slic3r::GL;
|
|||
class Renderer {
|
||||
protected:
|
||||
wxGLCanvas *m_canvas;
|
||||
shptr<wxGLContext> m_context;
|
||||
std::shared_ptr<wxGLContext> m_context;
|
||||
public:
|
||||
|
||||
Renderer(wxGLCanvas *c): m_canvas{c} {
|
||||
|
@ -86,16 +86,16 @@ public:
|
|||
class Canvas: public wxGLCanvas
|
||||
{
|
||||
// One display is active at a time, the OCSGRenderer by default.
|
||||
shptr<Slic3r::GL::Display> m_display;
|
||||
std::shared_ptr<Slic3r::GL::Display> m_display;
|
||||
|
||||
public:
|
||||
|
||||
template<class...Args>
|
||||
Canvas(Args &&...args): wxGLCanvas(std::forward<Args>(args)...) {}
|
||||
|
||||
shptr<Slic3r::GL::Display> get_display() const { return m_display; }
|
||||
std::shared_ptr<Slic3r::GL::Display> get_display() const { return m_display; }
|
||||
|
||||
void set_display(shptr<Slic3r::GL::Display> d) { m_display = d; }
|
||||
void set_display(std::shared_ptr<Slic3r::GL::Display> d) { m_display = d; }
|
||||
};
|
||||
|
||||
// Enumerate possible mouse events, we will record them.
|
||||
|
@ -197,14 +197,14 @@ public:
|
|||
class MyFrame: public wxFrame
|
||||
{
|
||||
// Instantiate the 3D engine.
|
||||
shptr<Scene> m_scene; // Model
|
||||
shptr<Canvas> m_canvas; // Views store
|
||||
shptr<OCSGRenderer> m_ocsgdisplay; // View
|
||||
shptr<ShaderCSGRenderer> m_shadercsg_display; // Another view
|
||||
shptr<Controller> m_ctl; // Controller
|
||||
std::shared_ptr<Scene> m_scene; // Model
|
||||
std::shared_ptr<Canvas> m_canvas; // Views store
|
||||
std::shared_ptr<OCSGRenderer> m_ocsgdisplay; // View
|
||||
std::shared_ptr<ShaderCSGRenderer> m_shadercsg_display; // Another view
|
||||
std::shared_ptr<Controller> m_ctl; // Controller
|
||||
|
||||
// Add a status bar with progress indication.
|
||||
shptr<Slic3r::GUI::ProgressStatusBar> m_stbar;
|
||||
std::shared_ptr<Slic3r::GUI::ProgressStatusBar> m_stbar;
|
||||
|
||||
RecorderMouseInput m_mouse;
|
||||
|
||||
|
@ -237,7 +237,7 @@ class MyFrame: public wxFrame
|
|||
}
|
||||
};
|
||||
|
||||
uqptr<SLAJob> m_ui_job;
|
||||
std::unique_ptr<SLAJob> m_ui_job;
|
||||
|
||||
// To keep track of the running average of measured fps values.
|
||||
double m_fps_avg = 0.;
|
||||
|
|
|
@ -149,10 +149,11 @@ namespace ImGui
|
|||
const wchar_t CustomSupportsMarker = 0x1D;
|
||||
const wchar_t CustomSeamMarker = 0x1E;
|
||||
const wchar_t MmuSegmentationMarker = 0x1F;
|
||||
// Do not forget use following letters only in wstring
|
||||
const wchar_t DocumentationButton = 0x2600;
|
||||
const wchar_t DocumentationHoverButton = 0x2601;
|
||||
const wchar_t ClippyMarker = 0x2602;
|
||||
|
||||
const wchar_t InfoMarker = 0x2603;
|
||||
|
||||
// void MyFunction(const char* name, const MyMatrix44& v);
|
||||
}
|
||||
|
|
|
@ -617,19 +617,35 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
case NODE_TYPE_VOLUME:
|
||||
{
|
||||
assert(m_object && m_volume);
|
||||
// Verify validity of face indices.
|
||||
for (Vec3i face : m_volume_facets)
|
||||
for (unsigned int tri_id : face)
|
||||
if (tri_id < 0 || tri_id >= m_object_vertices.size()) {
|
||||
this->stop("Malformed triangle mesh");
|
||||
return;
|
||||
}
|
||||
if (m_volume_facets.empty()) {
|
||||
this->stop("An empty triangle mesh found");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
TriangleMesh triangle_mesh { std::move(m_object_vertices), std::move(m_volume_facets) };
|
||||
if (triangle_mesh.volume() < 0)
|
||||
triangle_mesh.flip_triangles();
|
||||
m_volume->set_mesh(std::move(triangle_mesh));
|
||||
// Verify validity of face indices, find the vertex span.
|
||||
int min_id = m_volume_facets.front()[0];
|
||||
int max_id = min_id;
|
||||
for (const Vec3i& face : m_volume_facets) {
|
||||
for (const int tri_id : face) {
|
||||
if (tri_id < 0 || tri_id >= int(m_object_vertices.size())) {
|
||||
this->stop("Malformed triangle mesh");
|
||||
return;
|
||||
}
|
||||
min_id = std::min(min_id, tri_id);
|
||||
max_id = std::max(max_id, tri_id);
|
||||
}
|
||||
}
|
||||
|
||||
// rebase indices to the current vertices list
|
||||
for (Vec3i &face : m_volume_facets)
|
||||
face -= Vec3i(min_id, min_id, min_id);
|
||||
|
||||
indexed_triangle_set its { std::move(m_volume_facets), { m_object_vertices.begin() + min_id, m_object_vertices.begin() + max_id + 1 } };
|
||||
its_compactify_vertices(its);
|
||||
if (its_volume(its) < 0)
|
||||
its_flip_triangles(its);
|
||||
m_volume->set_mesh(std::move(its));
|
||||
}
|
||||
|
||||
// stores the volume matrix taken from the metadata, if present
|
||||
|
@ -646,7 +662,6 @@ void AMFParserContext::endElement(const char * /* name */)
|
|||
|
||||
m_volume->calculate_convex_hull();
|
||||
m_volume_facets.clear();
|
||||
m_object_vertices.clear();
|
||||
m_volume = nullptr;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -423,7 +423,7 @@ void fill_slicerconf(ConfMap &m, const SLAPrint &print)
|
|||
|
||||
} // namespace
|
||||
|
||||
uqptr<sla::RasterBase> SL1Archive::create_raster() const
|
||||
std::unique_ptr<sla::RasterBase> SL1Archive::create_raster() const
|
||||
{
|
||||
sla::RasterBase::Resolution res;
|
||||
sla::RasterBase::PixelDim pxdim;
|
||||
|
|
|
@ -12,7 +12,7 @@ class SL1Archive: public SLAArchive {
|
|||
SLAPrinterConfig m_cfg;
|
||||
|
||||
protected:
|
||||
uqptr<sla::RasterBase> create_raster() const override;
|
||||
std::unique_ptr<sla::RasterBase> create_raster() const override;
|
||||
sla::RasterEncoder get_encoder() const override;
|
||||
|
||||
public:
|
||||
|
|
|
@ -1456,7 +1456,7 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
|
|||
|
||||
begin = skip_whitespaces(begin, end);
|
||||
end = remove_eols(begin, end);
|
||||
if (begin != end)
|
||||
if (begin != end) {
|
||||
if (*begin == ';') {
|
||||
// Comment.
|
||||
begin = skip_whitespaces(++ begin, end);
|
||||
|
@ -1485,6 +1485,7 @@ void GCodeProcessor::apply_config_simplify3d(const std::string& filename)
|
|||
// Some non-empty G-code line detected, stop parsing config comments.
|
||||
reader.quit_parsing();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (m_result.extruders_count == 0)
|
||||
|
@ -2860,6 +2861,8 @@ void GCodeProcessor::process_M109(const GCodeReader::GCodeLine& line)
|
|||
else
|
||||
m_extruder_temps[m_extruder_id] = new_temp;
|
||||
}
|
||||
else if (line.has_value('S', new_temp))
|
||||
m_extruder_temps[m_extruder_id] = new_temp;
|
||||
}
|
||||
|
||||
void GCodeProcessor::process_M132(const GCodeReader::GCodeLine& line)
|
||||
|
|
|
@ -20,6 +20,12 @@
|
|||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#if defined(_MSC_VER) && defined(__clang__)
|
||||
#define BOOST_NO_CXX17_HDR_STRING_VIEW
|
||||
#endif
|
||||
|
||||
#include <boost/multiprecision/integer.hpp>
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
#include "SVG.hpp"
|
||||
#endif
|
||||
|
@ -1543,4 +1549,205 @@ double rotation_diff_z(const Vec3d &rot_xyz_from, const Vec3d &rot_xyz_to)
|
|||
return (axis.z() < 0) ? -angle : angle;
|
||||
}
|
||||
|
||||
} }
|
||||
namespace rotcalip {
|
||||
|
||||
using int256_t = boost::multiprecision::int256_t;
|
||||
using int128_t = boost::multiprecision::int128_t;
|
||||
|
||||
template<class Scalar = int64_t>
|
||||
inline Scalar magnsq(const Point &p)
|
||||
{
|
||||
return Scalar(p.x()) * p.x() + Scalar(p.y()) * p.y();
|
||||
}
|
||||
|
||||
template<class Scalar = int64_t>
|
||||
inline Scalar dot(const Point &a, const Point &b)
|
||||
{
|
||||
return Scalar(a.x()) * b.x() + Scalar(a.y()) * b.y();
|
||||
}
|
||||
|
||||
template<class Scalar = int64_t>
|
||||
inline Scalar dotperp(const Point &a, const Point &b)
|
||||
{
|
||||
return Scalar(a.x()) * b.y() - Scalar(a.y()) * b.x();
|
||||
}
|
||||
|
||||
using boost::multiprecision::abs;
|
||||
|
||||
// Compares the angle enclosed by vectors dir and dirA (alpha) with the angle
|
||||
// enclosed by -dir and dirB (beta). Returns -1 if alpha is less than beta, 0
|
||||
// if they are equal and 1 if alpha is greater than beta. Note that dir is
|
||||
// reversed for beta, because it represents the opposite side of a caliper.
|
||||
int cmp_angles(const Point &dir, const Point &dirA, const Point &dirB) {
|
||||
int128_t dotA = dot(dir, dirA);
|
||||
int128_t dotB = dot(-dir, dirB);
|
||||
int256_t dcosa = int256_t(magnsq(dirB)) * int256_t(abs(dotA)) * dotA;
|
||||
int256_t dcosb = int256_t(magnsq(dirA)) * int256_t(abs(dotB)) * dotB;
|
||||
int256_t diff = dcosa - dcosb;
|
||||
|
||||
return diff > 0? -1 : (diff < 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
// A helper class to navigate on a polygon. Given a vertex index, one can
|
||||
// get the edge belonging to that vertex, the coordinates of the vertex, the
|
||||
// next and previous edges. Stuff that is needed in the rotating calipers algo.
|
||||
class Idx
|
||||
{
|
||||
size_t m_idx;
|
||||
const Polygon *m_poly;
|
||||
public:
|
||||
explicit Idx(const Polygon &p): m_idx{0}, m_poly{&p} {}
|
||||
explicit Idx(size_t idx, const Polygon &p): m_idx{idx}, m_poly{&p} {}
|
||||
|
||||
size_t idx() const { return m_idx; }
|
||||
void set_idx(size_t i) { m_idx = i; }
|
||||
size_t next() const { return (m_idx + 1) % m_poly->size(); }
|
||||
size_t inc() { return m_idx = (m_idx + 1) % m_poly->size(); }
|
||||
Point prev_dir() const {
|
||||
return pt() - (*m_poly)[(m_idx + m_poly->size() - 1) % m_poly->size()];
|
||||
}
|
||||
|
||||
const Point &pt() const { return (*m_poly)[m_idx]; }
|
||||
const Point dir() const { return (*m_poly)[next()] - pt(); }
|
||||
const Point next_dir() const
|
||||
{
|
||||
return (*m_poly)[(m_idx + 2) % m_poly->size()] - (*m_poly)[next()];
|
||||
}
|
||||
const Polygon &poly() const { return *m_poly; }
|
||||
};
|
||||
|
||||
enum class AntipodalVisitMode { Full, EdgesOnly };
|
||||
|
||||
// Visit all antipodal pairs starting from the initial ia, ib pair which
|
||||
// has to be a valid antipodal pair (not checked). fn is called for every
|
||||
// antipodal pair encountered including the initial one.
|
||||
// The callback Fn has a signiture of bool(size_t i, size_t j, const Point &dir)
|
||||
// where i,j are the vertex indices of the antipodal pair and dir is the
|
||||
// direction of the calipers touching the i vertex.
|
||||
template<AntipodalVisitMode mode = AntipodalVisitMode::Full, class Fn>
|
||||
void visit_antipodals (Idx& ia, Idx &ib, Fn &&fn)
|
||||
{
|
||||
// Set current caliper direction to be the lower edge angle from X axis
|
||||
int cmp = cmp_angles(ia.prev_dir(), ia.dir(), ib.dir());
|
||||
Idx *current = cmp <= 0 ? &ia : &ib, *other = cmp <= 0 ? &ib : &ia;
|
||||
Idx *initial = current;
|
||||
bool visitor_continue = true;
|
||||
|
||||
size_t start = initial->idx();
|
||||
bool finished = false;
|
||||
|
||||
while (visitor_continue && !finished) {
|
||||
Point current_dir_a = current == &ia ? current->dir() : -current->dir();
|
||||
visitor_continue = fn(ia.idx(), ib.idx(), current_dir_a);
|
||||
|
||||
// Parallel edges encountered. An additional pair of antipodals
|
||||
// can be yielded.
|
||||
if constexpr (mode == AntipodalVisitMode::Full)
|
||||
if (cmp == 0 && visitor_continue) {
|
||||
visitor_continue = fn(current == &ia ? ia.idx() : ia.next(),
|
||||
current == &ib ? ib.idx() : ib.next(),
|
||||
current_dir_a);
|
||||
}
|
||||
|
||||
cmp = cmp_angles(current->dir(), current->next_dir(), other->dir());
|
||||
|
||||
current->inc();
|
||||
if (cmp > 0) {
|
||||
std::swap(current, other);
|
||||
}
|
||||
|
||||
if (initial->idx() == start) finished = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rotcalip
|
||||
|
||||
bool intersects(const Polygon &A, const Polygon &B)
|
||||
{
|
||||
using namespace rotcalip;
|
||||
|
||||
// Establish starting antipodals as extremes in XY plane. Use the
|
||||
// easily obtainable bounding boxes to check if A and B is disjoint
|
||||
// and return false if the are.
|
||||
struct BB
|
||||
{
|
||||
size_t xmin = 0, xmax = 0, ymin = 0, ymax = 0;
|
||||
const Polygon &P;
|
||||
static bool cmpy(const Point &l, const Point &u)
|
||||
{
|
||||
return l.y() < u.y() || (l.y() == u.y() && l.x() < u.x());
|
||||
}
|
||||
|
||||
BB(const Polygon &poly): P{poly}
|
||||
{
|
||||
for (size_t i = 0; i < P.size(); ++i) {
|
||||
if (P[i] < P[xmin]) xmin = i;
|
||||
if (P[xmax] < P[i]) xmax = i;
|
||||
if (cmpy(P[i], P[ymin])) ymin = i;
|
||||
if (cmpy(P[ymax], P[i])) ymax = i;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BB bA{A}, bB{B};
|
||||
BoundingBox bbA{{A[bA.xmin].x(), A[bA.ymin].y()}, {A[bA.xmax].x(), A[bA.ymax].y()}};
|
||||
BoundingBox bbB{{B[bB.xmin].x(), B[bB.ymin].y()}, {B[bB.xmax].x(), B[bB.ymax].y()}};
|
||||
|
||||
// if (!bbA.overlap(bbB))
|
||||
// return false;
|
||||
|
||||
// Establish starting antipodals as extreme vertex pairs in X or Y direction
|
||||
// which reside on different polygons. If no such pair is found, the two
|
||||
// polygons are certainly not disjoint.
|
||||
Idx imin{bA.xmin, A}, imax{bB.xmax, B};
|
||||
if (B[bB.xmin] < imin.pt()) imin = Idx{bB.xmin, B};
|
||||
if (imax.pt() < A[bA.xmax]) imax = Idx{bA.xmax, A};
|
||||
if (&imin.poly() == &imax.poly()) {
|
||||
imin = Idx{bA.ymin, A};
|
||||
imax = Idx{bB.ymax, B};
|
||||
if (B[bB.ymin] < imin.pt()) imin = Idx{bB.ymin, B};
|
||||
if (imax.pt() < A[bA.ymax]) imax = Idx{bA.ymax, A};
|
||||
}
|
||||
|
||||
if (&imin.poly() == &imax.poly())
|
||||
return true;
|
||||
|
||||
bool found_divisor = false;
|
||||
visit_antipodals<AntipodalVisitMode::EdgesOnly>(
|
||||
imin, imax,
|
||||
[&imin, &imax, &found_divisor](size_t ia, size_t ib, const Point &dir) {
|
||||
// std::cout << "A" << ia << " B" << ib << " dir " <<
|
||||
// dir.x() << " " << dir.y() << std::endl;
|
||||
const Polygon &A = imin.poly(), &B = imax.poly();
|
||||
|
||||
Point ref_a = A[(ia + 2) % A.size()], ref_b = B[(ib + 2) % B.size()];
|
||||
|
||||
bool is_left_a = dotperp( dir, ref_a - A[ia]) > 0;
|
||||
bool is_left_b = dotperp(-dir, ref_b - B[ib]) > 0;
|
||||
|
||||
// If both reference points are on the left (or right) of their
|
||||
// respective support lines and the opposite support line is to
|
||||
// the right (or left), the divisor line is found. We only test
|
||||
// the reference point, as by definition, if that is on one side,
|
||||
// all the other points must be on the same side of a support
|
||||
// line. If the support lines are collinear, the polygons must be
|
||||
// on the same side of their respective support lines.
|
||||
|
||||
auto d = dotperp(dir, B[ib] - A[ia]);
|
||||
if (d == 0) {
|
||||
// The caliper lines are collinear, not just parallel
|
||||
found_divisor = (is_left_a && is_left_b) || (!is_left_a && !is_left_b);
|
||||
} else if (d > 0) { // B is to the left of (A, A+1)
|
||||
found_divisor = !is_left_a && !is_left_b;
|
||||
} else { // B is to the right of (A, A+1)
|
||||
found_divisor = is_left_a && is_left_b;
|
||||
}
|
||||
|
||||
return !found_divisor;
|
||||
});
|
||||
|
||||
// Intersects if the divisor was not found
|
||||
return !found_divisor;
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::Geometry
|
||||
|
|
|
@ -532,6 +532,10 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation)
|
|||
return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z());
|
||||
}
|
||||
|
||||
// Returns true if the intersection of the two convex polygons A and B
|
||||
// is not an empty set.
|
||||
bool intersects(const Polygon &A, const Polygon &B);
|
||||
|
||||
} } // namespace Slicer::Geometry
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1300,7 +1300,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
num_extruders,
|
||||
painting_extruders,
|
||||
*print_object_regions,
|
||||
[&print_object, it_print_object, it_print_object_end, &update_apply_status](const PrintRegionConfig &old_config, const PrintRegionConfig &new_config, const t_config_option_keys &diff_keys) {
|
||||
[it_print_object, it_print_object_end, &update_apply_status](const PrintRegionConfig &old_config, const PrintRegionConfig &new_config, const t_config_option_keys &diff_keys) {
|
||||
for (auto it = it_print_object; it != it_print_object_end; ++it)
|
||||
if ((*it)->m_shared_regions != nullptr)
|
||||
update_apply_status((*it)->invalidate_state_by_config_options(old_config, new_config, diff_keys));
|
||||
|
|
|
@ -397,22 +397,6 @@ void PrintObject::generate_support_material()
|
|||
if (layer->empty())
|
||||
throw Slic3r::SlicingError("Levitating objects cannot be printed without supports.");
|
||||
#endif
|
||||
|
||||
// Do we have custom support data that would not be used?
|
||||
// Notify the user in that case.
|
||||
if (! this->has_support()) {
|
||||
for (const ModelVolume* mv : this->model_object()->volumes) {
|
||||
bool has_enforcers = mv->is_support_enforcer() ||
|
||||
(mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER));
|
||||
if (has_enforcers) {
|
||||
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL,
|
||||
L("An object has custom support enforcers which will not be used "
|
||||
"because supports are off. Consider turning them on.") + "\n" +
|
||||
(L("Object name")) + ": " + this->model_object()->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this->set_done(posSupportMaterial);
|
||||
}
|
||||
|
|
|
@ -13,10 +13,6 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
template<class T> using uqptr = std::unique_ptr<T>;
|
||||
template<class T> using shptr = std::shared_ptr<T>;
|
||||
template<class T> using wkptr = std::weak_ptr<T>;
|
||||
|
||||
namespace sla {
|
||||
|
||||
// Raw byte buffer paired with its size. Suitable for compressed image data.
|
||||
|
@ -112,7 +108,7 @@ struct PPMRasterEncoder {
|
|||
std::ostream& operator<<(std::ostream &stream, const EncodedRaster &bytes);
|
||||
|
||||
// If gamma is zero, thresholding will be performed which disables AA.
|
||||
uqptr<RasterBase> create_raster_grayscale_aa(
|
||||
std::unique_ptr<RasterBase> create_raster_grayscale_aa(
|
||||
const RasterBase::Resolution &res,
|
||||
const RasterBase::PixelDim & pxdim,
|
||||
double gamma = 1.0,
|
||||
|
|
|
@ -391,7 +391,7 @@ class SLAArchive {
|
|||
protected:
|
||||
std::vector<sla::EncodedRaster> m_layers;
|
||||
|
||||
virtual uqptr<sla::RasterBase> create_raster() const = 0;
|
||||
virtual std::unique_ptr<sla::RasterBase> create_raster() const = 0;
|
||||
virtual sla::RasterEncoder get_encoder() const = 0;
|
||||
|
||||
public:
|
||||
|
|
|
@ -2055,21 +2055,21 @@ static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts(
|
|||
// Trim the already created base layers above the current layer intersecting with the new bottom contacts layer.
|
||||
//FIXME Maybe this is no more needed, as the overlapping base layers are trimmed by the bottom layers at the final stage?
|
||||
touching = offset(touching, float(SCALED_EPSILON));
|
||||
for (int layer_id_above = layer_id + 1; layer_id_above < int(object.total_layer_count()); ++layer_id_above) {
|
||||
for (int layer_id_above = layer_id + 1; layer_id_above < int(object.total_layer_count()); ++ layer_id_above) {
|
||||
const Layer &layer_above = *object.layers()[layer_id_above];
|
||||
if (layer_above.print_z > layer_new.print_z - EPSILON)
|
||||
break;
|
||||
if (! layer_support_areas[layer_id_above].empty()) {
|
||||
if (Polygons &above = layer_support_areas[layer_id_above]; ! above.empty()) {
|
||||
#ifdef SLIC3R_DEBUG
|
||||
SVG::export_expolygons(debug_out_path("support-support-areas-raw-before-trimming-%d-with-%f-%lf.svg", iRun, layer.print_z, layer_above.print_z),
|
||||
{ { { union_ex(touching) }, { "touching", "blue", 0.5f } },
|
||||
{ { union_safety_offset_ex(layer_support_areas[layer_id_above]) }, { "above", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
|
||||
{ { { union_ex(touching) }, { "touching", "blue", 0.5f } },
|
||||
{ { union_safety_offset_ex(above) }, { "above", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
layer_support_areas[layer_id_above] = diff(layer_support_areas[layer_id_above], touching);
|
||||
above = diff(above, touching);
|
||||
#ifdef SLIC3R_DEBUG
|
||||
Slic3r::SVG::export_expolygons(
|
||||
debug_out_path("support-support-areas-raw-after-trimming-%d-with-%f-%lf.svg", iRun, layer.print_z, layer_above.print_z),
|
||||
union_ex(layer_support_areas[layer_id_above]));
|
||||
union_ex(above));
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
}
|
||||
}
|
||||
|
@ -2622,10 +2622,9 @@ void PrintObjectSupportMaterial::generate_base_layers(
|
|||
// New polygons for layer_intermediate.
|
||||
Polygons polygons_new;
|
||||
|
||||
// Use the precomputed layer_support_areas.
|
||||
idx_object_layer_above = std::max(0, idx_lower_or_equal(object.layers().begin(), object.layers().end(), idx_object_layer_above,
|
||||
[&layer_intermediate](const Layer *layer){ return layer->print_z <= layer_intermediate.print_z + EPSILON; }));
|
||||
polygons_new = layer_support_areas[idx_object_layer_above];
|
||||
// Use the precomputed layer_support_areas. "idx_object_layer_above": above means above since the last iteration, not above after this call.
|
||||
idx_object_layer_above = idx_lower_or_equal(object.layers().begin(), object.layers().end(), idx_object_layer_above,
|
||||
[&layer_intermediate](const Layer* layer) { return layer->print_z <= layer_intermediate.print_z + EPSILON; });
|
||||
|
||||
// Polygons to trim polygons_new.
|
||||
Polygons polygons_trimming;
|
||||
|
@ -2640,7 +2639,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
|
|||
idx_top_contact_above = idx_lower_or_equal(top_contacts, idx_top_contact_above,
|
||||
[&layer_intermediate](const MyLayer *layer){ return layer->bottom_z <= layer_intermediate.print_z - EPSILON; });
|
||||
// Collect all the top_contact layer intersecting with this layer.
|
||||
for ( int idx_top_contact_overlapping = idx_top_contact_above; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) {
|
||||
for (int idx_top_contact_overlapping = idx_top_contact_above; idx_top_contact_overlapping >= 0; -- idx_top_contact_overlapping) {
|
||||
MyLayer &layer_top_overlapping = *top_contacts[idx_top_contact_overlapping];
|
||||
if (layer_top_overlapping.print_z < layer_intermediate.bottom_z + EPSILON)
|
||||
break;
|
||||
|
@ -2651,6 +2650,22 @@ void PrintObjectSupportMaterial::generate_base_layers(
|
|||
polygons_append(polygons_trimming, layer_top_overlapping.polygons);
|
||||
}
|
||||
|
||||
if (idx_object_layer_above < 0) {
|
||||
// layer_support_areas are synchronized with object layers and they contain projections of the contact layers above them.
|
||||
// This intermediate layer is not above any object layer, thus there is no information in layer_support_areas about
|
||||
// towers supporting contact layers intersecting the first object layer. Project these contact layers now.
|
||||
polygons_new = layer_support_areas.front();
|
||||
double first_layer_z = object.layers().front()->print_z;
|
||||
for (int i = idx_top_contact_above + 1; i < int(top_contacts.size()); ++ i) {
|
||||
MyLayer &contacts = *top_contacts[i];
|
||||
if (contacts.print_z > first_layer_z + EPSILON)
|
||||
break;
|
||||
assert(contacts.bottom_z > layer_intermediate.print_z - EPSILON);
|
||||
polygons_append(polygons_new, contacts.polygons);
|
||||
}
|
||||
} else
|
||||
polygons_new = layer_support_areas[idx_object_layer_above];
|
||||
|
||||
// Trimming the base layer with any overlapping bottom layer.
|
||||
// Following cases are recognized:
|
||||
// 1) bottom.bottom_z >= base.top_z -> No overlap, no trimming needed.
|
||||
|
|
|
@ -325,20 +325,24 @@ void TriangleMesh::mirror(const Axis axis)
|
|||
void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed)
|
||||
{
|
||||
its_transform(its, t);
|
||||
if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.)
|
||||
double det = t.matrix().block(0, 0, 3, 3).determinant();
|
||||
if (fix_left_handed && det < 0.) {
|
||||
its_flip_triangles(its);
|
||||
else
|
||||
m_stats.volume = - m_stats.volume;
|
||||
det = -det;
|
||||
}
|
||||
m_stats.volume *= det;
|
||||
update_bounding_box(this->its, this->m_stats);
|
||||
}
|
||||
|
||||
void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed)
|
||||
{
|
||||
its_transform(its, m);
|
||||
if (fix_left_handed && m.determinant() < 0.)
|
||||
double det = m.block(0, 0, 3, 3).determinant();
|
||||
if (fix_left_handed && det < 0.) {
|
||||
its_flip_triangles(its);
|
||||
else
|
||||
m_stats.volume = - m_stats.volume;
|
||||
det = -det;
|
||||
}
|
||||
m_stats.volume *= det;
|
||||
update_bounding_box(this->its, this->m_stats);
|
||||
}
|
||||
|
||||
|
@ -461,7 +465,7 @@ TriangleMesh TriangleMesh::convex_hull_3d() const
|
|||
std::vector<int> map_dst_vertices;
|
||||
#ifndef NDEBUG
|
||||
Vec3f centroid = Vec3f::Zero();
|
||||
for (auto pt : this->its.vertices)
|
||||
for (const stl_vertex& pt : this->its.vertices)
|
||||
centroid += pt;
|
||||
centroid /= float(this->its.vertices.size());
|
||||
#endif // NDEBUG
|
||||
|
@ -1257,7 +1261,7 @@ bool its_write_stl_ascii(const char *file, const char *label, const std::vector<
|
|||
|
||||
fprintf(fp, "solid %s\n", label);
|
||||
|
||||
for (const stl_triangle_vertex_indices face : indices) {
|
||||
for (const stl_triangle_vertex_indices& face : indices) {
|
||||
Vec3f vertex[3] = { vertices[face(0)], vertices[face(1)], vertices[face(2)] };
|
||||
Vec3f normal = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[1]).normalized();
|
||||
fprintf(fp, " facet normal % .8E % .8E % .8E\n", normal(0), normal(1), normal(2));
|
||||
|
@ -1297,7 +1301,7 @@ bool its_write_stl_binary(const char *file, const char *label, const std::vector
|
|||
stl_facet f;
|
||||
f.extra[0] = 0;
|
||||
f.extra[1] = 0;
|
||||
for (const stl_triangle_vertex_indices face : indices) {
|
||||
for (const stl_triangle_vertex_indices& face : indices) {
|
||||
f.vertex[0] = vertices[face(0)];
|
||||
f.vertex[1] = vertices[face(1)];
|
||||
f.vertex[2] = vertices[face(2)];
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include "libslic3r/Platform.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
|
@ -40,7 +42,6 @@
|
|||
#include "slic3r/Utils/PresetUpdater.hpp"
|
||||
#include "format.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "UnsavedChangesDialog.hpp"
|
||||
|
||||
#if defined(__linux__) && defined(__WXGTK3__)
|
||||
|
@ -2477,6 +2478,46 @@ static std::string get_first_added_preset(const std::map<std::string, std::strin
|
|||
bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater, bool& apply_keeped_changes)
|
||||
{
|
||||
wxString header, caption = _L("Configuration is editing from ConfigWizard");
|
||||
const auto enabled_vendors = appconfig_new.vendors();
|
||||
|
||||
bool suppress_sla_printer = model_has_multi_part_objects(wxGetApp().model());
|
||||
PrinterTechnology preferred_pt = ptAny;
|
||||
auto get_preferred_printer_technology = [enabled_vendors, suppress_sla_printer](const std::string& bundle_name, const Bundle& bundle) {
|
||||
const auto config = enabled_vendors.find(bundle_name);
|
||||
PrinterTechnology pt = ptAny;
|
||||
if (config != enabled_vendors.end()) {
|
||||
for (const auto& model : bundle.vendor_profile->models) {
|
||||
if (const auto model_it = config->second.find(model.id);
|
||||
model_it != config->second.end() && model_it->second.size() > 0) {
|
||||
if (pt == ptAny)
|
||||
pt = model.technology;
|
||||
// if preferred printer model has SLA printer technology it's important to check the model for multypart state
|
||||
if (pt == ptSLA && suppress_sla_printer)
|
||||
continue;
|
||||
else
|
||||
return pt;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pt;
|
||||
};
|
||||
// Prusa printers are considered first, then 3rd party.
|
||||
if (preferred_pt = get_preferred_printer_technology("PrusaResearch", bundles.prusa_bundle());
|
||||
preferred_pt == ptAny || (preferred_pt == ptSLA && suppress_sla_printer)) {
|
||||
for (const auto& bundle : bundles) {
|
||||
if (bundle.second.is_prusa_bundle) { continue; }
|
||||
if (PrinterTechnology pt = get_preferred_printer_technology(bundle.first, bundle.second); pt == ptAny)
|
||||
continue;
|
||||
else if (preferred_pt == ptAny)
|
||||
preferred_pt = pt;
|
||||
if(!(preferred_pt == ptAny || (preferred_pt == ptSLA && suppress_sla_printer)))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (preferred_pt == ptSLA && !wxGetApp().may_switch_to_SLA_preset(caption))
|
||||
return false;
|
||||
|
||||
bool check_unsaved_preset_changes = page_welcome->reset_user_profile();
|
||||
if (check_unsaved_preset_changes)
|
||||
header = _L("All user presets will be deleted.");
|
||||
|
@ -2484,8 +2525,6 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
|||
if (!check_unsaved_preset_changes)
|
||||
act_btns |= UnsavedChangesDialog::ActionButtons::SAVE;
|
||||
|
||||
const auto enabled_vendors = appconfig_new.vendors();
|
||||
|
||||
// Install bundles from resources if needed:
|
||||
std::vector<std::string> install_bundles;
|
||||
for (const auto &pair : bundles) {
|
||||
|
@ -2564,13 +2603,14 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
|
|||
std::string preferred_model;
|
||||
std::string preferred_variant;
|
||||
const auto enabled_vendors_old = app_config->vendors();
|
||||
auto get_preferred_printer_model = [enabled_vendors, enabled_vendors_old](const std::string& bundle_name, const Bundle& bundle, std::string& variant) {
|
||||
auto get_preferred_printer_model = [enabled_vendors, enabled_vendors_old, preferred_pt](const std::string& bundle_name, const Bundle& bundle, std::string& variant) {
|
||||
const auto config = enabled_vendors.find(bundle_name);
|
||||
if (config == enabled_vendors.end())
|
||||
return std::string();
|
||||
for (const auto& model : bundle.vendor_profile->models) {
|
||||
if (const auto model_it = config->second.find(model.id);
|
||||
model_it != config->second.end() && model_it->second.size() > 0) {
|
||||
model_it != config->second.end() && model_it->second.size() > 0 &&
|
||||
preferred_pt == model.technology) {
|
||||
variant = *model_it->second.begin();
|
||||
const auto config_old = enabled_vendors_old.find(bundle_name);
|
||||
if (config_old == enabled_vendors_old.end())
|
||||
|
|
|
@ -2432,6 +2432,20 @@ void GUI_App::open_web_page_localized(const std::string &http_address)
|
|||
open_browser_with_warning_dialog(http_address + "&lng=" + this->current_language_code_safe());
|
||||
}
|
||||
|
||||
// If we are switching from the FFF-preset to the SLA, we should to control the printed objects if they have a part(s).
|
||||
// Because of we can't to print the multi-part objects with SLA technology.
|
||||
bool GUI_App::may_switch_to_SLA_preset(const wxString& caption)
|
||||
{
|
||||
if (model_has_multi_part_objects(model())) {
|
||||
show_info(nullptr,
|
||||
_L("It's impossible to print multi-part object(s) with SLA technology.") + "\n\n" +
|
||||
_L("Please check your object list before preset changing."),
|
||||
caption);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page)
|
||||
{
|
||||
wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null");
|
||||
|
@ -2447,13 +2461,9 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage
|
|||
if (res) {
|
||||
load_current_presets();
|
||||
|
||||
if (preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA
|
||||
&& Slic3r::model_has_multi_part_objects(wxGetApp().model())) {
|
||||
GUI::show_info(nullptr,
|
||||
_L("It's impossible to print multi-part object(s) with SLA technology.") + "\n\n" +
|
||||
_L("Please check and fix your object list."),
|
||||
_L("Attention!"));
|
||||
}
|
||||
// #ysFIXME - delete after testing: This part of code looks redundant. All checks are inside ConfigWizard::priv::apply_config()
|
||||
if (preset_bundle->printers.get_edited_preset().printer_technology() == ptSLA)
|
||||
may_switch_to_SLA_preset(_L("Configuration is editing from ConfigWizard"));
|
||||
}
|
||||
|
||||
return res;
|
||||
|
|
|
@ -309,6 +309,7 @@ public:
|
|||
PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); }
|
||||
|
||||
void open_web_page_localized(const std::string &http_address);
|
||||
bool may_switch_to_SLA_preset(const wxString& caption);
|
||||
bool run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage start_page = ConfigWizard::SP_WELCOME);
|
||||
void show_desktop_integration_dialog();
|
||||
|
||||
|
|
|
@ -377,54 +377,62 @@ void ObjectList::get_selection_indexes(std::vector<int>& obj_idxs, std::vector<i
|
|||
|
||||
int ObjectList::get_mesh_errors_count(const int obj_idx, const int vol_idx /*= -1*/) const
|
||||
{
|
||||
if (obj_idx < 0)
|
||||
return 0;
|
||||
|
||||
return (*m_objects)[obj_idx]->get_mesh_errors_count(vol_idx);
|
||||
return obj_idx >= 0 ? (*m_objects)[obj_idx]->get_mesh_errors_count(vol_idx) : 0;
|
||||
}
|
||||
|
||||
static std::string get_warning_icon_name(const TriangleMeshStats& stats)
|
||||
{
|
||||
return stats.repaired() ? (stats.manifold() ? "exclamation_manifold" : "exclamation") : "";
|
||||
return stats.manifold() ? (stats.repaired() ? "exclamation_manifold" : "") : "exclamation";
|
||||
}
|
||||
|
||||
std::pair<wxString, std::string> ObjectList::get_mesh_errors(const int obj_idx, const int vol_idx /*= -1*/, bool from_plater /*= false*/) const
|
||||
std::pair<wxString, std::string> ObjectList::get_mesh_errors(const int obj_idx, const int vol_idx /*= -1*/, wxString* sidebar_info /*= nullptr*/) const
|
||||
{
|
||||
const int errors = get_mesh_errors_count(obj_idx, vol_idx);
|
||||
|
||||
if (errors == 0)
|
||||
return { "", "" }; // hide tooltip
|
||||
|
||||
// Create tooltip string, if there are errors
|
||||
wxString tooltip = format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors) + ":\n";
|
||||
|
||||
const TriangleMeshStats& stats = vol_idx == -1 ?
|
||||
(*m_objects)[obj_idx]->get_object_stl_stats() :
|
||||
(*m_objects)[obj_idx]->volumes[vol_idx]->mesh().stats();
|
||||
|
||||
if (stats.degenerate_facets > 0)
|
||||
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d degenerate facet", "%1$d degenerate facets", stats.degenerate_facets), stats.degenerate_facets) + "\n";
|
||||
if (stats.edges_fixed > 0)
|
||||
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d edge fixed", "%1$d edges fixed", stats.edges_fixed), stats.edges_fixed) + "\n";
|
||||
if (stats.facets_removed > 0)
|
||||
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet removed", "%1$d facets removed", stats.facets_removed), stats.facets_removed) + "\n";
|
||||
if (stats.facets_reversed > 0)
|
||||
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet reversed", "%1$d facets reversed", stats.facets_reversed), stats.facets_reversed) + "\n";
|
||||
if (stats.backwards_edges > 0)
|
||||
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d backwards edge", "%1$d backwards edges", stats.backwards_edges), stats.backwards_edges) + "\n";
|
||||
if (!stats.repaired() && stats.manifold()) {
|
||||
if (sidebar_info)
|
||||
*sidebar_info = _L("No errors detected");
|
||||
return { {}, {} }; // hide tooltip
|
||||
}
|
||||
|
||||
wxString tooltip, auto_repaired_info, remaining_info;
|
||||
|
||||
// Create tooltip string, if there are errors
|
||||
if (stats.repaired()) {
|
||||
const int errors = get_mesh_errors_count(obj_idx, vol_idx);
|
||||
auto_repaired_info = format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors);
|
||||
tooltip += auto_repaired_info +":\n";
|
||||
|
||||
if (stats.degenerate_facets > 0)
|
||||
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d degenerate facet", "%1$d degenerate facets", stats.degenerate_facets), stats.degenerate_facets) + "\n";
|
||||
if (stats.edges_fixed > 0)
|
||||
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d edge fixed", "%1$d edges fixed", stats.edges_fixed), stats.edges_fixed) + "\n";
|
||||
if (stats.facets_removed > 0)
|
||||
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet removed", "%1$d facets removed", stats.facets_removed), stats.facets_removed) + "\n";
|
||||
if (stats.facets_reversed > 0)
|
||||
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d facet reversed", "%1$d facets reversed", stats.facets_reversed), stats.facets_reversed) + "\n";
|
||||
if (stats.backwards_edges > 0)
|
||||
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d backwards edge", "%1$d backwards edges", stats.backwards_edges), stats.backwards_edges) + "\n";
|
||||
}
|
||||
if (!stats.manifold()) {
|
||||
remaining_info = format_wxstr(_L_PLURAL("Remaining %1$d open edge", "Remaining %1$d open edges", stats.open_edges), stats.open_edges);
|
||||
|
||||
tooltip += _L("Remaning errors") + ":\n";
|
||||
tooltip += "\t" + format_wxstr(_L_PLURAL("%1$d open edge", "%1$d open edges", stats.open_edges), stats.open_edges) + "\n";
|
||||
}
|
||||
|
||||
if (is_windows10() && !from_plater)
|
||||
if (sidebar_info)
|
||||
*sidebar_info = stats.manifold() ? auto_repaired_info : (remaining_info + (stats.repaired() ? ("\n" + auto_repaired_info) : ""));
|
||||
|
||||
if (is_windows10() && !sidebar_info)
|
||||
tooltip += "\n" + _L("Right button click the icon to fix STL through Netfabb");
|
||||
|
||||
return { tooltip, get_warning_icon_name(stats) };
|
||||
}
|
||||
|
||||
std::pair<wxString, std::string> ObjectList::get_mesh_errors(bool from_plater /*= false*/)
|
||||
std::pair<wxString, std::string> ObjectList::get_mesh_errors(wxString* sidebar_info /*= nullptr*/)
|
||||
{
|
||||
if (!GetSelection())
|
||||
return { "", "" };
|
||||
|
@ -432,7 +440,7 @@ std::pair<wxString, std::string> ObjectList::get_mesh_errors(bool from_plater /*
|
|||
int obj_idx, vol_idx;
|
||||
get_selected_item_indexes(obj_idx, vol_idx);
|
||||
|
||||
return get_mesh_errors(obj_idx, vol_idx, from_plater);
|
||||
return get_mesh_errors(obj_idx, vol_idx, sidebar_info);
|
||||
}
|
||||
|
||||
void ObjectList::set_tooltip_for_item(const wxPoint& pt)
|
||||
|
@ -4043,17 +4051,21 @@ void ObjectList::fix_through_netfabb()
|
|||
// clear selections from the non-broken models if any exists
|
||||
// and than fill names of models to repairing
|
||||
if (vol_idxs.empty()) {
|
||||
#if !FIX_THROUGH_NETFABB_ALWAYS
|
||||
for (int i = int(obj_idxs.size())-1; i >= 0; --i)
|
||||
if (object(obj_idxs[i])->get_mesh_errors_count() == 0)
|
||||
obj_idxs.erase(obj_idxs.begin()+i);
|
||||
#endif // FIX_THROUGH_NETFABB_ALWAYS
|
||||
for (int obj_idx : obj_idxs)
|
||||
model_names.push_back(object(obj_idx)->name);
|
||||
}
|
||||
else {
|
||||
ModelObject* obj = object(obj_idxs.front());
|
||||
#if !FIX_THROUGH_NETFABB_ALWAYS
|
||||
for (int i = int(vol_idxs.size()) - 1; i >= 0; --i)
|
||||
if (obj->get_mesh_errors_count(vol_idxs[i]) == 0)
|
||||
vol_idxs.erase(vol_idxs.begin() + i);
|
||||
#endif // FIX_THROUGH_NETFABB_ALWAYS
|
||||
for (int vol_idx : vol_idxs)
|
||||
model_names.push_back(obj->volumes[vol_idx]->name);
|
||||
}
|
||||
|
@ -4106,8 +4118,10 @@ void ObjectList::fix_through_netfabb()
|
|||
if (vol_idxs.empty()) {
|
||||
int vol_idx{ -1 };
|
||||
for (int obj_idx : obj_idxs) {
|
||||
#if !FIX_THROUGH_NETFABB_ALWAYS
|
||||
if (object(obj_idx)->get_mesh_errors_count(vol_idx) == 0)
|
||||
continue;
|
||||
#endif // FIX_THROUGH_NETFABB_ALWAYS
|
||||
if (!fix_and_update_progress(obj_idx, vol_idx, model_idx, progress_dlg, succes_models, failed_models))
|
||||
break;
|
||||
model_idx++;
|
||||
|
|
|
@ -36,6 +36,9 @@ 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;
|
||||
|
||||
// Manifold mesh may contain self-intersections, so we want to always allow fixing the mesh.
|
||||
#define FIX_THROUGH_NETFABB_ALWAYS 1
|
||||
|
||||
namespace GUI {
|
||||
|
||||
wxDECLARE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent);
|
||||
|
@ -214,8 +217,8 @@ public:
|
|||
// Return value is a pair <Tooltip, warning_icon_name>, used for the tooltip and related warning icon
|
||||
// Function without parameters is for a call from Manipulation panel,
|
||||
// when we don't know parameters of selected item
|
||||
std::pair<wxString, std::string> get_mesh_errors(const int obj_idx, const int vol_idx = -1, bool from_plater = false) const;
|
||||
std::pair<wxString, std::string> get_mesh_errors(bool from_plater = false);
|
||||
std::pair<wxString, std::string> get_mesh_errors(const int obj_idx, const int vol_idx = -1, wxString* sidebar_info = nullptr) const;
|
||||
std::pair<wxString, std::string> get_mesh_errors(wxString* sidebar_info = nullptr);
|
||||
void set_tooltip_for_item(const wxPoint& pt);
|
||||
|
||||
void selection_changed();
|
||||
|
|
|
@ -497,14 +497,19 @@ void ObjectManipulation::update_ui_from_settings()
|
|||
int axis_id = 0;
|
||||
for (ManipulationEditor* editor : m_editors) {
|
||||
// editor->SetForegroundColour(m_use_colors ? wxColour(axes_color_text[axis_id]) : wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||
#ifdef _WIN32
|
||||
if (m_use_colors)
|
||||
if (m_use_colors) {
|
||||
editor->SetBackgroundColour(wxColour(axes_color_back[axis_id]));
|
||||
else
|
||||
if (wxGetApp().dark_mode())
|
||||
editor->SetForegroundColour(*wxBLACK);
|
||||
}
|
||||
else {
|
||||
#ifdef _WIN32
|
||||
wxGetApp().UpdateDarkUI(editor);
|
||||
#else
|
||||
editor->SetBackgroundColour(m_use_colors ? wxColour(axes_color_back[axis_id]) : wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
editor->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
editor->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||
#endif /* _WIN32 */
|
||||
}
|
||||
editor->Refresh();
|
||||
if (++axis_id == 3)
|
||||
axis_id = 0;
|
||||
|
@ -1009,10 +1014,10 @@ void ObjectManipulation::sys_color_changed()
|
|||
get_og()->sys_color_changed();
|
||||
wxGetApp().UpdateDarkUI(m_word_local_combo);
|
||||
wxGetApp().UpdateDarkUI(m_check_inch);
|
||||
|
||||
#endif
|
||||
for (ManipulationEditor* editor : m_editors)
|
||||
editor->sys_color_changed(this);
|
||||
#endif
|
||||
|
||||
// btn...->msw_rescale() updates icon on button, so use it
|
||||
m_mirror_bitmap_on.msw_rescale();
|
||||
m_mirror_bitmap_off.msw_rescale();
|
||||
|
@ -1045,6 +1050,7 @@ ManipulationEditor::ManipulationEditor(ObjectManipulation* parent,
|
|||
#endif // __WXOSX__
|
||||
if (parent->use_colors()) {
|
||||
this->SetBackgroundColour(wxColour(axes_color_back[axis]));
|
||||
this->SetForegroundColour(*wxBLACK);
|
||||
} else {
|
||||
wxGetApp().UpdateDarkUI(this);
|
||||
}
|
||||
|
@ -1097,10 +1103,14 @@ void ManipulationEditor::msw_rescale()
|
|||
|
||||
void ManipulationEditor::sys_color_changed(ObjectManipulation* parent)
|
||||
{
|
||||
if (!parent->use_colors())
|
||||
wxGetApp().UpdateDarkUI(this);
|
||||
else
|
||||
if (parent->use_colors())
|
||||
SetForegroundColour(*wxBLACK);
|
||||
else
|
||||
#ifdef _WIN32
|
||||
wxGetApp().UpdateDarkUI(this);
|
||||
#else
|
||||
SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
double ManipulationEditor::get_value()
|
||||
|
|
|
@ -748,7 +748,7 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee
|
|||
if( bottom_area - top_area > delta_area) {
|
||||
NotificationManager *notif_mngr = wxGetApp().plater()->get_notification_manager();
|
||||
notif_mngr->push_notification(
|
||||
NotificationType::SignDetected, NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
NotificationType::SignDetected, NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||
_u8L("NOTE:") + "\n" + _u8L("Sliced object looks like the sign") + "\n",
|
||||
_u8L("Apply auto color change to print"),
|
||||
[this](wxEvtHandler*) {
|
||||
|
@ -994,10 +994,9 @@ void Preview::load_print_as_fff(bool keep_z_range)
|
|||
if (0 <= type && type < static_cast<int>(GCodeViewer::EViewType::Count)) {
|
||||
m_choice_view_type->SetSelection(type);
|
||||
m_canvas->set_gcode_view_preview_type(static_cast<GCodeViewer::EViewType>(type));
|
||||
if (wxGetApp().is_gcode_viewer()) {
|
||||
if (wxGetApp().is_gcode_viewer())
|
||||
m_keep_current_preview_type = true;
|
||||
refresh_print();
|
||||
}
|
||||
refresh_print();
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_PREVIEW_LAYOUT
|
||||
|
|
|
@ -182,7 +182,7 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit)
|
|||
double cut_z = m_cut_z;
|
||||
if (imperial_units)
|
||||
cut_z *= ObjectManipulation::mm_to_in;
|
||||
ImGui::InputDouble("", &cut_z, 0.0f, 0.0f, "%.2f");
|
||||
ImGui::InputDouble("", &cut_z, 0.0f, 0.0f, "%.2f", ImGuiInputTextFlags_CharsDecimal);
|
||||
|
||||
ImGui::SameLine();
|
||||
m_imgui->text(imperial_units ? _L("in") : _L("mm"));
|
||||
|
|
|
@ -23,7 +23,7 @@ static inline void show_notification_extruders_limit_exceeded()
|
|||
wxGetApp()
|
||||
.plater()
|
||||
->get_notification_manager()
|
||||
->push_notification(NotificationType::MmSegmentationExceededExtrudersLimit, NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
->push_notification(NotificationType::MmSegmentationExceededExtrudersLimit, NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||
GUI::format(_L("Your printer has more extruders than the multi-material painting gizmo supports. For this reason, only the "
|
||||
"first %1% extruders will be able to be used for painting."), GLGizmoMmuSegmentation::EXTRUDERS_LIMIT));
|
||||
}
|
||||
|
|
|
@ -60,8 +60,9 @@ void GLGizmoSimplify::on_render_for_picking() {}
|
|||
void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limit)
|
||||
{
|
||||
create_gui_cfg();
|
||||
int obj_index;
|
||||
ModelVolume *act_volume = get_selected_volume(&obj_index);
|
||||
const Selection &selection = m_parent.get_selection();
|
||||
int obj_index = selection.get_object_idx();
|
||||
ModelVolume *act_volume = get_volume(selection, wxGetApp().plater()->model());
|
||||
if (act_volume == nullptr) {
|
||||
switch (m_state) {
|
||||
case State::settings: close(); break;
|
||||
|
@ -80,6 +81,10 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
|
|||
set_its(*m_original_its);
|
||||
}
|
||||
|
||||
// close suggestion notification
|
||||
auto notification_manager = wxGetApp().plater()->get_notification_manager();
|
||||
notification_manager->remove_simplify_suggestion_with_id(act_volume->get_object()->id());
|
||||
|
||||
m_obj_index = obj_index; // to remember correct object
|
||||
m_volume = act_volume;
|
||||
m_original_its = {};
|
||||
|
@ -363,7 +368,7 @@ void GLGizmoSimplify::on_set_state()
|
|||
auto notification_manager = wxGetApp().plater()->get_notification_manager();
|
||||
notification_manager->push_notification(
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||
_u8L("ERROR: Wait until Simplification ends or Cancel process."));
|
||||
return;
|
||||
}
|
||||
|
@ -423,17 +428,34 @@ bool GLGizmoSimplify::exist_volume(ModelVolume *volume) {
|
|||
return false;
|
||||
}
|
||||
|
||||
ModelVolume *GLGizmoSimplify::get_selected_volume(int *object_idx_ptr) const
|
||||
ModelVolume * GLGizmoSimplify::get_volume(const Selection &selection, Model &model)
|
||||
{
|
||||
const Selection &selection = m_parent.get_selection();
|
||||
int object_idx = selection.get_object_idx();
|
||||
if (object_idx_ptr != nullptr) *object_idx_ptr = object_idx;
|
||||
if (object_idx < 0) return nullptr;
|
||||
ModelObjectPtrs &objs = wxGetApp().plater()->model().objects;
|
||||
if (static_cast<int>(objs.size()) <= object_idx) return nullptr;
|
||||
ModelObject *obj = objs[object_idx];
|
||||
if (obj->volumes.empty()) return nullptr;
|
||||
return obj->volumes.front();
|
||||
const Selection::IndicesList& idxs = selection.get_volume_idxs();
|
||||
if (idxs.empty()) return nullptr;
|
||||
// only one selected volume
|
||||
if (idxs.size() != 1) return nullptr;
|
||||
const GLVolume *selected_volume = selection.get_volume(*idxs.begin());
|
||||
if (selected_volume == nullptr) return nullptr;
|
||||
|
||||
const GLVolume::CompositeID &cid = selected_volume->composite_id;
|
||||
const ModelObjectPtrs& objs = model.objects;
|
||||
if (cid.object_id < 0 || objs.size() <= static_cast<size_t>(cid.object_id))
|
||||
return nullptr;
|
||||
const ModelObject* obj = objs[cid.object_id];
|
||||
if (cid.volume_id < 0 || obj->volumes.size() <= static_cast<size_t>(cid.volume_id))
|
||||
return nullptr;
|
||||
return obj->volumes[cid.volume_id];
|
||||
}
|
||||
|
||||
const ModelVolume *GLGizmoSimplify::get_volume(const GLVolume::CompositeID &cid, const Model &model)
|
||||
{
|
||||
const ModelObjectPtrs &objs = model.objects;
|
||||
if (cid.object_id < 0 || objs.size() <= static_cast<size_t>(cid.object_id))
|
||||
return nullptr;
|
||||
const ModelObject *obj = objs[cid.object_id];
|
||||
if (cid.volume_id < 0 || obj->volumes.size() <= static_cast<size_t>(cid.volume_id))
|
||||
return nullptr;
|
||||
return obj->volumes[cid.volume_id];
|
||||
}
|
||||
|
||||
void GLGizmoSimplify::init_wireframe()
|
||||
|
@ -474,13 +496,17 @@ void GLGizmoSimplify::render_wireframe() const
|
|||
// is initialized?
|
||||
if (m_wireframe_VBO_id == 0 || m_wireframe_IBO_id == 0) return;
|
||||
if (!m_show_wireframe) return;
|
||||
ModelVolume *act_volume = get_selected_volume();
|
||||
if (act_volume == nullptr) return;
|
||||
const Transform3d trafo_matrix =
|
||||
act_volume->get_object()->instances[m_parent.get_selection().get_instance_idx()]
|
||||
->get_transformation().get_matrix() *
|
||||
act_volume->get_matrix();
|
||||
|
||||
const auto& selection = m_parent.get_selection();
|
||||
const auto& volume_idxs = selection.get_volume_idxs();
|
||||
if (volume_idxs.empty() || volume_idxs.size() != 1) return;
|
||||
const GLVolume *selected_volume = selection.get_volume(*volume_idxs.begin());
|
||||
|
||||
// check that selected model is wireframe initialized
|
||||
if (m_volume != get_volume(selected_volume->composite_id, *m_parent.get_model()))
|
||||
return;
|
||||
|
||||
const Transform3d trafo_matrix = selected_volume->world_matrix();
|
||||
glsafe(::glPushMatrix());
|
||||
glsafe(::glMultMatrixd(trafo_matrix.data()));
|
||||
|
||||
|
|
|
@ -43,7 +43,9 @@ private:
|
|||
void set_its(indexed_triangle_set &its);
|
||||
void create_gui_cfg();
|
||||
void request_rerender();
|
||||
ModelVolume *get_selected_volume(int *object_idx = nullptr) const;
|
||||
// move to global functions
|
||||
static ModelVolume *get_volume(const Selection &selection, Model &model);
|
||||
static const ModelVolume *get_volume(const GLVolume::CompositeID &cid, const Model &model);
|
||||
|
||||
// return false when volume was deleted
|
||||
static bool exist_volume(ModelVolume *volume);
|
||||
|
|
|
@ -192,7 +192,7 @@ bool GLGizmosManager::check_gizmos_closed_except(EType type) const
|
|||
if (get_current_type() != type && get_current_type() != Undefined) {
|
||||
wxGetApp().plater()->get_notification_manager()->push_notification(
|
||||
NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
|
||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||
_u8L("ERROR: Please close all manipulators available from "
|
||||
"the left toolbar first"));
|
||||
return false;
|
||||
|
|
|
@ -5,10 +5,14 @@
|
|||
#include "GUI_ObjectList.hpp"
|
||||
#include "GLCanvas3D.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "Tab.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/Preset.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
|
@ -159,6 +163,33 @@ TagCheckResult tag_check_system(const std::string& tag)
|
|||
return TagCheckNotCompatible;
|
||||
}
|
||||
|
||||
TagCheckResult tag_check_material(const std::string& tag)
|
||||
{
|
||||
if (const GUI::Tab* tab = wxGetApp().get_tab(Preset::Type::TYPE_FILAMENT)) {
|
||||
// search PrintConfig filament_type to find if allowed tag
|
||||
if (wxGetApp().app_config->get("filament_type").find(tag)) {
|
||||
const Preset& preset = tab->m_presets->get_edited_preset();
|
||||
const auto* opt = preset.config.opt<ConfigOptionStrings>("filament_type");
|
||||
if (opt->values[0] == tag)
|
||||
return TagCheckAffirmative;
|
||||
return TagCheckNegative;
|
||||
}
|
||||
return TagCheckNotCompatible;
|
||||
}
|
||||
/* TODO: SLA materials
|
||||
else if (const GUI::Tab* tab = wxGetApp().get_tab(Preset::Type::TYPE_SLA_MATERIAL)) {
|
||||
//if (wxGetApp().app_config->get("material_type").find(tag)) {
|
||||
const Preset& preset = tab->m_presets->get_edited_preset();
|
||||
const auto* opt = preset.config.opt<ConfigOptionStrings>("material_type");
|
||||
if (opt->values[0] == tag)
|
||||
return TagCheckAffirmative;
|
||||
return TagCheckNegative;
|
||||
//}
|
||||
return TagCheckNotCompatible;
|
||||
}*/
|
||||
return TagCheckNotCompatible;
|
||||
}
|
||||
|
||||
// return true if NOT in disabled mode.
|
||||
bool tags_check(const std::string& disabled_tags, const std::string& enabled_tags)
|
||||
{
|
||||
|
@ -189,6 +220,11 @@ bool tags_check(const std::string& disabled_tags, const std::string& enabled_tag
|
|||
if (result == TagCheckResult::TagCheckAffirmative)
|
||||
continue;
|
||||
result = tag_check_system(tag);
|
||||
if (result == TagCheckResult::TagCheckNegative)
|
||||
return false;
|
||||
if (result == TagCheckResult::TagCheckAffirmative)
|
||||
continue;
|
||||
result = tag_check_material(tag);
|
||||
if (result == TagCheckResult::TagCheckNegative)
|
||||
return false;
|
||||
if (result == TagCheckResult::TagCheckAffirmative)
|
||||
|
@ -225,6 +261,11 @@ bool tags_check(const std::string& disabled_tags, const std::string& enabled_tag
|
|||
if (result == TagCheckResult::TagCheckAffirmative)
|
||||
return false;
|
||||
result = tag_check_system(tag);
|
||||
if (result == TagCheckResult::TagCheckAffirmative)
|
||||
return false;
|
||||
if (result == TagCheckResult::TagCheckNegative)
|
||||
continue;
|
||||
result = tag_check_material(tag);
|
||||
if (result == TagCheckResult::TagCheckAffirmative)
|
||||
return false;
|
||||
if (result == TagCheckResult::TagCheckNegative)
|
||||
|
@ -917,29 +958,14 @@ void NotificationManager::HintNotification::render_right_arrow_button(ImGuiWrapp
|
|||
}
|
||||
void NotificationManager::HintNotification::render_logo(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));
|
||||
push_style_color(ImGuiCol_Text, ImVec4(1.f, 1.f, 1.f, 1.f), m_state == EState::FadingOut, m_current_fade_opacity);
|
||||
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::wstring button_text;
|
||||
button_text = ImGui::ClippyMarker;//LeftArrowButton;
|
||||
std::string placeholder_text;
|
||||
placeholder_text = ImGui::EjectButton;
|
||||
|
||||
ImVec2 button_pic_size = ImGui::CalcTextSize(placeholder_text.c_str());
|
||||
ImVec2 button_size(button_pic_size.x * 1.25f * 2.f, button_pic_size.y * 1.25f * 2.f);
|
||||
ImGui::SetCursorPosY(win_size.y / 2 - button_size.y * 1.1f);
|
||||
ImGui::SetCursorPosX(0);
|
||||
// shouldnt it render as text?
|
||||
if (imgui.button(button_text.c_str(), button_size.x, button_size.y))
|
||||
{
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor(5);
|
||||
std::wstring text;
|
||||
text = ImGui::ClippyMarker;
|
||||
ImGui::SetCursorPosX(button_pic_size.x / 3);
|
||||
ImGui::SetCursorPosY(win_size_y / 2 - button_pic_size.y * 2.f);
|
||||
imgui.text(text.c_str());
|
||||
}
|
||||
void NotificationManager::HintNotification::render_documentation_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
|
@ -1012,7 +1038,7 @@ void NotificationManager::HintNotification::retrieve_data(bool new_hint/* = true
|
|||
if(hint_data != nullptr)
|
||||
{
|
||||
NotificationData nd { NotificationType::DidYouKnowHint,
|
||||
NotificationLevel::RegularNotificationLevel,
|
||||
NotificationLevel::HintNotificationLevel,
|
||||
0,
|
||||
hint_data->text,
|
||||
hint_data->hypertext, nullptr,
|
||||
|
|
|
@ -48,6 +48,7 @@ static const std::map<const wchar_t, std::string> font_icons = {
|
|||
{ImGui::RightArrowHoverButton , "notification_right_hover" },
|
||||
{ImGui::PreferencesButton , "notification_preferences" },
|
||||
{ImGui::PreferencesHoverButton , "notification_preferences_hover"},
|
||||
|
||||
};
|
||||
static const std::map<const wchar_t, std::string> font_icons_large = {
|
||||
{ImGui::CloseNotifButton , "notification_close" },
|
||||
|
@ -65,6 +66,8 @@ static const std::map<const wchar_t, std::string> font_icons_large = {
|
|||
{ImGui::VarLayerHeightMarker , "layers" },
|
||||
{ImGui::DocumentationButton , "notification_documentation" },
|
||||
{ImGui::DocumentationHoverButton, "notification_documentation_hover"},
|
||||
{ImGui::InfoMarker , "notification_info" },
|
||||
|
||||
};
|
||||
|
||||
static const std::map<const wchar_t, std::string> font_icons_extra_large = {
|
||||
|
@ -392,7 +395,7 @@ bool ImGuiWrapper::image_button()
|
|||
|
||||
bool ImGuiWrapper::input_double(const std::string &label, const double &value, const std::string &format)
|
||||
{
|
||||
return ImGui::InputDouble(label.c_str(), const_cast<double*>(&value), 0.0f, 0.0f, format.c_str());
|
||||
return ImGui::InputDouble(label.c_str(), const_cast<double*>(&value), 0.0f, 0.0f, format.c_str(), ImGuiInputTextFlags_CharsDecimal);
|
||||
}
|
||||
|
||||
bool ImGuiWrapper::input_double(const wxString &label, const double &value, const std::string &format)
|
||||
|
|
|
@ -153,8 +153,8 @@ void SLAImportJob::process()
|
|||
break;
|
||||
}
|
||||
} catch (MissingProfileError &) {
|
||||
p->err = _L("The archive doesn't contain any profile data. Try to import after switching "
|
||||
"to an SLA profile that can be used as fallback.").ToStdString();
|
||||
p->err = _L("The SLA archive doesn't contain any presets. "
|
||||
"Please activate some SLA printer preset first before importing that SLA archive.").ToStdString();
|
||||
} catch (std::exception &ex) {
|
||||
p->err = ex.what();
|
||||
}
|
||||
|
@ -207,8 +207,8 @@ void SLAImportJob::finalize()
|
|||
m_plater->get_notification_manager()->push_notification(
|
||||
NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::WarningNotificationLevel,
|
||||
_L("Loaded archive did not contain any profile data. "
|
||||
"The current SLA profile was used as fallback.").ToStdString());
|
||||
_L("The imported SLA archive did not contain any presets. "
|
||||
"The current SLA presets were used as fallback.").ToStdString());
|
||||
}
|
||||
|
||||
if (p->sel != Sel::modelOnly) {
|
||||
|
|
|
@ -228,7 +228,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
|
|||
return;
|
||||
}
|
||||
// check unsaved changes only if project wasn't saved
|
||||
else if (saved_project == wxID_NO && event.CanVeto() &&
|
||||
else if (plater()->is_project_dirty() && saved_project == wxID_NO && event.CanVeto() &&
|
||||
!wxGetApp().check_and_save_current_preset_changes(_L("PrusaSlicer is closing"), _L("Closing PrusaSlicer while some presets are modified."))) {
|
||||
event.Veto();
|
||||
return;
|
||||
|
@ -834,7 +834,7 @@ bool MainFrame::can_save() const
|
|||
#if ENABLE_SAVE_COMMANDS_ALWAYS_ENABLED
|
||||
return (m_plater != nullptr) &&
|
||||
!m_plater->canvas3D()->get_gizmos_manager().is_in_editing_mode(false) &&
|
||||
!m_plater->get_project_filename().empty() && m_plater->is_project_dirty();
|
||||
m_plater->is_project_dirty();
|
||||
#else
|
||||
return (m_plater != nullptr) && !m_plater->model().objects.empty() &&
|
||||
!m_plater->canvas3D()->get_gizmos_manager().is_in_editing_mode(false) &&
|
||||
|
|
|
@ -43,10 +43,10 @@ const NotificationManager::NotificationData NotificationManager::basic_notificat
|
|||
},
|
||||
{NotificationType::NewAppAvailable, NotificationLevel::ImportantNotificationLevel, 20, _u8L("New version is available."), _u8L("See Releases page."), [](wxEvtHandler* evnthndlr) {
|
||||
wxGetApp().open_browser_with_warning_dialog("https://github.com/prusa3d/PrusaSlicer/releases"); return true; }},
|
||||
{NotificationType::EmptyColorChangeCode, NotificationLevel::RegularNotificationLevel, 10,
|
||||
{NotificationType::EmptyColorChangeCode, NotificationLevel::PrintInfoNotificationLevel, 10,
|
||||
_u8L("You have just added a G-code for color change, but its value is empty.\n"
|
||||
"To export the G-code correctly, check the \"Color Change G-code\" in \"Printer Settings > Custom G-code\"") },
|
||||
{NotificationType::EmptyAutoColorChange, NotificationLevel::RegularNotificationLevel, 10,
|
||||
{NotificationType::EmptyAutoColorChange, NotificationLevel::PrintInfoNotificationLevel, 10,
|
||||
_u8L("No color change event was added to the print. The print does not look like a sign.") },
|
||||
{NotificationType::DesktopIntegrationSuccess, NotificationLevel::RegularNotificationLevel, 10,
|
||||
_u8L("Desktop integration was successful.") },
|
||||
|
@ -276,7 +276,9 @@ void NotificationManager::PopNotification::count_spaces()
|
|||
m_line_height = ImGui::CalcTextSize("A").y;
|
||||
|
||||
m_left_indentation = m_line_height;
|
||||
if (m_data.level == NotificationLevel::ErrorNotificationLevel || m_data.level == NotificationLevel::WarningNotificationLevel) {
|
||||
if (m_data.level == NotificationLevel::ErrorNotificationLevel
|
||||
|| m_data.level == NotificationLevel::WarningNotificationLevel
|
||||
|| m_data.level == NotificationLevel::PrintInfoNotificationLevel) {
|
||||
std::string text;
|
||||
text = (m_data.level == NotificationLevel::ErrorNotificationLevel ? ImGui::ErrorMarker : ImGui::WarningMarker);
|
||||
float picture_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
|
@ -373,7 +375,7 @@ void NotificationManager::PopNotification::init()
|
|||
void NotificationManager::PopNotification::set_next_window_size(ImGuiWrapper& imgui)
|
||||
{
|
||||
m_window_height = m_multiline ?
|
||||
m_lines_count * m_line_height :
|
||||
std::max(m_lines_count, (size_t)2) * m_line_height :
|
||||
2 * m_line_height;
|
||||
m_window_height += 1 * m_line_height; // top and bottom
|
||||
}
|
||||
|
@ -511,7 +513,13 @@ void NotificationManager::PopNotification::render_left_sign(ImGuiWrapper& imgui)
|
|||
ImGui::SetCursorPosX(m_line_height / 3);
|
||||
ImGui::SetCursorPosY(m_window_height / 2 - m_line_height);
|
||||
imgui.text(text.c_str());
|
||||
}
|
||||
} else if (m_data.level == NotificationLevel::PrintInfoNotificationLevel) {
|
||||
std::wstring text;
|
||||
text = ImGui::InfoMarker;
|
||||
ImGui::SetCursorPosX(m_line_height / 3);
|
||||
ImGui::SetCursorPosY(m_window_height / 2 - m_line_height);
|
||||
imgui.text(text.c_str());
|
||||
}
|
||||
}
|
||||
void NotificationManager::PopNotification::render_minimize_button(ImGuiWrapper& imgui, const float win_pos_x, const float win_pos_y)
|
||||
{
|
||||
|
@ -1055,6 +1063,8 @@ void NotificationManager::UpdatedItemsInfoNotification::add_type(InfoItemType ty
|
|||
|
||||
std::string text;
|
||||
for (it = m_types_and_counts.begin(); it != m_types_and_counts.end(); ++it) {
|
||||
if ((*it).second == 0)
|
||||
continue;
|
||||
text += std::to_string((*it).second);
|
||||
text += _L_PLURAL(" Object was loaded with "," Objects were loaded with ", (*it).second).ToUTF8().data();
|
||||
switch ((*it).first) {
|
||||
|
@ -1066,6 +1076,7 @@ void NotificationManager::UpdatedItemsInfoNotification::add_type(InfoItemType ty
|
|||
default: BOOST_LOG_TRIVIAL(error) << "Unknown InfoItemType: " << (*it).second; break;
|
||||
}
|
||||
}
|
||||
m_state = EState::Unknown;
|
||||
NotificationData data { get_data().type, get_data().level , get_data().duration, text };
|
||||
update(data);
|
||||
}
|
||||
|
@ -1489,17 +1500,7 @@ void NotificationManager::push_notification(NotificationType type,
|
|||
std::function<bool(wxEvtHandler*)> callback,
|
||||
int timestamp)
|
||||
{
|
||||
int duration = 0;
|
||||
switch (level) {
|
||||
case NotificationLevel::RegularNotificationLevel: duration = 10; break;
|
||||
case NotificationLevel::ErrorNotificationLevel: break;
|
||||
case NotificationLevel::WarningNotificationLevel: break;
|
||||
case NotificationLevel::ImportantNotificationLevel: break;
|
||||
case NotificationLevel::ProgressBarNotificationLevel: break;
|
||||
default:
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
int duration = get_standart_duration(level);
|
||||
push_notification_data({ type, level, duration, text, hypertext, callback }, timestamp);
|
||||
}
|
||||
void NotificationManager::push_validate_error_notification(const std::string& text)
|
||||
|
@ -1518,7 +1519,7 @@ void NotificationManager::push_slicing_warning_notification(const std::string& t
|
|||
{
|
||||
NotificationData data { NotificationType::SlicingWarning, NotificationLevel::WarningNotificationLevel, 0, _u8L("WARNING:") + "\n" + text };
|
||||
|
||||
auto notification = std::make_unique<NotificationManager::SlicingWarningNotification>(data, m_id_provider, m_evt_handler);
|
||||
auto notification = std::make_unique<NotificationManager::ObjectIDNotification>(data, m_id_provider, m_evt_handler);
|
||||
notification->object_id = oid;
|
||||
notification->warning_step = warning_step;
|
||||
if (push_notification_data(std::move(notification), 0)) {
|
||||
|
@ -1609,12 +1610,11 @@ void NotificationManager::close_slicing_error_notification(const std::string& te
|
|||
}
|
||||
}
|
||||
}
|
||||
void NotificationManager::push_object_warning_notification(const std::string& text, ObjectID object_id, const std::string& hypertext/* = ""*/, std::function<bool(wxEvtHandler*)> callback/* = std::function<bool(wxEvtHandler*)>()*/)
|
||||
void NotificationManager::push_simplify_suggestion_notification(const std::string& text, ObjectID object_id, const std::string& hypertext/* = ""*/, std::function<bool(wxEvtHandler*)> callback/* = std::function<bool(wxEvtHandler*)>()*/)
|
||||
{
|
||||
NotificationData data{ NotificationType::ObjectWarning, NotificationLevel::WarningNotificationLevel, 0, text, hypertext, callback };
|
||||
auto notification = std::make_unique<NotificationManager::SlicingWarningNotification>(data, m_id_provider, m_evt_handler);
|
||||
NotificationData data{ NotificationType::SimplifySuggestion, NotificationLevel::PrintInfoNotificationLevel, 10, text, hypertext, callback };
|
||||
auto notification = std::make_unique<NotificationManager::ObjectIDNotification>(data, m_id_provider, m_evt_handler);
|
||||
notification->object_id = object_id;
|
||||
notification->warning_step = 0;
|
||||
push_notification_data(std::move(notification), 0);
|
||||
}
|
||||
void NotificationManager::close_notification_of_type(const NotificationType type)
|
||||
|
@ -1630,19 +1630,29 @@ void NotificationManager::remove_slicing_warnings_of_released_objects(const std:
|
|||
for (std::unique_ptr<PopNotification> ¬ification : m_pop_notifications)
|
||||
if (notification->get_type() == NotificationType::SlicingWarning) {
|
||||
if (! std::binary_search(living_oids.begin(), living_oids.end(),
|
||||
static_cast<SlicingWarningNotification*>(notification.get())->object_id))
|
||||
static_cast<ObjectIDNotification*>(notification.get())->object_id))
|
||||
notification->close();
|
||||
}
|
||||
}
|
||||
void NotificationManager::remove_object_warnings_of_released_objects(const std::vector<ObjectID>& living_oids)
|
||||
void NotificationManager::remove_simplify_suggestion_of_released_objects(const std::vector<ObjectID>& living_oids)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications)
|
||||
if (notification->get_type() == NotificationType::ObjectWarning) {
|
||||
if (notification->get_type() == NotificationType::SimplifySuggestion) {
|
||||
if (!std::binary_search(living_oids.begin(), living_oids.end(),
|
||||
static_cast<SlicingWarningNotification*>(notification.get())->object_id))
|
||||
static_cast<ObjectIDNotification*>(notification.get())->object_id))
|
||||
notification->close();
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::remove_simplify_suggestion_with_id(const ObjectID oid)
|
||||
{
|
||||
for (std::unique_ptr<PopNotification>& notification : m_pop_notifications)
|
||||
if (notification->get_type() == NotificationType::SimplifySuggestion) {
|
||||
if (static_cast<ObjectIDNotification*>(notification.get())->object_id == oid)
|
||||
notification->close();
|
||||
}
|
||||
}
|
||||
|
||||
void NotificationManager::push_exporting_finished_notification(const std::string& path, const std::string& dir_path, bool on_removable)
|
||||
{
|
||||
close_notification_of_type(NotificationType::ExportFinished);
|
||||
|
@ -1921,7 +1931,7 @@ void NotificationManager::push_updated_item_info_notification(InfoItemType type)
|
|||
}
|
||||
}
|
||||
|
||||
NotificationData data{ NotificationType::UpdatedItemsInfo, NotificationLevel::RegularNotificationLevel, 5, "" };
|
||||
NotificationData data{ NotificationType::UpdatedItemsInfo, NotificationLevel::PrintInfoNotificationLevel, 10, "" };
|
||||
auto notification = std::make_unique<NotificationManager::UpdatedItemsInfoNotification>(data, m_id_provider, m_evt_handler, type);
|
||||
if (push_notification_data(std::move(notification), 0)) {
|
||||
(dynamic_cast<UpdatedItemsInfoNotification*>(m_pop_notifications.back().get()))->add_type(type);
|
||||
|
@ -2084,8 +2094,8 @@ bool NotificationManager::activate_existing(const NotificationManager::PopNotifi
|
|||
continue;
|
||||
}
|
||||
} else if (new_type == NotificationType::SlicingWarning) {
|
||||
auto w1 = dynamic_cast<const SlicingWarningNotification*>(notification);
|
||||
auto w2 = dynamic_cast<const SlicingWarningNotification*>(it->get());
|
||||
auto w1 = dynamic_cast<const ObjectIDNotification*>(notification);
|
||||
auto w2 = dynamic_cast<const ObjectIDNotification*>(it->get());
|
||||
if (w1 != nullptr && w2 != nullptr) {
|
||||
if (!(*it)->compare_text(new_text) || w1->object_id != w2->object_id) {
|
||||
continue;
|
||||
|
|
|
@ -71,9 +71,6 @@ enum class NotificationType
|
|||
PlaterError,
|
||||
// Object fully outside the print volume, or extrusion outside the print volume. Slicing is not disabled.
|
||||
PlaterWarning,
|
||||
// Warning connected to single object id, appears at loading object, disapears at deletition.
|
||||
// Example: advice to simplify object with big amount of triangles.
|
||||
ObjectWarning,
|
||||
// Progress bar instead of text.
|
||||
ProgressBar,
|
||||
// Progress bar with info from Print Host Upload Queue dialog.
|
||||
|
@ -105,7 +102,10 @@ enum class NotificationType
|
|||
// Might contain logo taken from gizmos
|
||||
UpdatedItemsInfo,
|
||||
// Progress bar notification with methods to replace ProgressIndicator class.
|
||||
ProgressIndicator
|
||||
ProgressIndicator,
|
||||
// Give user advice to simplify object with big amount of triangles
|
||||
// Contains ObjectID for closing when object is deleted
|
||||
SimplifySuggestion
|
||||
};
|
||||
|
||||
class NotificationManager
|
||||
|
@ -121,6 +121,8 @@ public:
|
|||
HintNotificationLevel,
|
||||
// "Good to know" notification, usually but not always with a quick fade-out.
|
||||
RegularNotificationLevel,
|
||||
// Regular level notifiaction containing info about objects or print. Has Icon.
|
||||
PrintInfoNotificationLevel,
|
||||
// Information notification without a fade-out or with a longer fade-out.
|
||||
ImportantNotificationLevel,
|
||||
// Warning, no fade-out.
|
||||
|
@ -167,11 +169,12 @@ public:
|
|||
void close_plater_error_notification(const std::string& text);
|
||||
void close_plater_warning_notification(const std::string& text);
|
||||
// Object warning with ObjectID, closes when object is deleted. ID used is of object not print like in slicing warning.
|
||||
void push_object_warning_notification(const std::string& text, ObjectID object_id, const std::string& hypertext = "",
|
||||
void push_simplify_suggestion_notification(const std::string& text, ObjectID object_id, const std::string& hypertext = "",
|
||||
std::function<bool(wxEvtHandler*)> callback = std::function<bool(wxEvtHandler*)>());
|
||||
// Close object warnings, whose ObjectID is not in the list.
|
||||
// living_oids is expected to be sorted.
|
||||
void remove_object_warnings_of_released_objects(const std::vector<ObjectID>& living_oids);
|
||||
void remove_simplify_suggestion_of_released_objects(const std::vector<ObjectID>& living_oids);
|
||||
void remove_simplify_suggestion_with_id(const ObjectID oid);
|
||||
// Called when the side bar changes its visibility, as the "slicing complete" notification supplements
|
||||
// the "slicing info" normally shown at the side bar.
|
||||
void set_sidebar_collapsed(bool collapsed);
|
||||
|
@ -212,6 +215,7 @@ public:
|
|||
bool is_hint_notification_open();
|
||||
// Forces Hints to reload its content when next hint should be showed
|
||||
void deactivate_loaded_hints();
|
||||
// Adds counter to existing UpdatedItemsInfo notification or opens new one
|
||||
void push_updated_item_info_notification(InfoItemType type);
|
||||
// Close old notification ExportFinished.
|
||||
void new_export_began(bool on_removable);
|
||||
|
@ -394,12 +398,14 @@ private:
|
|||
|
||||
|
||||
|
||||
class SlicingWarningNotification : public PopNotification
|
||||
class ObjectIDNotification : public PopNotification
|
||||
{
|
||||
public:
|
||||
SlicingWarningNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler) : PopNotification(n, id_provider, evt_handler) {}
|
||||
ObjectIDNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler)
|
||||
: PopNotification(n, id_provider, evt_handler)
|
||||
{}
|
||||
ObjectID object_id;
|
||||
int warning_step;
|
||||
int warning_step { 0 };
|
||||
};
|
||||
|
||||
class PlaterWarningNotification : public PopNotification
|
||||
|
@ -649,6 +655,11 @@ private:
|
|||
}
|
||||
void count_spaces() override;
|
||||
void add_type(InfoItemType type);
|
||||
void close() override{
|
||||
for (auto& tac : m_types_and_counts)
|
||||
tac.second = 0;
|
||||
PopNotification::close();
|
||||
}
|
||||
protected:
|
||||
void render_left_sign(ImGuiWrapper& imgui) override;
|
||||
std::vector<std::pair<InfoItemType, size_t>> m_types_and_counts;
|
||||
|
@ -691,7 +702,21 @@ 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();
|
||||
|
||||
size_t get_standart_duration(NotificationLevel level)
|
||||
{
|
||||
switch (level) {
|
||||
|
||||
case NotificationLevel::ErrorNotificationLevel: return 0;
|
||||
case NotificationLevel::WarningNotificationLevel: return 0;
|
||||
case NotificationLevel::ImportantNotificationLevel: return 0;
|
||||
case NotificationLevel::ProgressBarNotificationLevel: return 2;
|
||||
case NotificationLevel::RegularNotificationLevel: return 10;
|
||||
case NotificationLevel::PrintInfoNotificationLevel: return 10;
|
||||
case NotificationLevel::HintNotificationLevel: return 300;
|
||||
default: return 10;
|
||||
}
|
||||
}
|
||||
|
||||
// set by init(), until false notifications are only added not updated and frame is not requested after push
|
||||
bool m_initialized{ false };
|
||||
// Target for wxWidgets events sent by clicking on the hyperlink available at some notifications.
|
||||
|
@ -715,7 +740,7 @@ private:
|
|||
NotificationType::PlaterWarning,
|
||||
NotificationType::ProgressBar,
|
||||
NotificationType::PrintHostUpload,
|
||||
NotificationType::ObjectWarning
|
||||
NotificationType::SimplifySuggestion
|
||||
};
|
||||
//prepared (basic) notifications
|
||||
static const NotificationData basic_notifications[];
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <wx/numdlg.h>
|
||||
#include <wx/debug.h>
|
||||
#include <wx/busyinfo.h>
|
||||
#include <wx/richtooltip.h>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Format/STL.hpp"
|
||||
|
@ -173,13 +174,10 @@ ObjectInfo::ObjectInfo(wxWindow *parent) :
|
|||
label_materials = init_info_label(&info_materials, _L("Materials"));
|
||||
Add(grid_sizer, 0, wxEXPAND);
|
||||
|
||||
auto *info_manifold_text = new wxStaticText(parent, wxID_ANY, _L("Manifold") + ":");
|
||||
info_manifold_text->SetFont(wxGetApp().small_font());
|
||||
info_manifold = new wxStaticText(parent, wxID_ANY, "");
|
||||
info_manifold->SetFont(wxGetApp().small_font());
|
||||
manifold_warning_icon = new wxStaticBitmap(parent, wxID_ANY, create_scaled_bitmap(m_warning_icon_name));
|
||||
auto *sizer_manifold = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer_manifold->Add(info_manifold_text, 0);
|
||||
sizer_manifold->Add(manifold_warning_icon, 0, wxLEFT, 2);
|
||||
sizer_manifold->Add(info_manifold, 0, wxLEFT, 2);
|
||||
Add(sizer_manifold, 0, wxEXPAND | wxTOP, 4);
|
||||
|
@ -201,10 +199,10 @@ void ObjectInfo::msw_rescale()
|
|||
|
||||
void ObjectInfo::update_warning_icon(const std::string& warning_icon_name)
|
||||
{
|
||||
if (warning_icon_name.empty())
|
||||
return;
|
||||
m_warning_icon_name = warning_icon_name;
|
||||
manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name));
|
||||
if (showing_manifold_warning_icon = !warning_icon_name.empty()) {
|
||||
m_warning_icon_name = warning_icon_name;
|
||||
manifold_warning_icon->SetBitmap(create_scaled_bitmap(m_warning_icon_name));
|
||||
}
|
||||
}
|
||||
|
||||
enum SlicedInfoIdx
|
||||
|
@ -611,6 +609,7 @@ struct Sidebar::priv
|
|||
|
||||
wxButton *btn_export_gcode;
|
||||
wxButton *btn_reslice;
|
||||
wxString btn_reslice_tip;
|
||||
ScalableButton *btn_send_gcode;
|
||||
//ScalableButton *btn_eject_device;
|
||||
ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected)
|
||||
|
@ -622,6 +621,7 @@ struct Sidebar::priv
|
|||
~priv();
|
||||
|
||||
void show_preset_comboboxes();
|
||||
void show_rich_tip(const wxString& tooltip, wxButton* btn);
|
||||
};
|
||||
|
||||
Sidebar::priv::~priv()
|
||||
|
@ -650,6 +650,18 @@ void Sidebar::priv::show_preset_comboboxes()
|
|||
scrolled->Refresh();
|
||||
}
|
||||
|
||||
void Sidebar::priv::show_rich_tip(const wxString& tooltip, wxButton* btn)
|
||||
{
|
||||
if (tooltip.IsEmpty())
|
||||
return;
|
||||
wxRichToolTip tip(tooltip, "");
|
||||
tip.SetIcon(wxICON_NONE);
|
||||
tip.SetTipKind(wxTipKind_BottomRight);
|
||||
tip.SetTitleFont(wxGetApp().normal_font());
|
||||
tip.SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
||||
tip.SetTimeout(1200);
|
||||
tip.ShowFor(btn);
|
||||
}
|
||||
|
||||
// Sidebar / public
|
||||
|
||||
|
@ -784,7 +796,11 @@ Sidebar::Sidebar(Plater *parent)
|
|||
#endif //__APPLE__
|
||||
ScalableBitmap bmp = ScalableBitmap(this, icon_name, bmp_px_cnt);
|
||||
*btn = new ScalableButton(this, wxID_ANY, bmp, "", wxBU_EXACTFIT);
|
||||
(*btn)->SetToolTip(tooltip);
|
||||
|
||||
(*btn)->Bind(wxEVT_ENTER_WINDOW, [tooltip, btn, this](wxMouseEvent& event) {
|
||||
p->show_rich_tip(tooltip, *btn);
|
||||
event.Skip();
|
||||
});
|
||||
(*btn)->Hide();
|
||||
};
|
||||
|
||||
|
@ -843,6 +859,11 @@ Sidebar::Sidebar(Plater *parent)
|
|||
p->plater->reslice();
|
||||
p->plater->select_view_3D("Preview");
|
||||
});
|
||||
|
||||
p->btn_reslice->Bind(wxEVT_ENTER_WINDOW, [this](wxMouseEvent& event) {
|
||||
p->show_rich_tip(p->btn_reslice_tip, p->btn_reslice);
|
||||
event.Skip();
|
||||
});
|
||||
p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); });
|
||||
// p->btn_eject_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); });
|
||||
p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); });
|
||||
|
@ -970,7 +991,7 @@ void Sidebar::update_reslice_btn_tooltip() const
|
|||
wxString tooltip = wxString("Slice") + " [" + GUI::shortkey_ctrl_prefix() + "R]";
|
||||
if (m_mode != comSimple)
|
||||
tooltip += wxString("\n") + _L("Hold Shift to Slice & Export G-code");
|
||||
p->btn_reslice->SetToolTip(tooltip);
|
||||
p->btn_reslice_tip = tooltip;
|
||||
}
|
||||
|
||||
void Sidebar::msw_rescale()
|
||||
|
@ -1144,24 +1165,13 @@ void Sidebar::show_info_sizer()
|
|||
p->object_info->info_facets->SetLabel(format_wxstr(_L_PLURAL("%1% (%2$d shell)", "%1% (%2$d shells)", stats.number_of_parts),
|
||||
static_cast<int>(model_object->facets_count()), stats.number_of_parts));
|
||||
|
||||
if (stats.repaired()) {
|
||||
int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + stats.facets_reversed + stats.backwards_edges;
|
||||
p->object_info->info_manifold->SetLabel(format_wxstr(_L_PLURAL("Auto-repaired %1$d error", "Auto-repaired %1$d errors", errors), errors));
|
||||
|
||||
auto mesh_errors = obj_list()->get_mesh_errors(true);
|
||||
wxString tooltip = mesh_errors.first;
|
||||
|
||||
p->object_info->update_warning_icon(mesh_errors.second);
|
||||
p->object_info->showing_manifold_warning_icon = true;
|
||||
p->object_info->info_manifold->SetToolTip(tooltip);
|
||||
p->object_info->manifold_warning_icon->SetToolTip(tooltip);
|
||||
}
|
||||
else {
|
||||
p->object_info->info_manifold->SetLabel(_L("Yes"));
|
||||
p->object_info->showing_manifold_warning_icon = false;
|
||||
p->object_info->info_manifold->SetToolTip("");
|
||||
p->object_info->manifold_warning_icon->SetToolTip("");
|
||||
}
|
||||
wxString info_manifold_label;
|
||||
auto mesh_errors = obj_list()->get_mesh_errors(&info_manifold_label);
|
||||
wxString tooltip = mesh_errors.first;
|
||||
p->object_info->update_warning_icon(mesh_errors.second);
|
||||
p->object_info->info_manifold->SetLabel(info_manifold_label);
|
||||
p->object_info->info_manifold->SetToolTip(tooltip);
|
||||
p->object_info->manifold_warning_icon->SetToolTip(tooltip);
|
||||
|
||||
p->object_info->show_sizer(true);
|
||||
|
||||
|
@ -2111,13 +2121,14 @@ void Plater::priv::update(unsigned int flags)
|
|||
}
|
||||
|
||||
unsigned int update_status = 0;
|
||||
if (this->printer_technology == ptSLA || (flags & (unsigned int)UpdateParams::FORCE_BACKGROUND_PROCESSING_UPDATE))
|
||||
const bool force_background_processing_restart = this->printer_technology == ptSLA || (flags & (unsigned int)UpdateParams::FORCE_BACKGROUND_PROCESSING_UPDATE);
|
||||
if (force_background_processing_restart)
|
||||
// Update the SLAPrint from the current Model, so that the reload_scene()
|
||||
// pulls the correct data.
|
||||
update_status = this->update_background_process(false, flags & (unsigned int)UpdateParams::POSTPONE_VALIDATION_ERROR_MESSAGE);
|
||||
this->view3D->reload_scene(false, flags & (unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH);
|
||||
this->preview->reload_print();
|
||||
if (this->printer_technology == ptSLA)
|
||||
if (force_background_processing_restart)
|
||||
this->restart_background_process(update_status);
|
||||
else
|
||||
this->schedule_background_process();
|
||||
|
@ -2348,7 +2359,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
for (std::string& name : names)
|
||||
notif_text += "\n - " + name;
|
||||
notification_manager->push_notification(NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotificationLevel, notif_text);
|
||||
NotificationManager::NotificationLevel::PrintInfoNotificationLevel, notif_text);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2444,7 +2455,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
for (ModelObject* model_object : model.objects) {
|
||||
if (!type_3mf && !type_zip_amf)
|
||||
model_object->center_around_origin(false);
|
||||
model_object->ensure_on_bed(is_project_file || type_3mf || type_zip_amf);
|
||||
model_object->ensure_on_bed(is_project_file);
|
||||
}
|
||||
|
||||
// check multi-part object adding for the SLA-printing
|
||||
|
@ -2461,7 +2472,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
|
|||
if (one_by_one) {
|
||||
if (type_3mf && !is_project_file)
|
||||
model.center_instances_around_point(bed_shape_bb().center());
|
||||
auto loaded_idxs = load_model_objects(model.objects, is_project_file || type_3mf || type_zip_amf);
|
||||
auto loaded_idxs = load_model_objects(model.objects, is_project_file);
|
||||
obj_idxs.insert(obj_idxs.end(), loaded_idxs.begin(), loaded_idxs.end());
|
||||
} else {
|
||||
// This must be an .stl or .obj file, which may contain a maximum of one volume.
|
||||
|
@ -2602,6 +2613,7 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs& mode
|
|||
// so 3D-scene should be updated before object additing to the ObjectList
|
||||
this->view3D->reload_scene(false, (unsigned int)UpdateParams::FORCE_FULL_SCREEN_REFRESH);
|
||||
|
||||
notification_manager->close_notification_of_type(NotificationType::UpdatedItemsInfo);
|
||||
for (const size_t idx : obj_idxs) {
|
||||
wxGetApp().obj_list()->add_object_to_list(idx);
|
||||
}
|
||||
|
@ -2911,7 +2923,7 @@ void Plater::priv::split_object()
|
|||
// If we splited object which is contain some parts/modifiers then all non-solid parts (modifiers) were deleted
|
||||
if (current_model_object->volumes.size() > 1 && current_model_object->volumes.size() != new_objects.size())
|
||||
notification_manager->push_notification(NotificationType::CustomNotification,
|
||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||
_u8L("All non-solid parts (modifiers) were deleted"));
|
||||
|
||||
Plater::TakeSnapshot snapshot(q, _L("Split to Objects"));
|
||||
|
@ -3081,10 +3093,13 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
|||
|
||||
//actualizate warnings
|
||||
if (invalidated != Print::APPLY_STATUS_UNCHANGED) {
|
||||
if (background_process.empty())
|
||||
process_validation_warning(std::string());
|
||||
actualize_slicing_warnings(*this->background_process.current_print());
|
||||
actualize_object_warnings(*this->background_process.current_print());
|
||||
show_warning_dialog = false;
|
||||
process_completed_with_error = false;
|
||||
|
||||
}
|
||||
|
||||
if (invalidated != Print::APPLY_STATUS_UNCHANGED && was_running && ! this->background_process.running() &&
|
||||
|
@ -3713,8 +3728,8 @@ void Plater::priv::create_simplify_notification(const std::vector<size_t>& obj_i
|
|||
"amount of triangles.");
|
||||
t.replace(t.find("@object_name"), sizeof("@object_name") - 1,
|
||||
model.objects[object_id]->name);
|
||||
std::stringstream text;
|
||||
text << _u8L("WARNING:") << "\n" << t << "\n";
|
||||
//std::stringstream text;
|
||||
//text << t << "\n";
|
||||
std::string hypertext = _u8L("Simplify model");
|
||||
|
||||
std::function<bool(wxEvtHandler *)> open_simplify = [object_id](wxEvtHandler *) {
|
||||
|
@ -3729,7 +3744,10 @@ void Plater::priv::create_simplify_notification(const std::vector<size_t>& obj_i
|
|||
manager.open_gizmo(GLGizmosManager::EType::Simplify);
|
||||
return true;
|
||||
};
|
||||
notification_manager->push_object_warning_notification(text.str(), model.objects[object_id]->id(), hypertext, open_simplify);
|
||||
notification_manager->push_simplify_suggestion_notification(t,
|
||||
model.objects[object_id]->id(),
|
||||
hypertext,
|
||||
open_simplify);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4002,7 +4020,7 @@ void Plater::priv::actualize_object_warnings(const PrintBase& print)
|
|||
ids.push_back(object->id());
|
||||
}
|
||||
std::sort(ids.begin(), ids.end());
|
||||
notification_manager->remove_object_warnings_of_released_objects(ids);
|
||||
notification_manager->remove_simplify_suggestion_of_released_objects(ids);
|
||||
}
|
||||
void Plater::priv::clear_warnings()
|
||||
{
|
||||
|
@ -4167,12 +4185,14 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
|
|||
if (printer_technology == ptSLA)
|
||||
menu = menus.sla_object_menu();
|
||||
else {
|
||||
const Selection& selection = get_selection();
|
||||
// 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 ? menus.object_menu() :
|
||||
get_selection().is_single_volume() ? menus.part_menu() : menus.multi_selection_menu();
|
||||
const bool is_some_full_instances = selection.is_single_full_instance() ||
|
||||
selection.is_single_full_object() ||
|
||||
selection.is_multiple_full_instance();
|
||||
const bool is_part = selection.is_single_volume() || selection.is_single_modifier();
|
||||
menu = is_some_full_instances ? menus.object_menu() :
|
||||
is_part ? menus.part_menu() : menus.multi_selection_menu();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4533,6 +4553,11 @@ bool Plater::priv::can_fix_through_netfabb() const
|
|||
std::vector<int> obj_idxs, vol_idxs;
|
||||
sidebar->obj_list()->get_selection_indexes(obj_idxs, vol_idxs);
|
||||
|
||||
#if FIX_THROUGH_NETFABB_ALWAYS
|
||||
// Fixing always.
|
||||
return ! obj_idxs.empty() || ! vol_idxs.empty();
|
||||
#else // FIX_THROUGH_NETFABB_ALWAYS
|
||||
// Fixing only if the model is not manifold.
|
||||
if (vol_idxs.empty()) {
|
||||
for (auto obj_idx : obj_idxs)
|
||||
if (model.objects[obj_idx]->get_mesh_errors_count() > 0)
|
||||
|
@ -4544,11 +4569,10 @@ bool Plater::priv::can_fix_through_netfabb() const
|
|||
for (auto vol_idx : vol_idxs)
|
||||
if (model.objects[obj_idx]->get_mesh_errors_count(vol_idx) > 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
#endif // FIX_THROUGH_NETFABB_ALWAYS
|
||||
}
|
||||
|
||||
|
||||
bool Plater::priv::can_simplify() const
|
||||
{
|
||||
// is object for simplification selected
|
||||
|
@ -6425,7 +6449,7 @@ void Plater::clear_before_change_mesh(int obj_idx)
|
|||
// snapshot_time is captured by copy so the lambda knows where to undo/redo to.
|
||||
get_notification_manager()->push_notification(
|
||||
NotificationType::CustomSupportsAndSeamRemovedAfterRepair,
|
||||
NotificationManager::NotificationLevel::RegularNotificationLevel,
|
||||
NotificationManager::NotificationLevel::PrintInfoNotificationLevel,
|
||||
_u8L("Custom supports, seams and multimaterial painting were "
|
||||
"removed after repairing the mesh."));
|
||||
// _u8L("Undo the repair"),
|
||||
|
|
|
@ -3236,7 +3236,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current /*=false*/,
|
|||
const PresetWithVendorProfile new_printer_preset_with_vendor_profile = m_presets->get_preset_with_vendor_profile(new_printer_preset);
|
||||
PrinterTechnology old_printer_technology = m_presets->get_edited_preset().printer_technology();
|
||||
PrinterTechnology new_printer_technology = new_printer_preset.printer_technology();
|
||||
if (new_printer_technology == ptSLA && old_printer_technology == ptFFF && !may_switch_to_SLA_preset())
|
||||
if (new_printer_technology == ptSLA && old_printer_technology == ptFFF && !wxGetApp().may_switch_to_SLA_preset(_L("New printer preset is selecting")))
|
||||
canceled = true;
|
||||
else {
|
||||
struct PresetUpdate {
|
||||
|
@ -3409,21 +3409,6 @@ bool Tab::may_discard_current_dirty_preset(PresetCollection* presets /*= nullptr
|
|||
return true;
|
||||
}
|
||||
|
||||
// If we are switching from the FFF-preset to the SLA, we should to control the printed objects if they have a part(s).
|
||||
// Because of we can't to print the multi-part objects with SLA technology.
|
||||
bool Tab::may_switch_to_SLA_preset()
|
||||
{
|
||||
if (model_has_multi_part_objects(wxGetApp().model()))
|
||||
{
|
||||
show_info( parent(),
|
||||
_(L("It's impossible to print multi-part object(s) with SLA technology.")) + "\n\n" +
|
||||
_(L("Please check your object list before preset changing.")),
|
||||
_(L("Attention!")) );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Tab::clear_pages()
|
||||
{
|
||||
// invalidated highlighter, if any exists
|
||||
|
|
|
@ -282,7 +282,6 @@ public:
|
|||
// Select a new preset, possibly delete the current one.
|
||||
void select_preset(std::string preset_name = "", bool delete_current = false, const std::string& last_selected_ph_printer_name = "");
|
||||
bool may_discard_current_dirty_preset(PresetCollection* presets = nullptr, const std::string& new_printer_name = "");
|
||||
bool may_switch_to_SLA_preset();
|
||||
|
||||
virtual void clear_pages();
|
||||
virtual void update_description_lines();
|
||||
|
|
|
@ -23,6 +23,7 @@ add_executable(${_TEST_NAME}_tests
|
|||
test_png_io.cpp
|
||||
test_timeutils.cpp
|
||||
test_indexed_triangle_set.cpp
|
||||
../libnest2d/printer_parts.cpp
|
||||
)
|
||||
|
||||
if (TARGET OpenVDB::openvdb)
|
||||
|
|
|
@ -9,6 +9,14 @@
|
|||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/ShortestPath.hpp"
|
||||
|
||||
//#include <random>
|
||||
//#include "libnest2d/tools/benchmark.h"
|
||||
#include "libslic3r/SVG.hpp"
|
||||
|
||||
#include "../libnest2d/printer_parts.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace Slic3r;
|
||||
|
||||
TEST_CASE("Polygon::contains works properly", "[Geometry]"){
|
||||
|
@ -452,3 +460,225 @@ SCENARIO("Ported from xs/t/14_geometry.t", "[Geometry]"){
|
|||
REQUIRE(! Slic3r::Geometry::directions_parallel(M_PI /2, PI, M_PI /180));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Convex polygon intersection on two disjoint squares", "[Geometry][Rotcalip]") {
|
||||
Polygon A{{0, 0}, {10, 0}, {10, 10}, {0, 10}};
|
||||
A.scale(1. / SCALING_FACTOR);
|
||||
|
||||
Polygon B = A;
|
||||
B.translate(20 / SCALING_FACTOR, 0);
|
||||
|
||||
bool is_inters = Geometry::intersects(A, B);
|
||||
|
||||
REQUIRE(is_inters == false);
|
||||
}
|
||||
|
||||
TEST_CASE("Convex polygon intersection on two intersecting squares", "[Geometry][Rotcalip]") {
|
||||
Polygon A{{0, 0}, {10, 0}, {10, 10}, {0, 10}};
|
||||
A.scale(1. / SCALING_FACTOR);
|
||||
|
||||
Polygon B = A;
|
||||
B.translate(5 / SCALING_FACTOR, 5 / SCALING_FACTOR);
|
||||
|
||||
bool is_inters = Geometry::intersects(A, B);
|
||||
|
||||
REQUIRE(is_inters == true);
|
||||
}
|
||||
|
||||
TEST_CASE("Convex polygon intersection on two squares touching one edge", "[Geometry][Rotcalip]") {
|
||||
Polygon A{{0, 0}, {10, 0}, {10, 10}, {0, 10}};
|
||||
A.scale(1. / SCALING_FACTOR);
|
||||
|
||||
Polygon B = A;
|
||||
B.translate(10 / SCALING_FACTOR, 0);
|
||||
|
||||
bool is_inters = Geometry::intersects(A, B);
|
||||
|
||||
REQUIRE(is_inters == false);
|
||||
}
|
||||
|
||||
TEST_CASE("Convex polygon intersection on two squares touching one vertex", "[Geometry][Rotcalip]") {
|
||||
Polygon A{{0, 0}, {10, 0}, {10, 10}, {0, 10}};
|
||||
A.scale(1. / SCALING_FACTOR);
|
||||
|
||||
Polygon B = A;
|
||||
B.translate(10 / SCALING_FACTOR, 10 / SCALING_FACTOR);
|
||||
|
||||
SVG svg{std::string("one_vertex_touch") + ".svg"};
|
||||
svg.draw(A, "blue");
|
||||
svg.draw(B, "green");
|
||||
svg.Close();
|
||||
|
||||
bool is_inters = Geometry::intersects(A, B);
|
||||
|
||||
REQUIRE(is_inters == false);
|
||||
}
|
||||
|
||||
TEST_CASE("Convex polygon intersection on two overlapping squares", "[Geometry][Rotcalip]") {
|
||||
Polygon A{{0, 0}, {10, 0}, {10, 10}, {0, 10}};
|
||||
A.scale(1. / SCALING_FACTOR);
|
||||
|
||||
Polygon B = A;
|
||||
|
||||
bool is_inters = Geometry::intersects(A, B);
|
||||
|
||||
REQUIRE(is_inters == true);
|
||||
}
|
||||
|
||||
//// Only for benchmarking
|
||||
//static Polygon gen_convex_poly(std::mt19937_64 &rg, size_t point_cnt)
|
||||
//{
|
||||
// std::uniform_int_distribution<coord_t> dist(0, 100);
|
||||
|
||||
// Polygon out;
|
||||
// out.points.reserve(point_cnt);
|
||||
|
||||
// coord_t tr = dist(rg) * 2 / SCALING_FACTOR;
|
||||
|
||||
// for (size_t i = 0; i < point_cnt; ++i)
|
||||
// out.points.emplace_back(tr + dist(rg) / SCALING_FACTOR,
|
||||
// tr + dist(rg) / SCALING_FACTOR);
|
||||
|
||||
// return Geometry::convex_hull(out.points);
|
||||
//}
|
||||
//TEST_CASE("Convex polygon intersection test on random polygons", "[Geometry]") {
|
||||
// constexpr size_t TEST_CNT = 1000;
|
||||
// constexpr size_t POINT_CNT = 1000;
|
||||
|
||||
// auto seed = std::random_device{}();
|
||||
//// unsigned long seed = 2525634386;
|
||||
// std::mt19937_64 rg{seed};
|
||||
// Benchmark bench;
|
||||
|
||||
// auto tests = reserve_vector<std::pair<Polygon, Polygon>>(TEST_CNT);
|
||||
// auto results = reserve_vector<bool>(TEST_CNT);
|
||||
// auto expects = reserve_vector<bool>(TEST_CNT);
|
||||
|
||||
// for (size_t i = 0; i < TEST_CNT; ++i) {
|
||||
// tests.emplace_back(gen_convex_poly(rg, POINT_CNT), gen_convex_poly(rg, POINT_CNT));
|
||||
// }
|
||||
|
||||
// bench.start();
|
||||
// for (const auto &test : tests)
|
||||
// results.emplace_back(Geometry::intersects(test.first, test.second));
|
||||
// bench.stop();
|
||||
|
||||
// std::cout << "Test time: " << bench.getElapsedSec() << std::endl;
|
||||
|
||||
// bench.start();
|
||||
// for (const auto &test : tests)
|
||||
// expects.emplace_back(!intersection(test.first, test.second).empty());
|
||||
// bench.stop();
|
||||
|
||||
// std::cout << "Clipper time: " << bench.getElapsedSec() << std::endl;
|
||||
|
||||
// REQUIRE(results.size() == expects.size());
|
||||
|
||||
// auto seedstr = std::to_string(seed);
|
||||
// for (size_t i = 0; i < results.size(); ++i) {
|
||||
// // std::cout << expects[i] << " ";
|
||||
|
||||
// if (results[i] != expects[i]) {
|
||||
// SVG svg{std::string("fail_seed") + seedstr + "_" + std::to_string(i) + ".svg"};
|
||||
// svg.draw(tests[i].first, "blue");
|
||||
// svg.draw(tests[i].second, "green");
|
||||
// svg.Close();
|
||||
|
||||
// // std::cout << std::endl;
|
||||
// }
|
||||
// REQUIRE(results[i] == expects[i]);
|
||||
// }
|
||||
// std::cout << std::endl;
|
||||
|
||||
//}
|
||||
|
||||
struct Pair
|
||||
{
|
||||
size_t first, second;
|
||||
bool operator==(const Pair &b) const { return first == b.first && second == b.second; }
|
||||
};
|
||||
|
||||
template<> struct std::hash<Pair> {
|
||||
size_t operator()(const Pair &c) const
|
||||
{
|
||||
return c.first * PRINTER_PART_POLYGONS.size() + c.second;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("Convex polygon intersection test prusa polygons", "[Geometry][Rotcalip]") {
|
||||
|
||||
// Overlap of the same polygon should always be an intersection
|
||||
for (size_t i = 0; i < PRINTER_PART_POLYGONS.size(); ++i) {
|
||||
Polygon P = PRINTER_PART_POLYGONS[i];
|
||||
P = Geometry::convex_hull(P.points);
|
||||
bool res = Geometry::intersects(P, P);
|
||||
if (!res) {
|
||||
SVG svg{std::string("fail_self") + std::to_string(i) + ".svg"};
|
||||
svg.draw(P, "green");
|
||||
svg.Close();
|
||||
}
|
||||
REQUIRE(res == true);
|
||||
}
|
||||
|
||||
std::unordered_set<Pair> combos;
|
||||
for (size_t i = 0; i < PRINTER_PART_POLYGONS.size(); ++i) {
|
||||
for (size_t j = 0; j < PRINTER_PART_POLYGONS.size(); ++j) {
|
||||
if (i != j) {
|
||||
size_t a = std::min(i, j), b = std::max(i, j);
|
||||
combos.insert(Pair{a, b});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// All disjoint
|
||||
for (const auto &combo : combos) {
|
||||
Polygon A = PRINTER_PART_POLYGONS[combo.first], B = PRINTER_PART_POLYGONS[combo.second];
|
||||
A = Geometry::convex_hull(A.points);
|
||||
B = Geometry::convex_hull(B.points);
|
||||
|
||||
auto bba = A.bounding_box();
|
||||
auto bbb = B.bounding_box();
|
||||
|
||||
A.translate(-bba.center());
|
||||
B.translate(-bbb.center());
|
||||
|
||||
B.translate(bba.size() + bbb.size());
|
||||
|
||||
bool res = Geometry::intersects(A, B);
|
||||
bool ref = !intersection(A, B).empty();
|
||||
|
||||
if (res != ref) {
|
||||
SVG svg{std::string("fail") + std::to_string(combo.first) + "_" + std::to_string(combo.second) + ".svg"};
|
||||
svg.draw(A, "blue");
|
||||
svg.draw(B, "green");
|
||||
svg.Close();
|
||||
}
|
||||
|
||||
REQUIRE(res == ref);
|
||||
}
|
||||
|
||||
// All intersecting
|
||||
for (const auto &combo : combos) {
|
||||
Polygon A = PRINTER_PART_POLYGONS[combo.first], B = PRINTER_PART_POLYGONS[combo.second];
|
||||
A = Geometry::convex_hull(A.points);
|
||||
B = Geometry::convex_hull(B.points);
|
||||
|
||||
auto bba = A.bounding_box();
|
||||
auto bbb = B.bounding_box();
|
||||
|
||||
A.translate(-bba.center());
|
||||
B.translate(-bbb.center());
|
||||
|
||||
bool res = Geometry::intersects(A, B);
|
||||
bool ref = !intersection(A, B).empty();
|
||||
|
||||
if (res != ref) {
|
||||
SVG svg{std::string("fail") + std::to_string(combo.first) + "_" + std::to_string(combo.second) + ".svg"};
|
||||
svg.draw(A, "blue");
|
||||
svg.draw(B, "green");
|
||||
svg.Close();
|
||||
}
|
||||
|
||||
REQUIRE(res == ref);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
set(SLIC3R_APP_NAME "PrusaSlicer")
|
||||
set(SLIC3R_APP_KEY "PrusaSlicer")
|
||||
set(SLIC3R_VERSION "2.4.0-alpha2")
|
||||
set(SLIC3R_VERSION "2.4.0-alpha3")
|
||||
set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN")
|
||||
set(SLIC3R_RC_VERSION "2,4,0,0")
|
||||
set(SLIC3R_RC_VERSION_DOTS "2.4.0.0")
|
||||
|
|
Loading…
Reference in a new issue