diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake index dd4ff5b20..70bbe05f5 100644 --- a/cmake/modules/FindOpenVDB.cmake +++ b/cmake/modules/FindOpenVDB.cmake @@ -283,9 +283,9 @@ endif() macro(just_fail msg) set(OpenVDB_FOUND FALSE) if(OpenVDB_FIND_REQUIRED) - message(FATAL_ERROR msg) + message(FATAL_ERROR ${msg}) elseif(NOT OpenVDB_FIND_QUIETLY) - message(ERROR msg) + message(WARNING ${msg}) endif() return() endmacro() diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 9083e35e0..ccc48d52b 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -199,13 +199,13 @@ public: // This vector holds position of selected support points for SLA. The data are // saved in mesh coordinates to allow using them for several instances. // The format is (x, y, z, point_size, supports_island) - std::vector sla_support_points; + sla::SupportPoints sla_support_points; // To keep track of where the points came from (used for synchronization between // the SLA gizmo and the backend). - sla::PointsStatus sla_points_status = sla::PointsStatus::NoPoints; + sla::PointsStatus sla_points_status = sla::PointsStatus::NoPoints; // Holes to be drilled into the object so resin can flow out - std::vector sla_drain_holes; + sla::DrainHoles sla_drain_holes; /* This vector accumulates the total translation applied to the object by the center_around_origin() method. Callers might want to apply the same translation diff --git a/src/libslic3r/SLA/Hollowing.cpp b/src/libslic3r/SLA/Hollowing.cpp index c3763dbc9..2a496d276 100644 --- a/src/libslic3r/SLA/Hollowing.cpp +++ b/src/libslic3r/SLA/Hollowing.cpp @@ -121,9 +121,9 @@ std::unique_ptr generate_interior(const TriangleMesh & mesh, bool DrainHole::operator==(const DrainHole &sp) const { - return (m_pos == sp.m_pos) && (m_normal == sp.m_normal) && - is_approx(m_radius, sp.m_radius) && - is_approx(m_height, sp.m_height); + return (pos == sp.pos) && (normal == sp.normal) && + is_approx(radius, sp.radius) && + is_approx(height, sp.height); } }} // namespace Slic3r::sla diff --git a/src/libslic3r/SLA/Hollowing.hpp b/src/libslic3r/SLA/Hollowing.hpp index cb5c2bd15..543f5e6cf 100644 --- a/src/libslic3r/SLA/Hollowing.hpp +++ b/src/libslic3r/SLA/Hollowing.hpp @@ -20,21 +20,17 @@ struct HollowingConfig struct DrainHole { - Vec3f m_pos; - Vec3f m_normal; - float m_radius; - float m_height; - + Vec3f pos; + Vec3f normal; + float radius; + float height; + DrainHole() - : m_pos(Vec3f::Zero()), m_normal(Vec3f::UnitZ()), m_radius(5.f), - m_height(10.f) + : pos(Vec3f::Zero()), normal(Vec3f::UnitZ()), radius(5.f), height(10.f) {} - - DrainHole(Vec3f position, Vec3f normal, float radius, float height) - : m_pos(position) - , m_normal(normal) - , m_radius(radius) - , m_height(height) + + DrainHole(Vec3f p, Vec3f n, float r, float h) + : pos(p), normal(n), radius(r), height(h) {} bool operator==(const DrainHole &sp) const; @@ -43,10 +39,12 @@ struct DrainHole template inline void serialize(Archive &ar) { - ar(m_pos, m_normal, m_radius, m_height); + ar(pos, normal, radius, height); } }; +using DrainHoles = std::vector; + std::unique_ptr generate_interior(const TriangleMesh &mesh, const HollowingConfig & = {}, const JobController &ctl = {}); diff --git a/src/libslic3r/SLA/SupportTreeBuilder.hpp b/src/libslic3r/SLA/SupportTreeBuilder.hpp index 4859b004c..90cf417c8 100644 --- a/src/libslic3r/SLA/SupportTreeBuilder.hpp +++ b/src/libslic3r/SLA/SupportTreeBuilder.hpp @@ -74,7 +74,7 @@ Contour3D sphere(double rho, Portion portion = make_portion(0.0, 2.0*PI), // h: Height // ssteps: how many edges will create the base circle // sp: starting point -Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp = {0,0,0}); +Contour3D cylinder(double r, double h, size_t ssteps = 45, const Vec3d &sp = {0,0,0}); const constexpr long ID_UNSET = -1; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 53a0e8223..61723721e 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -414,6 +414,12 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con model_object.sla_support_points = model_object_new.sla_support_points; } model_object.sla_points_status = model_object_new.sla_points_status; + + if (model_object.sla_drain_holes.size() != model_object_new.sla_drain_holes.size()) + { + model_object.sla_drain_holes = model_object_new.sla_drain_holes; + update_apply_status(it_print_object_status->print_object->invalidate_step(slaposHollowing)); + } // Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step. model_object.name = model_object_new.name; @@ -1154,21 +1160,30 @@ const TriangleMesh &SLAPrintObject::transformed_mesh() const { return m_transformed_rmesh.get(); } -std::vector SLAPrintObject::transformed_support_points() const +template::value_type> +std::vector transform_pts(It from, It to, Trafo &&tr) +{ + auto ret = reserve_vector(to - from); + for(auto it = from; it != to; ++it) { + V v = *it; + v.pos = tr * it->pos; + ret.emplace_back(std::move(v)); + } + return ret; +} + +sla::SupportPoints SLAPrintObject::transformed_support_points() const { assert(m_model_object != nullptr); - std::vector& spts = m_model_object->sla_support_points; + auto& spts = m_model_object->sla_support_points; + return transform_pts(spts.begin(), spts.end(), trafo().cast()); +} - // this could be cached as well - std::vector ret; - ret.reserve(spts.size()); - - for(sla::SupportPoint& sp : spts) { - Vec3f transformed_pos = trafo().cast() * sp.pos; - ret.emplace_back(transformed_pos, sp.head_front_radius, sp.is_new_island); - } - - return ret; +sla::DrainHoles SLAPrintObject::transformed_drainhole_points() const +{ + assert(m_model_object != nullptr); + auto& spts = m_model_object->sla_drain_holes; + return transform_pts(spts.begin(), spts.end(), trafo().cast()); } DynamicConfig SLAPrintStatistics::config() const diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 2edede109..8be69545e 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -83,7 +83,8 @@ public: // This will return the transformed mesh which is cached const TriangleMesh& transformed_mesh() const; - std::vector transformed_support_points() const; + sla::SupportPoints transformed_support_points() const; + sla::DrainHoles transformed_drainhole_points() const; // Get the needed Z elevation for the model geometry if supports should be // displayed. This Z offset should also be applied to the support diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 09d449576..55359cc5c 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -1,5 +1,9 @@ #include + +// Need the cylinder method for the the drainholes in hollowing step +#include + #include #include #include @@ -98,6 +102,42 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) BOOST_LOG_TRIVIAL(warning) << "Hollowed interior is empty!"; } +static void cut_drainholes(std::vector & obj_slices, + const std::vector &slicegrid, + float closing_radius, + const sla::DrainHoles & holes, + std::function thr) +{ + TriangleMesh mesh; + for (const sla::DrainHole &holept : holes) { + auto r = double(holept.radius); + auto h = double(holept.height); + sla::Contour3D hole = sla::cylinder(r, h); + Eigen::Quaterniond q; + q.setFromTwoVectors(Vec3d{0., 0., 1.}, holept.normal.cast()); + for(auto& p : hole.points) p = q * p + holept.pos.cast(); + mesh.merge(sla::to_triangle_mesh(hole)); + } + + if (mesh.empty()) return; + + mesh.require_shared_vertices(); + + TriangleMeshSlicer slicer(&mesh); + + std::vector hole_slices; + slicer.slice(slicegrid, closing_radius, &hole_slices, thr); + + if (obj_slices.size() != hole_slices.size()) + BOOST_LOG_TRIVIAL(warning) + << "Sliced object and drain-holes layer count does not match!"; + + size_t until = std::min(obj_slices.size(), hole_slices.size()); + + for (size_t i = 0; i < until; ++i) + obj_slices[i] = diff_ex(obj_slices[i], hole_slices[i]); +} + // The slicing will be performed on an imaginary 1D grid which starts from // the bottom of the bounding box created around the supported model. So // the first layer which is usually thicker will be part of the supports @@ -107,12 +147,10 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) // of it. In any case, the model and the supports have to be sliced in the // same imaginary grid (the height vector argument to TriangleMeshSlicer). void SLAPrint::Steps::slice_model(SLAPrintObject &po) -{ - +{ TriangleMesh hollowed_mesh; - bool is_hollowing = po.m_config.hollowing_enable.getBool() && - po.m_hollowing_data; + bool is_hollowing = po.m_config.hollowing_enable.getBool() && po.m_hollowing_data; if (is_hollowing) { hollowed_mesh = po.transformed_mesh(); @@ -120,8 +158,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) hollowed_mesh.require_shared_vertices(); } - const TriangleMesh &mesh = is_hollowing ? hollowed_mesh : - po.transformed_mesh(); + const TriangleMesh &mesh = is_hollowing ? hollowed_mesh : po.transformed_mesh(); // We need to prepare the slice index... @@ -163,10 +200,13 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) TriangleMeshSlicer slicer(&mesh); po.m_model_slices.clear(); - slicer.slice(po.m_model_height_levels, - float(po.config().slice_closing_radius.value), - &po.m_model_slices, - [this](){ m_print->throw_if_canceled(); }); + float closing_r = float(po.config().slice_closing_radius.value); + auto thr = [this]() { m_print->throw_if_canceled(); }; + auto &slice_grid = po.m_model_height_levels; + slicer.slice(slice_grid, closing_r, &po.m_model_slices, thr); + + sla::DrainHoles drainholes = po.transformed_drainhole_points(); + cut_drainholes(po.m_model_slices, slice_grid, closing_r, drainholes, thr); auto mit = slindex_it; double doffs = m_print->m_printer_config.absolute_correction.getFloat(); @@ -183,8 +223,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) mit->set_model_slice_idx(po, id); ++mit; } - if(po.m_config.supports_enable.getBool() || - po.m_config.pad_enable.getBool()) + if(po.m_config.supports_enable.getBool() || po.m_config.pad_enable.getBool()) { po.m_supportdata.reset( new SLAPrintObject::SupportData(po.transformed_mesh()) ); @@ -324,8 +363,7 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { // and before the supports had been sliced. (or the slicing has to be // repeated) - if(po.m_config.pad_enable.getBool()) - { + if(po.m_config.pad_enable.getBool()) { // Get the distilled pad configuration from the config sla::PadConfig pcfg = make_pad_cfg(po.m_config); @@ -368,13 +406,11 @@ void SLAPrint::Steps::slice_supports(SLAPrintObject &po) { if(sd) sd->support_slices.clear(); // Don't bother if no supports and no pad is present. - if (!po.m_config.supports_enable.getBool() && - !po.m_config.pad_enable.getBool()) + if (!po.m_config.supports_enable.getBool() && !po.m_config.pad_enable.getBool()) return; if(sd && sd->support_tree_ptr) { - - std::vector heights; heights.reserve(po.m_slice_index.size()); + auto heights = reserve_vector(po.m_slice_index.size()); for(auto& rec : po.m_slice_index) heights.emplace_back(rec.slice_level()); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index c6c02947f..b320c6bbe 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -250,7 +250,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons const sla::DrainHole& drain_hole = m_model_object->sla_drain_holes[i]; const bool& point_selected = m_selected[i]; - if (is_mesh_point_clipped(drain_hole.m_pos.cast())) + if (is_mesh_point_clipped(drain_hole.pos.cast())) continue; // First decide about the color of the point. @@ -281,7 +281,7 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. glsafe(::glPushMatrix()); - glsafe(::glTranslatef(drain_hole.m_pos(0), drain_hole.m_pos(1), drain_hole.m_pos(2))); + glsafe(::glTranslatef(drain_hole.pos(0), drain_hole.pos(1), drain_hole.pos(2))); glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); if (vol->is_left_handed()) @@ -290,17 +290,17 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons // Matrices set, we can render the point mark now. Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.m_normal).cast()); + q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); Eigen::AngleAxisd aa(q); glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); glsafe(::glPushMatrix()); - glsafe(::glTranslated(0., 0., -drain_hole.m_height)); - ::gluCylinder(m_quadric, drain_hole.m_radius, drain_hole.m_radius, drain_hole.m_height, 24, 1); - glsafe(::glTranslated(0., 0., drain_hole.m_height)); - ::gluDisk(m_quadric, 0.0, drain_hole.m_radius, 24, 1); - glsafe(::glTranslated(0., 0., -drain_hole.m_height)); + glsafe(::glTranslated(0., 0., -drain_hole.height)); + ::gluCylinder(m_quadric, drain_hole.radius, drain_hole.radius, drain_hole.height, 24, 1); + glsafe(::glTranslated(0., 0., drain_hole.height)); + ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); + glsafe(::glTranslated(0., 0., -drain_hole.height)); glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); - ::gluDisk(m_quadric, 0.0, drain_hole.m_radius, 24, 1); + ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); glsafe(::glPopMatrix()); if (vol->is_left_handed()) @@ -433,7 +433,7 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos m_model_object->sla_drain_holes.emplace_back(pos_and_normal.first + HoleStickOutLength * pos_and_normal.second, -pos_and_normal.second, m_new_hole_radius, m_new_hole_height+HoleStickOutLength); m_selected.push_back(false); - assert(m_selected.size == m_model_object->sla_drain_holes.size()); + assert(m_selected.size() == m_model_object->sla_drain_holes.size()); m_parent.set_as_dirty(); m_wait_for_up_event = true; } @@ -456,7 +456,7 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift)); std::vector points; for (unsigned int i=0; isla_drain_holes.size(); ++i) - points.push_back(trafo.get_matrix() * m_model_object->sla_drain_holes[i].m_pos.cast()); + points.push_back(trafo.get_matrix() * m_model_object->sla_drain_holes[i].pos.cast()); // Now ask the rectangle which of the points are inside. std::vector points_inside; @@ -558,8 +558,8 @@ void GLGizmoHollow::on_update(const UpdateData& data) std::pair pos_and_normal; if (! unproject_on_mesh(data.mouse_pos.cast(), pos_and_normal)) return; - m_model_object->sla_drain_holes[m_hover_id].m_pos = pos_and_normal.first + HoleStickOutLength * pos_and_normal.second; - m_model_object->sla_drain_holes[m_hover_id].m_normal = -pos_and_normal.second; + m_model_object->sla_drain_holes[m_hover_id].pos = pos_and_normal.first + HoleStickOutLength * pos_and_normal.second; + m_model_object->sla_drain_holes[m_hover_id].normal = -pos_and_normal.second; } } @@ -688,20 +688,20 @@ RENDER_AGAIN: if (ImGui::IsItemEdited()) { for (size_t idx=0; idxsla_drain_holes[idx].m_radius = m_new_hole_radius; + m_model_object->sla_drain_holes[idx].radius = m_new_hole_radius; } if (ImGui::IsItemDeactivatedAfterEdit()) { // momentarily restore the old value to take snapshot for (size_t idx=0; idxsla_drain_holes[idx].m_radius = m_old_hole_radius; + m_model_object->sla_drain_holes[idx].radius = m_old_hole_radius; float backup = m_new_hole_radius; m_new_hole_radius = m_old_hole_radius; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Change drainage hole diameter"))); m_new_hole_radius = backup; for (size_t idx=0; idxsla_drain_holes[idx].m_radius = m_new_hole_radius; + m_model_object->sla_drain_holes[idx].radius = m_new_hole_radius; m_old_hole_radius = 0.f; } @@ -863,7 +863,7 @@ void GLGizmoHollow::on_start_dragging() if (m_hover_id != -1) { select_point(NoPoints); select_point(m_hover_id); - m_hole_before_drag = m_model_object->sla_drain_holes[m_hover_id].m_pos; + m_hole_before_drag = m_model_object->sla_drain_holes[m_hover_id].pos; } else m_hole_before_drag = Vec3f::Zero(); @@ -873,14 +873,14 @@ void GLGizmoHollow::on_start_dragging() void GLGizmoHollow::on_stop_dragging() { if (m_hover_id != -1) { - Vec3f backup = m_model_object->sla_drain_holes[m_hover_id].m_pos; + Vec3f backup = m_model_object->sla_drain_holes[m_hover_id].pos; if (m_hole_before_drag != Vec3f::Zero() // some point was touched && backup != m_hole_before_drag) // and it was moved, not just selected { - m_model_object->sla_drain_holes[m_hover_id].m_pos = m_hole_before_drag; + m_model_object->sla_drain_holes[m_hover_id].pos = m_hole_before_drag; Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("Move drainage hole"))); - m_model_object->sla_drain_holes[m_hover_id].m_pos = backup; + m_model_object->sla_drain_holes[m_hover_id].pos = backup; } } m_hole_before_drag = Vec3f::Zero(); @@ -921,14 +921,14 @@ void GLGizmoHollow::select_point(int i) m_selection_empty = (i == NoPoints); if (i == AllPoints) - m_new_hole_radius = m_model_object->sla_drain_holes[0].m_radius; + m_new_hole_radius = m_model_object->sla_drain_holes[0].radius; } else { while (size_t(i) >= m_selected.size()) m_selected.push_back(false); m_selected[i] = true; m_selection_empty = false; - m_new_hole_radius = m_model_object->sla_drain_holes[i].m_radius; + m_new_hole_radius = m_model_object->sla_drain_holes[i].radius; } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index a7ad02cd8..15f46aab6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -336,7 +336,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) for (const sla::DrainHole& drain_hole : m_model_object->sla_drain_holes) { // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. glsafe(::glPushMatrix()); - glsafe(::glTranslatef(drain_hole.m_pos(0), drain_hole.m_pos(1), drain_hole.m_pos(2))); + glsafe(::glTranslatef(drain_hole.pos(0), drain_hole.pos(1), drain_hole.pos(2))); glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); if (vol->is_left_handed()) @@ -345,17 +345,17 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) // Matrices set, we can render the point mark now. Eigen::Quaterniond q; - q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.m_normal).cast()); + q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * (-drain_hole.normal).cast()); Eigen::AngleAxisd aa(q); glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); glsafe(::glPushMatrix()); - glsafe(::glTranslated(0., 0., -drain_hole.m_height)); - ::gluCylinder(m_quadric, drain_hole.m_radius, drain_hole.m_radius, drain_hole.m_height, 24, 1); - glsafe(::glTranslated(0., 0., drain_hole.m_height)); - ::gluDisk(m_quadric, 0.0, drain_hole.m_radius, 24, 1); - glsafe(::glTranslated(0., 0., -drain_hole.m_height)); + glsafe(::glTranslated(0., 0., -drain_hole.height)); + ::gluCylinder(m_quadric, drain_hole.radius, drain_hole.radius, drain_hole.height, 24, 1); + glsafe(::glTranslated(0., 0., drain_hole.height)); + ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); + glsafe(::glTranslated(0., 0., -drain_hole.height)); glsafe(::glRotatef(180.f, 1.f, 0.f, 0.f)); - ::gluDisk(m_quadric, 0.0, drain_hole.m_radius, 24, 1); + ::gluDisk(m_quadric, 0.0, drain_hole.radius, 24, 1); glsafe(::glPopMatrix()); if (vol->is_left_handed()) @@ -421,10 +421,10 @@ bool GLGizmoSlaSupports::is_point_in_hole(const Vec3f& pt) const for (const sla::DrainHole& hole : m_model_object->sla_drain_holes) { - if ( hole.m_normal.dot(pt-hole.m_pos) < EPSILON - || hole.m_normal.dot(pt-(hole.m_pos+hole.m_height * hole.m_normal)) > 0.f) + if ( hole.normal.dot(pt-hole.pos) < EPSILON + || hole.normal.dot(pt-(hole.pos+hole.height * hole.normal)) > 0.f) continue; - if ( squared_distance_from_line(pt, hole.m_pos, hole.m_normal) < pow(hole.m_radius, 2.f)) + if ( squared_distance_from_line(pt, hole.pos, hole.normal) < pow(hole.radius, 2.f)) return true; } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index a12c8d6c6..5900cb820 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -47,7 +47,7 @@ public: bool operator!=(const ClippingPlane& cp) const { return ! (*this==cp); } double distance(const Vec3d& pt) const { - assert(is_approx(get_normal().norm(), 1.)); + // FIXME: this fails: assert(is_approx(get_normal().norm(), 1.)); return (-get_normal().dot(pt) + m_data[3]); }