mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-11 00:37:51 -06:00
Cutting
This commit is contained in:
parent
4c007a410c
commit
6629d75853
19 changed files with 502 additions and 182 deletions
|
@ -951,25 +951,54 @@ bool ModelObject::needed_repair() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelObject::cut(coordf_t z, Model* model) const
|
template<class T> static void cut_reset_transform(T *thing) {
|
||||||
|
const Vec3d offset = thing->get_offset();
|
||||||
|
thing->set_transformation(Geometry::Transformation());
|
||||||
|
thing->set_offset(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z)
|
||||||
{
|
{
|
||||||
// clone this one to duplicate instances, materials etc.
|
// Clone the object to duplicate instances, materials etc.
|
||||||
ModelObject* upper = model->add_object(*this);
|
ModelObject* upper = ModelObject::new_clone(*this);
|
||||||
ModelObject* lower = model->add_object(*this);
|
ModelObject* lower = ModelObject::new_clone(*this);
|
||||||
|
upper->set_model(nullptr);
|
||||||
|
lower->set_model(nullptr);
|
||||||
upper->sla_support_points.clear();
|
upper->sla_support_points.clear();
|
||||||
lower->sla_support_points.clear();
|
lower->sla_support_points.clear();
|
||||||
upper->clear_volumes();
|
upper->clear_volumes();
|
||||||
lower->clear_volumes();
|
lower->clear_volumes();
|
||||||
upper->input_file = "";
|
upper->input_file = "";
|
||||||
lower->input_file = "";
|
lower->input_file = "";
|
||||||
|
|
||||||
for (ModelVolume *volume : this->volumes) {
|
const auto instance_matrix = instances[instance]->get_matrix(true);
|
||||||
|
|
||||||
|
// Because transformations are going to be applied to meshes directly,
|
||||||
|
// we reset transformation of all instances and volumes,
|
||||||
|
// _except_ for translation, which is preserved in the transformation matrix
|
||||||
|
// and not applied to the mesh transform.
|
||||||
|
// TODO: Do the same for Z-rotation as well?
|
||||||
|
|
||||||
|
// Convert z from relative to bb's base to object coordinates
|
||||||
|
// FIXME: doesn't work well for rotated objects
|
||||||
|
const auto bb = instance_bounding_box(instance, true);
|
||||||
|
z -= bb.min(2);
|
||||||
|
|
||||||
|
for (auto *instance : upper->instances) { cut_reset_transform(instance); }
|
||||||
|
for (auto *instance : lower->instances) { cut_reset_transform(instance); }
|
||||||
|
|
||||||
|
for (ModelVolume *volume : volumes) {
|
||||||
if (! volume->is_model_part()) {
|
if (! volume->is_model_part()) {
|
||||||
// don't cut modifiers
|
// don't cut modifiers
|
||||||
upper->add_volume(*volume);
|
upper->add_volume(*volume);
|
||||||
lower->add_volume(*volume);
|
lower->add_volume(*volume);
|
||||||
} else {
|
} else {
|
||||||
TriangleMesh upper_mesh, lower_mesh;
|
TriangleMesh upper_mesh, lower_mesh;
|
||||||
|
|
||||||
|
// Transform the mesh by the object transformation matrix
|
||||||
|
volume->mesh.transform(instance_matrix * volume->get_matrix(true));
|
||||||
|
cut_reset_transform(volume);
|
||||||
|
|
||||||
TriangleMeshSlicer tms(&volume->mesh);
|
TriangleMeshSlicer tms(&volume->mesh);
|
||||||
tms.cut(z, &upper_mesh, &lower_mesh);
|
tms.cut(z, &upper_mesh, &lower_mesh);
|
||||||
|
|
||||||
|
@ -977,7 +1006,7 @@ void ModelObject::cut(coordf_t z, Model* model) const
|
||||||
lower_mesh.repair();
|
lower_mesh.repair();
|
||||||
upper_mesh.reset_repair_stats();
|
upper_mesh.reset_repair_stats();
|
||||||
lower_mesh.reset_repair_stats();
|
lower_mesh.reset_repair_stats();
|
||||||
|
|
||||||
if (upper_mesh.facets_count() > 0) {
|
if (upper_mesh.facets_count() > 0) {
|
||||||
ModelVolume* vol = upper->add_volume(upper_mesh);
|
ModelVolume* vol = upper->add_volume(upper_mesh);
|
||||||
vol->name = volume->name;
|
vol->name = volume->name;
|
||||||
|
@ -992,6 +1021,15 @@ void ModelObject::cut(coordf_t z, Model* model) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
upper->invalidate_bounding_box();
|
||||||
|
lower->invalidate_bounding_box();
|
||||||
|
|
||||||
|
ModelObjectPtrs res;
|
||||||
|
if (upper->volumes.size() > 0) { res.push_back(upper); }
|
||||||
|
if (lower->volumes.size() > 0) { res.push_back(lower); }
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelObject::split(ModelObjectPtrs* new_objects)
|
void ModelObject::split(ModelObjectPtrs* new_objects)
|
||||||
|
@ -1011,7 +1049,8 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
|
||||||
|
|
||||||
mesh->repair();
|
mesh->repair();
|
||||||
|
|
||||||
ModelObject* new_object = m_model->add_object();
|
// XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed?
|
||||||
|
ModelObject* new_object = m_model->add_object();
|
||||||
new_object->name = this->name;
|
new_object->name = this->name;
|
||||||
new_object->config = this->config;
|
new_object->config = this->config;
|
||||||
new_object->instances.reserve(this->instances.size());
|
new_object->instances.reserve(this->instances.size());
|
||||||
|
|
|
@ -237,7 +237,7 @@ public:
|
||||||
size_t materials_count() const;
|
size_t materials_count() const;
|
||||||
size_t facets_count() const;
|
size_t facets_count() const;
|
||||||
bool needed_repair() const;
|
bool needed_repair() const;
|
||||||
void cut(coordf_t z, Model* model) const;
|
ModelObjectPtrs cut(size_t instance, coordf_t z);
|
||||||
void split(ModelObjectPtrs* new_objects);
|
void split(ModelObjectPtrs* new_objects);
|
||||||
void repair();
|
void repair();
|
||||||
|
|
||||||
|
@ -607,7 +607,7 @@ public:
|
||||||
bool delete_object(ModelID id);
|
bool delete_object(ModelID id);
|
||||||
bool delete_object(ModelObject* object);
|
bool delete_object(ModelObject* object);
|
||||||
void clear_objects();
|
void clear_objects();
|
||||||
|
|
||||||
ModelMaterial* add_material(t_model_material_id material_id);
|
ModelMaterial* add_material(t_model_material_id material_id);
|
||||||
ModelMaterial* add_material(t_model_material_id material_id, const ModelMaterial &other);
|
ModelMaterial* add_material(t_model_material_id material_id, const ModelMaterial &other);
|
||||||
ModelMaterial* get_material(t_model_material_id material_id) {
|
ModelMaterial* get_material(t_model_material_id material_id) {
|
||||||
|
|
|
@ -192,20 +192,21 @@ int main(int argc, char **argv)
|
||||||
model.repair();
|
model.repair();
|
||||||
model.translate(0, 0, - model.bounding_box().min(2));
|
model.translate(0, 0, - model.bounding_box().min(2));
|
||||||
if (! model.objects.empty()) {
|
if (! model.objects.empty()) {
|
||||||
Model out;
|
// XXX
|
||||||
model.objects.front()->cut(cli_config.cut, &out);
|
// Model out;
|
||||||
ModelObject &upper = *out.objects[0];
|
// model.objects.front()->cut(cli_config.cut, &out);
|
||||||
ModelObject &lower = *out.objects[1];
|
// ModelObject &upper = *out.objects[0];
|
||||||
// Use the input name and trim off the extension.
|
// ModelObject &lower = *out.objects[1];
|
||||||
std::string outfile = cli_config.output.value;
|
// // Use the input name and trim off the extension.
|
||||||
if (outfile.empty())
|
// std::string outfile = cli_config.output.value;
|
||||||
outfile = model.objects.front()->input_file;
|
// if (outfile.empty())
|
||||||
outfile = outfile.substr(0, outfile.find_last_of('.'));
|
// outfile = model.objects.front()->input_file;
|
||||||
std::cerr << outfile << "\n";
|
// outfile = outfile.substr(0, outfile.find_last_of('.'));
|
||||||
if (upper.facets_count() > 0)
|
// std::cerr << outfile << "\n";
|
||||||
upper.mesh().write_binary((outfile + "_upper.stl").c_str());
|
// if (upper.facets_count() > 0)
|
||||||
if (lower.facets_count() > 0)
|
// upper.mesh().write_binary((outfile + "_upper.stl").c_str());
|
||||||
lower.mesh().write_binary((outfile + "_lower.stl").c_str());
|
// if (lower.facets_count() > 0)
|
||||||
|
// lower.mesh().write_binary((outfile + "_lower.stl").c_str());
|
||||||
}
|
}
|
||||||
} else if (cli_config.slice) {
|
} else if (cli_config.slice) {
|
||||||
std::string outfile = cli_config.output.value;
|
std::string outfile = cli_config.output.value;
|
||||||
|
|
|
@ -1915,11 +1915,6 @@ void _3DScene::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape)
|
||||||
s_canvas_mgr.set_bed_shape(canvas, shape);
|
s_canvas_mgr.set_bed_shape(canvas, shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _3DScene::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons)
|
|
||||||
{
|
|
||||||
s_canvas_mgr.set_cutting_plane(canvas, z, polygons);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _3DScene::set_color_by(wxGLCanvas* canvas, const std::string& value)
|
void _3DScene::set_color_by(wxGLCanvas* canvas, const std::string& value)
|
||||||
{
|
{
|
||||||
s_canvas_mgr.set_color_by(canvas, value);
|
s_canvas_mgr.set_color_by(canvas, value);
|
||||||
|
|
|
@ -577,8 +577,6 @@ public:
|
||||||
|
|
||||||
static void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape);
|
static void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape);
|
||||||
|
|
||||||
static void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons);
|
|
||||||
|
|
||||||
static void set_color_by(wxGLCanvas* canvas, const std::string& value);
|
static void set_color_by(wxGLCanvas* canvas, const std::string& value);
|
||||||
|
|
||||||
static bool is_layers_editing_enabled(wxGLCanvas* canvas);
|
static bool is_layers_editing_enabled(wxGLCanvas* canvas);
|
||||||
|
|
|
@ -641,70 +641,6 @@ void GLCanvas3D::Axes::render(bool depth_test) const
|
||||||
::glEnd();
|
::glEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
GLCanvas3D::CuttingPlane::CuttingPlane()
|
|
||||||
: m_z(-1.0f)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GLCanvas3D::CuttingPlane::set(float z, const ExPolygons& polygons)
|
|
||||||
{
|
|
||||||
m_z = z;
|
|
||||||
|
|
||||||
// grow slices in order to display them better
|
|
||||||
ExPolygons expolygons = offset_ex(polygons, (float)scale_(0.1));
|
|
||||||
Lines lines = to_lines(expolygons);
|
|
||||||
return m_lines.set_from_lines(lines, m_z);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLCanvas3D::CuttingPlane::render(const BoundingBoxf3& bb) const
|
|
||||||
{
|
|
||||||
_render_plane(bb);
|
|
||||||
_render_contour();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLCanvas3D::CuttingPlane::_render_plane(const BoundingBoxf3& bb) const
|
|
||||||
{
|
|
||||||
if (m_z >= 0.0f)
|
|
||||||
{
|
|
||||||
::glDisable(GL_CULL_FACE);
|
|
||||||
::glEnable(GL_BLEND);
|
|
||||||
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
||||||
|
|
||||||
float margin = 20.0f;
|
|
||||||
float min_x = bb.min(0) - margin;
|
|
||||||
float max_x = bb.max(0) + margin;
|
|
||||||
float min_y = bb.min(1) - margin;
|
|
||||||
float max_y = bb.max(1) + margin;
|
|
||||||
|
|
||||||
::glBegin(GL_QUADS);
|
|
||||||
::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
|
|
||||||
::glVertex3f(min_x, min_y, m_z);
|
|
||||||
::glVertex3f(max_x, min_y, m_z);
|
|
||||||
::glVertex3f(max_x, max_y, m_z);
|
|
||||||
::glVertex3f(min_x, max_y, m_z);
|
|
||||||
::glEnd();
|
|
||||||
|
|
||||||
::glEnable(GL_CULL_FACE);
|
|
||||||
::glDisable(GL_BLEND);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLCanvas3D::CuttingPlane::_render_contour() const
|
|
||||||
{
|
|
||||||
::glEnableClientState(GL_VERTEX_ARRAY);
|
|
||||||
|
|
||||||
if (m_z >= 0.0f)
|
|
||||||
{
|
|
||||||
unsigned int lines_vcount = m_lines.get_vertices_count();
|
|
||||||
|
|
||||||
::glLineWidth(2.0f);
|
|
||||||
::glColor3f(0.0f, 0.0f, 0.0f);
|
|
||||||
::glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)m_lines.get_vertices());
|
|
||||||
::glDrawArrays(GL_LINES, 0, (GLsizei)lines_vcount);
|
|
||||||
}
|
|
||||||
|
|
||||||
::glDisableClientState(GL_VERTEX_ARRAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
GLCanvas3D::Shader::Shader()
|
GLCanvas3D::Shader::Shader()
|
||||||
: m_shader(nullptr)
|
: m_shader(nullptr)
|
||||||
|
@ -2380,6 +2316,13 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent)
|
||||||
|
|
||||||
m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo));
|
m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo));
|
||||||
|
|
||||||
|
gizmo = new GLGizmoCut(parent);
|
||||||
|
if (! gizmo->init()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_gizmos.insert({ Cut, gizmo });
|
||||||
|
|
||||||
gizmo = new GLGizmoSlaSupports(parent);
|
gizmo = new GLGizmoSlaSupports(parent);
|
||||||
if (gizmo == nullptr)
|
if (gizmo == nullptr)
|
||||||
return false;
|
return false;
|
||||||
|
@ -2391,7 +2334,6 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent)
|
||||||
|
|
||||||
m_gizmos.insert(GizmosMap::value_type(SlaSupports, gizmo));
|
m_gizmos.insert(GizmosMap::value_type(SlaSupports, gizmo));
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2764,6 +2706,13 @@ void GLCanvas3D::Gizmos::render_overlay(const GLCanvas3D& canvas) const
|
||||||
::glPopMatrix();
|
::glPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLCanvas3D::Gizmos::create_external_gizmo_widgets(wxWindow *parent)
|
||||||
|
{
|
||||||
|
for (auto &entry : m_gizmos) {
|
||||||
|
entry.second->create_external_gizmo_widgets(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void GLCanvas3D::Gizmos::_reset()
|
void GLCanvas3D::Gizmos::_reset()
|
||||||
{
|
{
|
||||||
for (GizmosMap::value_type& gizmo : m_gizmos)
|
for (GizmosMap::value_type& gizmo : m_gizmos)
|
||||||
|
@ -3166,6 +3115,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas)
|
||||||
, m_regenerate_volumes(true)
|
, m_regenerate_volumes(true)
|
||||||
, m_color_by("volume")
|
, m_color_by("volume")
|
||||||
, m_reload_delayed(false)
|
, m_reload_delayed(false)
|
||||||
|
, m_external_gizmo_widgets_parent(nullptr)
|
||||||
{
|
{
|
||||||
if (m_canvas != nullptr)
|
if (m_canvas != nullptr)
|
||||||
{
|
{
|
||||||
|
@ -3267,8 +3217,16 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl)
|
||||||
if (!m_volumes.empty())
|
if (!m_volumes.empty())
|
||||||
m_volumes.finalize_geometry(m_use_VBOs);
|
m_volumes.finalize_geometry(m_use_VBOs);
|
||||||
|
|
||||||
if (m_gizmos.is_enabled() && !m_gizmos.init(*this))
|
if (m_gizmos.is_enabled()) {
|
||||||
return false;
|
if (! m_gizmos.init(*this)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_external_gizmo_widgets_parent != nullptr) {
|
||||||
|
m_gizmos.create_external_gizmo_widgets(m_external_gizmo_widgets_parent);
|
||||||
|
m_canvas->GetParent()->Layout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!_init_toolbar())
|
if (!_init_toolbar())
|
||||||
return false;
|
return false;
|
||||||
|
@ -3371,11 +3329,6 @@ void GLCanvas3D::set_axes_length(float length)
|
||||||
m_axes.length = length;
|
m_axes.length = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::set_cutting_plane(float z, const ExPolygons& polygons)
|
|
||||||
{
|
|
||||||
m_cutting_plane.set(z, polygons);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLCanvas3D::set_color_by(const std::string& value)
|
void GLCanvas3D::set_color_by(const std::string& value)
|
||||||
{
|
{
|
||||||
m_color_by = value;
|
m_color_by = value;
|
||||||
|
@ -3629,7 +3582,6 @@ void GLCanvas3D::render()
|
||||||
#endif // ENABLE_GIZMOS_ON_TOP
|
#endif // ENABLE_GIZMOS_ON_TOP
|
||||||
|
|
||||||
_render_current_gizmo();
|
_render_current_gizmo();
|
||||||
_render_cutting_plane();
|
|
||||||
#if ENABLE_SHOW_CAMERA_TARGET
|
#if ENABLE_SHOW_CAMERA_TARGET
|
||||||
_render_camera_target();
|
_render_camera_target();
|
||||||
#endif // ENABLE_SHOW_CAMERA_TARGET
|
#endif // ENABLE_SHOW_CAMERA_TARGET
|
||||||
|
@ -4524,6 +4476,11 @@ void GLCanvas3D::set_tooltip(const std::string& tooltip) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLCanvas3D::set_external_gizmo_widgets_parent(wxWindow *parent)
|
||||||
|
{
|
||||||
|
m_external_gizmo_widgets_parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
bool GLCanvas3D::_is_shown_on_screen() const
|
bool GLCanvas3D::_is_shown_on_screen() const
|
||||||
{
|
{
|
||||||
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
|
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
|
||||||
|
@ -4624,14 +4581,6 @@ bool GLCanvas3D::_init_toolbar()
|
||||||
if (!m_toolbar.add_item(item))
|
if (!m_toolbar.add_item(item))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
item.name = "cut";
|
|
||||||
item.tooltip = GUI::L_str("Cut...");
|
|
||||||
item.sprite_id = 7;
|
|
||||||
item.is_toggable = false;
|
|
||||||
item.action_event = EVT_GLTOOLBAR_CUT;
|
|
||||||
if (!m_toolbar.add_item(item))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!m_toolbar.add_separator())
|
if (!m_toolbar.add_separator())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -5030,11 +4979,6 @@ void GLCanvas3D::_render_selection() const
|
||||||
m_selection.render();
|
m_selection.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::_render_cutting_plane() const
|
|
||||||
{
|
|
||||||
m_cutting_plane.render(volumes_bounding_box());
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLCanvas3D::_render_warning_texture() const
|
void GLCanvas3D::_render_warning_texture() const
|
||||||
{
|
{
|
||||||
if (!m_warning_texture_enabled)
|
if (!m_warning_texture_enabled)
|
||||||
|
|
|
@ -16,6 +16,7 @@ class wxKeyEvent;
|
||||||
class wxMouseEvent;
|
class wxMouseEvent;
|
||||||
class wxTimerEvent;
|
class wxTimerEvent;
|
||||||
class wxPaintEvent;
|
class wxPaintEvent;
|
||||||
|
class wxGLCanvas;
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
@ -219,23 +220,6 @@ class GLCanvas3D
|
||||||
void render(bool depth_test) const;
|
void render(bool depth_test) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CuttingPlane
|
|
||||||
{
|
|
||||||
float m_z;
|
|
||||||
GeometryBuffer m_lines;
|
|
||||||
|
|
||||||
public:
|
|
||||||
CuttingPlane();
|
|
||||||
|
|
||||||
bool set(float z, const ExPolygons& polygons);
|
|
||||||
|
|
||||||
void render(const BoundingBoxf3& bb) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void _render_plane(const BoundingBoxf3& bb) const;
|
|
||||||
void _render_contour() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Shader
|
class Shader
|
||||||
{
|
{
|
||||||
GLShader* m_shader;
|
GLShader* m_shader;
|
||||||
|
@ -479,6 +463,8 @@ public:
|
||||||
Selection();
|
Selection();
|
||||||
|
|
||||||
void set_volumes(GLVolumePtrs* volumes);
|
void set_volumes(GLVolumePtrs* volumes);
|
||||||
|
|
||||||
|
Model* get_model() const { return m_model; }
|
||||||
void set_model(Model* model);
|
void set_model(Model* model);
|
||||||
|
|
||||||
EMode get_mode() const { return m_mode; }
|
EMode get_mode() const { return m_mode; }
|
||||||
|
@ -580,6 +566,7 @@ private:
|
||||||
Rotate,
|
Rotate,
|
||||||
Flatten,
|
Flatten,
|
||||||
SlaSupports,
|
SlaSupports,
|
||||||
|
Cut,
|
||||||
Num_Types
|
Num_Types
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -642,6 +629,8 @@ private:
|
||||||
|
|
||||||
void render_overlay(const GLCanvas3D& canvas) const;
|
void render_overlay(const GLCanvas3D& canvas) const;
|
||||||
|
|
||||||
|
void create_external_gizmo_widgets(wxWindow *parent);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _reset();
|
void _reset();
|
||||||
|
|
||||||
|
@ -698,7 +687,6 @@ private:
|
||||||
Camera m_camera;
|
Camera m_camera;
|
||||||
Bed m_bed;
|
Bed m_bed;
|
||||||
Axes m_axes;
|
Axes m_axes;
|
||||||
CuttingPlane m_cutting_plane;
|
|
||||||
LayersEditing m_layers_editing;
|
LayersEditing m_layers_editing;
|
||||||
Shader m_shader;
|
Shader m_shader;
|
||||||
Mouse m_mouse;
|
Mouse m_mouse;
|
||||||
|
@ -734,6 +722,8 @@ private:
|
||||||
|
|
||||||
GCodePreviewVolumeIndex m_gcode_preview_volume_index;
|
GCodePreviewVolumeIndex m_gcode_preview_volume_index;
|
||||||
|
|
||||||
|
wxWindow *m_external_gizmo_widgets_parent;
|
||||||
|
|
||||||
void post_event(wxEvent &&event);
|
void post_event(wxEvent &&event);
|
||||||
void viewport_changed();
|
void viewport_changed();
|
||||||
|
|
||||||
|
@ -775,8 +765,6 @@ public:
|
||||||
|
|
||||||
void set_axes_length(float length);
|
void set_axes_length(float length);
|
||||||
|
|
||||||
void set_cutting_plane(float z, const ExPolygons& polygons);
|
|
||||||
|
|
||||||
void set_color_by(const std::string& value);
|
void set_color_by(const std::string& value);
|
||||||
|
|
||||||
float get_camera_zoom() const;
|
float get_camera_zoom() const;
|
||||||
|
@ -855,6 +843,8 @@ public:
|
||||||
|
|
||||||
void set_tooltip(const std::string& tooltip) const;
|
void set_tooltip(const std::string& tooltip) const;
|
||||||
|
|
||||||
|
void set_external_gizmo_widgets_parent(wxWindow *parent);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _is_shown_on_screen() const;
|
bool _is_shown_on_screen() const;
|
||||||
void _force_zoom_to_bed();
|
void _force_zoom_to_bed();
|
||||||
|
@ -881,7 +871,6 @@ private:
|
||||||
void _render_axes(bool depth_test) const;
|
void _render_axes(bool depth_test) const;
|
||||||
void _render_objects() const;
|
void _render_objects() const;
|
||||||
void _render_selection() const;
|
void _render_selection() const;
|
||||||
void _render_cutting_plane() const;
|
|
||||||
void _render_warning_texture() const;
|
void _render_warning_texture() const;
|
||||||
void _render_legend_texture() const;
|
void _render_legend_texture() const;
|
||||||
void _render_layer_editing_overlay() const;
|
void _render_layer_editing_overlay() const;
|
||||||
|
|
|
@ -293,13 +293,6 @@ void GLCanvas3DManager::set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape)
|
||||||
it->second->set_bed_shape(shape);
|
it->second->set_bed_shape(shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3DManager::set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons)
|
|
||||||
{
|
|
||||||
CanvasesMap::iterator it = _get_canvas(canvas);
|
|
||||||
if (it != m_canvases.end())
|
|
||||||
it->second->set_cutting_plane(z, polygons);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLCanvas3DManager::set_color_by(wxGLCanvas* canvas, const std::string& value)
|
void GLCanvas3DManager::set_color_by(wxGLCanvas* canvas, const std::string& value)
|
||||||
{
|
{
|
||||||
CanvasesMap::iterator it = _get_canvas(canvas);
|
CanvasesMap::iterator it = _get_canvas(canvas);
|
||||||
|
|
|
@ -99,8 +99,6 @@ public:
|
||||||
|
|
||||||
void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape);
|
void set_bed_shape(wxGLCanvas* canvas, const Pointfs& shape);
|
||||||
|
|
||||||
void set_cutting_plane(wxGLCanvas* canvas, float z, const ExPolygons& polygons);
|
|
||||||
|
|
||||||
void set_color_by(wxGLCanvas* canvas, const std::string& value);
|
void set_color_by(wxGLCanvas* canvas, const std::string& value);
|
||||||
|
|
||||||
bool is_layers_editing_enabled(wxGLCanvas* canvas) const;
|
bool is_layers_editing_enabled(wxGLCanvas* canvas) const;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "../../libslic3r/libslic3r.h"
|
#include "libslic3r/libslic3r.h"
|
||||||
#include "GLGizmo.hpp"
|
#include "GLGizmo.hpp"
|
||||||
|
|
||||||
#include "GUI.hpp"
|
#include "GUI.hpp"
|
||||||
|
@ -8,15 +8,29 @@
|
||||||
#include "PresetBundle.hpp"
|
#include "PresetBundle.hpp"
|
||||||
|
|
||||||
#include <Eigen/Dense>
|
#include <Eigen/Dense>
|
||||||
#include "../../libslic3r/Geometry.hpp"
|
#include "libslic3r/Geometry.hpp"
|
||||||
|
|
||||||
#include <igl/unproject_onto_mesh.h>
|
#include <igl/unproject_onto_mesh.h>
|
||||||
#include <GL/glew.h>
|
#include <GL/glew.h>
|
||||||
|
|
||||||
#include <SLA/SLASupportTree.hpp>
|
#include <SLA/SLASupportTree.hpp>
|
||||||
|
|
||||||
#include <iostream>
|
#include <cstdio>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <wx/sizer.h>
|
||||||
|
#include <wx/panel.h>
|
||||||
|
#include <wx/button.h>
|
||||||
|
#include <wx/checkbox.h>
|
||||||
|
#include <wx/stattext.h>
|
||||||
|
#include <wx/debug.h>
|
||||||
|
|
||||||
|
#include "GUI.hpp"
|
||||||
|
#include "GUI_Utils.hpp"
|
||||||
|
#include "GUI_App.hpp"
|
||||||
|
|
||||||
|
// TODO: Display tooltips quicker on Linux
|
||||||
|
|
||||||
static const float DEFAULT_BASE_COLOR[3] = { 0.625f, 0.625f, 0.625f };
|
static const float DEFAULT_BASE_COLOR[3] = { 0.625f, 0.625f, 0.625f };
|
||||||
static const float DEFAULT_DRAG_COLOR[3] = { 1.0f, 1.0f, 1.0f };
|
static const float DEFAULT_DRAG_COLOR[3] = { 1.0f, 1.0f, 1.0f };
|
||||||
|
@ -252,6 +266,8 @@ void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLGizmoBase::create_external_gizmo_widgets(wxWindow *parent) {}
|
||||||
|
|
||||||
void GLGizmoBase::set_tooltip(const std::string& tooltip) const
|
void GLGizmoBase::set_tooltip(const std::string& tooltip) const
|
||||||
{
|
{
|
||||||
m_parent.set_tooltip(tooltip);
|
m_parent.set_tooltip(tooltip);
|
||||||
|
@ -259,9 +275,10 @@ void GLGizmoBase::set_tooltip(const std::string& tooltip) const
|
||||||
|
|
||||||
std::string GLGizmoBase::format(float value, unsigned int decimals) const
|
std::string GLGizmoBase::format(float value, unsigned int decimals) const
|
||||||
{
|
{
|
||||||
char buf[1024];
|
size_t needed_size = std::snprintf(nullptr, 0, "%.*f", decimals, value);
|
||||||
::sprintf(buf, "%.*f", decimals, value);
|
std::string res(needed_size, '\0');
|
||||||
return buf;
|
std::snprintf(&res.front(), res.size(), "%.*f", decimals, value);
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
const float GLGizmoRotate::Offset = 5.0f;
|
const float GLGizmoRotate::Offset = 5.0f;
|
||||||
|
@ -1770,5 +1787,237 @@ std::string GLGizmoSlaSupports::on_get_name() const
|
||||||
return L("SLA Support Points");
|
return L("SLA Support Points");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// GLGizmoCut
|
||||||
|
|
||||||
|
class GLGizmoCutPanel : public wxPanel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GLGizmoCutPanel(wxWindow *parent);
|
||||||
|
|
||||||
|
void display(bool display);
|
||||||
|
private:
|
||||||
|
bool m_active;
|
||||||
|
wxCheckBox *m_cb_rotate;
|
||||||
|
wxButton *m_btn_cut;
|
||||||
|
wxButton *m_btn_cancel;
|
||||||
|
};
|
||||||
|
|
||||||
|
GLGizmoCutPanel::GLGizmoCutPanel(wxWindow *parent)
|
||||||
|
: wxPanel(parent)
|
||||||
|
, m_active(false)
|
||||||
|
, m_cb_rotate(new wxCheckBox(this, wxID_ANY, _(L("Rotate lower part upwards"))))
|
||||||
|
, m_btn_cut(new wxButton(this, wxID_OK, _(L("Perform cut"))))
|
||||||
|
, m_btn_cancel(new wxButton(this, wxID_CANCEL, _(L("Cancel"))))
|
||||||
|
{
|
||||||
|
enum { MARGIN = 5 };
|
||||||
|
|
||||||
|
auto *sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
|
auto *label = new wxStaticText(this, wxID_ANY, _(L("Cut object:")));
|
||||||
|
sizer->Add(label, 0, wxALL | wxALIGN_CENTER, MARGIN);
|
||||||
|
sizer->Add(m_cb_rotate, 0, wxALL | wxALIGN_CENTER, MARGIN);
|
||||||
|
sizer->AddStretchSpacer();
|
||||||
|
sizer->Add(m_btn_cut, 0, wxALL | wxALIGN_CENTER, MARGIN);
|
||||||
|
sizer->Add(m_btn_cancel, 0, wxALL | wxALIGN_CENTER, MARGIN);
|
||||||
|
|
||||||
|
SetSizer(sizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLGizmoCutPanel::display(bool display)
|
||||||
|
{
|
||||||
|
Show(display);
|
||||||
|
GetParent()->Layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const double GLGizmoCut::Offset = 10.0;
|
||||||
|
const double GLGizmoCut::Margin = 20.0;
|
||||||
|
const std::array<float, 3> GLGizmoCut::GrabberColor = { 1.0, 0.5, 0.0 };
|
||||||
|
|
||||||
|
GLGizmoCut::GLGizmoCut(GLCanvas3D& parent)
|
||||||
|
: GLGizmoBase(parent)
|
||||||
|
, m_cut_z(0.0)
|
||||||
|
, m_panel(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void GLGizmoCut::create_external_gizmo_widgets(wxWindow *parent)
|
||||||
|
{
|
||||||
|
wxASSERT(m_panel == nullptr);
|
||||||
|
|
||||||
|
m_panel = new GLGizmoCutPanel(parent);
|
||||||
|
parent->GetSizer()->Add(m_panel, 0, wxEXPAND);
|
||||||
|
|
||||||
|
parent->Layout();
|
||||||
|
parent->Fit();
|
||||||
|
auto prev_heigh = parent->GetMinSize().GetHeight();
|
||||||
|
parent->SetMinSize(wxSize(-1, std::max(prev_heigh, m_panel->GetSize().GetHeight())));
|
||||||
|
|
||||||
|
m_panel->Hide();
|
||||||
|
m_panel->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
|
||||||
|
perform_cut();
|
||||||
|
}, wxID_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLGizmoCut::on_init()
|
||||||
|
{
|
||||||
|
// TODO: icon
|
||||||
|
|
||||||
|
std::string path = resources_dir() + "/icons/overlay/";
|
||||||
|
|
||||||
|
if (!m_textures[Off].load_from_file(path + "cut_off.png", false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_textures[Hover].load_from_file(path + "cut_hover.png", false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_textures[On].load_from_file(path + "cut_on.png", false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_grabbers.emplace_back();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GLGizmoCut::on_get_name() const
|
||||||
|
{
|
||||||
|
return L("Cut");
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLGizmoCut::on_set_state()
|
||||||
|
{
|
||||||
|
// Reset m_cut_z on gizmo activation
|
||||||
|
if (get_state() == On) {
|
||||||
|
m_cut_z = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display or hide the extra panel
|
||||||
|
if (m_panel != nullptr) {
|
||||||
|
m_panel->display(get_state() == On);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GLGizmoCut::on_is_activable(const GLCanvas3D::Selection& selection) const
|
||||||
|
{
|
||||||
|
return selection.is_single_full_instance() && !selection.is_wipe_tower();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLGizmoCut::on_start_dragging(const GLCanvas3D::Selection& selection)
|
||||||
|
{
|
||||||
|
if (m_hover_id == -1) { return; }
|
||||||
|
|
||||||
|
const BoundingBoxf3& box = selection.get_bounding_box();
|
||||||
|
m_start_z = m_cut_z;
|
||||||
|
m_max_z = box.size()(2) / 2.0;
|
||||||
|
m_drag_pos = m_grabbers[m_hover_id].center;
|
||||||
|
m_drag_center = box.center();
|
||||||
|
m_drag_center(2) += m_cut_z;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLGizmoCut::on_update(const UpdateData& data)
|
||||||
|
{
|
||||||
|
if (m_hover_id != -1) {
|
||||||
|
// Clamp the plane to the object's bounding box
|
||||||
|
const double new_z = m_start_z + calc_projection(data.mouse_ray);
|
||||||
|
m_cut_z = std::max(-m_max_z, std::min(m_max_z, new_z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLGizmoCut::on_render(const GLCanvas3D::Selection& selection) const
|
||||||
|
{
|
||||||
|
if (m_grabbers[0].dragging) {
|
||||||
|
set_tooltip("Z: " + format(m_cut_z, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
const BoundingBoxf3& box = selection.get_bounding_box();
|
||||||
|
Vec3d plane_center = box.center();
|
||||||
|
plane_center(2) += m_cut_z;
|
||||||
|
|
||||||
|
const float min_x = box.min(0) - Margin;
|
||||||
|
const float max_x = box.max(0) + Margin;
|
||||||
|
const float min_y = box.min(1) - Margin;
|
||||||
|
const float max_y = box.max(1) + Margin;
|
||||||
|
::glEnable(GL_DEPTH_TEST);
|
||||||
|
::glDisable(GL_CULL_FACE);
|
||||||
|
::glEnable(GL_BLEND);
|
||||||
|
::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
|
// Draw the cutting plane
|
||||||
|
::glBegin(GL_QUADS);
|
||||||
|
::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
|
||||||
|
::glVertex3f(min_x, min_y, plane_center(2));
|
||||||
|
::glVertex3f(max_x, min_y, plane_center(2));
|
||||||
|
::glVertex3f(max_x, max_y, plane_center(2));
|
||||||
|
::glVertex3f(min_x, max_y, plane_center(2));
|
||||||
|
::glEnd();
|
||||||
|
|
||||||
|
::glEnable(GL_CULL_FACE);
|
||||||
|
::glDisable(GL_BLEND);
|
||||||
|
|
||||||
|
// TODO: draw cut part contour?
|
||||||
|
|
||||||
|
// Draw the grabber and the connecting line
|
||||||
|
m_grabbers[0].center = plane_center;
|
||||||
|
m_grabbers[0].center(2) = plane_center(2) + Offset;
|
||||||
|
|
||||||
|
::glDisable(GL_DEPTH_TEST);
|
||||||
|
::glLineWidth(m_hover_id != -1 ? 2.0f : 1.5f);
|
||||||
|
::glColor3f(1.0, 1.0, 0.0);
|
||||||
|
::glBegin(GL_LINES);
|
||||||
|
::glVertex3dv(plane_center.data());
|
||||||
|
::glVertex3dv(m_grabbers[0].center.data());
|
||||||
|
::glEnd();
|
||||||
|
|
||||||
|
std::copy(std::begin(GrabberColor), std::end(GrabberColor), m_grabbers[0].color);
|
||||||
|
m_grabbers[0].render(m_hover_id == 0, box.max_size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLGizmoCut::on_render_for_picking(const GLCanvas3D::Selection& selection) const
|
||||||
|
{
|
||||||
|
::glDisable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
render_grabbers_for_picking(selection.get_bounding_box());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLGizmoCut::perform_cut()
|
||||||
|
{
|
||||||
|
const auto &selection = m_parent.get_selection();
|
||||||
|
|
||||||
|
const auto instance_idx = selection.get_instance_idx();
|
||||||
|
const auto object_idx = selection.get_object_idx();
|
||||||
|
|
||||||
|
wxCHECK_RET(instance_idx >= 0 && object_idx >= 0, "GLGizmoCut: Invalid object selection");
|
||||||
|
|
||||||
|
wxGetApp().plater()->cut(object_idx, instance_idx, m_cut_z);
|
||||||
|
}
|
||||||
|
|
||||||
|
double GLGizmoCut::calc_projection(const Linef3& mouse_ray) const
|
||||||
|
{
|
||||||
|
double projection = 0.0;
|
||||||
|
|
||||||
|
const Vec3d starting_vec = m_drag_pos - m_drag_center;
|
||||||
|
const double len_starting_vec = starting_vec.norm();
|
||||||
|
if (len_starting_vec != 0.0)
|
||||||
|
{
|
||||||
|
Vec3d mouse_dir = mouse_ray.unit_vector();
|
||||||
|
// finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
|
||||||
|
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
|
||||||
|
// in our case plane normal and ray direction are the same (orthogonal view)
|
||||||
|
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
|
||||||
|
Vec3d inters = mouse_ray.a + (m_drag_pos - mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir;
|
||||||
|
// vector from the starting position to the found intersection
|
||||||
|
Vec3d inters_vec = inters - m_drag_pos;
|
||||||
|
|
||||||
|
// finds projection of the vector along the staring direction
|
||||||
|
projection = inters_vec.dot(starting_vec.normalized());
|
||||||
|
}
|
||||||
|
return projection;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
|
@ -6,7 +6,13 @@
|
||||||
#include "../../libslic3r/Point.hpp"
|
#include "../../libslic3r/Point.hpp"
|
||||||
#include "../../libslic3r/BoundingBox.hpp"
|
#include "../../libslic3r/BoundingBox.hpp"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
|
class wxWindow;
|
||||||
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -120,6 +126,8 @@ public:
|
||||||
void render(const GLCanvas3D::Selection& selection) const { on_render(selection); }
|
void render(const GLCanvas3D::Selection& selection) const { on_render(selection); }
|
||||||
void render_for_picking(const GLCanvas3D::Selection& selection) const { on_render_for_picking(selection); }
|
void render_for_picking(const GLCanvas3D::Selection& selection) const { on_render_for_picking(selection); }
|
||||||
|
|
||||||
|
virtual void create_external_gizmo_widgets(wxWindow *parent);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual bool on_init() = 0;
|
virtual bool on_init() = 0;
|
||||||
virtual std::string on_get_name() const = 0;
|
virtual std::string on_get_name() const = 0;
|
||||||
|
@ -450,6 +458,43 @@ protected:
|
||||||
bool on_is_activable(const GLCanvas3D::Selection& selection) const override;
|
bool on_is_activable(const GLCanvas3D::Selection& selection) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class GLGizmoCutPanel;
|
||||||
|
|
||||||
|
class GLGizmoCut : public GLGizmoBase
|
||||||
|
{
|
||||||
|
static const double Offset;
|
||||||
|
static const double Margin;
|
||||||
|
static const std::array<float, 3> GrabberColor;
|
||||||
|
|
||||||
|
double m_cut_z;
|
||||||
|
double m_start_z;
|
||||||
|
double m_max_z;
|
||||||
|
Vec3d m_drag_pos;
|
||||||
|
Vec3d m_drag_center;
|
||||||
|
GLGizmoCutPanel *m_panel;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit GLGizmoCut(GLCanvas3D& parent);
|
||||||
|
|
||||||
|
virtual void create_external_gizmo_widgets(wxWindow *parent);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool on_init();
|
||||||
|
virtual std::string on_get_name() const;
|
||||||
|
virtual void on_set_state();
|
||||||
|
virtual bool on_is_activable(const GLCanvas3D::Selection& selection) const;
|
||||||
|
virtual void on_start_dragging(const GLCanvas3D::Selection& selection);
|
||||||
|
virtual void on_update(const UpdateData& data);
|
||||||
|
virtual void on_render(const GLCanvas3D::Selection& selection) const;
|
||||||
|
virtual void on_render_for_picking(const GLCanvas3D::Selection& selection) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void perform_cut();
|
||||||
|
double calc_projection(const Linef3& mouse_ray) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ wxDEFINE_EVENT(EVT_GLTOOLBAR_MORE, SimpleEvent);
|
||||||
wxDEFINE_EVENT(EVT_GLTOOLBAR_FEWER, SimpleEvent);
|
wxDEFINE_EVENT(EVT_GLTOOLBAR_FEWER, SimpleEvent);
|
||||||
wxDEFINE_EVENT(EVT_GLTOOLBAR_SPLIT_OBJECTS, SimpleEvent);
|
wxDEFINE_EVENT(EVT_GLTOOLBAR_SPLIT_OBJECTS, SimpleEvent);
|
||||||
wxDEFINE_EVENT(EVT_GLTOOLBAR_SPLIT_VOLUMES, SimpleEvent);
|
wxDEFINE_EVENT(EVT_GLTOOLBAR_SPLIT_VOLUMES, SimpleEvent);
|
||||||
wxDEFINE_EVENT(EVT_GLTOOLBAR_CUT, SimpleEvent);
|
|
||||||
wxDEFINE_EVENT(EVT_GLTOOLBAR_LAYERSEDITING, SimpleEvent);
|
wxDEFINE_EVENT(EVT_GLTOOLBAR_LAYERSEDITING, SimpleEvent);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ wxDECLARE_EVENT(EVT_GLTOOLBAR_MORE, SimpleEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_FEWER, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLTOOLBAR_FEWER, SimpleEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_SPLIT_OBJECTS, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLTOOLBAR_SPLIT_OBJECTS, SimpleEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_SPLIT_VOLUMES, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLTOOLBAR_SPLIT_VOLUMES, SimpleEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_CUT, SimpleEvent);
|
|
||||||
wxDECLARE_EVENT(EVT_GLTOOLBAR_LAYERSEDITING, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLTOOLBAR_LAYERSEDITING, SimpleEvent);
|
||||||
|
|
||||||
class GLToolbarItem
|
class GLToolbarItem
|
||||||
|
|
|
@ -15,6 +15,18 @@ namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
|
|
||||||
|
wxTopLevelWindow* find_toplevel_parent(wxWindow *window)
|
||||||
|
{
|
||||||
|
for (; window != nullptr; window = window->GetParent()) {
|
||||||
|
if (window->IsTopLevel()) {
|
||||||
|
return dynamic_cast<wxTopLevelWindow*>(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
CheckboxFileDialog::ExtraPanel::ExtraPanel(wxWindow *parent)
|
CheckboxFileDialog::ExtraPanel::ExtraPanel(wxWindow *parent)
|
||||||
: wxPanel(parent, wxID_ANY)
|
: wxPanel(parent, wxID_ANY)
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
|
#include <wx/event.h>
|
||||||
#include <wx/filedlg.h>
|
#include <wx/filedlg.h>
|
||||||
#include <wx/gdicmn.h>
|
#include <wx/gdicmn.h>
|
||||||
#include <wx/panel.h>
|
#include <wx/panel.h>
|
||||||
|
@ -19,6 +20,48 @@ namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
|
|
||||||
|
wxTopLevelWindow* find_toplevel_parent(wxWindow *window);
|
||||||
|
|
||||||
|
|
||||||
|
class EventGuard
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EventGuard() {}
|
||||||
|
EventGuard(const EventGuard&) = delete;
|
||||||
|
EventGuard(EventGuard &&other) : unbinder(std::move(other.unbinder)) {}
|
||||||
|
|
||||||
|
~EventGuard() {
|
||||||
|
if (unbinder) {
|
||||||
|
unbinder(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class EvTag, class Fun> void bind(wxEvtHandler *emitter, const EvTag &type, Fun fun)
|
||||||
|
{
|
||||||
|
// This is a way to type-erase both the event type as well as the handler:
|
||||||
|
|
||||||
|
unbinder = std::move([=](bool bind) {
|
||||||
|
if (bind) {
|
||||||
|
emitter->Bind(type, fun);
|
||||||
|
} else {
|
||||||
|
emitter->Unbind(type, fun);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
unbinder(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
EventGuard& operator=(const EventGuard&) = delete;
|
||||||
|
EventGuard& operator=(EventGuard &&other)
|
||||||
|
{
|
||||||
|
unbinder.swap(other.unbinder);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::function<void(bool)> unbinder;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class CheckboxFileDialog : public wxFileDialog
|
class CheckboxFileDialog : public wxFileDialog
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <wx/menu.h>
|
#include <wx/menu.h>
|
||||||
#include <wx/progdlg.h>
|
#include <wx/progdlg.h>
|
||||||
#include <wx/tooltip.h>
|
#include <wx/tooltip.h>
|
||||||
|
#include <wx/glcanvas.h>
|
||||||
#include <wx/debug.h>
|
#include <wx/debug.h>
|
||||||
|
|
||||||
#include "Tab.hpp"
|
#include "Tab.hpp"
|
||||||
|
@ -100,7 +101,6 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
|
||||||
});
|
});
|
||||||
|
|
||||||
update_ui_from_settings();
|
update_ui_from_settings();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainFrame::init_tabpanel()
|
void MainFrame::init_tabpanel()
|
||||||
|
|
|
@ -81,6 +81,7 @@ public:
|
||||||
MainFrame(const bool no_plater, const bool loaded);
|
MainFrame(const bool no_plater, const bool loaded);
|
||||||
~MainFrame() {}
|
~MainFrame() {}
|
||||||
|
|
||||||
|
Plater* plater() { return m_plater; }
|
||||||
|
|
||||||
void init_tabpanel();
|
void init_tabpanel();
|
||||||
const std::map<std::string, Tab*>& options_tabs() const { return m_options_tabs; }
|
const std::map<std::string, Tab*>& options_tabs() const { return m_options_tabs; }
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
#include <wx/progdlg.h>
|
#include <wx/progdlg.h>
|
||||||
#include <wx/wupdlock.h>
|
#include <wx/wupdlock.h>
|
||||||
#include <wx/colordlg.h>
|
#include <wx/colordlg.h>
|
||||||
#include <wx/numdlg.h>
|
#include <wx/numdlg.h>
|
||||||
|
#include <wx/debug.h>
|
||||||
|
|
||||||
#include "libslic3r/libslic3r.h"
|
#include "libslic3r/libslic3r.h"
|
||||||
#include "libslic3r/PrintConfig.hpp"
|
#include "libslic3r/PrintConfig.hpp"
|
||||||
|
@ -881,6 +882,7 @@ struct Plater::priv
|
||||||
// GUI elements
|
// GUI elements
|
||||||
wxNotebook *notebook;
|
wxNotebook *notebook;
|
||||||
Sidebar *sidebar;
|
Sidebar *sidebar;
|
||||||
|
wxWindow *panel3d;
|
||||||
wxGLCanvas *canvas3D; // TODO: Use GLCanvas3D when we can
|
wxGLCanvas *canvas3D; // TODO: Use GLCanvas3D when we can
|
||||||
Preview *preview;
|
Preview *preview;
|
||||||
|
|
||||||
|
@ -959,7 +961,6 @@ struct Plater::priv
|
||||||
void on_action_add(SimpleEvent&);
|
void on_action_add(SimpleEvent&);
|
||||||
void on_action_split_objects(SimpleEvent&);
|
void on_action_split_objects(SimpleEvent&);
|
||||||
void on_action_split_volumes(SimpleEvent&);
|
void on_action_split_volumes(SimpleEvent&);
|
||||||
void on_action_cut(SimpleEvent&);
|
|
||||||
void on_action_layersediting(SimpleEvent&);
|
void on_action_layersediting(SimpleEvent&);
|
||||||
|
|
||||||
void on_object_select(SimpleEvent&);
|
void on_object_select(SimpleEvent&);
|
||||||
|
@ -978,7 +979,6 @@ private:
|
||||||
bool can_decrease_instances() const;
|
bool can_decrease_instances() const;
|
||||||
bool can_split_to_objects() const;
|
bool can_split_to_objects() const;
|
||||||
bool can_split_to_volumes() const;
|
bool can_split_to_volumes() const;
|
||||||
bool can_cut_object() const;
|
|
||||||
bool layers_height_allowed() const;
|
bool layers_height_allowed() const;
|
||||||
bool can_delete_all() const;
|
bool can_delete_all() const;
|
||||||
bool can_arrange() const;
|
bool can_arrange() const;
|
||||||
|
@ -988,19 +988,20 @@ private:
|
||||||
const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase);
|
const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase);
|
||||||
const std::regex Plater::priv::pattern_3mf(".*3mf", std::regex::icase);
|
const std::regex Plater::priv::pattern_3mf(".*3mf", std::regex::icase);
|
||||||
const std::regex Plater::priv::pattern_zip_amf(".*[.]zip[.]amf", std::regex::icase);
|
const std::regex Plater::priv::pattern_zip_amf(".*[.]zip[.]amf", std::regex::icase);
|
||||||
Plater::priv::priv(Plater *q, MainFrame *main_frame) :
|
Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||||
q(q),
|
: q(q)
|
||||||
main_frame(main_frame),
|
, main_frame(main_frame)
|
||||||
config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({
|
, config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({
|
||||||
"bed_shape", "complete_objects", "extruder_clearance_radius", "skirts", "skirt_distance",
|
"bed_shape", "complete_objects", "extruder_clearance_radius", "skirts", "skirt_distance",
|
||||||
"brim_width", "variable_layer_height", "serial_port", "serial_speed", "host_type", "print_host",
|
"brim_width", "variable_layer_height", "serial_port", "serial_speed", "host_type", "print_host",
|
||||||
"printhost_apikey", "printhost_cafile", "nozzle_diameter", "single_extruder_multi_material",
|
"printhost_apikey", "printhost_cafile", "nozzle_diameter", "single_extruder_multi_material",
|
||||||
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle",
|
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle",
|
||||||
"extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology"
|
"extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology"
|
||||||
})),
|
}))
|
||||||
notebook(new wxNotebook(q, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_BOTTOM)),
|
, notebook(new wxNotebook(q, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_BOTTOM))
|
||||||
sidebar(new Sidebar(q)),
|
, sidebar(new Sidebar(q))
|
||||||
canvas3D(GLCanvas3DManager::create_wxglcanvas(notebook))
|
, panel3d(new wxWindow(notebook, wxID_ANY))
|
||||||
|
, canvas3D(GLCanvas3DManager::create_wxglcanvas(panel3d))
|
||||||
#if ENABLE_NEW_MENU_LAYOUT
|
#if ENABLE_NEW_MENU_LAYOUT
|
||||||
, project_filename(wxEmptyString)
|
, project_filename(wxEmptyString)
|
||||||
#endif // ENABLE_NEW_MENU_LAYOUT
|
#endif // ENABLE_NEW_MENU_LAYOUT
|
||||||
|
@ -1027,9 +1028,19 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) :
|
||||||
|
|
||||||
_3DScene::add_canvas(canvas3D);
|
_3DScene::add_canvas(canvas3D);
|
||||||
_3DScene::allow_multisample(canvas3D, GLCanvas3DManager::can_multisample());
|
_3DScene::allow_multisample(canvas3D, GLCanvas3DManager::can_multisample());
|
||||||
notebook->AddPage(canvas3D, _(L("3D")));
|
|
||||||
|
auto *panel3dsizer = new wxBoxSizer(wxVERTICAL);
|
||||||
|
panel3dsizer->Add(canvas3D, 1, wxEXPAND);
|
||||||
|
auto *panel_gizmo_widgets = new wxPanel(panel3d, wxID_ANY);
|
||||||
|
panel_gizmo_widgets->SetSizer(new wxBoxSizer(wxVERTICAL));
|
||||||
|
panel3dsizer->Add(panel_gizmo_widgets, 0, wxEXPAND);
|
||||||
|
|
||||||
|
panel3d->SetSizer(panel3dsizer);
|
||||||
|
notebook->AddPage(panel3d, _(L("3D")));
|
||||||
preview = new GUI::Preview(notebook, config, &print, &gcode_preview_data, [this](){ schedule_background_process(); });
|
preview = new GUI::Preview(notebook, config, &print, &gcode_preview_data, [this](){ schedule_background_process(); });
|
||||||
|
|
||||||
|
_3DScene::get_canvas(canvas3D)->set_external_gizmo_widgets_parent(panel_gizmo_widgets);
|
||||||
|
|
||||||
// XXX: If have OpenGL
|
// XXX: If have OpenGL
|
||||||
_3DScene::enable_picking(canvas3D, true);
|
_3DScene::enable_picking(canvas3D, true);
|
||||||
_3DScene::enable_moving(canvas3D, true);
|
_3DScene::enable_moving(canvas3D, true);
|
||||||
|
@ -1092,7 +1103,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) :
|
||||||
canvas3D->Bind(EVT_GLTOOLBAR_FEWER, [q](SimpleEvent&) { q->decrease_instances(); });
|
canvas3D->Bind(EVT_GLTOOLBAR_FEWER, [q](SimpleEvent&) { q->decrease_instances(); });
|
||||||
canvas3D->Bind(EVT_GLTOOLBAR_SPLIT_OBJECTS, &priv::on_action_split_objects, this);
|
canvas3D->Bind(EVT_GLTOOLBAR_SPLIT_OBJECTS, &priv::on_action_split_objects, this);
|
||||||
canvas3D->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this);
|
canvas3D->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this);
|
||||||
canvas3D->Bind(EVT_GLTOOLBAR_CUT, &priv::on_action_cut, this);
|
|
||||||
canvas3D->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this);
|
canvas3D->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this);
|
||||||
|
|
||||||
// Preview events:
|
// Preview events:
|
||||||
|
@ -1475,7 +1485,6 @@ void Plater::priv::selection_changed()
|
||||||
_3DScene::enable_toolbar_item(canvas3D, "fewer", can_decrease_instances());
|
_3DScene::enable_toolbar_item(canvas3D, "fewer", can_decrease_instances());
|
||||||
_3DScene::enable_toolbar_item(canvas3D, "splitobjects", can_split_to_objects());
|
_3DScene::enable_toolbar_item(canvas3D, "splitobjects", can_split_to_objects());
|
||||||
_3DScene::enable_toolbar_item(canvas3D, "splitvolumes", can_split_to_volumes());
|
_3DScene::enable_toolbar_item(canvas3D, "splitvolumes", can_split_to_volumes());
|
||||||
_3DScene::enable_toolbar_item(canvas3D, "cut", can_cut_object());
|
|
||||||
_3DScene::enable_toolbar_item(canvas3D, "layersediting", layers_height_allowed());
|
_3DScene::enable_toolbar_item(canvas3D, "layersediting", layers_height_allowed());
|
||||||
// forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears)
|
// forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears)
|
||||||
_3DScene::render(canvas3D);
|
_3DScene::render(canvas3D);
|
||||||
|
@ -1970,11 +1979,6 @@ void Plater::priv::on_action_split_volumes(SimpleEvent&)
|
||||||
split_volume();
|
split_volume();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::priv::on_action_cut(SimpleEvent&)
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
void Plater::priv::on_action_layersediting(SimpleEvent&)
|
void Plater::priv::on_action_layersediting(SimpleEvent&)
|
||||||
{
|
{
|
||||||
bool enable = !_3DScene::is_layers_editing_enabled(canvas3D);
|
bool enable = !_3DScene::is_layers_editing_enabled(canvas3D);
|
||||||
|
@ -2114,12 +2118,6 @@ bool Plater::priv::can_split_to_volumes() const
|
||||||
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && !model.objects[obj_idx]->is_multiparts();
|
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && !model.objects[obj_idx]->is_multiparts();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Plater::priv::can_cut_object() const
|
|
||||||
{
|
|
||||||
int obj_idx = get_selected_object_idx();
|
|
||||||
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Plater::priv::layers_height_allowed() const
|
bool Plater::priv::layers_height_allowed() const
|
||||||
{
|
{
|
||||||
int obj_idx = get_selected_object_idx();
|
int obj_idx = get_selected_object_idx();
|
||||||
|
@ -2313,6 +2311,21 @@ void Plater::set_number_of_copies(/*size_t num*/)
|
||||||
decrease_instances(-diff);
|
decrease_instances(-diff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z)
|
||||||
|
{
|
||||||
|
wxCHECK_RET(obj_idx < p->model.objects.size(), "obj_idx out of bounds");
|
||||||
|
auto *object = p->model.objects[obj_idx];
|
||||||
|
|
||||||
|
wxCHECK_RET(instance_idx < object->instances.size(), "instance_idx out of bounds");
|
||||||
|
|
||||||
|
const auto new_objects = object->cut(instance_idx, z);
|
||||||
|
|
||||||
|
remove(obj_idx);
|
||||||
|
p->load_model_objects(new_objects);
|
||||||
|
|
||||||
|
p->arrange();
|
||||||
|
}
|
||||||
|
|
||||||
void Plater::export_gcode(fs::path output_path)
|
void Plater::export_gcode(fs::path output_path)
|
||||||
{
|
{
|
||||||
if (p->model.objects.empty())
|
if (p->model.objects.empty())
|
||||||
|
|
|
@ -135,6 +135,8 @@ public:
|
||||||
void decrease_instances(size_t num = 1);
|
void decrease_instances(size_t num = 1);
|
||||||
void set_number_of_copies(/*size_t num*/);
|
void set_number_of_copies(/*size_t num*/);
|
||||||
|
|
||||||
|
void cut(size_t obj_idx, size_t instance_idx, coordf_t z);
|
||||||
|
|
||||||
// Note: empty path means "use the default"
|
// Note: empty path means "use the default"
|
||||||
void export_gcode(boost::filesystem::path output_path = boost::filesystem::path());
|
void export_gcode(boost::filesystem::path output_path = boost::filesystem::path());
|
||||||
void export_stl();
|
void export_stl();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue