mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-25 07:34:03 -06:00
Update the codes to 01.01.00.10 for the formal release
1. first formal version of macos 2. add the bambu networking plugin install logic 3. auto compute the wipe volume when filament change 4. add the logic of wiping into support 5. refine the GUI layout and icons, improve the gui apperance in lots of small places 6. serveral improve to support 7. support AMS auto-mapping 8. disable lots of unstable features: such as params table, media file download, HMS 9. fix serveral kinds of bugs 10. update the document of building 11. ...
This commit is contained in:
parent
e1528e4299
commit
e9e4d75877
267 changed files with 10326 additions and 32228 deletions
|
@ -256,9 +256,15 @@ void AppConfig::set_defaults()
|
|||
set("backup_interval", "10");
|
||||
}
|
||||
|
||||
#if BBL_RELEASE_TO_PUBLIC
|
||||
if (get("iot_environment").empty()) {
|
||||
set("iot_environment", "3");
|
||||
}
|
||||
#else
|
||||
if (get("iot_environment").empty()) {
|
||||
set("iot_environment", "1");
|
||||
}
|
||||
#endif
|
||||
|
||||
// Remove legacy window positions/sizes
|
||||
erase("app", "main_frame_maximized");
|
||||
|
@ -1003,12 +1009,7 @@ std::string AppConfig::get_region()
|
|||
std::string AppConfig::get_country_code()
|
||||
{
|
||||
std::string region = get_region();
|
||||
/* fix PRE environment when release to public */
|
||||
#if 0
|
||||
this->set("iot_environment", "2");
|
||||
return "ENV_CN_PRE";
|
||||
#else
|
||||
//if (is_engineering_region()) { return region; }
|
||||
if (is_engineering_region()) { return region; }
|
||||
if (region == "CHN" || region == "China")
|
||||
return "CN";
|
||||
else if (region == "USA")
|
||||
|
@ -1022,7 +1023,7 @@ std::string AppConfig::get_country_code()
|
|||
else
|
||||
return "Others";
|
||||
return "";
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
bool AppConfig::is_engineering_region(){
|
||||
|
|
|
@ -762,40 +762,7 @@ bool compSecondMoment(const ExPolygons& expolys, double& smExpolysX, double& smE
|
|||
return true;
|
||||
}
|
||||
|
||||
// BBS: thermal length is calculated according to the material of a volume
|
||||
double getThermalLength(const ModelVolume* modelVolumePtr) {
|
||||
double thermalLength = 200.;
|
||||
auto aa = modelVolumePtr->extruder_id();
|
||||
if (Model::extruderParamsMap.find(aa) != Model::extruderParamsMap.end()) {
|
||||
if (Model::extruderParamsMap.at(aa).materialName == "ABS" ||
|
||||
Model::extruderParamsMap.at(aa).materialName == "PA-CF" ||
|
||||
Model::extruderParamsMap.at(aa).materialName == "PET-CF") {
|
||||
thermalLength = 100;
|
||||
}
|
||||
if (Model::extruderParamsMap.at(aa).materialName == "PC") {
|
||||
thermalLength = 40;
|
||||
}
|
||||
if (Model::extruderParamsMap.at(aa).materialName == "TPU") {
|
||||
thermalLength = 1000;
|
||||
}
|
||||
|
||||
}
|
||||
return thermalLength;
|
||||
}
|
||||
|
||||
// BBS: thermal length calculation for a group of volumes
|
||||
double getThermalLength(const std::vector<ModelVolume*> modelVolumePtrs)
|
||||
{
|
||||
double thermalLength = 1250.;
|
||||
|
||||
for (const auto& modelVolumePtr : modelVolumePtrs) {
|
||||
if (modelVolumePtr != nullptr) {
|
||||
// the thermal length of a group is decided by the volume with shortest thermal length
|
||||
thermalLength = std::min(thermalLength, getThermalLength(modelVolumePtr));
|
||||
}
|
||||
}
|
||||
return thermalLength;
|
||||
}
|
||||
|
||||
//BBS: config brimwidth by volumes
|
||||
double configBrimWidthByVolumes(double deltaT, double adhension, double maxSpeed, const ModelVolume* modelVolumePtr, const ExPolygons& expolys)
|
||||
|
@ -825,7 +792,7 @@ double configBrimWidthByVolumes(double deltaT, double adhension, double maxSpeed
|
|||
const double& bboxX = bbox2.size()(0);
|
||||
const double& bboxY = bbox2.size()(1);
|
||||
double thermalLength = sqrt(bboxX * bboxX + bboxY * bboxY) * SCALING_FACTOR;
|
||||
double thermalLengthRef = getThermalLength(modelVolumePtr);
|
||||
double thermalLengthRef = Model::getThermalLength(modelVolumePtr);
|
||||
|
||||
double height_to_area = std::max(height / Ixx * (bbox2.size()(1) * SCALING_FACTOR), height / Iyy * (bbox2.size()(0) * SCALING_FACTOR));
|
||||
double brim_width = adhension * std::min(std::min(std::max(height_to_area * maxSpeed / 24, thermalLength * 8. / thermalLengthRef * std::min(height, 30.) / 30.), 18.), 1.5 * thermalLength);
|
||||
|
@ -846,8 +813,12 @@ double configBrimWidthByVolumeGroups(double adhension, double maxSpeed, const st
|
|||
BoundingBoxf3 mergedBbx;
|
||||
for (const auto& modelVolumePtr : modelVolumePtrs) {
|
||||
if (modelVolumePtr->is_model_part()) {
|
||||
auto rawBoundingbox = modelVolumePtr->mesh().transformed_bounding_box(modelVolumePtr->get_matrix());
|
||||
auto bbox = modelVolumePtr->get_object()->instances.front()->transform_bounding_box(rawBoundingbox);
|
||||
Slic3r::Transform3d t;
|
||||
if (modelVolumePtr->get_object()->instances.size() > 0)
|
||||
t = modelVolumePtr->get_object()->instances.front()->get_matrix() * modelVolumePtr->get_matrix();
|
||||
else
|
||||
t = modelVolumePtr->get_matrix();
|
||||
auto bbox = modelVolumePtr->mesh().transformed_bounding_box(t);
|
||||
mergedBbx.merge(bbox);
|
||||
}
|
||||
}
|
||||
|
@ -870,7 +841,7 @@ double configBrimWidthByVolumeGroups(double adhension, double maxSpeed, const st
|
|||
const double& bboxX = bbox2.size()(0);
|
||||
const double& bboxY = bbox2.size()(1);
|
||||
double thermalLength = sqrt(bboxX * bboxX + bboxY * bboxY) * SCALING_FACTOR;
|
||||
double thermalLengthRef = getThermalLength(modelVolumePtrs);
|
||||
double thermalLengthRef = Model::getThermalLength(modelVolumePtrs);
|
||||
|
||||
double height_to_area = std::max(height / Ixx * (bbox2.size()(1) * SCALING_FACTOR), height / Iyy * (bbox2.size()(0) * SCALING_FACTOR));
|
||||
double brim_width = adhension * std::min(std::min(std::max(height_to_area * maxSpeed / 24, thermalLength * 8. / thermalLengthRef * std::min(height, 30.) / 30.), 18.), 1.5 * thermalLength);
|
||||
|
@ -884,26 +855,13 @@ double configBrimWidthByVolumeGroups(double adhension, double maxSpeed, const st
|
|||
}
|
||||
|
||||
//BBS: create all brims
|
||||
static ExPolygons outer_inner_brim_area(const Print& print, const ConstPrintObjectPtrs& top_level_objects_with_brim,
|
||||
static ExPolygons outer_inner_brim_area(const Print& print,
|
||||
const float no_brim_offset, std::map<ObjectID, ExPolygons>& brimAreaMap,
|
||||
std::map<ObjectID, ExPolygons>& supportBrimAreaMap,
|
||||
std::vector<std::pair<ObjectID, unsigned int>>& objPrintVec,
|
||||
std::vector<unsigned int>& printExtruders)
|
||||
{
|
||||
std::unordered_set<size_t> top_level_objects_idx;
|
||||
top_level_objects_idx.reserve(top_level_objects_with_brim.size());
|
||||
for (const PrintObject* object : top_level_objects_with_brim)
|
||||
top_level_objects_idx.insert(object->id().id);
|
||||
|
||||
unsigned int support_material_extruder = printExtruders.front() + 1;
|
||||
auto allExtruders = print.extruders();
|
||||
if (print.has_support_material()) {
|
||||
assert(top_level_objects_with_brim.front()->config().support_filament >= 0);
|
||||
if (top_level_objects_with_brim.front()->config().support_filament > 0)
|
||||
support_material_extruder = top_level_objects_with_brim.front()->config().support_filament;
|
||||
allExtruders.push_back(support_material_extruder - 1);
|
||||
sort_remove_duplicates(allExtruders);
|
||||
}
|
||||
|
||||
ExPolygons brim_area;
|
||||
ExPolygons no_brim_area;
|
||||
|
@ -927,7 +885,6 @@ static ExPolygons outer_inner_brim_area(const Print& print, const ConstPrintObje
|
|||
float brim_offset = scale_(object->config().brim_object_gap.value);
|
||||
double flowWidth = print.brim_flow().scaled_spacing() * SCALING_FACTOR;
|
||||
float brim_width = scale_(floor(object->config().brim_width.value / flowWidth / 2) * flowWidth * 2);
|
||||
const bool top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end();
|
||||
const float scaled_flow_width = print.brim_flow().scaled_spacing();
|
||||
const float scaled_additional_brim_width = scale_(floor(5 / flowWidth / 2) * flowWidth * 2);
|
||||
const float scaled_half_min_adh_length = scale_(1.1);
|
||||
|
@ -1017,8 +974,13 @@ static ExPolygons outer_inner_brim_area(const Print& print, const ConstPrintObje
|
|||
if (brimAreaMap.find(object->id()) != brimAreaMap.end())
|
||||
expolygons_append(brim_area, brimAreaMap[object->id()]);
|
||||
}
|
||||
if ((print.config().print_sequence == PrintSequence::ByObject) && top_level_objects_with_brim.front()->config().support_filament == 0)
|
||||
support_material_extruder = objectWithExtruder.second;
|
||||
support_material_extruder = object->config().support_filament;
|
||||
if (support_material_extruder == 0 && object->has_support_material()) {
|
||||
if (print.config().print_sequence == PrintSequence::ByObject)
|
||||
support_material_extruder = objectWithExtruder.second;
|
||||
else
|
||||
support_material_extruder = printExtruders.front() + 1;
|
||||
}
|
||||
if (support_material_extruder == extruderNo && brimToWrite.at(object->id()).sup) {
|
||||
if (!object->support_layers().empty()) {
|
||||
for (const Polygon& support_contour : object->support_layers().front()->support_fills.polygons_covered_by_spacing()) {
|
||||
|
@ -1658,10 +1620,7 @@ void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_
|
|||
std::map<ObjectID, ExPolygons> supportBrimAreaMap;
|
||||
Flow flow = print.brim_flow();
|
||||
const auto scaled_resolution = scaled<double>(print.config().resolution.value);
|
||||
std::vector<ExPolygons> bottom_layers_expolygons = get_print_bottom_layers_expolygons(print);
|
||||
ConstPrintObjectPtrs top_level_objects_with_brim = get_top_level_objects_with_brim(print, bottom_layers_expolygons);
|
||||
Polygons islands = top_level_outer_brim_islands(top_level_objects_with_brim, scaled_resolution);
|
||||
ExPolygons islands_area_ex = outer_inner_brim_area(print, top_level_objects_with_brim,
|
||||
ExPolygons islands_area_ex = outer_inner_brim_area(print,
|
||||
float(flow.scaled_spacing()), brimAreaMap, supportBrimAreaMap, objPrintVec, printExtruders);
|
||||
|
||||
// BBS: Find boundingbox of the first layer
|
||||
|
|
|
@ -122,8 +122,6 @@ set(lisbslic3r_sources
|
|||
GCode/SpiralVase.hpp
|
||||
GCode/SeamPlacer.cpp
|
||||
GCode/SeamPlacer.hpp
|
||||
GCode/SpeedGenerator.cpp
|
||||
GCode/SpeedGenerator.hpp
|
||||
GCode/ToolOrdering.cpp
|
||||
GCode/ToolOrdering.hpp
|
||||
GCode/WipeTower.cpp
|
||||
|
@ -317,6 +315,12 @@ set(lisbslic3r_sources
|
|||
SLA/ReprojectPointsOnMesh.hpp
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
list(APPEND lisbslic3r_sources
|
||||
MacUtils.mm
|
||||
)
|
||||
endif ()
|
||||
|
||||
add_library(libslic3r STATIC ${lisbslic3r_sources}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/libslic3r_version.h"
|
||||
${OpenVDBUtils_SOURCES})
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
#include <cassert>
|
||||
#include "Geometry.hpp"
|
||||
|
||||
|
||||
//BBS: Refer to ArcWelderLib for the arc fitting functions
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
//BBS: threshold used to judge collineation
|
||||
|
|
|
@ -127,6 +127,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
|||
std::vector<std::vector<const SurfaceFillParams*>> region_to_surface_params(layer.regions().size(), std::vector<const SurfaceFillParams*>());
|
||||
SurfaceFillParams params;
|
||||
bool has_internal_voids = false;
|
||||
const PrintObjectConfig& object_config = layer.object()->config();
|
||||
for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) {
|
||||
const LayerRegion &layerm = *layer.regions()[region_id];
|
||||
region_to_surface_params[region_id].assign(layerm.fill_surfaces.size(), nullptr);
|
||||
|
@ -163,7 +164,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
|
|||
params.bridge = is_bridge || Fill::use_bridge_flow(params.pattern);
|
||||
params.flow = params.bridge ?
|
||||
//BBS: always enable thick bridge for internal bridge
|
||||
layerm.bridging_flow(extrusion_role, (surface.is_bridge() && !surface.is_external()) || g_config_thick_bridges) :
|
||||
layerm.bridging_flow(extrusion_role, (surface.is_bridge() && !surface.is_external()) || object_config.thick_bridges) :
|
||||
layerm.flow(extrusion_role, (surface.thickness == -1) ? layer.height : surface.thickness);
|
||||
|
||||
// Calculate flow spacing for infill pattern generation.
|
||||
|
|
|
@ -80,9 +80,8 @@ void FillConcentricWGapFill::fill_surface_extrusion(const Surface* surface, cons
|
|||
|
||||
ThickPolylines polylines;
|
||||
for (ExPolygon& ex : gaps_ex_sorted) {
|
||||
//BBS: medial axis algorithm can't handle duplicated points in expolygon.
|
||||
//Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well.
|
||||
ex.douglas_peucker(SCALED_RESOLUTION);
|
||||
//BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well.
|
||||
ex.douglas_peucker(SCALED_RESOLUTION * 0.1);
|
||||
ex.medial_axis(max, min, &polylines);
|
||||
}
|
||||
|
||||
|
@ -114,9 +113,8 @@ void FillConcentricWGapFill::fill_surface_extrusion(const Surface* surface, cons
|
|||
|
||||
ThickPolylines polylines;
|
||||
for (ExPolygon& ex : external_gaps_collapsed) {
|
||||
//BBS: medial axis algorithm can't handle duplicated points in expolygon.
|
||||
//Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well.
|
||||
ex.douglas_peucker(SCALED_RESOLUTION);
|
||||
//BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well.
|
||||
ex.douglas_peucker(SCALED_RESOLUTION * 0.1);
|
||||
ex.medial_axis(max, min, &polylines);
|
||||
}
|
||||
|
||||
|
|
|
@ -3156,7 +3156,7 @@ void FillMonotonicLineWGapFill::fill_surface_extrusion(const Surface* surface, c
|
|||
ThickPolylines polylines;
|
||||
for (ExPolygon& ex : gaps_ex_sorted) {
|
||||
//BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well.
|
||||
ex.douglas_peucker(SCALED_RESOLUTION);
|
||||
ex.douglas_peucker(SCALED_RESOLUTION * 0.1);
|
||||
ex.medial_axis(max, min, &polylines);
|
||||
}
|
||||
|
||||
|
|
|
@ -232,6 +232,7 @@ static constexpr const char* LOCK_ATTR = "locked";
|
|||
static constexpr const char* GCODE_FILE_ATTR = "gcode_file";
|
||||
static constexpr const char* THUMBNAIL_FILE_ATTR = "thumbnail_file";
|
||||
static constexpr const char* PATTERN_FILE_ATTR = "pattern_file";
|
||||
static constexpr const char* PATTERN_BBOX_FILE_ATTR = "pattern_bbox_file";
|
||||
static constexpr const char* OBJECT_ID_ATTR = "object_id";
|
||||
static constexpr const char* INSTANCEID_ATTR = "instance_id";
|
||||
static constexpr const char* PLATERID_ATTR = "plater_id";
|
||||
|
@ -451,7 +452,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
info.id = it->first;
|
||||
info.used_m = used_filament_m;
|
||||
info.used_g = used_filament_g;
|
||||
slice_flaments_info.push_back(info);
|
||||
slice_filaments_info.push_back(info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1464,9 +1465,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
plate_data_list[it->first-1]->gcode_prediction = it->second->gcode_prediction;
|
||||
plate_data_list[it->first-1]->gcode_weight = it->second->gcode_weight;
|
||||
plate_data_list[it->first-1]->toolpath_outside = it->second->toolpath_outside;
|
||||
plate_data_list[it->first-1]->slice_flaments_info = it->second->slice_flaments_info;
|
||||
plate_data_list[it->first-1]->slice_filaments_info = it->second->slice_filaments_info;
|
||||
plate_data_list[it->first-1]->thumbnail_file = (m_load_restore || it->second->thumbnail_file.empty()) ? it->second->thumbnail_file : m_backup_path + "/" + it->second->thumbnail_file;
|
||||
plate_data_list[it->first-1]->pattern_file = (m_load_restore || it->second->pattern_file.empty()) ? it->second->pattern_file : m_backup_path + "/" + it->second->pattern_file;
|
||||
plate_data_list[it->first-1]->pattern_bbox_file = (m_load_restore || it->second->pattern_bbox_file.empty()) ? it->second->pattern_bbox_file : m_backup_path + "/" + it->second->pattern_bbox_file;
|
||||
it++;
|
||||
}
|
||||
|
||||
|
@ -3039,6 +3041,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
{
|
||||
m_curr_plater->pattern_file = value;
|
||||
}
|
||||
else if (key == PATTERN_BBOX_FILE_ATTR)
|
||||
{
|
||||
m_curr_plater->pattern_bbox_file = value;
|
||||
}
|
||||
else if (key == INSTANCEID_ATTR)
|
||||
{
|
||||
m_curr_instance.instance_id = atoi(value.c_str());
|
||||
|
@ -3110,7 +3116,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
filament_info.color = color;
|
||||
filament_info.used_m = atof(used_m.c_str());
|
||||
filament_info.used_g = atof(used_g.c_str());
|
||||
m_curr_plater->slice_flaments_info.push_back(filament_info);
|
||||
m_curr_plater->slice_filaments_info.push_back(filament_info);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -5327,10 +5333,14 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << THUMBNAIL_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << thumbnail_file_in_3mf << "\"/>\n";
|
||||
}
|
||||
|
||||
if (plate_data->pattern_thumbnail.is_valid()) {
|
||||
if (!plate_data->pattern_file.empty()) {
|
||||
std::string pattern_file_in_3mf = (boost::format(PATTERN_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
|
||||
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PATTERN_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << pattern_file_in_3mf << "\"/>\n";
|
||||
}
|
||||
if (!plate_data->pattern_bbox_file.empty()) {
|
||||
std::string pattern_bbox_file_in_3mf = (boost::format(PATTERN_CONFIG_FILE_FORMAT) % (plate_data->plate_index + 1)).str();
|
||||
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PATTERN_BBOX_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << pattern_bbox_file_in_3mf << "\"/>\n";
|
||||
}
|
||||
|
||||
if (instance_size > 0)
|
||||
{
|
||||
|
@ -5430,7 +5440,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << SLICE_WEIGHT_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->get_gcode_weight_str() << "\"/>\n";
|
||||
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << OUTSIDE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha<< plate_data->toolpath_outside << "\"/>\n";
|
||||
|
||||
for (auto it = plate_data->slice_flaments_info.begin(); it != plate_data->slice_flaments_info.end(); it++)
|
||||
for (auto it = plate_data->slice_filaments_info.begin(); it != plate_data->slice_filaments_info.end(); it++)
|
||||
{
|
||||
stream << " <" << FILAMENT_TAG << " " << FILAMENT_ID_TAG << "=\"" << std::to_string(it->id + 1) << "\" "
|
||||
<< FILAMENT_TYPE_TAG << "=\"" << it->type << "\" "
|
||||
|
@ -5903,7 +5913,8 @@ private:
|
|||
break;
|
||||
case AddObject: {
|
||||
{
|
||||
_BBS_3MF_Exporter e;
|
||||
CNumericLocalesSetter locales_setter;
|
||||
_BBS_3MF_Exporter e;
|
||||
e.save_object_mesh(t.path, *t.object, (int) t.id);
|
||||
// response to delete cloned object
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
std::string _3mf_thumbnail;
|
||||
std::string _3mf_printer_thumbnail_middle;
|
||||
std::string _3mf_printer_thumbnail_small;
|
||||
|
||||
|
||||
PackingTemporaryData() {}
|
||||
};
|
||||
|
||||
|
@ -63,9 +63,10 @@ struct PlateData
|
|||
ThumbnailData plate_thumbnail;
|
||||
ThumbnailData pattern_thumbnail;
|
||||
std::string pattern_file;
|
||||
std::string pattern_bbox_file;
|
||||
std::string gcode_prediction;
|
||||
std::string gcode_weight;
|
||||
std::vector<FilamentInfo> slice_flaments_info;
|
||||
std::vector<FilamentInfo> slice_filaments_info;
|
||||
bool is_sliced_valid = false;
|
||||
bool toolpath_outside {false};
|
||||
|
||||
|
@ -114,7 +115,7 @@ enum class LoadStrategy
|
|||
{
|
||||
Default = 0,
|
||||
AddDefaultInstances = 1,
|
||||
CheckVersion = 2,
|
||||
CheckVersion = 2,
|
||||
LoadModel = 4,
|
||||
LoadConfig = 8,
|
||||
LoadAuxiliary = 16,
|
||||
|
@ -223,7 +224,7 @@ extern bool store_bbs_3mf(StoreParams& store_params);
|
|||
|
||||
extern void release_PlateData_list(PlateDataPtrs& plate_data_list);
|
||||
|
||||
// backup & restore project
|
||||
// backup & restore project
|
||||
|
||||
extern void save_object_mesh(ModelObject& object);
|
||||
|
||||
|
|
|
@ -2542,6 +2542,24 @@ GCode::LayerResult GCode::process_layer(
|
|||
// Shall the support interface be printed with the active extruder, preferably with non-soluble, to avoid tool changes?
|
||||
bool interface_dontcare = object.config().support_interface_filament.value == 0;
|
||||
|
||||
// BBS: apply wiping overridden extruders
|
||||
WipingExtrusions& wiping_extrusions = const_cast<LayerTools&>(layer_tools).wiping_extrusions();
|
||||
if (support_dontcare) {
|
||||
int extruder_override = wiping_extrusions.get_support_extruder_overrides(&object);
|
||||
if (extruder_override >= 0) {
|
||||
support_extruder = extruder_override;
|
||||
support_dontcare = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (interface_dontcare) {
|
||||
int extruder_override = wiping_extrusions.get_support_interface_extruder_overrides(&object);
|
||||
if (extruder_override >= 0) {
|
||||
interface_extruder = extruder_override;
|
||||
interface_dontcare = false;
|
||||
}
|
||||
}
|
||||
|
||||
// BBS: try to print support base with a filament other than interface filament
|
||||
if (support_dontcare && !interface_dontcare) {
|
||||
unsigned int dontcare_extruder = first_extruder_id;
|
||||
|
@ -2609,7 +2627,16 @@ GCode::LayerResult GCode::process_layer(
|
|||
// Shall the support interface be printed with the active extruder, preferably with non-soluble, to avoid tool changes?
|
||||
bool interface_dontcare = object.config().support_interface_filament.value == 0;
|
||||
|
||||
// BBS: apply wiping overridden extruders
|
||||
WipingExtrusions& wiping_extrusions = const_cast<LayerTools&>(layer_tools).wiping_extrusions();
|
||||
if (support_dontcare) {
|
||||
int extruder_override = wiping_extrusions.get_support_extruder_overrides(&object);
|
||||
if (extruder_override >= 0) {
|
||||
support_extruder = extruder_override;
|
||||
support_dontcare = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (support_dontcare || interface_dontcare) {
|
||||
// Some support will be printed with "don't care" material, preferably non-soluble.
|
||||
// Is the current extruder assigned a soluble filament?
|
||||
|
@ -2897,8 +2924,12 @@ GCode::LayerResult GCode::process_layer(
|
|||
|
||||
// BBS
|
||||
WipingExtrusions& wiping_extrusions = const_cast<LayerTools&>(layer_tools).wiping_extrusions();
|
||||
bool support_overridden = wiping_extrusions.is_support_overridden(layer.object());
|
||||
bool support_intf_overridden = wiping_extrusions.is_support_interface_overridden(layer.object());
|
||||
|
||||
ExtrusionRole support_extrusion_role = instance_to_print.object_by_extruder.support_extrusion_role;
|
||||
if (print_wipe_extrusions == 0)
|
||||
bool is_overridden = support_extrusion_role == erSupportMaterialInterface ? support_intf_overridden : support_overridden;
|
||||
if (is_overridden == (print_wipe_extrusions != 0))
|
||||
support_eec.entities = filter_by_extrusion_role(instance_to_print.object_by_extruder.support->entities, instance_to_print.object_by_extruder.support_extrusion_role);
|
||||
|
||||
for (auto& ptr : support_eec.entities)
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
#include "SpeedGenerator.hpp"
|
||||
#include "libslic3r/ExtrusionEntity.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
#define BOOST_SPIRIT_THREADSAFE
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/date_time.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
SpeedGenerator::SpeedGenerator() {
|
||||
// default is 100 speed
|
||||
for (int i = 0; i < 6; i++) {
|
||||
speed_table[i] = 100;
|
||||
}
|
||||
std::string config_file = resources_dir() + "/PerimeterSpeedConfig.json";
|
||||
std::string encoded_path = encode_path(config_file.c_str());
|
||||
boost::property_tree::read_json<boost::property_tree::ptree>(encoded_path, root);
|
||||
if (root.count("speed_table")) {
|
||||
int i = 0;
|
||||
auto array6 = root.get_child("speed_table");
|
||||
for (auto pos = array6.begin(); pos != array6.end() && i < 6; pos++, i++)
|
||||
speed_table[i] = pos->second.get_value<int>();
|
||||
}
|
||||
}
|
||||
|
||||
double SpeedGenerator::calculate_speed(const ExtrusionPath& path, double max_speed, double min_speed) {
|
||||
// limit the speed in case of F0 generated in gcode when user set 0 speed in UI
|
||||
// which cause printer stopped. 1mm/s is slow enough and can make printer not really stopped.
|
||||
max_speed = max_speed < 1 ? 1 : max_speed;
|
||||
min_speed = min_speed < 1 ? 1 : min_speed;
|
||||
// switch min and max speed if user set the max speed to be slower than min_speed
|
||||
if (max_speed < min_speed) {
|
||||
double temp = max_speed;
|
||||
max_speed = min_speed;
|
||||
min_speed = temp;
|
||||
}
|
||||
speed_table[0] = max_speed;
|
||||
|
||||
int overhang_degree = path.get_overhang_degree();
|
||||
assert(overhang_degree >= 0 && overhang_degree <= 6);
|
||||
double speed = (double)speed_table[overhang_degree];
|
||||
speed = std::max(speed, min_speed);
|
||||
speed = std::min(speed, max_speed);
|
||||
return speed;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
#ifndef libslic3r_SpeedGenerator_hpp_
|
||||
#define libslic3r_SpeedGenerator_hpp_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class ExtrusionPath;
|
||||
|
||||
class SpeedGenerator {
|
||||
public:
|
||||
SpeedGenerator();
|
||||
double calculate_speed(const ExtrusionPath& path, double max_speed, double min_speed);
|
||||
|
||||
private:
|
||||
boost::property_tree::ptree root;
|
||||
int speed_table[6];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // libslic3r_SpeedGenerator_hpp_
|
|
@ -304,6 +304,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
|
|||
layer_tools.extruders.push_back(extruder_interface);
|
||||
if (has_support || has_interface) {
|
||||
layer_tools.has_support = true;
|
||||
layer_tools.wiping_extrusions().is_support_overriddable_and_mark(role, object);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -321,6 +322,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
|
|||
layer_tools.extruders.push_back(extruder_interface);
|
||||
if (has_support || has_interface) {
|
||||
layer_tools.has_support = true;
|
||||
layer_tools.wiping_extrusions().is_support_overriddable_and_mark(role, object);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -797,6 +799,19 @@ void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, size
|
|||
copies_vector[copy_id] = extruder;
|
||||
}
|
||||
|
||||
// BBS
|
||||
void WipingExtrusions::set_support_extruder_override(const PrintObject* object, size_t copy_id, int extruder, size_t num_of_copies)
|
||||
{
|
||||
something_overridden = true;
|
||||
support_map.emplace(object, extruder);
|
||||
}
|
||||
|
||||
void WipingExtrusions::set_support_interface_extruder_override(const PrintObject* object, size_t copy_id, int extruder, size_t num_of_copies)
|
||||
{
|
||||
something_overridden = true;
|
||||
support_intf_map.emplace(object, extruder);
|
||||
}
|
||||
|
||||
// Finds first non-soluble extruder on the layer
|
||||
int WipingExtrusions::first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const
|
||||
{
|
||||
|
@ -834,6 +849,25 @@ bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, con
|
|||
return true;
|
||||
}
|
||||
|
||||
// BBS
|
||||
bool WipingExtrusions::is_support_overriddable(const ExtrusionRole role, const PrintObject& object) const
|
||||
{
|
||||
if (!object.config().flush_into_support)
|
||||
return false;
|
||||
|
||||
if (role == erMixed) {
|
||||
return object.config().support_filament == 0 || object.config().support_interface_filament == 0;
|
||||
}
|
||||
else if (role == erSupportMaterial || role == erSupportTransition) {
|
||||
return object.config().support_filament == 0;
|
||||
}
|
||||
else if (role == erSupportMaterialInterface) {
|
||||
return object.config().support_interface_filament == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Following function iterates through all extrusions on the layer, remembers those that could be used for wiping after toolchange
|
||||
// and returns volume that is left to be wiped on the wipe tower.
|
||||
float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int old_extruder, unsigned int new_extruder, float volume_to_wipe)
|
||||
|
@ -844,6 +878,10 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
|
|||
if (! this->something_overridable || volume_to_wipe <= 0. || print.config().filament_soluble.get_at(old_extruder) || print.config().filament_soluble.get_at(new_extruder))
|
||||
return std::max(0.f, volume_to_wipe); // Soluble filament cannot be wiped in a random infill, neither the filament after it
|
||||
|
||||
// BBS
|
||||
if (print.config().filament_is_support.get_at(old_extruder))
|
||||
return std::max(0.f, volume_to_wipe); // Support filament cannot be used to print support, infill, wipe_tower, etc.
|
||||
|
||||
// we will sort objects so that dedicated for wiping are at the beginning:
|
||||
ConstPrintObjectPtrs object_list = print.objects().vector();
|
||||
std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config().flush_into_objects; });
|
||||
|
@ -876,7 +914,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
|
|||
for (const LayerRegion *layerm : this_layer->regions()) {
|
||||
const auto ®ion = layerm->region();
|
||||
|
||||
if (!object->config().flush_into_infill && !object->config().flush_into_objects)
|
||||
if (!object->config().flush_into_infill && !object->config().flush_into_objects && !object->config().flush_into_support)
|
||||
continue;
|
||||
bool wipe_into_infill_only = !object->config().flush_into_objects && object->config().flush_into_infill;
|
||||
bool is_infill_first = print.config().wall_infill_order == WallInfillOrder::InfillInnerOuter ||
|
||||
|
@ -918,6 +956,46 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BBS
|
||||
if (object->config().flush_into_support) {
|
||||
auto& object_config = object->config();
|
||||
const SupportLayer* this_support_layer = object->get_support_layer_at_printz(lt.print_z, EPSILON);
|
||||
const TreeSupportLayer* this_tree_support_layer = object->get_tree_support_layer_at_printz(lt.print_z, EPSILON);
|
||||
|
||||
do {
|
||||
if (this_support_layer == nullptr && this_tree_support_layer == nullptr)
|
||||
break;
|
||||
|
||||
bool support_overriddable = object_config.support_filament == 0;
|
||||
bool support_intf_overriddable = object_config.support_interface_filament == 0;
|
||||
if (!support_overriddable && !support_intf_overriddable)
|
||||
break;
|
||||
|
||||
auto& entities = this_support_layer != nullptr ? this_support_layer->support_fills.entities : this_tree_support_layer->support_fills.entities;
|
||||
if (support_overriddable && !is_support_overridden(object)) {
|
||||
set_support_extruder_override(object, copy, new_extruder, num_of_copies);
|
||||
for (const ExtrusionEntity* ee : entities) {
|
||||
if (ee->role() == erSupportMaterial || ee->role() == erSupportTransition)
|
||||
volume_to_wipe -= ee->total_volume();
|
||||
|
||||
if (volume_to_wipe <= 0.f)
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
|
||||
if (support_intf_overriddable && !is_support_interface_overridden(object)) {
|
||||
set_support_interface_extruder_override(object, copy, new_extruder, num_of_copies);
|
||||
for (const ExtrusionEntity* ee : entities) {
|
||||
if (ee->role() == erSupportMaterialInterface)
|
||||
volume_to_wipe -= ee->total_volume();
|
||||
|
||||
if (volume_to_wipe <= 0.f)
|
||||
return 0.f;
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Some purge remains to be done on the Wipe Tower.
|
||||
|
@ -1010,4 +1088,24 @@ const WipingExtrusions::ExtruderPerCopy* WipingExtrusions::get_extruder_override
|
|||
return overrides;
|
||||
}
|
||||
|
||||
// BBS
|
||||
int WipingExtrusions::get_support_extruder_overrides(const PrintObject* object)
|
||||
{
|
||||
auto iter = support_map.find(object);
|
||||
if (iter != support_map.end())
|
||||
return iter->second;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int WipingExtrusions::get_support_interface_extruder_overrides(const PrintObject* object)
|
||||
{
|
||||
auto iter = support_intf_map.find(object);
|
||||
if (iter != support_intf_map.end())
|
||||
return iter->second;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -32,6 +32,8 @@ public:
|
|||
|
||||
// This is called from GCode::process_layer - see implementation for further comments:
|
||||
const ExtruderPerCopy* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies);
|
||||
int get_support_extruder_overrides(const PrintObject* object);
|
||||
int get_support_interface_extruder_overrides(const PrintObject* object);
|
||||
|
||||
// This function goes through all infill entities, decides which ones will be used for wiping and
|
||||
// marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower:
|
||||
|
@ -46,6 +48,22 @@ public:
|
|||
return out;
|
||||
}
|
||||
|
||||
// BBS
|
||||
bool is_support_overriddable(const ExtrusionRole role, const PrintObject& object) const;
|
||||
bool is_support_overriddable_and_mark(const ExtrusionRole role, const PrintObject& object) {
|
||||
bool out = this->is_support_overriddable(role, object);
|
||||
this->something_overridable |= out;
|
||||
return out;
|
||||
}
|
||||
|
||||
bool is_support_overridden(const PrintObject* object) const {
|
||||
return support_map.find(object) != support_map.end();
|
||||
}
|
||||
|
||||
bool is_support_interface_overridden(const PrintObject* object) const {
|
||||
return support_intf_map.find(object) != support_intf_map.end();
|
||||
}
|
||||
|
||||
void set_layer_tools_ptr(const LayerTools* lt) { m_layer_tools = lt; }
|
||||
|
||||
private:
|
||||
|
@ -54,6 +72,9 @@ private:
|
|||
|
||||
// This function is called from mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
|
||||
void set_extruder_override(const ExtrusionEntity* entity, size_t copy_id, int extruder, size_t num_of_copies);
|
||||
// BBS
|
||||
void set_support_extruder_override(const PrintObject* object, size_t copy_id, int extruder, size_t num_of_copies);
|
||||
void set_support_interface_extruder_override(const PrintObject* object, size_t copy_id, int extruder, size_t num_of_copies);
|
||||
|
||||
// Returns true in case that entity is not printed with its usual extruder for a given copy:
|
||||
bool is_entity_overridden(const ExtrusionEntity* entity, size_t copy_id) const {
|
||||
|
@ -62,6 +83,9 @@ private:
|
|||
}
|
||||
|
||||
std::map<const ExtrusionEntity*, ExtruderPerCopy> entity_map; // to keep track of who prints what
|
||||
// BBS
|
||||
std::map<const PrintObject*, int> support_map;
|
||||
std::map<const PrintObject*, int> support_intf_map;
|
||||
bool something_overridable = false;
|
||||
bool something_overridden = false;
|
||||
const LayerTools* m_layer_tools = nullptr; // so we know which LayerTools object this belongs to
|
||||
|
|
|
@ -251,6 +251,12 @@ public:
|
|||
};
|
||||
std::vector<std::pair<ExPolygon *, int>> area_groups;
|
||||
|
||||
enum OverhangType {
|
||||
Detected=0,
|
||||
Enforced
|
||||
};
|
||||
std::map<const ExPolygon *, OverhangType> overhang_types;
|
||||
|
||||
virtual bool has_extrusions() const { return !support_fills.empty(); }
|
||||
|
||||
void simplify_support_extrusion_path() { this->simplify_support_entity_collection(&support_fills);}
|
||||
|
|
|
@ -70,6 +70,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
|
|||
|
||||
const PrintConfig &print_config = this->layer()->object()->print()->config();
|
||||
const PrintRegionConfig ®ion_config = this->region().config();
|
||||
const PrintObjectConfig& object_config = this->layer()->object()->config();
|
||||
// This needs to be in sync with PrintObject::_slice() slicing_mode_normal_below_layer!
|
||||
bool spiral_mode = print_config.spiral_mode &&
|
||||
//FIXME account for raft layers.
|
||||
|
@ -100,7 +101,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
|
|||
|
||||
g.layer_id = (int)this->layer()->id();
|
||||
g.ext_perimeter_flow = this->flow(frExternalPerimeter);
|
||||
g.overhang_flow = this->bridging_flow(frPerimeter, g_config_thick_bridges);
|
||||
g.overhang_flow = this->bridging_flow(frPerimeter, object_config.thick_bridges);
|
||||
g.solid_infill_flow = this->flow(frSolidInfill);
|
||||
|
||||
g.process();
|
||||
|
@ -118,6 +119,9 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
|||
const bool has_infill = this->region().config().sparse_infill_density.value > 0.;
|
||||
const float margin = float(scale_(EXTERNAL_INFILL_MARGIN));
|
||||
|
||||
// BBS
|
||||
const PrintObjectConfig& object_config = this->layer()->object()->config();
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-initial");
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
|
@ -285,7 +289,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
|||
// would get merged into a single one while they need different directions
|
||||
// also, supply the original expolygon instead of the grown one, because in case
|
||||
// of very thin (but still working) anchors, the grown expolygon would go beyond them
|
||||
BridgeDetector bd(initial, lower_layer->lslices, this->bridging_flow(frInfill, g_config_thick_bridges).scaled_width());
|
||||
BridgeDetector bd(initial, lower_layer->lslices, this->bridging_flow(frInfill, object_config.thick_bridges).scaled_width());
|
||||
#ifdef SLIC3R_DEBUG
|
||||
printf("Processing bridge at layer %zu:\n", this->layer()->id());
|
||||
#endif
|
||||
|
|
10
src/libslic3r/MacUtils.hpp
Normal file
10
src/libslic3r/MacUtils.hpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
#ifndef __MAC_UTILS_H
|
||||
#define __MAC_UTILS_H
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
bool is_macos_support_boost_add_file_log();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
15
src/libslic3r/MacUtils.mm
Normal file
15
src/libslic3r/MacUtils.mm
Normal file
|
@ -0,0 +1,15 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import "MacUtils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
bool is_macos_support_boost_add_file_log()
|
||||
{
|
||||
if (@available(macOS 12.0, *)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
|
@ -2754,6 +2754,41 @@ double Model::findMaxSpeed(const ModelObject* object) {
|
|||
if (objMaxSpeed <= 0) objMaxSpeed = 250.;
|
||||
return objMaxSpeed;
|
||||
}
|
||||
|
||||
// BBS: thermal length is calculated according to the material of a volume
|
||||
double Model::getThermalLength(const ModelVolume* modelVolumePtr) {
|
||||
double thermalLength = 200.;
|
||||
auto aa = modelVolumePtr->extruder_id();
|
||||
if (Model::extruderParamsMap.find(aa) != Model::extruderParamsMap.end()) {
|
||||
if (Model::extruderParamsMap.at(aa).materialName == "ABS" ||
|
||||
Model::extruderParamsMap.at(aa).materialName == "PA-CF" ||
|
||||
Model::extruderParamsMap.at(aa).materialName == "PET-CF") {
|
||||
thermalLength = 100;
|
||||
}
|
||||
if (Model::extruderParamsMap.at(aa).materialName == "PC") {
|
||||
thermalLength = 40;
|
||||
}
|
||||
if (Model::extruderParamsMap.at(aa).materialName == "TPU") {
|
||||
thermalLength = 1000;
|
||||
}
|
||||
|
||||
}
|
||||
return thermalLength;
|
||||
}
|
||||
|
||||
// BBS: thermal length calculation for a group of volumes
|
||||
double Model::getThermalLength(const std::vector<ModelVolume*> modelVolumePtrs)
|
||||
{
|
||||
double thermalLength = 1250.;
|
||||
|
||||
for (const auto& modelVolumePtr : modelVolumePtrs) {
|
||||
if (modelVolumePtr != nullptr) {
|
||||
// the thermal length of a group is decided by the volume with shortest thermal length
|
||||
thermalLength = std::min(thermalLength, getThermalLength(modelVolumePtr));
|
||||
}
|
||||
}
|
||||
return thermalLength;
|
||||
}
|
||||
// max printing speed, difference in bed temperature and envirument temperature and bed adhension coefficients are considered
|
||||
double ModelInstance::get_auto_brim_width(double deltaT, double adhension) const
|
||||
{
|
||||
|
@ -2763,9 +2798,10 @@ double ModelInstance::get_auto_brim_width(double deltaT, double adhension) const
|
|||
auto bbox_size = transform_bounding_box(raw_bbox).size();
|
||||
double height_to_area = std::max(bbox_size(2) / (bbox_size(0) * bbox_size(0) * bbox_size(1)),
|
||||
bbox_size(2) / (bbox_size(1) * bbox_size(1) * bbox_size(0)));
|
||||
double thermalLength = std::max(bbox_size(0), bbox_size(1));
|
||||
double thermalLength = sqrt(bbox_size(0)* bbox_size(0) + bbox_size(1)* bbox_size(1));
|
||||
double thermalLengthRef = Model::getThermalLength(object->volumes);
|
||||
|
||||
double brim_width = adhension * std::min(std::min(std::max(height_to_area * 200 * maxSpeed/200, (deltaT-30)/75 * thermalLength * 0.15), 20.), 1.5 * thermalLength);
|
||||
double brim_width = adhension * std::min(std::min(std::max(height_to_area * 200 * maxSpeed/200, thermalLength * 8. / thermalLengthRef * std::min(bbox_size(2), 30.) / 30.), 20.), 1.5 * thermalLength);
|
||||
// small brims are omitted
|
||||
if (brim_width < 5 && brim_width < 1.5 * thermalLength)
|
||||
brim_width = 0;
|
||||
|
|
|
@ -1274,6 +1274,9 @@ public:
|
|||
ImportStepProgressFn stepFn = nullptr, StepIsUtf8Fn stepIsUtf8Fn = nullptr, BBLProject* project = nullptr);
|
||||
// BBS
|
||||
static double findMaxSpeed(const ModelObject* object);
|
||||
static double getThermalLength(const ModelVolume* modelVolumePtr);
|
||||
static double getThermalLength(const std::vector<ModelVolume*> modelVolumePtrs);
|
||||
|
||||
// BBS: backup
|
||||
static Model read_from_archive(
|
||||
const std::string& input_file,
|
||||
|
|
|
@ -553,58 +553,33 @@ void PerimeterGenerator::process()
|
|||
offset_top_surface = 0;
|
||||
//don't takes into account too thin areas
|
||||
double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2 + 10), 1.0 * (double(perimeter_width)));
|
||||
//make thin upper surfaces disapear with -+offset_top_surface
|
||||
ExPolygons grown_upper_slices;
|
||||
//do offset2 per island, to avoid big blob merging
|
||||
//remove polygon too thin (but don't mess with holes)
|
||||
for (const ExPolygon& expoly_to_grow : *this->upper_slices) {
|
||||
//only offset the contour, as it can merge holes
|
||||
Polygons contour = offset2({ ExPolygon(expoly_to_grow.contour) }, -offset_top_surface, offset_top_surface + min_width_top_surface);
|
||||
if (!contour.empty()) {
|
||||
if (expoly_to_grow.holes.empty()) {
|
||||
for (Polygon& p : contour)
|
||||
grown_upper_slices.push_back(ExPolygon{ p });
|
||||
}
|
||||
else {
|
||||
Polygons holes = expoly_to_grow.holes;
|
||||
for (Polygon& h : holes)
|
||||
h.reverse();
|
||||
holes = offset(holes, -min_width_top_surface);
|
||||
for (ExPolygon p : diff_ex(contour, holes))
|
||||
grown_upper_slices.push_back(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
grown_upper_slices = union_ex(grown_upper_slices);
|
||||
ExPolygons grown_upper_slices = offset_ex(*this->upper_slices, min_width_top_surface);
|
||||
//set the clip to a virtual "second perimeter"
|
||||
fill_clip = offset_ex(last, -double(ext_perimeter_spacing));
|
||||
auto fill_clip_old = fill_clip;
|
||||
// get the real top surface
|
||||
const ExPolygons top_grown_polygons = diff_ex(last, grown_upper_slices, ApplySafetyOffset::Yes);
|
||||
ExPolygons top_polygons = diff_ex(last, grown_upper_slices, ApplySafetyOffset::Yes);
|
||||
|
||||
//get the not-top surface, from the "real top" but enlarged by external_infill_margin (and the min_width_top_surface we removed a bit before)
|
||||
//also remove the ext_perimeter_spacing/2 width because we are faking the external periemter, and we will remove ext_perimeter_spacing2
|
||||
const ExPolygons inner_polygons = diff_ex(last,
|
||||
offset_ex(top_grown_polygons, offset_top_surface + min_width_top_surface - double(ext_perimeter_spacing / 2)),
|
||||
ApplySafetyOffset::Yes);
|
||||
ExPolygons inner_polygons = diff_ex(last,
|
||||
offset_ex(top_polygons, offset_top_surface + min_width_top_surface - double(ext_perimeter_spacing / 2)),
|
||||
ApplySafetyOffset::Yes);
|
||||
// get the enlarged top surface, by using inner_polygons instead of upper_slices, and clip it for it to be exactly the polygons to fill.
|
||||
const ExPolygons top_polygons = diff_ex(fill_clip, inner_polygons, ApplySafetyOffset::Yes);
|
||||
top_polygons = diff_ex(fill_clip, inner_polygons, ApplySafetyOffset::Yes);
|
||||
// increase by half peri the inner space to fill the frontier between last and stored.
|
||||
top_fills = union_ex(top_fills, top_polygons);
|
||||
//set the clip to the external wall but go back inside by infill_extrusion_width/2 to be sure the extrusion won't go outside even with a 100% overlap.
|
||||
double infill_spacing_unscaled = this->config->sparse_infill_line_width.value;
|
||||
//if (infill_spacing_unscaled == 0) infill_spacing_unscaled = Flow::auto_extrusion_width(frInfill, nozzle_diameter);
|
||||
fill_clip = offset_ex(last, double(ext_perimeter_spacing / 2) - scale_(infill_spacing_unscaled / 2));
|
||||
last = intersection_ex(inner_polygons, last);
|
||||
//{
|
||||
// std::stringstream stri;
|
||||
// stri << this->layer->id() << "_1_"<< i <<"_only_one_peri"<< ".svg";
|
||||
// SVG svg(stri.str());
|
||||
// svg.draw(to_polylines(oldLast), "orange");
|
||||
// svg.draw(to_polylines(fill_clip), "purple");
|
||||
// svg.draw(to_polylines(top_fills), "green");
|
||||
// svg.draw(to_polylines(inner_polygons), "yellow");
|
||||
// svg.draw(to_polylines(top_polygons), "cyan");
|
||||
// svg.draw(to_polylines(oldLast), "orange");
|
||||
// svg.draw(to_polylines(last), "red");
|
||||
// svg.draw(to_polylines(fill_clip_old), "green");
|
||||
// svg.Close();
|
||||
//}
|
||||
}
|
||||
|
@ -700,9 +675,8 @@ void PerimeterGenerator::process()
|
|||
offset2_ex(gaps, - float(max / 2.), float(max / 2. + ClipperSafetyOffset)));
|
||||
ThickPolylines polylines;
|
||||
for (ExPolygon& ex : gaps_ex) {
|
||||
//BBS: medial axis algorithm can't handle duplicated points in expolygon.
|
||||
//Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well.
|
||||
ex.douglas_peucker(SCALED_RESOLUTION);
|
||||
//BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well.
|
||||
ex.douglas_peucker(surface_simplify_resolution);
|
||||
ex.medial_axis(max, min, &polylines);
|
||||
}
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ public:
|
|||
overhang_flow(flow), solid_infill_flow(flow),
|
||||
config(config), object_config(object_config), print_config(print_config),
|
||||
m_spiral_vase(spiral_mode),
|
||||
m_scaled_resolution(scaled<double>(print_config->resolution.value)),
|
||||
m_scaled_resolution(scaled<double>(print_config->resolution.value > EPSILON ? print_config->resolution.value : EPSILON)),
|
||||
loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces),
|
||||
m_ext_mm3_per_mm(-1), m_mm3_per_mm(-1), m_mm3_per_mm_overhang(-1), m_ext_mm3_per_mm_smaller_width(-1)
|
||||
{}
|
||||
|
|
|
@ -311,6 +311,54 @@ bool Polyline::is_straight() const
|
|||
return true;
|
||||
}
|
||||
|
||||
void Polyline::append(const Polyline &src)
|
||||
{
|
||||
if (!src.is_valid()) return;
|
||||
|
||||
if (this->points.empty()) {
|
||||
this->points = src.points;
|
||||
this->fitting_result = src.fitting_result;
|
||||
} else {
|
||||
//BBS: append the first point to create connection first, update the fitting date as well
|
||||
this->append(src.points[0]);
|
||||
//BBS: append a polyline which has fitting data to a polyline without fitting data.
|
||||
//Then create a fake fitting data first, so that we can keep the fitting data in last polyline
|
||||
if (this->fitting_result.empty() &&
|
||||
!src.fitting_result.empty()) {
|
||||
this->fitting_result.emplace_back(PathFittingData{ 0, this->points.size() - 1, EMovePathType::Linear_move, ArcSegment() });
|
||||
}
|
||||
//BBS: then append the remain points
|
||||
MultiPoint::append(src.points.begin() + 1, src.points.end());
|
||||
//BBS: finally append the fitting data
|
||||
append_fitting_result_after_append_polyline(src);
|
||||
}
|
||||
}
|
||||
|
||||
void Polyline::append(Polyline &&src)
|
||||
{
|
||||
if (!src.is_valid()) return;
|
||||
|
||||
if (this->points.empty()) {
|
||||
this->points = std::move(src.points);
|
||||
this->fitting_result = std::move(src.fitting_result);
|
||||
} else {
|
||||
//BBS: append the first point to create connection first, update the fitting date as well
|
||||
this->append(src.points[0]);
|
||||
//BBS: append a polyline which has fitting data to a polyline without fitting data.
|
||||
//Then create a fake fitting data first, so that we can keep the fitting data in last polyline
|
||||
if (this->fitting_result.empty() &&
|
||||
!src.fitting_result.empty()) {
|
||||
this->fitting_result.emplace_back(PathFittingData{ 0, this->points.size() - 1, EMovePathType::Linear_move, ArcSegment() });
|
||||
}
|
||||
//BBS: then append the remain points
|
||||
MultiPoint::append(src.points.begin() + 1, src.points.end());
|
||||
//BBS: finally append the fitting data
|
||||
append_fitting_result_after_append_polyline(src);
|
||||
src.points.clear();
|
||||
src.fitting_result.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void Polyline::append_fitting_result_after_append_points() {
|
||||
if (!fitting_result.empty()) {
|
||||
if (fitting_result.back().is_linear_move()) {
|
||||
|
@ -326,14 +374,6 @@ void Polyline::append_fitting_result_after_append_points() {
|
|||
|
||||
void Polyline::append_fitting_result_after_append_polyline(const Polyline& src)
|
||||
{
|
||||
//BBS: append a polyline which has fitting data to a polyline without fitting data.
|
||||
//Then create a fake fitting data first, so that we can keep the fitting data in last polyline
|
||||
if (this->fitting_result.empty() &&
|
||||
!src.fitting_result.empty()) {
|
||||
if (!this->points.empty())
|
||||
this->fitting_result.emplace_back(PathFittingData{ 0, this->size() - 1, EMovePathType::Linear_move, ArcSegment() });
|
||||
}
|
||||
|
||||
if (!this->fitting_result.empty()) {
|
||||
//BBS: offset and save the fitting_result from src polyline
|
||||
if (!src.fitting_result.empty()) {
|
||||
|
@ -346,7 +386,7 @@ void Polyline::append_fitting_result_after_append_polyline(const Polyline& src)
|
|||
}
|
||||
} else {
|
||||
//BBS: the append polyline has no fitting data, then append as linear move directly
|
||||
size_t new_start = fitting_result.back().end_point_index;
|
||||
size_t new_start = this->fitting_result.back().end_point_index;
|
||||
size_t new_end = this->size() - 1;
|
||||
if (new_start != new_end)
|
||||
this->fitting_result.emplace_back(PathFittingData{ new_start, new_end, EMovePathType::Linear_move, ArcSegment() });
|
||||
|
|
|
@ -99,41 +99,8 @@ public:
|
|||
MultiPoint::append(std::move(src));
|
||||
append_fitting_result_after_append_points();
|
||||
}
|
||||
void append(const Polyline &src)
|
||||
{
|
||||
if (!src.is_valid()) return;
|
||||
|
||||
if (this->points.empty()) {
|
||||
this->points = src.points;
|
||||
this->fitting_result = src.fitting_result;
|
||||
} else {
|
||||
//BBS: append the first point to create connection first, update the fitting date as well
|
||||
this->append(src.points[0]);
|
||||
//BBS: then append the remain points
|
||||
MultiPoint::append(src.points.begin() + 1, src.points.end());
|
||||
//BBS: finally append the fitting data
|
||||
append_fitting_result_after_append_polyline(src);
|
||||
}
|
||||
}
|
||||
|
||||
void append(Polyline &&src)
|
||||
{
|
||||
if (!src.is_valid()) return;
|
||||
|
||||
if (this->points.empty()) {
|
||||
this->points = std::move(src.points);
|
||||
this->fitting_result = std::move(src.fitting_result);
|
||||
} else {
|
||||
//BBS: append the first point to create connection first, update the fitting date as well
|
||||
this->append(src.points[0]);
|
||||
//BBS: then append the remain points
|
||||
MultiPoint::append(src.points.begin() + 1, src.points.end());
|
||||
//BBS: finally append the fitting data
|
||||
append_fitting_result_after_append_polyline(src);
|
||||
src.points.clear();
|
||||
src.fitting_result.clear();
|
||||
}
|
||||
}
|
||||
void append(const Polyline& src);
|
||||
void append(Polyline&& src);
|
||||
|
||||
const Point& last_point() const override { return this->points.back(); }
|
||||
const Point& leftmost_point() const;
|
||||
|
|
|
@ -666,7 +666,7 @@ static std::vector<std::string> s_Preset_print_options {
|
|||
//"independent_support_layer_height",
|
||||
"support_angle", "support_interface_top_layers", "support_interface_bottom_layers",
|
||||
"support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern",
|
||||
"support_top_z_distance", "support_on_build_plate_only", "bridge_no_support","max_bridge_length", "print_sequence",
|
||||
"support_top_z_distance", "support_on_build_plate_only", "bridge_no_support", "thick_bridges", "max_bridge_length", "print_sequence",
|
||||
"filename_format", "wall_filament",
|
||||
"sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament",
|
||||
"ooze_prevention", "standby_temperature_delta", "interface_shells", "line_width", "initial_layer_line_width",
|
||||
|
@ -675,7 +675,7 @@ static std::vector<std::string> s_Preset_print_options {
|
|||
"elefant_foot_compensation", "xy_contour_compensation", "xy_hole_compensation", "resolution", "enable_prime_tower",
|
||||
"prime_tower_width", "prime_tower_brim_width", "prime_volume",
|
||||
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits",
|
||||
"flush_into_infill", "flush_into_objects",
|
||||
"flush_into_infill", "flush_into_objects", "flush_into_support",
|
||||
// BBS
|
||||
"tree_support_branch_angle", "tree_support_with_infill", "tree_support_wall_count", "tree_support_branch_distance",
|
||||
"tree_support_branch_diameter",
|
||||
|
|
|
@ -181,6 +181,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||
|| opt_key == "flush_volumes_matrix"
|
||||
|| opt_key == "prime_volume"
|
||||
|| opt_key == "flush_into_infill"
|
||||
|| opt_key == "flush_into_support"
|
||||
|| opt_key == "initial_layer_infill_speed"
|
||||
|| opt_key == "travel_speed"
|
||||
|| opt_key == "travel_speed_z"
|
||||
|
|
|
@ -165,7 +165,6 @@ class ConstSupportLayerPtrsAdaptor : public ConstVectorOfPtrsAdaptor<SupportLaye
|
|||
|
||||
// BBS
|
||||
typedef std::vector<TreeSupportLayer*> TreeSupportLayerPtrs;
|
||||
typedef std::vector<const TreeSupportLayer*> ConstTreeSupportLayerPtrs;
|
||||
class ConstTreeSupportLayerPtrsAdaptor : public ConstVectorOfPtrsAdaptor<TreeSupportLayer> {
|
||||
friend PrintObject;
|
||||
ConstTreeSupportLayerPtrsAdaptor(const TreeSupportLayerPtrs* data) : ConstVectorOfPtrsAdaptor<TreeSupportLayer>(data) {}
|
||||
|
@ -300,7 +299,8 @@ public:
|
|||
const Layer* current_layer,
|
||||
float extrusion_width,
|
||||
PolysType* overhang_regions,
|
||||
float max_bridge_length = scale_(10));
|
||||
float max_bridge_length = scale_(10),
|
||||
bool break_bridge=false);
|
||||
|
||||
// Bounding box is used to align the object infill patterns, and to calculate attractor for the rear seam.
|
||||
// The bounding box may not be quite snug.
|
||||
|
@ -340,6 +340,10 @@ public:
|
|||
// Get a layer approximately at print_z.
|
||||
const Layer* get_layer_at_printz(coordf_t print_z, coordf_t epsilon) const;
|
||||
Layer* get_layer_at_printz(coordf_t print_z, coordf_t epsilon);
|
||||
// BBS
|
||||
const Layer* get_layer_at_bottomz(coordf_t bottom_z, coordf_t epsilon) const;
|
||||
Layer* get_layer_at_bottomz(coordf_t bottom_z, coordf_t epsilon);
|
||||
|
||||
// Get the first layer approximately bellow print_z.
|
||||
const Layer* get_first_layer_bellow_printz(coordf_t print_z, coordf_t epsilon) const;
|
||||
|
||||
|
@ -354,6 +358,7 @@ public:
|
|||
void clear_tree_support_layers();
|
||||
size_t tree_support_layer_count() const { return m_tree_support_layers.size(); }
|
||||
std::shared_ptr<TreeSupportData> alloc_tree_support_preview_cache();
|
||||
void clear_tree_support_preview_cache() { m_tree_support_preview_cache.reset(); }
|
||||
|
||||
size_t support_layer_count() const { return m_support_layers.size(); }
|
||||
void clear_support_layers();
|
||||
|
|
|
@ -686,7 +686,15 @@ void PrintConfigDef::init_fff_params()
|
|||
def->tooltip = L("Don't support the whole bridge area which make support very large. "
|
||||
"Bridge usually can be printing directly without support if not very long");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("thick_bridges", coBool);
|
||||
def->label = L("Thick bridges");
|
||||
def->category = L("Layers and Perimeters");
|
||||
def->tooltip = L("If enabled, bridges are more reliable, can bridge longer distances, but may look worse. "
|
||||
"If disabled, bridges look better but are reliable just for shorter bridged distances.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("max_bridge_length", coFloat);
|
||||
def->label = L("Max bridge length");
|
||||
|
@ -722,10 +730,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_keys_map = &ConfigOptionEnum<InfillPattern>::get_enum_values();
|
||||
def->enum_values.push_back("concentric");
|
||||
def->enum_values.push_back("zig-zag");
|
||||
#if !BBL_RELEASE_TO_PUBLIC
|
||||
def->enum_values.push_back("monotonic");
|
||||
#endif
|
||||
//BBS: use monotonicline pattern to replace monotonic for top and bottom surface
|
||||
def->enum_values.push_back("monotonicline");
|
||||
//def->enum_values.push_back("alignedrectilinear");
|
||||
//def->enum_values.push_back("hilbertcurve");
|
||||
|
@ -733,9 +738,7 @@ void PrintConfigDef::init_fff_params()
|
|||
//def->enum_values.push_back("octagramspiral");
|
||||
def->enum_labels.push_back(L("Concentric"));
|
||||
def->enum_labels.push_back(L("Zig zag"));
|
||||
#if !BBL_RELEASE_TO_PUBLIC
|
||||
def->enum_labels.push_back(L("Monotonic"));
|
||||
#endif
|
||||
def->enum_labels.push_back(L("Monotonic line"));
|
||||
//def->enum_labels.push_back(L("Aligned Rectilinear"));
|
||||
//def->enum_labels.push_back(L("Hilbert Curve"));
|
||||
|
@ -1627,8 +1630,10 @@ void PrintConfigDef::init_fff_params()
|
|||
|
||||
def = this->add("reduce_infill_retraction", coBool);
|
||||
def->label = L("Reduce infill retraction");
|
||||
def->tooltip = L("Don't retract when the travel is in infill area absolutely. That means the oozing can't been seen");
|
||||
def->mode = comDevelop;
|
||||
def->tooltip = L("Don't retract when the travel is in infill area absolutely. That means the oozing can't been seen. "
|
||||
"This can reduce times of retraction for complex model and save printing time, but make slicing and "
|
||||
"G-code generating slower");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
def = this->add("ooze_prevention", coBool);
|
||||
|
@ -2545,6 +2550,13 @@ void PrintConfigDef::init_fff_params()
|
|||
"If the walls are printed with transparent filament, the mixed color infill will be seen outside");
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("flush_into_support", coBool);
|
||||
def->category = L("Flush options");
|
||||
def->label = L("Flush into objects' support");
|
||||
def->tooltip = L("Purging after filament change will be done inside objects' support. "
|
||||
"This may lower the amount of waste and decrease the print time");
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
def = this->add("flush_into_objects", coBool);
|
||||
def->category = L("Flush options");
|
||||
def->label = L("Flush into this object");
|
||||
|
@ -3345,12 +3357,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||
//But now these key-value must be absolute value.
|
||||
//Reset to default value by erasing these key to avoid parsing error.
|
||||
opt_key = "";
|
||||
} else if (opt_key == "top_surface_pattern" || opt_key == "bottom_surface_pattern") {
|
||||
#if BBL_RELEASE_TO_PUBLIC
|
||||
//BBS: replace monotonic pattern to be monotonicline for top and bottom surface
|
||||
if (value == "monotonic")
|
||||
value = "monotonicline";
|
||||
#endif
|
||||
} else if (opt_key == "filament_type" && value == "PA-CF") {
|
||||
value == "PA";
|
||||
} else if (opt_key == "inherits_cummulative") {
|
||||
|
@ -3372,7 +3378,7 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||
, "max_volumetric_extrusion_rate_slope_positive", "max_volumetric_extrusion_rate_slope_negative"
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
// BBS
|
||||
, "thick_bridges","support_sharp_tails","remove_small_overhangs", "support_with_sheath",
|
||||
, "support_sharp_tails","remove_small_overhangs", "support_with_sheath",
|
||||
"tree_support_branch_diameter_angle", "tree_support_collision_resolution",
|
||||
"small_perimeter_speed", "max_volumetric_speed", "max_print_speed",
|
||||
"support_bottom_z_distance", "support_closing_radius", "slicing_mode", "slice_closing_radius",
|
||||
|
|
|
@ -596,6 +596,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
((ConfigOptionEnum<SupportMaterialStyle>, support_style))
|
||||
// BBS
|
||||
((ConfigOptionBool, independent_support_layer_height))
|
||||
((ConfigOptionBool, thick_bridges))
|
||||
// Overhang angle threshold.
|
||||
((ConfigOptionInt, support_threshold_angle))
|
||||
((ConfigOptionFloat, support_object_xy_distance))
|
||||
|
@ -604,6 +605,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
((ConfigOptionBool, flush_into_objects))
|
||||
// BBS
|
||||
((ConfigOptionBool, flush_into_infill))
|
||||
((ConfigOptionBool, flush_into_support))
|
||||
// BBS
|
||||
((ConfigOptionFloat, tree_support_branch_distance))
|
||||
((ConfigOptionFloat, tree_support_branch_diameter))
|
||||
|
|
|
@ -566,7 +566,7 @@ void PrintObject::clear_tree_support_layers()
|
|||
|
||||
std::shared_ptr<TreeSupportData> PrintObject::alloc_tree_support_preview_cache()
|
||||
{
|
||||
if (m_tree_support_preview_cache == nullptr) {
|
||||
if (!m_tree_support_preview_cache) {
|
||||
const coordf_t layer_height = m_config.layer_height.value;
|
||||
const coordf_t xy_distance = m_config.support_object_xy_distance.value;
|
||||
const double angle = m_config.tree_support_branch_angle.value * M_PI / 180.;
|
||||
|
@ -787,7 +787,8 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||
invalidated |= m_print->invalidate_step(psGCodeExport);
|
||||
} else if (
|
||||
opt_key == "flush_into_infill"
|
||||
|| opt_key == "flush_into_objects") {
|
||||
|| opt_key == "flush_into_objects"
|
||||
|| opt_key == "flush_into_support") {
|
||||
invalidated |= m_print->invalidate_step(psWipeTower);
|
||||
invalidated |= m_print->invalidate_step(psGCodeExport);
|
||||
} else {
|
||||
|
@ -827,7 +828,7 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
|
|||
}
|
||||
|
||||
// Wipe tower depends on the ordering of extruders, which in turn depends on everything.
|
||||
// It also decides about what the flush_into_infill / wipe_into_object features will do,
|
||||
// It also decides about what the flush_into_infill / wipe_into_object / flush_into_support features will do,
|
||||
// and that too depends on many of the settings.
|
||||
invalidated |= m_print->invalidate_step(psWipeTower);
|
||||
// Invalidate G-code export in any case.
|
||||
|
@ -2297,7 +2298,8 @@ void PrintObject::remove_bridges_from_contacts(
|
|||
const Layer* current_layer,
|
||||
float extrusion_width,
|
||||
PolysType* overhang_regions,
|
||||
float max_bridge_length)
|
||||
float max_bridge_length,
|
||||
bool break_bridge)
|
||||
{
|
||||
// Extrusion width accounts for the roundings of the extrudates.
|
||||
// It is the maximum widh of the extrudate.
|
||||
|
@ -2305,6 +2307,7 @@ void PrintObject::remove_bridges_from_contacts(
|
|||
Lines overhang_perimeters = to_lines(*overhang_regions);
|
||||
auto layer_regions = current_layer->regions();
|
||||
Polygons lower_layer_polygons = to_polygons(lower_layer->lslices);
|
||||
const PrintObjectConfig& object_config = current_layer->object()->config();
|
||||
|
||||
Polygons all_bridges;
|
||||
for (LayerRegion* layerm : layer_regions)
|
||||
|
@ -2321,7 +2324,7 @@ void PrintObject::remove_bridges_from_contacts(
|
|||
// since we're dealing with bridges, we can't assume width is larger than spacing,
|
||||
// so we take the largest value and also apply safety offset to be ensure no gaps
|
||||
// are left in between
|
||||
Flow bridge_flow = layerm->bridging_flow(frPerimeter, g_config_thick_bridges);
|
||||
Flow bridge_flow = layerm->bridging_flow(frPerimeter, object_config.thick_bridges);
|
||||
float w = float(std::max(bridge_flow.scaled_width(), bridge_flow.scaled_spacing()));
|
||||
for (Polyline& polyline : overhang_perimeters)
|
||||
if (polyline.is_straight()) {
|
||||
|
@ -2338,12 +2341,16 @@ void PrintObject::remove_bridges_from_contacts(
|
|||
if (supported[0] && supported[1]) {
|
||||
Polylines lines;
|
||||
if (polyline.length() > max_bridge_length + 10) {
|
||||
// equally divide the polyline
|
||||
float len = polyline.length() / ceil(polyline.length() / max_bridge_length);
|
||||
lines = polyline.equally_spaced_lines(len);
|
||||
for (auto& line : lines) {
|
||||
line.clip_start(fw);
|
||||
line.clip_end(fw);
|
||||
if (break_bridge) {
|
||||
// equally divide the polyline
|
||||
float len = polyline.length() / ceil(polyline.length() / max_bridge_length);
|
||||
lines = polyline.equally_spaced_lines(len);
|
||||
for (auto& line : lines) {
|
||||
if (line.is_valid())
|
||||
line.clip_start(fw);
|
||||
if (line.is_valid())
|
||||
line.clip_end(fw);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -2357,8 +2364,52 @@ void PrintObject::remove_bridges_from_contacts(
|
|||
// remove the entire bridges and only support the unsupported edges
|
||||
//FIXME the brided regions are already collected as layerm->bridged. Use it?
|
||||
for (const Surface& surface : layerm->fill_surfaces.surfaces)
|
||||
if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1)
|
||||
polygons_append(bridges, surface.expolygon);
|
||||
if (surface.surface_type == stBottomBridge && surface.bridge_angle != -1) {
|
||||
auto bbox = get_extents(surface.expolygon);
|
||||
auto bbox_size = bbox.size();
|
||||
if (bbox_size[0] < max_bridge_length || bbox_size[1] < max_bridge_length)
|
||||
polygons_append(bridges, surface.expolygon);
|
||||
else {
|
||||
if (break_bridge) {
|
||||
Polygons holes;
|
||||
int x0 = bbox.min.x();
|
||||
int x1 = bbox.max.x();
|
||||
int y0 = bbox.min.y();
|
||||
int y1 = bbox.max.y();
|
||||
const int grid_lw = int(w/2); // grid line width
|
||||
|
||||
#if 1
|
||||
if (fabs(surface.bridge_angle-0)<fabs(surface.bridge_angle-M_PI_2)) {
|
||||
int step = bbox_size(0) / ceil(bbox_size(0) / max_bridge_length);
|
||||
for (int x = x0 + step; x < x1; x += step) {
|
||||
Polygon poly;
|
||||
poly.points = {Point(x - grid_lw, y0), Point(x + grid_lw, y0), Point(x + grid_lw, y1), Point(x - grid_lw, y1)};
|
||||
holes.emplace_back(poly);
|
||||
}
|
||||
} else {
|
||||
int step = bbox_size(1) / ceil(bbox_size(1) / max_bridge_length);
|
||||
for (int y = y0 + step; y < y1; y += step) {
|
||||
Polygon poly;
|
||||
poly.points = {Point(x0, y - grid_lw), Point(x0, y + grid_lw), Point(x1, y + grid_lw), Point(x1, y - grid_lw)};
|
||||
holes.emplace_back(poly);
|
||||
}
|
||||
}
|
||||
#else
|
||||
int stepx = bbox_size(0) / ceil(bbox_size(0) / max_bridge_length);
|
||||
int stepy = bbox_size(1) / ceil(bbox_size(1) / max_bridge_length);
|
||||
for (int x = x0 + stepx; x < x1; x += stepx)
|
||||
for (int y = y0 + stepy; y < y1; y += stepy) {
|
||||
Polygon poly;
|
||||
poly.points = {Point(x-grid_lw, y - grid_lw), Point(x+grid_lw, y - grid_lw), Point(x+grid_lw, y + grid_lw), Point(x-grid_lw, y + grid_lw)};
|
||||
holes.emplace_back(poly);
|
||||
}
|
||||
|
||||
#endif
|
||||
auto expoly = diff_ex(surface.expolygon, holes);
|
||||
polygons_append(bridges, expoly);
|
||||
}
|
||||
}
|
||||
}
|
||||
//FIXME add the gap filled areas. Extrude the gaps with a bridge flow?
|
||||
// Remove the unsupported ends of the bridges from the bridged areas.
|
||||
//FIXME add supports at regular intervals to support long bridges!
|
||||
|
@ -2380,13 +2431,13 @@ template void PrintObject::remove_bridges_from_contacts<ExPolygons>(
|
|||
const Layer* current_layer,
|
||||
float extrusion_width,
|
||||
ExPolygons* overhang_regions,
|
||||
float max_bridge_length);
|
||||
float max_bridge_length, bool break_bridge);
|
||||
template void PrintObject::remove_bridges_from_contacts<Polygons>(
|
||||
const Layer* lower_layer,
|
||||
const Layer* current_layer,
|
||||
float extrusion_width,
|
||||
Polygons* overhang_regions,
|
||||
float max_bridge_length);
|
||||
float max_bridge_length, bool break_bridge);
|
||||
|
||||
|
||||
bool PrintObject::is_support_necessary()
|
||||
|
@ -2721,4 +2772,20 @@ const Layer *PrintObject::get_first_layer_bellow_printz(coordf_t print_z, coordf
|
|||
return (it == m_layers.begin()) ? nullptr : *(--it);
|
||||
}
|
||||
|
||||
// BBS
|
||||
const Layer* PrintObject::get_layer_at_bottomz(coordf_t bottom_z, coordf_t epsilon) const {
|
||||
coordf_t limit_upper = bottom_z + epsilon;
|
||||
coordf_t limit_lower = bottom_z - epsilon;
|
||||
|
||||
for (const Layer* layer : m_layers) {
|
||||
if (layer->bottom_z() > limit_lower)
|
||||
return layer->bottom_z() < limit_upper ? layer : nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Layer* PrintObject::get_layer_at_bottomz(coordf_t bottom_z, coordf_t epsilon) { return const_cast<Layer*>(std::as_const(*this).get_layer_at_bottomz(bottom_z, epsilon)); }
|
||||
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -446,6 +446,9 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
|||
std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std::function<void()> &throw_if_canceled)
|
||||
{
|
||||
std::string error_msg;//BBS
|
||||
|
||||
if (layers.size() == 0) return error_msg;
|
||||
|
||||
// Collect layers with slicing errors.
|
||||
// These layers will be fixed in parallel.
|
||||
std::vector<size_t> buggy_layers;
|
||||
|
@ -740,24 +743,39 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
|
|||
bool doesVolumeIntersect(VolumeSlices& vs1, VolumeSlices& vs2)
|
||||
{
|
||||
if (vs1.volume_id == vs2.volume_id) return true;
|
||||
if (vs1.slices.size() != vs1.slices.size()) return false;
|
||||
if (vs1.slices.size() != vs2.slices.size()) return false;
|
||||
|
||||
double offsetValue = 0.4 / SCALING_FACTOR;
|
||||
for (int i = 0; i != vs1.slices.size(); ++i) {
|
||||
auto eps1 = offset_ex(vs1.slices[i], offsetValue);
|
||||
auto eps2 = offset_ex(vs2.slices[i], offsetValue);
|
||||
|
||||
if (!intersection_ex(eps1, eps2).empty()) return true;
|
||||
if (vs1.slices[i].empty()) continue;
|
||||
if (!vs2.slices[i].empty() && !intersection_ex(vs1.slices[i], vs2.slices[i]).empty()) return true;
|
||||
if (i + 1 != vs2.slices.size() && !vs2.slices[i + 1].empty()) {
|
||||
if (!intersection_ex(vs1.slices[i], vs2.slices[i + 1]).empty()) return true;
|
||||
}
|
||||
if (i - 1 >= 0 && !vs2.slices[i - 1].empty()) {
|
||||
if (!intersection_ex(vs1.slices[i], vs2.slices[i - 1]).empty()) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//BBS: grouping the volumes of an object according to their connection relationship
|
||||
bool groupingVolumes(std::vector<VolumeSlices>& objSliceByVolume, std::vector<groupedVolumeSlices>& groups)
|
||||
bool groupingVolumes(std::vector<VolumeSlices> objSliceByVolume, std::vector<groupedVolumeSlices>& groups, double resolution)
|
||||
{
|
||||
int existGroups = 0;
|
||||
std::vector<int> groupIndex(objSliceByVolume.size(), -1);
|
||||
|
||||
double offsetValue = 0.4 / SCALING_FACTOR;
|
||||
|
||||
for (int i = 0; i != objSliceByVolume.size(); ++i) {
|
||||
for (int j = 0; j != objSliceByVolume[i].slices.size(); ++j) {
|
||||
objSliceByVolume[i].slices[j] = offset_ex(objSliceByVolume[i].slices[j], offsetValue);
|
||||
for (ExPolygon& poly_ex : objSliceByVolume[i].slices[j])
|
||||
poly_ex.douglas_peucker(resolution);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i != objSliceByVolume.size(); ++i) {
|
||||
if (groupIndex[i] < 0) {
|
||||
groupIndex[i] = i;
|
||||
|
@ -765,7 +783,7 @@ bool groupingVolumes(std::vector<VolumeSlices>& objSliceByVolume, std::vector<gr
|
|||
}
|
||||
for (int j = i + 1; j != objSliceByVolume.size(); ++j) {
|
||||
if (doesVolumeIntersect(objSliceByVolume[i], objSliceByVolume[j])) {
|
||||
if (groupIndex[j] < 0) groupIndex[j] = i;
|
||||
if (groupIndex[j] < 0) groupIndex[j] = groupIndex[i];
|
||||
if (groupIndex[j] != groupIndex[i]) {
|
||||
int retain = std::min(groupIndex[i], groupIndex[j]);
|
||||
int cover = std::max(groupIndex[i], groupIndex[j]);
|
||||
|
@ -791,9 +809,6 @@ bool groupingVolumes(std::vector<VolumeSlices>& objSliceByVolume, std::vector<gr
|
|||
if (!exist) groupVector.push_back(gi);
|
||||
}
|
||||
|
||||
if (groupVector.size() != existGroups);
|
||||
|
||||
|
||||
// group volumes and their slices according to the grouping Vector
|
||||
groups.clear();
|
||||
|
||||
|
@ -808,9 +823,7 @@ bool groupingVolumes(std::vector<VolumeSlices>& objSliceByVolume, std::vector<gr
|
|||
}
|
||||
|
||||
// the slices of a group should be unioned
|
||||
double offsetValue = 0.4 / SCALING_FACTOR;
|
||||
gvs.slices = offset_ex(union_ex(offset_ex(gvs.slices, offsetValue)), -offsetValue);
|
||||
double resolution = 0.0125 / SCALING_FACTOR;
|
||||
gvs.slices = offset_ex(union_ex(gvs.slices), -offsetValue);
|
||||
for (ExPolygon& poly_ex : gvs.slices)
|
||||
poly_ex.douglas_peucker(resolution);
|
||||
|
||||
|
@ -830,6 +843,24 @@ std::vector<VolumeSlices> findPartVolumes(const std::vector<VolumeSlices>& objSl
|
|||
return outPut;
|
||||
}
|
||||
|
||||
void applyNegtiveVolumes(ModelVolumePtrs model_volumes, const std::vector<VolumeSlices>& objSliceByVolume, std::vector<groupedVolumeSlices>& groups, double resolution) {
|
||||
ExPolygons negTotal;
|
||||
for (const auto& vs : objSliceByVolume) {
|
||||
for (const auto& mv : model_volumes) {
|
||||
if (vs.volume_id == mv->id() && mv->is_negative_volume()) {
|
||||
if (vs.slices.size() > 0) {
|
||||
append(negTotal, vs.slices.front());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& g : groups) {
|
||||
g.slices = diff_ex(g.slices, negTotal);
|
||||
for (ExPolygon& poly_ex : g.slices)
|
||||
poly_ex.douglas_peucker(resolution);
|
||||
}
|
||||
}
|
||||
// 1) Decides Z positions of the layers,
|
||||
// 2) Initializes layers and their regions
|
||||
// 3) Slices the object meshes
|
||||
|
@ -873,9 +904,10 @@ void PrintObject::slice_volumes()
|
|||
}
|
||||
|
||||
//BBS: "model_part" volumes are grouded according to their connections
|
||||
const auto scaled_resolution = scaled<double>(print->config().resolution.value);
|
||||
std::vector<VolumeSlices> objSliceByVolumeParts = findPartVolumes(objSliceByVolume, this->model_object()->volumes);
|
||||
groupingVolumes(objSliceByVolumeParts, firstLayerObjSliceByGroups);
|
||||
|
||||
groupingVolumes(objSliceByVolumeParts, firstLayerObjSliceByGroups, scaled_resolution);
|
||||
applyNegtiveVolumes(this->model_object()->volumes, objSliceByVolume, firstLayerObjSliceByGroups, scaled_resolution);
|
||||
|
||||
std::vector<std::vector<ExPolygons>> region_slices = slices_to_regions(this->model_object()->volumes, *m_shared_regions, slice_zs,
|
||||
std::move(objSliceByVolume),
|
||||
|
|
|
@ -29,12 +29,13 @@ enum MachineBedType {
|
|||
|
||||
struct FilamentInfo
|
||||
{
|
||||
int id; // filament id = extruder id, start with 0.
|
||||
int id; // filament id = extruder id, start with 0.
|
||||
std::string type;
|
||||
std::string color;
|
||||
float used_m;
|
||||
float used_g;
|
||||
int tray_id;
|
||||
int tray_id; // start with 0
|
||||
float distance;
|
||||
};
|
||||
|
||||
class BBLSliceInfo {
|
||||
|
|
|
@ -19,6 +19,7 @@ public:
|
|||
float height;
|
||||
bool flipY;
|
||||
|
||||
SVG() = default;
|
||||
SVG(const char* afilename) :
|
||||
arrows(false), fill("grey"), stroke("black"), filename(afilename), flipY(false)
|
||||
{ open(filename); }
|
||||
|
|
|
@ -118,10 +118,11 @@ inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters
|
|||
sp1.first_print_layer_height == sp2.first_print_layer_height &&
|
||||
sp1.first_object_layer_height == sp2.first_object_layer_height &&
|
||||
sp1.first_object_layer_bridging == sp2.first_object_layer_bridging &&
|
||||
// BBS: following are not required for equal layer height.
|
||||
// Since the z-gap diff may be multiple of layer height.
|
||||
#if 0
|
||||
sp1.soluble_interface == sp2.soluble_interface &&
|
||||
sp1.gap_raft_object == sp2.gap_raft_object &&
|
||||
// BBS
|
||||
#if 0
|
||||
sp1.gap_object_support == sp2.gap_object_support &&
|
||||
sp1.gap_support_object == sp2.gap_support_object &&
|
||||
#endif
|
||||
|
|
|
@ -379,7 +379,7 @@ PrintObjectSupportMaterial::PrintObjectSupportMaterial(const PrintObject *object
|
|||
m_support_params.gap_xy = m_object_config->support_object_xy_distance.value;
|
||||
bridge_flow /= object->num_printing_regions();
|
||||
|
||||
m_support_params.support_material_bottom_interface_flow = m_slicing_params.soluble_interface || ! g_config_thick_bridges ?
|
||||
m_support_params.support_material_bottom_interface_flow = m_slicing_params.soluble_interface || ! m_object_config->thick_bridges ?
|
||||
m_support_params.support_material_interface_flow.with_flow_ratio(bridge_flow) :
|
||||
Flow::bridging_flow(bridge_flow * m_support_params.support_material_interface_flow.nozzle_diameter(), m_support_params.support_material_interface_flow.nozzle_diameter());
|
||||
|
||||
|
@ -1339,7 +1339,7 @@ namespace SupportMaterialInternal {
|
|||
}
|
||||
}
|
||||
|
||||
static void remove_bridges_from_contactsxx(
|
||||
static void remove_bridges_from_contacts(
|
||||
const PrintConfig &print_config,
|
||||
const Layer &lower_layer,
|
||||
const Polygons &lower_layer_polygons,
|
||||
|
@ -1374,7 +1374,9 @@ namespace SupportMaterialInternal {
|
|||
// since we're dealing with bridges, we can't assume width is larger than spacing,
|
||||
// so we take the largest value and also apply safety offset to be ensure no gaps
|
||||
// are left in between
|
||||
Flow perimeter_bridge_flow = layerm.bridging_flow(frPerimeter, g_config_thick_bridges);
|
||||
// BBS
|
||||
const PrintObjectConfig& object_config = layerm.layer()->object()->config();
|
||||
Flow perimeter_bridge_flow = layerm.bridging_flow(frPerimeter, object_config.thick_bridges);
|
||||
//FIXME one may want to use a maximum of bridging flow width and normal flow width, as the perimeters are calculated using the normal flow
|
||||
// and then turned to bridging flow, thus their centerlines are derived from non-bridging flow and expanding them by a bridging flow
|
||||
// may not expand them to the edge of their respective islands.
|
||||
|
@ -1509,7 +1511,7 @@ static inline Polygons detect_overhangs(
|
|||
M_PI * double(object_config.support_threshold_angle.value + 1) / 180. : // +1 makes the threshold inclusive
|
||||
0.;
|
||||
const coordf_t max_bridge_length = scale_(object_config.max_bridge_length.value);
|
||||
const bool bridge_no_support = max_bridge_length > 0;// config.bridge_no_support.value;
|
||||
const bool bridge_no_support = object_config.bridge_no_support.value;
|
||||
|
||||
if (layer_id == 0)
|
||||
{
|
||||
|
@ -1667,7 +1669,8 @@ static inline Polygons detect_overhangs(
|
|||
|
||||
if (bridge_no_support) {
|
||||
//FIXME Expensive, potentially not precise enough. Misses gap fill extrusions, which bridge.
|
||||
PrintObject::remove_bridges_from_contacts(&lower_layer, &layer, fw, &diff_polygons, max_bridge_length);
|
||||
SupportMaterialInternal::remove_bridges_from_contacts(
|
||||
print_config, lower_layer, lower_layer_polygons, *layerm, fw, diff_polygons);
|
||||
}
|
||||
|
||||
if (diff_polygons.empty() || offset(diff_polygons, -0.1 * fw).empty())
|
||||
|
@ -1861,7 +1864,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
|
|||
|
||||
// Contact layer will be printed with a normal flow, but
|
||||
// it will support layers printed with a bridging flow.
|
||||
if (g_config_thick_bridges && SupportMaterialInternal::has_bridging_extrusions(layer)) {
|
||||
if (object_config.thick_bridges && SupportMaterialInternal::has_bridging_extrusions(layer)) {
|
||||
coordf_t bridging_height = 0.;
|
||||
for (const LayerRegion* region : layer.regions())
|
||||
bridging_height += region->region().bridging_height_avg(print_config);
|
||||
|
@ -2429,7 +2432,7 @@ static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts(
|
|||
layer.print_z + layer_new.height + slicing_params.gap_object_support;
|
||||
layer_new.bottom_z = layer.print_z;
|
||||
layer_new.idx_object_layer_below = layer_id;
|
||||
layer_new.bridging = !slicing_params.soluble_interface && g_config_thick_bridges;
|
||||
layer_new.bridging = !slicing_params.soluble_interface && object.config().thick_bridges;
|
||||
//FIXME how much to inflate the bottom surface, as it is being extruded with a bridging flow? The following line uses a normal flow.
|
||||
layer_new.polygons = expand(touching, float(support_params.support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS);
|
||||
|
||||
|
@ -3234,7 +3237,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
|
|||
polygons_append(polygons_trimming, offset({ expoly }, trimming_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
}
|
||||
}
|
||||
if (! m_slicing_params.soluble_interface && g_config_thick_bridges) {
|
||||
if (! m_slicing_params.soluble_interface && m_object_config->thick_bridges) {
|
||||
// Collect all bottom surfaces, which will be extruded with a bridging flow.
|
||||
for (; i < object.layers().size(); ++ i) {
|
||||
const Layer &object_layer = *object.layers()[i];
|
||||
|
@ -4515,6 +4518,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
|||
angles[support_layer_id % angles.size()] :
|
||||
// Use interface angle for the interface layers.
|
||||
m_support_params.interface_angle + interface_angle_delta;
|
||||
|
||||
double density = interface_as_base ? m_support_params.support_density : m_support_params.interface_density;
|
||||
filler_interface->spacing = interface_as_base ? m_support_params.support_material_flow.spacing() : m_support_params.support_material_interface_flow.spacing();
|
||||
filler_interface->link_max_length = coord_t(scale_(filler_interface->spacing * link_max_length_factor / density));
|
||||
|
|
|
@ -9,15 +9,11 @@
|
|||
#include "SVG.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include <libnest2d/backends/libslic3r/geometries.hpp>
|
||||
|
||||
#define _L(s) Slic3r::I18N::translate(s)
|
||||
|
||||
#define SQUARE_SUPPORT 0
|
||||
#if SQUARE_SUPPORT
|
||||
#define CIRCLE_RESOLUTION 4 // 100 //The number of vertices in each circle.
|
||||
#else
|
||||
#define CIRCLE_RESOLUTION 100 //The number of vertices in each circle.
|
||||
#endif
|
||||
|
||||
#define MAX_BRANCH_RADIUS 10.0
|
||||
#define HEIGHT_TO_SWITCH_INFILL_DIRECTION 30 // change infill direction every 20mm
|
||||
#define DO_NOT_MOVER_UNDER_MM 5 // do not move contact points under 5mm
|
||||
|
@ -31,7 +27,7 @@
|
|||
#define TAU (2.0 * M_PI)
|
||||
#define NO_INDEX (std::numeric_limits<unsigned int>::max())
|
||||
|
||||
//#define SUPPORT_TREE_DEBUG_TO_SVG
|
||||
#define SUPPORT_TREE_DEBUG_TO_SVG
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
@ -225,14 +221,18 @@ static void draw_contours_and_nodes_to_svg
|
|||
bbox.max.x() = std::max(bbox.max.x(), (coord_t)scale_(10));
|
||||
bbox.max.y() = std::max(bbox.max.y(), (coord_t)scale_(10));
|
||||
|
||||
SVG svg(get_svg_filename(std::to_string(layer_nr), name_prefix), bbox);
|
||||
SVG svg;
|
||||
if(layer_nr>=0)
|
||||
svg.open(get_svg_filename(std::to_string(layer_nr), name_prefix), bbox);
|
||||
else
|
||||
svg.open(name_prefix, bbox);
|
||||
if (!svg.is_opened()) return;
|
||||
|
||||
// draw grid
|
||||
svg.draw_grid(bbox, "gray", coord_t(scale_(0.05)));
|
||||
|
||||
// draw overhang areas
|
||||
svg.draw(union_ex(overhangs), colors[0]);
|
||||
svg.draw_outline(union_ex(overhangs), colors[0]);
|
||||
svg.draw_outline(union_ex(overhangs_after_offset), colors[1]);
|
||||
svg.draw_outline(outlines_below, colors[2]);
|
||||
|
||||
|
@ -661,6 +661,8 @@ void TreeSupport::detect_object_overhangs()
|
|||
|
||||
// Create Tree Support Layers
|
||||
m_object->clear_tree_support_layers();
|
||||
m_object->clear_tree_support_preview_cache();
|
||||
|
||||
create_tree_support_layers();
|
||||
m_ts_data = m_object->alloc_tree_support_preview_cache();
|
||||
|
||||
|
@ -932,7 +934,7 @@ void TreeSupport::detect_object_overhangs()
|
|||
|
||||
|
||||
if (bridge_no_support && overhang_areas.size()>0) {
|
||||
m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &overhang_areas, max_bridge_length);
|
||||
m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &overhang_areas, max_bridge_length, true);
|
||||
}
|
||||
|
||||
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
|
||||
|
@ -1081,9 +1083,6 @@ void TreeSupport::detect_object_overhangs()
|
|||
}
|
||||
}
|
||||
|
||||
total_overhang_area = 0;
|
||||
max_overhang_area = 0;
|
||||
total_overhang_layer_cnt = 0;
|
||||
for (int layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) {
|
||||
if (m_object->print()->canceled())
|
||||
break;
|
||||
|
@ -1095,6 +1094,10 @@ void TreeSupport::detect_object_overhangs()
|
|||
ts_layer->overhang_areas = diff_ex(ts_layer->overhang_areas, offset_ex(blocker, scale_(radius_sample_resolution)));
|
||||
}
|
||||
|
||||
for (auto &area : ts_layer->overhang_areas) {
|
||||
ts_layer->overhang_types.emplace(&area, TreeSupportLayer::Detected);
|
||||
}
|
||||
|
||||
if (layer_nr < enforcers.size()) {
|
||||
Polygons& enforcer = enforcers[layer_nr];
|
||||
// coconut: enforcer can't do offset2_ex, otherwise faces with angle near 90 degrees can't have enforcers, which
|
||||
|
@ -1102,15 +1105,9 @@ void TreeSupport::detect_object_overhangs()
|
|||
//enforcer = std::move(offset2_ex(enforcer, -0.1 * extrusion_width_scaled, 0.1 * extrusion_width_scaled));
|
||||
for (const Polygon& poly : enforcer) {
|
||||
ts_layer->overhang_areas.emplace_back(poly);
|
||||
ts_layer->overhang_types.emplace(&ts_layer->overhang_areas.back(), TreeSupportLayer::Enforced);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ts_layer->overhang_areas.empty()) {
|
||||
float a = area(ts_layer->overhang_areas);
|
||||
total_overhang_area += a;
|
||||
max_overhang_area = std::max(max_overhang_area, a);
|
||||
total_overhang_layer_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
|
||||
|
@ -1825,17 +1822,25 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
{
|
||||
const PrintObjectConfig &config = m_object->config();
|
||||
bool has_brim = m_object->print()->has_brim();
|
||||
bool has_infill = config.tree_support_with_infill.value;
|
||||
int bottom_gap_layers = round(m_slicing_params.gap_object_support / m_slicing_params.layer_height);
|
||||
const coordf_t branch_radius = config.tree_support_branch_diameter.value / 2;
|
||||
const coordf_t branch_radius_scaled = scale_(branch_radius);
|
||||
Polygon branch_circle; //Pre-generate a circle with correct diameter so that we don't have to recompute those (co)sines every time.
|
||||
|
||||
// Use square support if there are too many nodes per layer because circle support needs much longer time to compute
|
||||
// Hower circle support can be printed faster, so we prefer circle for fewer nodes case.
|
||||
const bool SQUARE_SUPPORT = avg_node_per_layer > 200;
|
||||
const int CIRCLE_RESOLUTION = SQUARE_SUPPORT ? 4 : 100; // The number of vertices in each circle.
|
||||
|
||||
|
||||
for (unsigned int i = 0; i < CIRCLE_RESOLUTION; i++)
|
||||
{
|
||||
#if SQUARE_SUPPORT
|
||||
double angle = (double)i / CIRCLE_RESOLUTION * TAU + TAU/8.0;
|
||||
#else
|
||||
double angle = (double)i / CIRCLE_RESOLUTION * TAU;
|
||||
#endif
|
||||
double angle;
|
||||
if (SQUARE_SUPPORT)
|
||||
angle = (double) i / CIRCLE_RESOLUTION * TAU + PI / 4.0 + nodes_angle;
|
||||
else
|
||||
angle = (double) i / CIRCLE_RESOLUTION * TAU;
|
||||
branch_circle.append(Point(cos(angle) * branch_radius_scaled, sin(angle) * branch_radius_scaled));
|
||||
}
|
||||
|
||||
|
@ -1850,7 +1855,6 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
}
|
||||
|
||||
// generate areas
|
||||
const coordf_t circle_side_length = 2 * branch_radius * sin(M_PI / CIRCLE_RESOLUTION); //Side length of a regular polygon.
|
||||
const coordf_t layer_height = config.layer_height.value;
|
||||
const size_t top_interface_layers = config.support_interface_top_layers.value;
|
||||
const size_t bottom_interface_layers = config.support_interface_bottom_layers.value;
|
||||
|
@ -2024,16 +2028,12 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
base_areas = std::move(diff_ex(base_areas, roof_areas));
|
||||
base_areas = std::move(diff_ex(base_areas, roof_1st_layer));
|
||||
|
||||
#if SQUARE_SUPPORT
|
||||
if (m_object->print()->config().enable_arc_fitting.value == false) {
|
||||
// simplify support contours if arc fitting is disabled
|
||||
if (SQUARE_SUPPORT) {
|
||||
// simplify support contours
|
||||
ExPolygons base_areas_simplified;
|
||||
for (auto& area : base_areas) {
|
||||
area.simplify(scale_(line_width / 2), &base_areas_simplified, SimplifyMethodDP);
|
||||
}
|
||||
for (auto &area : base_areas) { area.simplify(scale_(line_width / 2), &base_areas_simplified, SimplifyMethodDP); }
|
||||
base_areas = std::move(base_areas_simplified);
|
||||
}
|
||||
#endif
|
||||
//Subtract support floors. We can only compute floor_areas here instead of with roof_areas,
|
||||
// or we'll get much wider floor than necessary.
|
||||
if (bottom_interface_layers + bottom_gap_layers > 0)
|
||||
|
@ -2073,15 +2073,13 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
}),
|
||||
expoly->holes.end());
|
||||
}
|
||||
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
|
||||
draw_contours_and_nodes_to_svg(layer_nr, base_areas, roof_areas, roof_1st_layer, {}, {}, "circles", { "base","roof","roof1st" });
|
||||
#endif
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
#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();
|
||||
|
@ -2099,18 +2097,15 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
max_dist = dist2;
|
||||
pt_far_on_poly = from;
|
||||
}
|
||||
if (dist2 < dist_thresh) {
|
||||
return true;
|
||||
}
|
||||
if (dist2 < dist_thresh) { return true; }
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
std::map<const Polygon *, int> holeDepth;
|
||||
std::map<const Polygon *, Point> holeDiretions;
|
||||
std::map<const Polygon *, Point> holeFarPoints;
|
||||
for (int layer_nr = m_object->layer_count()-1; layer_nr >0; layer_nr--) {
|
||||
// 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());
|
||||
|
||||
|
@ -2127,47 +2122,88 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
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;
|
||||
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 == 1 || area_group.second == 2) continue;
|
||||
const auto base_area = area_group.first;
|
||||
for (const auto &hole : base_area->holes) {
|
||||
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 == 1 || area_group.second == 2) continue;
|
||||
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);
|
||||
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]));
|
||||
holeDepth.insert({&base_area_lower.holes.back(), 15});
|
||||
holeDiretions.insert({&base_area_lower.holes.back(), direction});
|
||||
holeFarPoints.insert({&base_area_lower.holes.back(), pt_far_on_poly});
|
||||
holePropagationInfos.insert({&base_area_lower.holes.back(), {25, direction, pt_far_on_poly}});
|
||||
}
|
||||
break;
|
||||
} else if (holeDepth.find(&hole) != holeDepth.end() && holeDepth[&hole] > 0 && base_area_lower.contour.contains(holeFarPoints[&hole])) {
|
||||
} 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;
|
||||
hole_lower.translate(holeDiretions[&hole]);
|
||||
Point farPoint = holeFarPoints[&hole] + holeDiretions[&hole];
|
||||
{
|
||||
base_area_lower.holes.push_back(std::move(hole_lower));
|
||||
holeDepth.insert({&base_area_lower.holes.back(), holeDepth[&hole]-1});
|
||||
holeDiretions.insert({&base_area_lower.holes.back(), holeDiretions[&hole]});
|
||||
holeFarPoints.insert({&base_area_lower.holes.back(), farPoint});
|
||||
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) {
|
||||
//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); });
|
||||
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});
|
||||
if (!tmp.empty()) roof1.contour = tmp[0];
|
||||
|
||||
// make sure 1) roof1 and object 2) roof1 and roof, won't intersect
|
||||
// Note: We can't replace roof1 directly, as we have recorded its address.
|
||||
// So instead we need to replace its members one by one.
|
||||
auto tmp1 = diff_ex(roof1, m_ts_data->get_collision((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr));
|
||||
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);
|
||||
}
|
||||
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"});
|
||||
}
|
||||
#endif
|
||||
|
||||
TreeSupportLayerPtrs& ts_layers = m_object->tree_support_layers();
|
||||
|
@ -2257,6 +2293,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
|
|||
next_node->distance_to_top++;
|
||||
next_node->support_roof_layers_below--;
|
||||
next_node->print_z -= m_object->get_layer(layer_nr)->height;
|
||||
next_node->to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], next_node->position);
|
||||
contact_nodes[layer_nr - 1].emplace_back(next_node);
|
||||
}
|
||||
}
|
||||
|
@ -2290,10 +2327,6 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
|
|||
for (Node* p_node : layer_contact_nodes)
|
||||
{
|
||||
const Node& node = *p_node;
|
||||
if (node.type == ePolygon) {
|
||||
// polygon node do not merge or move
|
||||
continue;
|
||||
}
|
||||
|
||||
if (support_on_buildplate_only && !node.to_buildplate) //Can't rest on model and unable to reach the build plate. Then we must drop the node and leave parts unsupported.
|
||||
{
|
||||
|
@ -2305,6 +2338,10 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
|
|||
nodes_per_part[0][node.position] = p_node;
|
||||
continue;
|
||||
}
|
||||
if (node.type == ePolygon) {
|
||||
// polygon node do not merge or move
|
||||
continue;
|
||||
}
|
||||
/* Find which part this node is located in and group the nodes in
|
||||
* the same part together. Since nodes have a radius and the
|
||||
* avoidance areas are offset by that radius, the set of parts may
|
||||
|
@ -2720,7 +2757,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
|
|||
coordf_t z_distance_top = m_slicing_params.gap_support_object;
|
||||
// BBS: add extra distance if thick bridge is enabled
|
||||
// Note: normal support uses print_z, but tree support uses integer layers, so we need to subtract layer_height
|
||||
if (!m_slicing_params.soluble_interface && g_config_thick_bridges) {
|
||||
if (!m_slicing_params.soluble_interface && m_object_config->thick_bridges) {
|
||||
z_distance_top += m_object->layers()[0]->regions()[0]->region().bridging_height_avg(m_object->print()->config()) - layer_height;
|
||||
}
|
||||
const size_t z_distance_top_layers = round_up_divide(scale_(z_distance_top), scale_(layer_height)) + 1; //Support must always be 1 layer below overhang.
|
||||
|
@ -2729,17 +2766,20 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
|
|||
coordf_t thresh_angle = config.support_threshold_angle.value < EPSILON ? 30.f : config.support_threshold_angle.value;
|
||||
coordf_t half_overhang_distance = scale_(tan(thresh_angle * M_PI / 180.0) * layer_height / 2);
|
||||
|
||||
m_highest_overhang_layer = 0;
|
||||
// fix bug of generating support for very thin objects
|
||||
if (m_object->layers().size() <= z_distance_top_layers + 1)
|
||||
return;
|
||||
|
||||
m_highest_overhang_layer = 0;
|
||||
int nonempty_layers = 0;
|
||||
std::vector<Slic3r::Vec3f> all_nodes;
|
||||
for (size_t layer_nr = 1; layer_nr < m_object->layers().size() - z_distance_top_layers; layer_nr++)
|
||||
{
|
||||
if (m_object->print()->canceled())
|
||||
break;
|
||||
|
||||
const ExPolygons &overhang = m_object->get_tree_support_layer(layer_nr + m_raft_layers + z_distance_top_layers)->overhang_areas;
|
||||
auto ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers + z_distance_top_layers);
|
||||
const ExPolygons &overhang = ts_layer->overhang_areas;
|
||||
auto & curr_nodes = contact_nodes[layer_nr];
|
||||
if (overhang.empty())
|
||||
continue;
|
||||
|
||||
|
@ -2757,7 +2797,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
|
|||
Node *contact_node = new Node(candidate, 0, (layer_nr + z_distance_top_layers) % 2, support_roof_layers, true, Node::NO_PARENT, print_z, height);
|
||||
contact_node->type = ePolygon;
|
||||
contact_node->overhang = &overhang_part;
|
||||
contact_nodes[layer_nr].emplace_back(contact_node);
|
||||
curr_nodes.emplace_back(contact_node);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -2783,7 +2823,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
|
|||
constexpr size_t distance_to_top = 0;
|
||||
constexpr bool to_buildplate = true;
|
||||
Node* contact_node = new Node(candidate, distance_to_top, (layer_nr + z_distance_top_layers) % 2, support_roof_layers, to_buildplate, Node::NO_PARENT,print_z,height);
|
||||
contact_nodes[layer_nr].emplace_back(contact_node);
|
||||
curr_nodes.emplace_back(contact_node);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
|
@ -2793,38 +2833,81 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
|
|||
if (!added) //If we didn't add any points due to bad luck, we want to add one anyway such that loose parts are also supported.
|
||||
{
|
||||
auto bbox = overhang_part.contour.bounding_box();
|
||||
Points candidates = { bbox.min, bounding_box_middle(bbox), bbox.max };
|
||||
Points candidates;
|
||||
if (ts_layer->overhang_types[&overhang_part] == TreeSupportLayer::Detected)
|
||||
candidates = {bbox.min, bounding_box_middle(bbox), bbox.max};
|
||||
else
|
||||
candidates = {bounding_box_middle(bbox)};
|
||||
|
||||
for (Point candidate : candidates) {
|
||||
if (!overhang_part.contains(candidate))
|
||||
move_inside_expoly(overhang_part, candidate);
|
||||
constexpr size_t distance_to_top = 0;
|
||||
constexpr bool to_buildplate = true;
|
||||
Node * contact_node = new Node(candidate, distance_to_top, layer_nr % 2, support_roof_layers, to_buildplate, Node::NO_PARENT, print_z, height);
|
||||
contact_nodes[layer_nr].emplace_back(contact_node);
|
||||
curr_nodes.emplace_back(contact_node);
|
||||
}
|
||||
}
|
||||
|
||||
// add points at corners
|
||||
auto& points = overhang_part.contour.points;
|
||||
for (int i=0;i<points.size();i++)
|
||||
{
|
||||
auto pt = points[i];
|
||||
auto v1 = (pt - points[(i - 1 + points.size()) % points.size()]).normalized();
|
||||
auto v2 = (pt - points[(i + 1) % points.size()]).normalized();
|
||||
if (v1.dot(v2) > -0.7) {
|
||||
Node* contact_node = new Node(pt, 0, layer_nr % 2, support_roof_layers, true, Node::NO_PARENT, print_z, height);
|
||||
contact_nodes[layer_nr].emplace_back(contact_node);
|
||||
if (ts_layer->overhang_types[&overhang_part] == TreeSupportLayer::Detected) {
|
||||
// add points at corners
|
||||
auto &points = overhang_part.contour.points;
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
auto pt = points[i];
|
||||
auto v1 = (pt - points[(i - 1 + points.size()) % points.size()]).normalized();
|
||||
auto v2 = (pt - points[(i + 1) % points.size()]).normalized();
|
||||
if (v1.dot(v2) > -0.7) {
|
||||
Node *contact_node = new Node(pt, 0, layer_nr % 2, support_roof_layers, true, Node::NO_PARENT, print_z, height);
|
||||
curr_nodes.emplace_back(contact_node);
|
||||
}
|
||||
}
|
||||
} else if(ts_layer->overhang_types[&overhang_part] == TreeSupportLayer::Enforced){
|
||||
// remove close points in Enforcers
|
||||
auto above_nodes = contact_nodes[layer_nr - 1];
|
||||
if (!curr_nodes.empty() && !above_nodes.empty()) {
|
||||
for (auto it = curr_nodes.begin(); it != curr_nodes.end();) {
|
||||
bool is_duplicate = false;
|
||||
Slic3r::Vec3f curr_pt((*it)->position(0), (*it)->position(1), scale_((*it)->print_z));
|
||||
for (auto &pt : all_nodes) {
|
||||
auto dif = curr_pt - pt;
|
||||
if (dif.norm() < scale_(2)) {
|
||||
delete (*it);
|
||||
it = curr_nodes.erase(it);
|
||||
is_duplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!is_duplicate) it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (!curr_nodes.empty()) nonempty_layers++;
|
||||
for (auto node : curr_nodes) { all_nodes.emplace_back(node->position(0), node->position(1), scale_(node->print_z)); }
|
||||
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
|
||||
draw_contours_and_nodes_to_svg(layer_nr, overhang, m_ts_data->m_layer_outlines_below[layer_nr], {},
|
||||
contact_nodes[layer_nr], {}, "init_contact_points", { "overhang","outlines","" });
|
||||
#endif
|
||||
}
|
||||
int nNodes = all_nodes.size();
|
||||
avg_node_per_layer = nodes_angle = 0;
|
||||
if (nNodes > 0) {
|
||||
avg_node_per_layer = nNodes / nonempty_layers;
|
||||
// get orientation of nodes by line fitting
|
||||
// line: y=kx+b, where
|
||||
// k=tan(nodes_angle)=(n\sum{xy}-\sum{x}\sum{y})/(n\sum{x^2}-\sum{x}^2)
|
||||
float mx = 0, my = 0, mxy = 0, mx2 = 0;
|
||||
for (auto &pt : all_nodes) {
|
||||
float x = unscale_(pt(0));
|
||||
float y = unscale_(pt(1));
|
||||
mx += x;
|
||||
my += y;
|
||||
mxy += x * y;
|
||||
mx2 += x * x;
|
||||
}
|
||||
nodes_angle = atan2(nNodes * mxy - mx * my, nNodes * mx2 - SQ(mx));
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "avg_node_per_layer=" << avg_node_per_layer << ", nodes_angle=" << nodes_angle;
|
||||
}
|
||||
}
|
||||
|
||||
void TreeSupport::insert_dropped_node(std::vector<Node*>& nodes_layer, Node* p_node)
|
||||
|
|
|
@ -340,9 +340,8 @@ public:
|
|||
bool with_sheath;
|
||||
};
|
||||
|
||||
float total_overhang_area;
|
||||
float max_overhang_area;
|
||||
size_t total_overhang_layer_cnt;
|
||||
int avg_node_per_layer = 0;
|
||||
float nodes_angle = 0;
|
||||
bool has_sharp_tail;
|
||||
private:
|
||||
/*!
|
||||
|
|
|
@ -497,15 +497,10 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& trafod,
|
|||
|
||||
TriangleMesh TriangleMesh::convex_hull_3d() const
|
||||
{
|
||||
// BBS: don't compute convex hull for objects like a single sheet
|
||||
if (this->m_stats.volume>0.001) {
|
||||
TriangleMesh mesh(its_convex_hull(this->its));
|
||||
// Quite often qhull produces non-manifold mesh.
|
||||
// assert(mesh.stats().manifold());
|
||||
return mesh;
|
||||
}
|
||||
else
|
||||
return TriangleMesh();
|
||||
TriangleMesh mesh(its_convex_hull(this->its));
|
||||
// Quite often qhull produces non-manifold mesh.
|
||||
// assert(mesh.stats().manifold());
|
||||
return mesh;
|
||||
}
|
||||
|
||||
std::vector<ExPolygons> TriangleMesh::slice(const std::vector<double> &z) const
|
||||
|
|
|
@ -84,6 +84,124 @@ static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline& thi
|
|||
return paths;
|
||||
}
|
||||
|
||||
//BBS: new function to filter width to avoid too fragmented segments
|
||||
static ExtrusionPaths thick_polyline_to_extrusion_paths_2(const ThickPolyline& thick_polyline, ExtrusionRole role, const Flow& flow, const float tolerance)
|
||||
{
|
||||
ExtrusionPaths paths;
|
||||
ExtrusionPath path(role);
|
||||
ThickLines lines = thick_polyline.thicklines();
|
||||
|
||||
size_t start_index = 0;
|
||||
double max_width, min_width;
|
||||
|
||||
for (int i = 0; i < (int)lines.size(); ++i) {
|
||||
const ThickLine& line = lines[i];
|
||||
|
||||
if (i == 0) {
|
||||
max_width = line.a_width;
|
||||
min_width = line.a_width;
|
||||
}
|
||||
|
||||
const coordf_t line_len = line.length();
|
||||
if (line_len < SCALED_EPSILON) continue;
|
||||
|
||||
double thickness_delta = std::max(fabs(max_width - line.b_width), fabs(min_width - line.b_width));
|
||||
//BBS: has large difference in width
|
||||
if (thickness_delta > tolerance) {
|
||||
//BBS: 1 generate path from start_index to i(not included)
|
||||
if (start_index != i){
|
||||
path = ExtrusionPath(role);
|
||||
double length = lines[start_index].length();
|
||||
double sum = lines[start_index].length() * lines[start_index].a_width;
|
||||
path.polyline.append(lines[start_index].a);
|
||||
for (int idx = start_index + 1; idx < i; idx++) {
|
||||
length += lines[idx].length();
|
||||
sum += lines[idx].length() * lines[idx].a_width;
|
||||
path.polyline.append(lines[idx].a);
|
||||
}
|
||||
path.polyline.append(lines[i].a);
|
||||
if (length > SCALED_EPSILON) {
|
||||
double w = sum / length;
|
||||
Flow new_flow = flow.with_width(unscale<float>(w) + flow.height() * float(1. - 0.25 * PI));
|
||||
path.mm3_per_mm = new_flow.mm3_per_mm();
|
||||
path.width = new_flow.width();
|
||||
path.height = new_flow.height();
|
||||
paths.emplace_back(std::move(path));
|
||||
}
|
||||
}
|
||||
|
||||
start_index = i;
|
||||
max_width = line.a_width;
|
||||
min_width = line.a_width;
|
||||
|
||||
//BBS: 2 handle the i-th segment
|
||||
thickness_delta = fabs(line.a_width - line.b_width);
|
||||
if (thickness_delta > tolerance){
|
||||
const unsigned int segments = (unsigned int)ceil(thickness_delta / tolerance);
|
||||
const coordf_t seg_len = line_len / segments;
|
||||
Points pp;
|
||||
std::vector<coordf_t> width;
|
||||
{
|
||||
pp.push_back(line.a);
|
||||
width.push_back(line.a_width);
|
||||
for (size_t j = 1; j < segments; ++j) {
|
||||
pp.push_back((line.a.cast<double>() + (line.b - line.a).cast<double>().normalized() * (j * seg_len)).cast<coord_t>());
|
||||
|
||||
coordf_t w = line.a_width + (j * seg_len) * (line.b_width - line.a_width) / line_len;
|
||||
width.push_back(w);
|
||||
width.push_back(w);
|
||||
}
|
||||
pp.push_back(line.b);
|
||||
width.push_back(line.b_width);
|
||||
|
||||
assert(pp.size() == segments + 1u);
|
||||
assert(width.size() == segments * 2);
|
||||
}
|
||||
|
||||
// delete this line and insert new ones
|
||||
lines.erase(lines.begin() + i);
|
||||
for (size_t j = 0; j < segments; ++j) {
|
||||
ThickLine new_line(pp[j], pp[j + 1]);
|
||||
new_line.a_width = width[2 * j];
|
||||
new_line.b_width = width[2 * j + 1];
|
||||
lines.insert(lines.begin() + i + j, new_line);
|
||||
}
|
||||
--i;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//BBS: just update the max and min width and continue
|
||||
else {
|
||||
max_width = std::max(max_width, std::max(line.a_width, line.b_width));
|
||||
min_width = std::min(min_width, std::min(line.a_width, line.b_width));
|
||||
}
|
||||
}
|
||||
//BBS: handle the remaining segment
|
||||
size_t final_size = lines.size();
|
||||
if (start_index < final_size) {
|
||||
path = ExtrusionPath(role);
|
||||
double length = lines[start_index].length();
|
||||
double sum = lines[start_index].length() * lines[start_index].a_width;
|
||||
path.polyline.append(lines[start_index].a);
|
||||
for (int idx = start_index + 1; idx < final_size; idx++) {
|
||||
length += lines[idx].length();
|
||||
sum += lines[idx].length() * lines[idx].a_width;
|
||||
path.polyline.append(lines[idx].a);
|
||||
}
|
||||
path.polyline.append(lines[final_size - 1].b);
|
||||
if (length > SCALED_EPSILON) {
|
||||
double w = sum / length;
|
||||
Flow new_flow = flow.with_width(unscale<float>(w) + flow.height() * float(1. - 0.25 * PI));
|
||||
path.mm3_per_mm = new_flow.mm3_per_mm();
|
||||
path.width = new_flow.width();
|
||||
path.height = new_flow.height();
|
||||
paths.emplace_back(std::move(path));
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
void variable_width(const ThickPolylines& polylines, ExtrusionRole role, const Flow& flow, std::vector<ExtrusionEntity*>& out)
|
||||
{
|
||||
// This value determines granularity of adaptive width, as G-code does not allow
|
||||
|
@ -91,7 +209,7 @@ void variable_width(const ThickPolylines& polylines, ExtrusionRole role, const F
|
|||
// of segments, and any pruning shall be performed before we apply this tolerance.
|
||||
const float tolerance = float(scale_(0.05));
|
||||
for (const ThickPolyline& p : polylines) {
|
||||
ExtrusionPaths paths = thick_polyline_to_extrusion_paths(p, role, flow, tolerance);
|
||||
ExtrusionPaths paths = thick_polyline_to_extrusion_paths_2(p, role, flow, tolerance);
|
||||
// Append paths to collection.
|
||||
if (!paths.empty()) {
|
||||
if (paths.front().first_point() == paths.back().last_point())
|
||||
|
|
|
@ -84,7 +84,6 @@ static constexpr bool RELATIVE_E_AXIS = 1;
|
|||
#endif /* UNUSED */
|
||||
|
||||
//BBS: some global const config which user can not change, but developer can
|
||||
static constexpr bool g_config_thick_bridges = true;
|
||||
static constexpr bool g_config_support_sharp_tails = true;
|
||||
static constexpr bool g_config_remove_small_overhangs = true;
|
||||
static constexpr float g_config_tree_support_collision_resolution = 0.2;
|
||||
|
|
|
@ -11,6 +11,10 @@
|
|||
#include "Time.hpp"
|
||||
#include "libslic3r.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include "MacUtils.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#include <psapi.h>
|
||||
|
@ -292,12 +296,21 @@ namespace keywords = boost::log::keywords;
|
|||
namespace attrs = boost::log::attributes;
|
||||
void set_log_path_and_level(const std::string& file, unsigned int level)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
//currently on old macos, the boost::log::add_file_log will crash
|
||||
//TODO: need to be fixed
|
||||
if (!is_macos_support_boost_add_file_log()) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
//BBS log file at C:\\Users\\[yourname]\\AppData\\Roaming\\BambuStudio\\log\\[log_filename].log
|
||||
auto log_folder = boost::filesystem::path(g_data_dir) / "log";
|
||||
if (!boost::filesystem::exists(log_folder)) {
|
||||
boost::filesystem::create_directory(log_folder);
|
||||
}
|
||||
auto full_path = (log_folder / file).make_preferred();
|
||||
|
||||
g_log_sink = boost::log::add_file_log(
|
||||
keywords::file_name = full_path.string() + ".%N",
|
||||
keywords::rotation_size = 100 * 1024 * 1024,
|
||||
|
@ -309,6 +322,7 @@ void set_log_path_and_level(const std::string& file, unsigned int level)
|
|||
<< ":" << expr::smessage
|
||||
)
|
||||
);
|
||||
|
||||
logging::add_common_attributes();
|
||||
|
||||
set_logging_level(level);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue