From 06bf02df695ee70f266a5c33c55f73f5f7714d1d Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 26 Feb 2021 15:37:48 +0100 Subject: [PATCH] Fix Gizmo preview with hollowed mesh --- src/libslic3r/SLAPrintSteps.cpp | 305 ++++++++++++----------- src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp | 18 +- src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp | 2 + src/slic3r/GUI/MeshUtils.cpp | 19 ++ src/slic3r/GUI/MeshUtils.hpp | 3 + 5 files changed, 194 insertions(+), 153 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 59ad20c72..4637aa761 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -84,17 +84,17 @@ SLAPrint::Steps::Steps(SLAPrint *print) void SLAPrint::Steps::apply_printer_corrections(SLAPrintObject &po, SliceOrigin o) { if (o == soSupport && !po.m_supportdata) return; - + auto faded_lyrs = size_t(po.m_config.faded_layers.getInt()); double min_w = m_print->m_printer_config.elefant_foot_min_width.getFloat() / 2.; double start_efc = m_print->m_printer_config.elefant_foot_compensation.getFloat(); - + double doffs = m_print->m_printer_config.absolute_correction.getFloat(); coord_t clpr_offs = scaled(doffs); - + faded_lyrs = std::min(po.m_slice_index.size(), faded_lyrs); size_t faded_lyrs_efc = std::max(size_t(1), faded_lyrs - 1); - + auto efc = [start_efc, faded_lyrs_efc](size_t pos) { return (faded_lyrs_efc - pos) * start_efc / faded_lyrs_efc; }; @@ -102,13 +102,13 @@ void SLAPrint::Steps::apply_printer_corrections(SLAPrintObject &po, SliceOrigin std::vector &slices = o == soModel ? po.m_model_slices : po.m_supportdata->support_slices; - + if (clpr_offs != 0) for (size_t i = 0; i < po.m_slice_index.size(); ++i) { size_t idx = po.m_slice_index[i].get_slice_idx(o); if (idx < slices.size()) slices[idx] = offset_ex(slices[idx], float(clpr_offs)); } - + if (start_efc > 0.) for (size_t i = 0; i < faded_lyrs; ++i) { size_t idx = po.m_slice_index[i].get_slice_idx(o); if (idx < slices.size()) @@ -124,7 +124,7 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!"; return; } - + BOOST_LOG_TRIVIAL(info) << "Performing hollowing step!"; double thickness = po.m_config.hollowing_min_thickness.getFloat(); @@ -169,16 +169,17 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) sla::hollow_mesh(hollowed_mesh, *po.m_hollowing_data->interior); TriangleMesh &mesh_view = po.m_hollowing_data->hollow_mesh_with_holes_trimmed; - sla::remove_inside_triangles(mesh_view, *po.m_hollowing_data->interior); + mesh_view = po.transformed_mesh(); + sla::hollow_mesh(mesh_view, *po.m_hollowing_data->interior, sla::hfRemoveInsideTriangles); if (! needs_drilling) { BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes)."; return; } - + BOOST_LOG_TRIVIAL(info) << "Drilling drainage holes."; sla::DrainHoles drainholes = po.transformed_drainhole_points(); - + std::uniform_real_distribution dist(0., float(EPSILON)); auto holes_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal({}); for (sla::DrainHole holept : drainholes) { @@ -190,12 +191,12 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) auto cgal_m = MeshBoolean::cgal::triangle_mesh_to_cgal(m); MeshBoolean::cgal::plus(*holes_mesh_cgal, *cgal_m); } - + if (MeshBoolean::cgal::does_self_intersect(*holes_mesh_cgal)) throw Slic3r::SlicingError(L("Too many overlapping holes.")); - + auto hollowed_mesh_cgal = MeshBoolean::cgal::triangle_mesh_to_cgal(hollowed_mesh); - + try { MeshBoolean::cgal::minus(*hollowed_mesh_cgal, *holes_mesh_cgal); hollowed_mesh = MeshBoolean::cgal::cgal_to_triangle_mesh(*hollowed_mesh_cgal); @@ -215,11 +216,11 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) // of it. In any case, the model and the supports have to be sliced in the // same imaginary grid (the height vector argument to TriangleMeshSlicer). void SLAPrint::Steps::slice_model(SLAPrintObject &po) -{ +{ const TriangleMesh &mesh = po.get_mesh_to_slice(); // We need to prepare the slice index... - + double lhd = m_print->m_objects.front()->m_config.layer_height.getFloat(); float lh = float(lhd); coord_t lhs = scaled(lhd); @@ -229,40 +230,40 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) auto minZf = float(minZ); coord_t minZs = scaled(minZ); coord_t maxZs = scaled(maxZ); - + po.m_slice_index.clear(); - + size_t cap = size_t(1 + (maxZs - minZs - ilhs) / lhs); po.m_slice_index.reserve(cap); - + po.m_slice_index.emplace_back(minZs + ilhs, minZf + ilh / 2.f, ilh); - + for(coord_t h = minZs + ilhs + lhs; h <= maxZs; h += lhs) po.m_slice_index.emplace_back(h, unscaled(h) - lh / 2.f, lh); - + // Just get the first record that is from the model: auto slindex_it = po.closest_slice_record(po.m_slice_index, float(bb3d.min(Z))); - + if(slindex_it == po.m_slice_index.end()) //TRN To be shown at the status bar on SLA slicing error. throw Slic3r::RuntimeError( L("Slicing had to be stopped due to an internal error: " "Inconsistent slice index.")); - + po.m_model_height_levels.clear(); po.m_model_height_levels.reserve(po.m_slice_index.size()); for(auto it = slindex_it; it != po.m_slice_index.end(); ++it) po.m_model_height_levels.emplace_back(it->slice_level()); - + TriangleMeshSlicer slicer(&mesh); - + po.m_model_slices.clear(); float closing_r = float(po.config().slice_closing_radius.value); auto thr = [this]() { m_print->throw_if_canceled(); }; auto &slice_grid = po.m_model_height_levels; slicer.slice(slice_grid, SlicingMode::Regular, closing_r, &po.m_model_slices, thr); - + sla::Interior *interior = po.m_hollowing_data ? po.m_hollowing_data->interior.get() : nullptr; @@ -282,17 +283,17 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) diff_ex(po.m_model_slices[i], slice); }); } - + auto mit = slindex_it; for (size_t id = 0; id < po.m_model_slices.size() && mit != po.m_slice_index.end(); id++) { mit->set_model_slice_idx(po, id); ++mit; } - + // We apply the printer correction offset here. apply_printer_corrections(po, soModel); - + if(po.m_config.supports_enable.getBool() || po.m_config.pad_enable.getBool()) { po.m_supportdata.reset(new SLAPrintObject::SupportData(mesh)); @@ -305,22 +306,22 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) { // If supports are disabled, we can skip the model scan. if(!po.m_config.supports_enable.getBool()) return; - + const TriangleMesh &mesh = po.get_mesh_to_slice(); - + if (!po.m_supportdata) po.m_supportdata.reset(new SLAPrintObject::SupportData(mesh)); - + const ModelObject& mo = *po.m_model_object; - + BOOST_LOG_TRIVIAL(debug) << "Support point count " << mo.sla_support_points.size(); - + // Unless the user modified the points or we already did the calculation, // we will do the autoplacement. Otherwise we will just blindly copy the // frontend data into the backend cache. if (mo.sla_points_status != sla::PointsStatus::UserModified) { - + // calculate heights of slices (slices are calculated already) const std::vector& heights = po.m_model_height_levels; @@ -328,27 +329,27 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) // calculated on slices, the algorithm then raycasts the points // so they actually lie on the mesh. // po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points()); - + throw_if_canceled(); sla::SupportPointGenerator::Config config; const SLAPrintObjectConfig& cfg = po.config(); - + // the density config value is in percents: config.density_relative = float(cfg.support_points_density_relative / 100.f); config.minimal_distance = float(cfg.support_points_minimal_distance); config.head_diameter = float(cfg.support_head_front_diameter); - + // scaling for the sub operations double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportPoints] / 100.0; double init = current_status(); - + auto statuscb = [this, d, init](unsigned st) { double current = init + st * d; if(std::round(current_status()) < std::round(current)) report_status(current, OBJ_STEP_LABELS(slaposSupportPoints)); }; - + // Construction of this object does the calculation. throw_if_canceled(); sla::SupportPointGenerator auto_supports( @@ -359,10 +360,10 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) const std::vector& points = auto_supports.output(); throw_if_canceled(); po.m_supportdata->pts = points; - + BOOST_LOG_TRIVIAL(debug) << "Automatic support points: " << po.m_supportdata->pts.size(); - + // Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass // the update status to GLGizmoSlaSupports report_status(-1, L("Generating support points"), @@ -377,9 +378,9 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po) void SLAPrint::Steps::support_tree(SLAPrintObject &po) { if(!po.m_supportdata) return; - + sla::PadConfig pcfg = make_pad_cfg(po.m_config); - + if (pcfg.embed_object) po.m_supportdata->emesh.ground_level_offset(pcfg.wall_thickness_mm); @@ -389,15 +390,15 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po) remove_bottom_points(po.m_supportdata->pts, float(po.m_supportdata->emesh.ground_level() + EPSILON)); } - + po.m_supportdata->cfg = make_support_cfg(po.m_config); // po.m_supportdata->emesh.load_holes(po.transformed_drainhole_points()); - + // scaling for the sub operations double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportTree] / 100.0; double init = current_status(); sla::JobController ctl; - + ctl.statuscb = [this, d, init](unsigned st, const std::string &logmsg) { double current = init + st * d; if (std::round(current_status()) < std::round(current)) @@ -406,26 +407,26 @@ void SLAPrint::Steps::support_tree(SLAPrintObject &po) }; ctl.stopcondition = [this]() { return canceled(); }; ctl.cancelfn = [this]() { throw_if_canceled(); }; - + po.m_supportdata->create_support_tree(ctl); - + if (!po.m_config.supports_enable.getBool()) return; - + throw_if_canceled(); - + // Create the unified mesh auto rc = SlicingStatus::RELOAD_SCENE; - + // This is to prevent "Done." being displayed during merged_mesh() report_status(-1, L("Visualizing supports")); - + BOOST_LOG_TRIVIAL(debug) << "Processed support point count " << po.m_supportdata->pts.size(); - + // Check the mesh for later troubleshooting. if(po.support_mesh().empty()) BOOST_LOG_TRIVIAL(warning) << "Support mesh is empty"; - + report_status(-1, L("Visualizing supports"), rc); } @@ -433,15 +434,15 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { // this step can only go after the support tree has been created // and before the supports had been sliced. (or the slicing has to be // repeated) - + if(po.m_config.pad_enable.getBool()) { // Get the distilled pad configuration from the config sla::PadConfig pcfg = make_pad_cfg(po.m_config); - + ExPolygons bp; // This will store the base plate of the pad. double pad_h = pcfg.full_height(); const TriangleMesh &trmesh = po.transformed_mesh(); - + if (!po.m_config.supports_enable.getBool() || pcfg.embed_object) { // No support (thus no elevation) or zero elevation mode // we sometimes call it "builtin pad" is enabled so we will @@ -451,19 +452,19 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { float(po.m_config.layer_height.getFloat()), [this](){ throw_if_canceled(); }); } - + po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg); auto &pad_mesh = po.m_supportdata->support_tree_ptr->retrieve_mesh(sla::MeshType::Pad); - + if (!validate_pad(pad_mesh, pcfg)) throw Slic3r::SlicingError( L("No pad can be generated for this model with the " "current configuration")); - + } else if(po.m_supportdata && po.m_supportdata->support_tree_ptr) { po.m_supportdata->support_tree_ptr->remove_pad(); } - + throw_if_canceled(); report_status(-1, L("Visualizing supports"), SlicingStatus::RELOAD_SCENE); } @@ -473,25 +474,25 @@ void SLAPrint::Steps::generate_pad(SLAPrintObject &po) { // be part of the slices) void SLAPrint::Steps::slice_supports(SLAPrintObject &po) { auto& sd = po.m_supportdata; - + if(sd) sd->support_slices.clear(); - + // Don't bother if no supports and no pad is present. if (!po.m_config.supports_enable.getBool() && !po.m_config.pad_enable.getBool()) return; - + if(sd && sd->support_tree_ptr) { auto heights = reserve_vector(po.m_slice_index.size()); - + for(auto& rec : po.m_slice_index) heights.emplace_back(rec.slice_level()); sd->support_slices = sd->support_tree_ptr->slice( heights, float(po.config().slice_closing_radius.value)); } - - for (size_t i = 0; i < sd->support_slices.size() && i < po.m_slice_index.size(); ++i) + + for (size_t i = 0; i < sd->support_slices.size() && i < po.m_slice_index.size(); ++i) po.m_slice_index[i].set_support_slice_idx(po, i); - + apply_printer_corrections(po, soSupport); // Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update @@ -506,37 +507,37 @@ using ClipperPolygons = std::vector; static ClipperPolygons polyunion(const ClipperPolygons &subjects) { ClipperLib::Clipper clipper; - + bool closed = true; - + for(auto& path : subjects) { clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); } - + auto mode = ClipperLib::pftPositive; - + return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode); } static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPolygons& clips) { ClipperLib::Clipper clipper; - + bool closed = true; - + for(auto& path : subjects) { clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); } - + for(auto& path : clips) { clipper.AddPath(path.Contour, ClipperLib::ptClip, closed); clipper.AddPaths(path.Holes, ClipperLib::ptClip, closed); } - + auto mode = ClipperLib::pftPositive; - + return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode); } @@ -544,28 +545,28 @@ static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPo static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o) { namespace sl = libnest2d::sl; - + if (!record.print_obj()) return {}; - + ClipperPolygons polygons; auto &input_polygons = record.get_slice(o); auto &instances = record.print_obj()->instances(); bool is_lefthanded = record.print_obj()->is_left_handed(); polygons.reserve(input_polygons.size() * instances.size()); - + for (const ExPolygon& polygon : input_polygons) { if(polygon.contour.empty()) continue; - + for (size_t i = 0; i < instances.size(); ++i) { ClipperPolygon poly; - + // We need to reverse if is_lefthanded is true but bool needreverse = is_lefthanded; - + // should be a move poly.Contour.reserve(polygon.contour.size() + 1); - + auto& cntr = polygon.contour.points; if(needreverse) for(auto it = cntr.rbegin(); it != cntr.rend(); ++it) @@ -573,12 +574,12 @@ static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o else for(auto& p : cntr) poly.Contour.emplace_back(p.x(), p.y()); - + for(auto& h : polygon.holes) { poly.Holes.emplace_back(); auto& hole = poly.Holes.back(); hole.reserve(h.points.size() + 1); - + if(needreverse) for(auto it = h.points.rbegin(); it != h.points.rend(); ++it) hole.emplace_back(it->x(), it->y()); @@ -586,42 +587,42 @@ static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o for(auto& p : h.points) hole.emplace_back(p.x(), p.y()); } - + if(is_lefthanded) { for(auto& p : poly.Contour) p.X = -p.X; for(auto& h : poly.Holes) for(auto& p : h) p.X = -p.X; } - + sl::rotate(poly, double(instances[i].rotation)); sl::translate(poly, ClipperPoint{instances[i].shift.x(), instances[i].shift.y()}); - + polygons.emplace_back(std::move(poly)); } } - + return polygons; } void SLAPrint::Steps::initialize_printer_input() { auto &printer_input = m_print->m_printer_input; - + // clear the rasterizer input printer_input.clear(); - + size_t mx = 0; for(SLAPrintObject * o : m_print->m_objects) { if(auto m = o->get_slice_index().size() > mx) mx = m; } - + printer_input.reserve(mx); - + auto eps = coord_t(SCALED_EPSILON); - + for(SLAPrintObject * o : m_print->m_objects) { coord_t gndlvl = o->get_slice_index().front().print_level() - ilhs; - + for(const SliceRecord& slicerecord : o->get_slice_index()) { if (!slicerecord.is_valid()) throw Slic3r::SlicingError( @@ -630,7 +631,7 @@ void SLAPrint::Steps::initialize_printer_input() "objects printable.")); coord_t lvlid = slicerecord.print_level() - gndlvl; - + // Neat trick to round the layer levels to the grid. lvlid = eps * (lvlid / eps); @@ -640,8 +641,8 @@ void SLAPrint::Steps::initialize_printer_input() if(it == printer_input.end() || it->level() != lvlid) it = printer_input.insert(it, PrintLayer(lvlid)); - - + + it->add(slicerecord); } } @@ -650,53 +651,53 @@ void SLAPrint::Steps::initialize_printer_input() // Merging the slices from all the print objects into one slice grid and // calculating print statistics from the merge result. void SLAPrint::Steps::merge_slices_and_eval_stats() { - + initialize_printer_input(); - + auto &print_statistics = m_print->m_print_statistics; auto &printer_config = m_print->m_printer_config; auto &material_config = m_print->m_material_config; auto &printer_input = m_print->m_printer_input; - + print_statistics.clear(); - + // libnest calculates positive area for clockwise polygons, Slic3r is in counter-clockwise auto areafn = [](const ClipperPolygon& poly) { return - libnest2d::sl::area(poly); }; - + const double area_fill = printer_config.area_fill.getFloat()*0.01;// 0.5 (50%); const double fast_tilt = printer_config.fast_tilt_time.getFloat();// 5.0; const double slow_tilt = printer_config.slow_tilt_time.getFloat();// 8.0; - + const double init_exp_time = material_config.initial_exposure_time.getFloat(); const double exp_time = material_config.exposure_time.getFloat(); - + const int fade_layers_cnt = m_print->m_default_object_config.faded_layers.getInt();// 10 // [3;20] - + const auto width = scaled(printer_config.display_width.getFloat()); const auto height = scaled(printer_config.display_height.getFloat()); const double display_area = width*height; - + double supports_volume(0.0); double models_volume(0.0); - + double estim_time(0.0); std::vector layers_times; layers_times.reserve(printer_input.size()); - + size_t slow_layers = 0; size_t fast_layers = 0; - + const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1); double fade_layer_time = init_exp_time; - + sla::ccr::SpinningMutex mutex; using Lock = std::lock_guard; - + // Going to parallel: auto printlayerfn = [this, // functions and read only vars areafn, area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time, - + // write vars &mutex, &models_volume, &supports_volume, &estim_time, &slow_layers, &fast_layers, &fade_layer_time, &layers_times](size_t sliced_layer_cnt) @@ -705,87 +706,87 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { // vector of slice record references auto& slicerecord_references = layer.slices(); - + if(slicerecord_references.empty()) return; - + // Layer height should match for all object slices for a given level. const auto l_height = double(slicerecord_references.front().get().layer_height()); - + // Calculation of the consumed material - + ClipperPolygons model_polygons; ClipperPolygons supports_polygons; - + size_t c = std::accumulate(layer.slices().begin(), layer.slices().end(), size_t(0), [](size_t a, const SliceRecord &sr) { return a + sr.get_slice(soModel).size(); }); - + model_polygons.reserve(c); - + c = std::accumulate(layer.slices().begin(), layer.slices().end(), size_t(0), [](size_t a, const SliceRecord &sr) { return a + sr.get_slice(soModel).size(); }); - + supports_polygons.reserve(c); - + for(const SliceRecord& record : layer.slices()) { - + ClipperPolygons modelslices = get_all_polygons(record, soModel); for(ClipperPolygon& p_tmp : modelslices) model_polygons.emplace_back(std::move(p_tmp)); - + ClipperPolygons supportslices = get_all_polygons(record, soSupport); for(ClipperPolygon& p_tmp : supportslices) supports_polygons.emplace_back(std::move(p_tmp)); - + } - + model_polygons = polyunion(model_polygons); double layer_model_area = 0; for (const ClipperPolygon& polygon : model_polygons) layer_model_area += areafn(polygon); - + if (layer_model_area < 0 || layer_model_area > 0) { Lock lck(mutex); models_volume += layer_model_area * l_height; } - + if(!supports_polygons.empty()) { if(model_polygons.empty()) supports_polygons = polyunion(supports_polygons); else supports_polygons = polydiff(supports_polygons, model_polygons); // allegedly, union of subject is done withing the diff according to the pftPositive polyFillType } - + double layer_support_area = 0; for (const ClipperPolygon& polygon : supports_polygons) layer_support_area += areafn(polygon); - + if (layer_support_area < 0 || layer_support_area > 0) { Lock lck(mutex); supports_volume += layer_support_area * l_height; } - + // Here we can save the expensively calculated polygons for printing ClipperPolygons trslices; trslices.reserve(model_polygons.size() + supports_polygons.size()); for(ClipperPolygon& poly : model_polygons) trslices.emplace_back(std::move(poly)); for(ClipperPolygon& poly : supports_polygons) trslices.emplace_back(std::move(poly)); - + layer.transformed_slices(polyunion(trslices)); - + // Calculation of the slow and fast layers to the future controlling those values on FW - + const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill; const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt; - + { Lock lck(mutex); if (is_fast_layer) fast_layers++; else slow_layers++; - + // Calculation of the printing time double layer_times = 0.0; @@ -803,15 +804,15 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { estim_time += layer_times; } }; - + // sequential version for debugging: // for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i); sla::ccr::for_each(size_t(0), printer_input.size(), printlayerfn); - + auto SCALING2 = SCALING_FACTOR * SCALING_FACTOR; print_statistics.support_used_material = supports_volume * SCALING2; print_statistics.objects_used_material = models_volume * SCALING2; - + // Estimated printing time // A layers count o the highest object if (printer_input.size() == 0) @@ -820,10 +821,10 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { print_statistics.estimated_print_time = estim_time; print_statistics.layers_times = layers_times; } - + print_statistics.fast_layers_count = fast_layers; print_statistics.slow_layers_count = slow_layers; - + report_status(-2, "", SlicingStatus::RELOAD_SLA_PREVIEW); } @@ -831,23 +832,23 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { void SLAPrint::Steps::rasterize() { if(canceled() || !m_print->m_printer) return; - + // coefficient to map the rasterization state (0-99) to the allocated // portion (slot) of the process state double sd = (100 - max_objstatus) / 100.0; - + // slot is the portion of 100% that is realted to rasterization unsigned slot = PRINT_STEP_LEVELS[slapsRasterize]; - + // pst: previous state double pst = current_status(); - + double increment = (slot * sd) / m_print->m_printer_input.size(); double dstatus = current_status(); - + sla::ccr::SpinningMutex slck; using Lock = std::lock_guard; - + // procedure to process one height level. This will run in parallel auto lvlfn = [this, &slck, increment, &dstatus, &pst] @@ -855,10 +856,10 @@ void SLAPrint::Steps::rasterize() { PrintLayer& printlayer = m_print->m_printer_input[idx]; if(canceled()) return; - + for (const ClipperLib::Polygon& poly : printlayer.transformed_slices()) raster.draw(poly); - + // Status indication guarded with the spinlock { Lock lck(slck); @@ -870,10 +871,10 @@ void SLAPrint::Steps::rasterize() } } }; - + // last minute escape if(canceled()) return; - + // Print all the layers in parallel m_print->m_printer->draw_layers(m_print->m_printer_input.size(), lvlfn); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index a34c7562e..7f6b10670 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -200,12 +200,20 @@ void HollowedMesh::on_update() if (print_object->is_step_done(slaposDrillHoles) && print_object->has_mesh(slaposDrillHoles)) { size_t timestamp = print_object->step_state_with_timestamp(slaposDrillHoles).timestamp; if (timestamp > m_old_hollowing_timestamp) { - const TriangleMesh& backend_mesh = print_object->get_mesh_to_print(); + const TriangleMesh& backend_mesh = print_object->get_mesh_to_slice(); if (! backend_mesh.empty()) { m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh)); Transform3d trafo_inv = canvas->sla_print()->sla_trafo(*mo).inverse(); m_hollowed_mesh_transformed->transform(trafo_inv); m_old_hollowing_timestamp = timestamp; + + const TriangleMesh &interior = print_object->hollowed_interior_mesh(); + if (!interior.empty()) { + m_hollowed_interior_transformed = std::make_unique(interior); + m_hollowed_interior_transformed->repaired = false; + m_hollowed_interior_transformed->repair(true); + m_hollowed_interior_transformed->transform(trafo_inv); + } } else m_hollowed_mesh_transformed.reset(nullptr); @@ -230,6 +238,10 @@ const TriangleMesh* HollowedMesh::get_hollowed_mesh() const return m_hollowed_mesh_transformed.get(); } +const TriangleMesh* HollowedMesh::get_hollowed_interior() const +{ + return m_hollowed_interior_transformed.get(); +} @@ -306,6 +318,10 @@ void ObjectClipper::on_update() m_clippers.back()->set_mesh(*mesh); } m_old_meshes = meshes; + + if (has_hollowed) + m_clippers.front()->set_negative_mesh(*get_pool()->hollowed_mesh()->get_hollowed_interior()); + m_active_inst_bb_radius = mo->instance_bounding_box(get_pool()->selection_info()->get_active_instance()).radius(); //if (has_hollowed && m_clp_ratio != 0.) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp index 61c273297..ace256748 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.hpp @@ -199,6 +199,7 @@ public: #endif // NDEBUG const TriangleMesh* get_hollowed_mesh() const; + const TriangleMesh* get_hollowed_interior() const; protected: void on_update() override; @@ -206,6 +207,7 @@ protected: private: std::unique_ptr m_hollowed_mesh_transformed; + std::unique_ptr m_hollowed_interior_transformed; size_t m_old_hollowing_timestamp = 0; int m_print_object_idx = -1; int m_print_objects_count = 0; diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index ee0abe76f..f9ccfd0d6 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -2,6 +2,7 @@ #include "libslic3r/Tesselate.hpp" #include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/ClipperUtils.hpp" #include "slic3r/GUI/Camera.hpp" @@ -31,6 +32,15 @@ void MeshClipper::set_mesh(const TriangleMesh& mesh) } } +void MeshClipper::set_negative_mesh(const TriangleMesh& mesh) +{ + if (m_negative_mesh != &mesh) { + m_negative_mesh = &mesh; + m_triangles_valid = false; + m_triangles2d.resize(0); + } +} + void MeshClipper::set_transformation(const Geometry::Transformation& trafo) @@ -74,6 +84,15 @@ void MeshClipper::recalculate_triangles() std::vector list_of_expolys; m_tms->set_up_direction(up.cast()); m_tms->slice(std::vector{height_mesh}, SlicingMode::Regular, 0.f, &list_of_expolys, [](){}); + + if (m_negative_mesh && !m_negative_mesh->empty()) { + TriangleMeshSlicer negative_tms{m_negative_mesh}; + negative_tms.set_up_direction(up.cast()); + + std::vector neg_polys; + negative_tms.slice(std::vector{height_mesh}, SlicingMode::Regular, 0.f, &neg_polys, [](){}); + list_of_expolys.front() = diff_ex(list_of_expolys.front(), neg_polys.front()); + } m_triangles2d = triangulate_expolygons_2f(list_of_expolys[0], m_trafo.get_matrix().matrix().determinant() < 0.); // Rotate the cut into world coords: diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 60dcb30c8..09caf199b 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -78,6 +78,8 @@ public: // must make sure that it stays valid. void set_mesh(const TriangleMesh& mesh); + void set_negative_mesh(const TriangleMesh &mesh); + // Inform the MeshClipper about the transformation that transforms the mesh // into world coordinates. void set_transformation(const Geometry::Transformation& trafo); @@ -91,6 +93,7 @@ private: Geometry::Transformation m_trafo; const TriangleMesh* m_mesh = nullptr; + const TriangleMesh* m_negative_mesh = nullptr; ClippingPlane m_plane; std::vector m_triangles2d; GLIndexedVertexArray m_vertex_array;