mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-20 07:11:12 -06:00 
			
		
		
		
	NEW: enable lightning infill pattern for model and tree support
Change-Id: I6e2cbfdd30f8d222f88301ed0c8cc89e21cfdc24 (cherry picked from commit ddfee7c069cfc42685be509d48b8c609e1dc0cfc)
This commit is contained in:
		
							parent
							
								
									f331d5998e
								
							
						
					
					
						commit
						9cf95696a4
					
				
					 22 changed files with 1014 additions and 321 deletions
				
			
		|  | @ -11,6 +11,8 @@ | |||
| #include "I18N.hpp" | ||||
| #include <libnest2d/backends/libslic3r/geometries.hpp> | ||||
| 
 | ||||
| #include "Fill/FillBase.hpp" | ||||
| 
 | ||||
| #define _L(s) Slic3r::I18N::translate(s) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -283,6 +285,49 @@ static void draw_layer_mst | |||
|     svg.draw(lines, "blue", coord_t(scale_(0.05))); | ||||
|     svg.draw_outline(outline, "yellow"); | ||||
| } | ||||
| 
 | ||||
| static void draw_two_overhangs_to_svg(TreeSupportLayer* ts_layer, const ExPolygons& overhangs1, const ExPolygons& overhangs2) | ||||
| { | ||||
|     if (overhangs1.empty() && overhangs2.empty()) | ||||
|         return; | ||||
|     BoundingBox bbox1 = get_extents(overhangs1); | ||||
|     BoundingBox bbox2 = get_extents(overhangs2); | ||||
|     bbox1.merge(bbox2); | ||||
| 
 | ||||
|     SVG svg(get_svg_filename(std::to_string(ts_layer->print_z), "two_overhangs"), bbox1); | ||||
|     if (!svg.is_opened())        return; | ||||
| 
 | ||||
|     svg.draw(union_ex(overhangs1), "blue"); | ||||
|     svg.draw(union_ex(overhangs2), "red"); | ||||
| } | ||||
| 
 | ||||
| static void draw_polylines(TreeSupportLayer* ts_layer, Polylines& polylines) | ||||
| { | ||||
|     if (polylines.empty()) | ||||
|         return; | ||||
|     BoundingBox bbox = get_extents(polylines); | ||||
| 
 | ||||
|     SVG svg(get_svg_filename(std::to_string(ts_layer->print_z), "lightnings"), bbox); | ||||
|     if (!svg.is_opened())        return; | ||||
| 
 | ||||
|     int id = 0; | ||||
|     for (Polyline& pline : polylines) | ||||
|     { | ||||
|         int i1, i2; | ||||
|         for (size_t i = 0; i < pline.size() - 1; i++) | ||||
|         { | ||||
|             i1 = i; | ||||
|             i2 = i + 1; | ||||
|             svg.draw(Line(pline.points[i1], pline.points[i2]), "blue"); | ||||
|             svg.draw(pline.points[i1], "red"); | ||||
|             id++; | ||||
|             svg.draw_text(pline.points[i1], std::to_string(id).c_str(), "black", 1); | ||||
|         } | ||||
|         svg.draw(pline.points[i2], "red"); | ||||
|         id++; | ||||
|         svg.draw_text(pline.points[i2], std::to_string(id).c_str(), "black", 1); | ||||
|     } | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| // Move point from inside polygon if distance>0, outside if distance<0.
 | ||||
|  | @ -641,7 +686,11 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p | |||
|     m_raft_layers = slicing_params.base_raft_layers + slicing_params.interface_raft_layers; | ||||
|          | ||||
|     SupportMaterialPattern support_pattern  = m_object_config->support_base_pattern; | ||||
|     m_support_params.base_fill_pattern      = support_pattern == smpHoneycomb                                         ? ipHoneycomb : | ||||
|     m_support_params.base_fill_pattern      =  | ||||
| #if HAS_LIGHTNING_INFILL | ||||
|         support_pattern == smpLightning ? ipLightning : | ||||
| #endif | ||||
|         support_pattern == smpHoneycomb ? ipHoneycomb : | ||||
|                                               m_support_params.support_density > 0.95 || m_support_params.with_sheath ? ipRectilinear : | ||||
|                                                                                                                         ipSupportBase; | ||||
|     m_support_params.interface_fill_pattern = (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase); | ||||
|  | @ -1507,7 +1556,7 @@ void TreeSupport::generate_toolpaths() | |||
|                         ExtrusionRole role; | ||||
|                         Flow flow = (layer_id == 0 && m_raft_layers == 0) ? m_object->print()->brim_flow() : | ||||
|                             (m_support_params.base_fill_pattern == ipRectilinear && (layer_id % num_layers_to_change_infill_direction == 0) ? support_transition_flow(m_object) : support_flow); | ||||
|                         if (with_infill && layer_id > 0) { | ||||
|                         if (with_infill && layer_id > 0 && m_support_params.base_fill_pattern != ipLightning) { | ||||
|                             if (m_support_params.base_fill_pattern == ipRectilinear) { | ||||
|                                 role = erSupportMaterial;// layer_id% num_layers_to_change_infill_direction == 0 ? erSupportTransition : erSupportMaterial;
 | ||||
|                                 filler_support->angle = Geometry::deg2rad(object_config.support_angle.value);// obj_is_vertical* M_PI_2;// (obj_is_vertical + int(layer_id / num_layers_to_change_infill_direction))* M_PI_2;
 | ||||
|  | @ -1541,6 +1590,67 @@ void TreeSupport::generate_toolpaths() | |||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 if (with_infill && m_support_params.base_fill_pattern == ipLightning) | ||||
|                 { | ||||
|                     double print_z = ts_layer->print_z; | ||||
|                     if (printZ_to_lightninglayer.find(print_z) == printZ_to_lightninglayer.end()) | ||||
|                         continue; | ||||
|                     //TODO:
 | ||||
|                     //1.the second parameter of convertToLines seems to decide how long the lightning should be trimmed from its root, so that the root wont overlap/detach the support contour.
 | ||||
|                     // whether current value works correctly remained to be tested
 | ||||
|                     //2.related to previous one, that lightning roots need to be trimed more when support has multiple walls
 | ||||
|                     //3.function connect_infill() and variable 'params' helps create connection pattern along contours between two lightning roots,
 | ||||
|                     // strengthen lightnings while it may make support harder. decide to enable it or not. if yes, proper values for params are remained to be tested
 | ||||
|                     auto& lightning_layer = generator->getTreesForLayer(printZ_to_lightninglayer[print_z]); | ||||
| 
 | ||||
|                     Flow       flow  = (layer_id == 0 && m_raft_layers == 0) ? | ||||
|                                            m_object->print()->brim_flow() : | ||||
|                                            (m_support_params.base_fill_pattern == ipRectilinear && (layer_id % num_layers_to_change_infill_direction == 0) ? | ||||
|                                                 support_transition_flow(m_object) : | ||||
|                                                 support_flow); | ||||
|                     ExPolygons areas = offset_ex(ts_layer->base_areas, -flow.scaled_spacing()); | ||||
| 
 | ||||
|                     for (auto& area : areas) | ||||
|                     { | ||||
|                         Polylines polylines = lightning_layer.convertToLines(to_polygons(area), 0); | ||||
|                         for (auto itr = polylines.begin(); itr != polylines.end();) | ||||
|                         { | ||||
|                             if (itr->length() < scale_(1.0)) | ||||
|                                 itr = polylines.erase(itr); | ||||
|                             else | ||||
|                                 itr++; | ||||
|                         } | ||||
|                         Polylines opt_polylines; | ||||
| #if 1 | ||||
|                         //this wont create connection patterns along contours
 | ||||
|                         append(opt_polylines, chain_polylines(std::move(polylines))); | ||||
| #else | ||||
|                         //this will create connection patterns along contours
 | ||||
|                         FillParams params; | ||||
|                         params.anchor_length = float(Fill::infill_anchor * 0.01 * flow.spacing()); | ||||
|                         params.anchor_length_max = Fill::infill_anchor_max; | ||||
|                         params.anchor_length = std::min(params.anchor_length, params.anchor_length_max); | ||||
|                         Fill::connect_infill(std::move(polylines), area, opt_polylines, flow.spacing(), params); | ||||
| #endif | ||||
|                         extrusion_entities_append_paths(ts_layer->support_fills.entities, opt_polylines, erSupportMaterial, | ||||
|                             float(flow.mm3_per_mm()), float(flow.width()), float(flow.height())); | ||||
| 
 | ||||
| #ifdef SUPPORT_TREE_DEBUG_TO_SVG | ||||
|                         std::string prefix = "./SVG/"; | ||||
|                         std::string suffix = ".svg"; | ||||
|                         std::string name = prefix + "trees_polyline" + "_" + std::to_string(ts_layer->print_z) /*+ "_" + std::to_string(rand_num)*/ + suffix; | ||||
|                         BoundingBox bbox = get_extents(ts_layer->base_areas); | ||||
|                         SVG svg(name, bbox); | ||||
| 
 | ||||
|                         svg.draw(ts_layer->base_areas, "blue"); | ||||
|                         svg.draw(generator->Overhangs()[printZ_to_lightninglayer[print_z]], "red"); | ||||
|                         for (auto& line : opt_polylines) | ||||
|                         { | ||||
|                             svg.draw(line, "yellow"); | ||||
|                         } | ||||
| #endif | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 // sort extrusions to reduce travel, also make sure walls go before infills
 | ||||
|                 chain_and_reorder_extrusion_entities(ts_layer->support_fills.entities); | ||||
|  | @ -1895,6 +2005,15 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no | |||
|     const coordf_t line_width = config.support_line_width; | ||||
|     const coordf_t line_width_scaled           = scale_(line_width); | ||||
| 
 | ||||
|     const bool with_lightning_infill = config.tree_support_with_infill.value && config.support_base_pattern.value == smpLightning; | ||||
|     coordf_t support_extrusion_width = config.support_line_width.value > 0 ? config.support_line_width : config.line_width; | ||||
|     const size_t wall_count = config.tree_support_wall_count.value; | ||||
| 
 | ||||
|     const PrintObjectConfig& object_config = m_object->config(); | ||||
|     auto m_support_material_flow = support_material_flow(m_object, float(m_slicing_params.layer_height)); | ||||
|     coordf_t support_spacing = object_config.support_base_pattern_spacing.value + m_support_material_flow.spacing(); | ||||
|     coordf_t support_density = std::min(1., m_support_material_flow.spacing() / support_spacing); | ||||
| 
 | ||||
|     // coconut: previously std::unordered_map in m_collision_cache is not multi-thread safe which may cause programs stuck, here we change to tbb::concurrent_unordered_map
 | ||||
|     tbb::parallel_for( | ||||
|         tbb::blocked_range<size_t>(0, m_object->layer_count()), | ||||
|  | @ -2060,100 +2179,167 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no | |||
|         }); | ||||
| 
 | ||||
| #if 1 | ||||
|         // move the holes to contour so they can be well supported
 | ||||
|     if (!has_infill) { | ||||
|         // check if poly's contour intersects with expoly's contour
 | ||||
|         auto intersects_contour = [](Polygon poly, ExPolygon expoly, Point &pt_on_poly, Point &pt_on_expoly, Point &pt_far_on_poly, float dist_thresh = 0.01) { | ||||
|             float min_dist = std::numeric_limits<float>::max(); | ||||
|             float max_dist = 0; | ||||
|             for (auto from : poly.points) { | ||||
|                 for (int i = 0; i < expoly.num_contours(); i++) { | ||||
|                     const Point *candidate = expoly.contour_or_hole(i).closest_point(from); | ||||
|                     double       dist2     = vsize2_with_unscale(*candidate - from); | ||||
|                     if (dist2 < min_dist) { | ||||
|                         min_dist     = dist2; | ||||
|                         pt_on_poly   = from; | ||||
|                         pt_on_expoly = *candidate; | ||||
|                     } | ||||
|                     if (dist2 > max_dist) { | ||||
|                         max_dist       = dist2; | ||||
|                         pt_far_on_poly = from; | ||||
|                     } | ||||
|                     if (dist2 < dist_thresh) { return true; } | ||||
|         if (with_lightning_infill) | ||||
|         { | ||||
|             const bool global_lightning_infill = true; | ||||
| 
 | ||||
|             std::vector<Polygons> contours; | ||||
|             std::vector<Polygons> overhangs; | ||||
|             for (int layer_nr = 1; layer_nr < m_object->layer_count(); layer_nr++) { | ||||
|                 if (print->canceled()) break; | ||||
|                 const std::vector<Node*>& curr_layer_nodes = contact_nodes[layer_nr]; | ||||
|                 TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers); | ||||
|                 assert(ts_layer != nullptr); | ||||
| 
 | ||||
|                 // skip if current layer has no points. This fixes potential crash in get_collision (see jira BBL001-355)
 | ||||
|                 if (curr_layer_nodes.empty()) continue; | ||||
|                 if (ts_layer->height < EPSILON) continue; | ||||
|                 if (ts_layer->area_groups.empty()) continue; | ||||
| 
 | ||||
|                 ExPolygons& base_areas = ts_layer->base_areas; | ||||
| 
 | ||||
|                 int layer_nr_lower = layer_nr - 1; | ||||
|                 for (layer_nr_lower; layer_nr_lower >= 0; layer_nr_lower--) { | ||||
|                     if (!m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break; | ||||
|                 } | ||||
|                 TreeSupportLayer* lower_layer = m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers); | ||||
|                 ExPolygons& base_areas_lower = m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->base_areas; | ||||
| 
 | ||||
|                 ExPolygons overhang; | ||||
| 
 | ||||
|                 if (layer_nr_lower == 0) | ||||
|                     continue; | ||||
| 
 | ||||
|                 if (global_lightning_infill) | ||||
|                 { | ||||
|                     //search overhangs globally
 | ||||
|                     overhang = std::move(diff_ex(offset_ex(base_areas_lower, -2.0 * scale_(support_extrusion_width)), base_areas)); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     //search overhangs only on floating islands
 | ||||
|                     for (auto& base_area : base_areas) | ||||
|                         for (auto& hole : base_area.holes) | ||||
|                         { | ||||
|                             Polygon rev_hole = hole; | ||||
|                             rev_hole.make_counter_clockwise(); | ||||
|                             ExPolygons ex_hole = to_expolygons(ExPolygon(rev_hole)); | ||||
|                             for (auto& other_area : base_areas) | ||||
|                                 //if (&other_area != &base_area)
 | ||||
|                                     ex_hole = std::move(diff_ex(ex_hole, other_area)); | ||||
|                             overhang = std::move(union_ex(overhang, ex_hole)); | ||||
|                         } | ||||
|                     overhang = std::move(intersection_ex(overhang, offset_ex(base_areas_lower, -0.5 * scale_(support_extrusion_width)))); | ||||
|                 } | ||||
| 
 | ||||
|                 overhangs.emplace_back(to_polygons(overhang)); | ||||
|                 contours.emplace_back(to_polygons(base_areas_lower));   //cant guarantee for 100% success probability, infill fails sometimes
 | ||||
|                 printZ_to_lightninglayer[lower_layer->print_z] = overhangs.size() - 1; | ||||
| 
 | ||||
| #ifdef SUPPORT_TREE_DEBUG_TO_SVG | ||||
|                 draw_two_overhangs_to_svg(m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers), base_areas_lower, to_expolygons(overhangs.back())); | ||||
| #endif | ||||
|             } | ||||
|             return false; | ||||
|         }; | ||||
| 
 | ||||
|         // polygon pointer: depth, direction, farPoint
 | ||||
|         std::map<const Polygon *, std::tuple<int, Point, Point>> holePropagationInfos; | ||||
|         for (int layer_nr = m_object->layer_count() - 1; layer_nr > 0; layer_nr--) { | ||||
|             if (print->canceled()) break; | ||||
|             m_object->print()->set_status(66, (boost::format(_L("Support: fix holes at layer %d")) % layer_nr).str()); | ||||
|             generator = std::make_unique<FillLightning::Generator>(m_object, contours, overhangs, support_density); | ||||
|         } | ||||
| 
 | ||||
|             const std::vector<Node *> &curr_layer_nodes = contact_nodes[layer_nr]; | ||||
|             TreeSupportLayer *         ts_layer         = m_object->get_tree_support_layer(layer_nr + m_raft_layers); | ||||
|             assert(ts_layer != nullptr); | ||||
|         else if (!has_infill) { | ||||
|             // move the holes to contour so they can be well supported
 | ||||
| 
 | ||||
|             // skip if current layer has no points. This fixes potential crash in get_collision (see jira BBL001-355)
 | ||||
|             if (curr_layer_nodes.empty()) continue; | ||||
|             if (ts_layer->height < EPSILON) continue; | ||||
|             if (ts_layer->area_groups.empty()) continue; | ||||
| 
 | ||||
|             int layer_nr_lower = layer_nr - 1; | ||||
|             for (layer_nr_lower; layer_nr_lower >= 0; layer_nr_lower--) { | ||||
|                 if (!m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break; | ||||
|             } | ||||
|             auto &area_groups_lower = m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->area_groups; | ||||
| 
 | ||||
|             for (const auto &area_group : ts_layer->area_groups) { | ||||
|                 if (area_group.second != TreeSupportLayer::BaseType) continue; | ||||
|                 const auto area = area_group.first; | ||||
|                 for (const auto &hole : area->holes) { | ||||
|                     // auto hole_bbox = get_extents(hole).polygon();
 | ||||
|                     for (auto &area_group_lower : area_groups_lower) { | ||||
|                         if (area_group.second != TreeSupportLayer::BaseType) continue; | ||||
|                         auto &base_area_lower = *area_group_lower.first; | ||||
|                         Point pt_on_poly, pt_on_expoly, pt_far_on_poly; | ||||
|                         // if a hole doesn't intersect with lower layer's contours, add a hole to lower layer and move it slightly to the contour
 | ||||
|                         if (base_area_lower.contour.contains(hole.points.front()) && !intersects_contour(hole, base_area_lower, pt_on_poly, pt_on_expoly, pt_far_on_poly)) { | ||||
|                             Polygon hole_lower = hole; | ||||
|                             Point   direction  = normal(pt_on_expoly - pt_on_poly, line_width_scaled / 2); | ||||
|                             hole_lower.translate(direction); | ||||
|                             // note to expand a hole, we need to do negative offset
 | ||||
|                             auto hole_expanded = offset(hole_lower, -line_width_scaled / 4, ClipperLib::JoinType::jtSquare); | ||||
|                             if (!hole_expanded.empty()) { | ||||
|                                 base_area_lower.holes.push_back(std::move(hole_expanded[0])); | ||||
|                                 holePropagationInfos.insert({&base_area_lower.holes.back(), {25, direction, pt_far_on_poly}}); | ||||
|         // check if poly's contour intersects with expoly's contour
 | ||||
|             auto intersects_contour = [](Polygon poly, ExPolygon expoly, Point& pt_on_poly, Point& pt_on_expoly, Point& pt_far_on_poly, float dist_thresh = 0.01) { | ||||
|                 float min_dist = std::numeric_limits<float>::max(); | ||||
|                 float max_dist = 0; | ||||
|                 for (auto from : poly.points) { | ||||
|                     for (int i = 0; i < expoly.num_contours(); i++) { | ||||
|                         const Point* candidate = expoly.contour_or_hole(i).closest_point(from); | ||||
|                         double       dist2 = vsize2_with_unscale(*candidate - from); | ||||
|                         if (dist2 < min_dist) { | ||||
|                             min_dist = dist2; | ||||
|                             pt_on_poly = from; | ||||
|                             pt_on_expoly = *candidate; | ||||
|                             } | ||||
|                             break; | ||||
|                         } else if (holePropagationInfos.find(&hole) != holePropagationInfos.end() && std::get<0>(holePropagationInfos[&hole]) > 0 && | ||||
|                                    base_area_lower.contour.contains(std::get<2>(holePropagationInfos[&hole]))) { | ||||
|                             Polygon hole_lower = hole; | ||||
|                             auto && direction  = std::get<1>(holePropagationInfos[&hole]); | ||||
|                             hole_lower.translate(direction); | ||||
|                             // note to shrink a hole, we need to do positive offset
 | ||||
|                             auto  hole_expanded = offset(hole_lower, line_width_scaled / 2, ClipperLib::JoinType::jtSquare); | ||||
|                             Point farPoint      = std::get<2>(holePropagationInfos[&hole]) + direction * 2; | ||||
|                             if (!hole_expanded.empty()) { | ||||
|                                 base_area_lower.holes.push_back(std::move(hole_expanded[0])); | ||||
|                                 holePropagationInfos.insert({&base_area_lower.holes.back(), {std::get<0>(holePropagationInfos[&hole]) - 1, direction, farPoint}}); | ||||
|                         if (dist2 > max_dist) { | ||||
|                             max_dist = dist2; | ||||
|                             pt_far_on_poly = from; | ||||
|                             } | ||||
|                             break; | ||||
|                         if (dist2 < dist_thresh) { return true; } | ||||
|                         } | ||||
|                     } | ||||
|                     { | ||||
|                 return false; | ||||
|                 }; | ||||
| 
 | ||||
|             // polygon pointer: depth, direction, farPoint
 | ||||
|             std::map<const Polygon*, std::tuple<int, Point, Point>> holePropagationInfos; | ||||
|             for (int layer_nr = m_object->layer_count() - 1; layer_nr > 0; layer_nr--) { | ||||
|                 if (print->canceled()) break; | ||||
|                 m_object->print()->set_status(66, (boost::format(_L("Support: fix holes at layer %d")) % layer_nr).str()); | ||||
| 
 | ||||
|                 const std::vector<Node*>& curr_layer_nodes = contact_nodes[layer_nr]; | ||||
|                 TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers); | ||||
|                 assert(ts_layer != nullptr); | ||||
| 
 | ||||
|                 // skip if current layer has no points. This fixes potential crash in get_collision (see jira BBL001-355)
 | ||||
|                 if (curr_layer_nodes.empty()) continue; | ||||
|                 if (ts_layer->height < EPSILON) continue; | ||||
|                 if (ts_layer->area_groups.empty()) continue; | ||||
| 
 | ||||
|                 int layer_nr_lower = layer_nr - 1; | ||||
|                 for (layer_nr_lower; layer_nr_lower >= 0; layer_nr_lower--) { | ||||
|                     if (!m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break; | ||||
|                     } | ||||
|                 auto& area_groups_lower = m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->area_groups; | ||||
| 
 | ||||
|                 for (const auto& area_group : ts_layer->area_groups) { | ||||
|                     if (area_group.second != TreeSupportLayer::BaseType) continue; | ||||
|                     const auto area = area_group.first; | ||||
|                     for (const auto& hole : area->holes) { | ||||
|                         // auto hole_bbox = get_extents(hole).polygon();
 | ||||
|                         for (auto& area_group_lower : area_groups_lower) { | ||||
|                             if (area_group.second != TreeSupportLayer::BaseType) continue; | ||||
|                             auto& base_area_lower = *area_group_lower.first; | ||||
|                             Point pt_on_poly, pt_on_expoly, pt_far_on_poly; | ||||
|                             // if a hole doesn't intersect with lower layer's contours, add a hole to lower layer and move it slightly to the contour
 | ||||
|                             if (base_area_lower.contour.contains(hole.points.front()) && !intersects_contour(hole, base_area_lower, pt_on_poly, pt_on_expoly, pt_far_on_poly)) { | ||||
|                                 Polygon hole_lower = hole; | ||||
|                                 Point   direction = normal(pt_on_expoly - pt_on_poly, line_width_scaled / 2); | ||||
|                                 hole_lower.translate(direction); | ||||
|                                 // note to expand a hole, we need to do negative offset
 | ||||
|                                 auto hole_expanded = offset(hole_lower, -line_width_scaled / 4, ClipperLib::JoinType::jtSquare); | ||||
|                                 if (!hole_expanded.empty()) { | ||||
|                                     base_area_lower.holes.push_back(std::move(hole_expanded[0])); | ||||
|                                     holePropagationInfos.insert({ &base_area_lower.holes.back(), {25, direction, pt_far_on_poly} }); | ||||
|                                     } | ||||
|                                 break; | ||||
|                                 } | ||||
|                             else if (holePropagationInfos.find(&hole) != holePropagationInfos.end() && std::get<0>(holePropagationInfos[&hole]) > 0 && | ||||
|                                 base_area_lower.contour.contains(std::get<2>(holePropagationInfos[&hole]))) { | ||||
|                                 Polygon hole_lower = hole; | ||||
|                                 auto&& direction = std::get<1>(holePropagationInfos[&hole]); | ||||
|                                 hole_lower.translate(direction); | ||||
|                                 // note to shrink a hole, we need to do positive offset
 | ||||
|                                 auto  hole_expanded = offset(hole_lower, line_width_scaled / 2, ClipperLib::JoinType::jtSquare); | ||||
|                                 Point farPoint = std::get<2>(holePropagationInfos[&hole]) + direction * 2; | ||||
|                                 if (!hole_expanded.empty()) { | ||||
|                                     base_area_lower.holes.push_back(std::move(hole_expanded[0])); | ||||
|                                     holePropagationInfos.insert({ &base_area_lower.holes.back(), {std::get<0>(holePropagationInfos[&hole]) - 1, direction, farPoint} }); | ||||
|                                     } | ||||
|                                 break; | ||||
|                                 } | ||||
|                             } | ||||
|                         { | ||||
|                         // if roof1 interface is inside a hole, need to expand the interface
 | ||||
|                         for (auto &roof1 : ts_layer->roof_1st_layer) { | ||||
|                         for (auto& roof1 : ts_layer->roof_1st_layer) { | ||||
|                             //if (hole.contains(roof1.contour.points.front()) && hole.contains(roof1.contour.bounding_box().center())) 
 | ||||
|                             bool is_inside_hole = std::all_of(roof1.contour.points.begin(), roof1.contour.points.end(), [&hole](Point &pt) { return hole.contains(pt); }); | ||||
|                             bool is_inside_hole = std::all_of(roof1.contour.points.begin(), roof1.contour.points.end(), [&hole](Point& pt) { return hole.contains(pt); }); | ||||
|                             if (is_inside_hole) { | ||||
|                                 Polygon hole_reoriented = hole; | ||||
|                                 if (roof1.contour.is_counter_clockwise()) | ||||
|                                     hole_reoriented.make_counter_clockwise(); | ||||
|                                 else if (roof1.contour.is_clockwise()) | ||||
|                                     hole_reoriented.make_clockwise(); | ||||
|                                 auto tmp = union_({roof1.contour}, {hole_reoriented}); | ||||
|                                 auto tmp = union_({ roof1.contour }, { hole_reoriented }); | ||||
|                                 if (!tmp.empty()) roof1.contour = tmp[0]; | ||||
| 
 | ||||
|                                 // make sure 1) roof1 and object 2) roof1 and roof, won't intersect
 | ||||
|  | @ -2163,38 +2349,38 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no | |||
|                                 tmp1 = diff_ex(tmp1, ts_layer->roof_areas); | ||||
|                                 if (!tmp1.empty()) { | ||||
|                                     roof1.contour = std::move(tmp1[0].contour); | ||||
|                                     roof1.holes   = std::move(tmp1[0].holes); | ||||
|                                 } | ||||
|                                     roof1.holes = std::move(tmp1[0].holes); | ||||
|                                     } | ||||
|                                 break; | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
| #ifdef SUPPORT_TREE_DEBUG_TO_SVG | ||||
|         for (int layer_nr = m_object->layer_count() - 1; layer_nr > 0; layer_nr--) { | ||||
|             TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers); | ||||
|             ExPolygons& base_areas = ts_layer->base_areas; | ||||
|             ExPolygons& roof_areas = ts_layer->roof_areas; | ||||
|             ExPolygons& roof_1st_layer = ts_layer->roof_1st_layer; | ||||
|             ExPolygons& floor_areas = ts_layer->floor_areas; | ||||
|             if (base_areas.empty() && roof_areas.empty() && roof_1st_layer.empty()) continue; | ||||
|             char fname[10]; sprintf(fname, "%d_%.2f", layer_nr, ts_layer->print_z); | ||||
|             draw_contours_and_nodes_to_svg(-1, base_areas, roof_areas, roof_1st_layer, {}, {}, get_svg_filename(fname, "circles"), {"base", "roof", "roof1st"}); | ||||
|         } | ||||
|     for (int layer_nr = m_object->layer_count() - 1; layer_nr > 0; layer_nr--) { | ||||
|         TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers); | ||||
|         ExPolygons& base_areas = ts_layer->base_areas; | ||||
|         ExPolygons& roof_areas = ts_layer->roof_areas; | ||||
|         ExPolygons& roof_1st_layer = ts_layer->roof_1st_layer; | ||||
|         ExPolygons& floor_areas = ts_layer->floor_areas; | ||||
|         if (base_areas.empty() && roof_areas.empty() && roof_1st_layer.empty()) continue; | ||||
|         char fname[10]; sprintf(fname, "%d_%.2f", layer_nr, ts_layer->print_z); | ||||
|         draw_contours_and_nodes_to_svg(-1, base_areas, roof_areas, roof_1st_layer, {}, {}, get_svg_filename(fname, "circles"), { "base", "roof", "roof1st" }); | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
|         TreeSupportLayerPtrs& ts_layers = m_object->tree_support_layers(); | ||||
|         auto iter = std::remove_if(ts_layers.begin(), ts_layers.end(), [](TreeSupportLayer* ts_layer) { return ts_layer->height < EPSILON; }); | ||||
|         ts_layers.erase(iter, ts_layers.end()); | ||||
|         for (int layer_nr = 0; layer_nr < ts_layers.size(); layer_nr++) { | ||||
|             ts_layers[layer_nr]->upper_layer = layer_nr != ts_layers.size() - 1 ? ts_layers[layer_nr + 1] : nullptr; | ||||
|             ts_layers[layer_nr]->lower_layer = layer_nr > 0 ? ts_layers[layer_nr - 1] : nullptr; | ||||
|         } | ||||
|     TreeSupportLayerPtrs& ts_layers = m_object->tree_support_layers(); | ||||
|     auto iter = std::remove_if(ts_layers.begin(), ts_layers.end(), [](TreeSupportLayer* ts_layer) { return ts_layer->height < EPSILON; }); | ||||
|     ts_layers.erase(iter, ts_layers.end()); | ||||
|     for (int layer_nr = 0; layer_nr < ts_layers.size(); layer_nr++) { | ||||
|         ts_layers[layer_nr]->upper_layer = layer_nr != ts_layers.size() - 1 ? ts_layers[layer_nr + 1] : nullptr; | ||||
|         ts_layers[layer_nr]->lower_layer = layer_nr > 0 ? ts_layers[layer_nr - 1] : nullptr; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 xiang.zeng
						xiang.zeng