mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-12-11 16:00:17 -07:00
Merge branch 'master-remote' into feature/1.5
Signed-off-by: SoftFever <softfeverever@gmail.com> # Conflicts: # bbl/i18n/BambuStudio.pot # bbl/i18n/de/BambuStudio_de.po # bbl/i18n/en/BambuStudio_en.po # bbl/i18n/es/BambuStudio_es.po # bbl/i18n/fr/BambuStudio_fr.po # bbl/i18n/hu/BambuStudio_hu.po # bbl/i18n/it/BambuStudio_it.po # bbl/i18n/nl/BambuStudio_nl.po # bbl/i18n/sv/BambuStudio_sv.po # bbl/i18n/zh_cn/BambuStudio_zh_CN.po # deps/Boost/Boost.cmake # deps/wxWidgets/wxWidgets.cmake # resources/config.json # resources/i18n/de/BambuStudio.mo # resources/i18n/en/BambuStudio.mo # resources/i18n/es/BambuStudio.mo # resources/i18n/fr/BambuStudio.mo # resources/i18n/hu/BambuStudio.mo # resources/i18n/it/BambuStudio.mo # resources/i18n/nl/BambuStudio.mo # resources/i18n/sv/BambuStudio.mo # resources/i18n/zh_cn/BambuStudio.mo # resources/images/tips_arrow.svg # resources/profiles/Anycubic.json # resources/profiles/Anycubic/filament/Anycubic Generic ABS.json # resources/profiles/Anycubic/filament/Anycubic Generic ASA.json # resources/profiles/Anycubic/filament/Anycubic Generic PA-CF.json # resources/profiles/Anycubic/filament/Anycubic Generic PA.json # resources/profiles/Anycubic/filament/Anycubic Generic PC.json # resources/profiles/Anycubic/filament/Anycubic Generic PETG.json # resources/profiles/Anycubic/filament/Anycubic Generic PLA-CF.json # resources/profiles/Anycubic/filament/Anycubic Generic PLA.json # resources/profiles/Anycubic/filament/Anycubic Generic PVA.json # resources/profiles/Anycubic/filament/Anycubic Generic TPU.json # resources/profiles/Anycubic/filament/fdm_filament_common.json # resources/profiles/Anycubic/machine/Anycubic 4Max Pro 0.4 nozzle.json # resources/profiles/Anycubic/machine/Anycubic 4Max Pro.json # resources/profiles/Anycubic/process/0.20mm Standard @4MaxPro.json # resources/profiles/Anycubic/process/fdm_process_common.json # resources/profiles/BBL.json # resources/profiles/BBL/machine/Bambu Lab P1P 0.2 nozzle.json # resources/profiles/BBL/machine/Bambu Lab P1P 0.4 nozzle.json # resources/profiles/BBL/machine/Bambu Lab P1P 0.6 nozzle.json # resources/profiles/BBL/machine/Bambu Lab P1P 0.8 nozzle.json # resources/profiles/BBL/machine/Bambu Lab X1 0.2 nozzle.json # resources/profiles/BBL/machine/Bambu Lab X1 0.4 nozzle.json # resources/profiles/BBL/machine/Bambu Lab X1 0.6 nozzle.json # resources/profiles/BBL/machine/Bambu Lab X1 0.8 nozzle.json # resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.2 nozzle.json # resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.4 nozzle.json # resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.6 nozzle.json # resources/profiles/BBL/machine/Bambu Lab X1 Carbon 0.8 nozzle.json # resources/profiles/BBL/machine/fdm_bbl_3dp_001_common.json # resources/profiles/Voron.json # resources/web/data/text.js # resources/web/image/printer/Anycubic 4Max Pro_cover.png # src/BambuStudio.cpp # src/libslic3r/GCode.cpp # src/libslic3r/GCode.hpp # src/libslic3r/GCode/GCodeProcessor.cpp # src/libslic3r/GCodeWriter.hpp # src/libslic3r/PerimeterGenerator.cpp # src/libslic3r/PresetBundle.cpp # src/libslic3r/Print.cpp # src/libslic3r/Print.hpp # src/libslic3r/PrintConfig.cpp # src/libslic3r/PrintConfig.hpp # src/libslic3r/PrintObject.cpp # src/slic3r/GUI/AMSMaterialsSetting.cpp # src/slic3r/GUI/AMSMaterialsSetting.hpp # src/slic3r/GUI/AmsMappingPopup.cpp # src/slic3r/GUI/AmsMappingPopup.hpp # src/slic3r/GUI/Auxiliary.cpp # src/slic3r/GUI/BackgroundSlicingProcess.cpp # src/slic3r/GUI/ConfigManipulation.cpp # src/slic3r/GUI/DeviceManager.cpp # src/slic3r/GUI/DeviceManager.hpp # src/slic3r/GUI/ExtrusionCalibration.cpp # src/slic3r/GUI/GCodeViewer.cpp # src/slic3r/GUI/GCodeViewer.hpp # src/slic3r/GUI/GUI_App.cpp # src/slic3r/GUI/IMSlider.cpp # src/slic3r/GUI/Jobs/PrintJob.cpp # src/slic3r/GUI/Jobs/PrintJob.hpp # src/slic3r/GUI/Jobs/SendJob.cpp # src/slic3r/GUI/Jobs/SendJob.hpp # src/slic3r/GUI/MainFrame.cpp # src/slic3r/GUI/MainFrame.hpp # src/slic3r/GUI/MediaPlayCtrl.cpp # src/slic3r/GUI/OptionsGroup.cpp # src/slic3r/GUI/PhysicalPrinterDialog.cpp # src/slic3r/GUI/Plater.cpp # src/slic3r/GUI/PrintHostDialogs.cpp # src/slic3r/GUI/Printer/BambuTunnel.h # src/slic3r/GUI/Printer/PrinterFileSystem.cpp # src/slic3r/GUI/Printer/gstbambusrc.c # src/slic3r/GUI/Printer/gstbambusrc.h # src/slic3r/GUI/ReleaseNote.cpp # src/slic3r/GUI/ReleaseNote.hpp # src/slic3r/GUI/SelectMachine.cpp # src/slic3r/GUI/SendToPrinter.cpp # src/slic3r/GUI/SetBedTypeDialog.cpp # src/slic3r/GUI/StatusPanel.cpp # src/slic3r/GUI/StatusPanel.hpp # src/slic3r/GUI/Tab.cpp # src/slic3r/GUI/Widgets/AMSControl.cpp # src/slic3r/GUI/Widgets/AMSControl.hpp # src/slic3r/GUI/Widgets/ImageSwitchButton.cpp # src/slic3r/GUI/Widgets/Label.cpp # src/slic3r/GUI/WipeTowerDialog.cpp # src/slic3r/Utils/Process.cpp # src/slic3r/Utils/bambu_networking.hpp # version.inc
This commit is contained in:
commit
5ef51f6c8a
339 changed files with 37169 additions and 5445 deletions
|
|
@ -257,6 +257,10 @@ void AppConfig::set_defaults()
|
|||
set("mouse_supported", "mouse left/mouse middle/mouse right");
|
||||
}
|
||||
|
||||
if (get("privacy_version").empty()) {
|
||||
set("privacy_version", "00.00.00.00");
|
||||
}
|
||||
|
||||
if (get("rotate_view").empty()) {
|
||||
set("rotate_view", "none/mouse left");
|
||||
}
|
||||
|
|
@ -462,8 +466,11 @@ std::string AppConfig::load()
|
|||
for(auto& element: iter.value()) {
|
||||
if (idx == 0)
|
||||
m_storage[it.key()]["filament"] = element;
|
||||
else
|
||||
m_storage[it.key()]["filament_" + std::to_string(idx)] = element;
|
||||
else {
|
||||
auto n = std::to_string(idx);
|
||||
if (n.length() == 1) n = "0" + n;
|
||||
m_storage[it.key()]["filament_" + n] = element;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
} else {
|
||||
|
|
@ -483,8 +490,6 @@ std::string AppConfig::load()
|
|||
m_filament_presets = iter.value().get<std::vector<std::string>>();
|
||||
} else if (iter.key() == "filament_colors") {
|
||||
m_filament_colors = iter.value().get<std::vector<std::string>>();
|
||||
} else if (iter.key() == "flushing_volumes") {
|
||||
m_flush_volumes_matrix = iter.value().get<std::vector<float>>();
|
||||
}
|
||||
else {
|
||||
if (iter.value().is_string())
|
||||
|
|
@ -577,10 +582,6 @@ void AppConfig::save()
|
|||
j["app"]["filament_colors"].push_back(filament_color);
|
||||
}
|
||||
|
||||
for (double flushing_volume : m_flush_volumes_matrix) {
|
||||
j["app"]["flushing_volumes"].push_back(flushing_volume);
|
||||
}
|
||||
|
||||
// Write the other categories.
|
||||
for (const auto& category : m_storage) {
|
||||
if (category.first.empty())
|
||||
|
|
|
|||
|
|
@ -177,11 +177,6 @@ public:
|
|||
m_filament_colors = filament_colors;
|
||||
m_dirty = true;
|
||||
}
|
||||
const std::vector<float> &get_flush_volumes_matrix() const { return m_flush_volumes_matrix; }
|
||||
void set_flush_volumes_matrix(const std::vector<float> &flush_volumes_matrix){
|
||||
m_flush_volumes_matrix = flush_volumes_matrix;
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
// return recent/last_opened_folder or recent/settings_folder or empty string.
|
||||
std::string get_last_dir() const;
|
||||
|
|
@ -281,7 +276,6 @@ private:
|
|||
|
||||
std::vector<std::string> m_filament_presets;
|
||||
std::vector<std::string> m_filament_colors;
|
||||
std::vector<float> m_flush_volumes_matrix;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
|||
|
|
@ -452,20 +452,25 @@ protected:
|
|||
}
|
||||
|
||||
std::set<int> extruder_ids;
|
||||
int non_virt_cnt = 0;
|
||||
for (int i = 0; i < m_items.size(); i++) {
|
||||
Item& p = m_items[i];
|
||||
if (p.is_virt_object) continue;
|
||||
extruder_ids.insert(p.extrude_id);
|
||||
// add a large cost if not multi materials on same plate is not allowed
|
||||
if (!params.allow_multi_materials_on_same_plate)
|
||||
score += LARGE_COST_TO_REJECT * (item.extrude_id != p.extrude_id);
|
||||
extruder_ids.insert(p.extrude_ids.begin(),p.extrude_ids.end());
|
||||
non_virt_cnt++;
|
||||
}
|
||||
extruder_ids.insert(item.extrude_ids.begin(),item.extrude_ids.end());
|
||||
|
||||
// add a large cost if not multi materials on same plate is not allowed
|
||||
if (!params.allow_multi_materials_on_same_plate) {
|
||||
// non_virt_cnt==0 means it's the first object, which can be multi-color
|
||||
if (extruder_ids.size() > 1 && non_virt_cnt > 0)
|
||||
score += LARGE_COST_TO_REJECT * 1.1;
|
||||
}
|
||||
// for layered printing, we want extruder change as few as possible
|
||||
// this has very weak effect, CAN NOT use a large weight
|
||||
if (!params.is_seq_print) {
|
||||
extruder_ids.insert(item.extrude_id);
|
||||
score += 1 * std::max(0, ((int)extruder_ids.size() - 1));
|
||||
score += 1 * std::max(0, ((int) extruder_ids.size() - 1));
|
||||
}
|
||||
|
||||
return std::make_tuple(score, fullbb);
|
||||
|
|
@ -601,7 +606,7 @@ public:
|
|||
}
|
||||
else {
|
||||
return i1.bed_temp != i2.bed_temp ? (i1.bed_temp > i2.bed_temp) :
|
||||
(i1.extrude_id != i2.extrude_id ? (i1.extrude_id < i2.extrude_id) : (i1.area() > i2.area()));
|
||||
(i1.extrude_ids != i2.extrude_ids ? (i1.extrude_ids.front() < i2.extrude_ids.front()) : (i1.area() > i2.area()));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -850,7 +855,7 @@ static void process_arrangeable(const ArrangePolygon &arrpoly,
|
|||
item.binId(arrpoly.bed_idx);
|
||||
item.priority(arrpoly.priority);
|
||||
item.itemId(arrpoly.itemid);
|
||||
item.extrude_id = arrpoly.extrude_ids.front();
|
||||
item.extrude_ids = arrpoly.extrude_ids;
|
||||
item.height = arrpoly.height;
|
||||
item.name = arrpoly.name;
|
||||
//BBS: add virtual object logic
|
||||
|
|
|
|||
|
|
@ -165,17 +165,6 @@ static Polygons top_level_outer_brim_islands(const ConstPrintObjectPtrs &top_lev
|
|||
}
|
||||
}
|
||||
|
||||
// BBS
|
||||
if (!object->tree_support_layers().empty()) {
|
||||
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
|
||||
Polygons contour_offset = offset(ex_poly.contour, brim_object_gap, ClipperLib::jtSquare);
|
||||
for (Polygon& poly : contour_offset)
|
||||
poly.douglas_peucker(scaled_resolution);
|
||||
|
||||
polygons_append(islands_object, std::move(contour_offset));
|
||||
}
|
||||
}
|
||||
|
||||
for (const PrintInstance &instance : object->instances())
|
||||
append_and_translate(islands, islands_object, instance);
|
||||
}
|
||||
|
|
@ -248,19 +237,6 @@ static ExPolygons top_level_outer_brim_area(const Print &print
|
|||
}
|
||||
}
|
||||
|
||||
// BBS
|
||||
if (!object->tree_support_layers().empty()) {
|
||||
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
|
||||
if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) && is_top_outer_brim)
|
||||
append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_object_gap, jtRound, scaled_resolution), offset(ex_poly.contour, brim_object_gap)));
|
||||
|
||||
if (brim_type != BrimType::btNoBrim)
|
||||
append(no_brim_area_object, offset_ex(ExPolygon(ex_poly.contour), brim_object_gap));
|
||||
|
||||
no_brim_area_object.emplace_back(ex_poly.contour);
|
||||
}
|
||||
}
|
||||
|
||||
for (const PrintInstance &instance : object->instances()) {
|
||||
append_and_translate(brim_area, brim_area_object, instance);
|
||||
append_and_translate(no_brim_area, no_brim_area_object, instance);
|
||||
|
|
@ -360,21 +336,6 @@ static ExPolygons top_level_outer_brim_area(const Print& print, const ConstPrint
|
|||
}
|
||||
}
|
||||
|
||||
// BBS
|
||||
if (!object->tree_support_layers().empty()) {
|
||||
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
|
||||
if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) && is_top_outer_brim)
|
||||
append(brim_area_support, diff_ex(offset(ex_poly.contour, brim_width, jtRound, SCALED_RESOLUTION), offset(ex_poly.contour, 0)));
|
||||
|
||||
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
|
||||
append(no_brim_area_support, offset_ex(ex_poly.holes, -no_brim_offset));
|
||||
|
||||
if (brim_type != BrimType::btNoBrim)
|
||||
append(no_brim_area_support, offset_ex(ex_poly.contour, 0));
|
||||
|
||||
no_brim_area_support.emplace_back(ex_poly.contour);
|
||||
}
|
||||
}
|
||||
brimToWrite.at(object->id()).sup = false;
|
||||
for (const PrintInstance& instance : object->instances()) {
|
||||
if (!brim_area_support.empty())
|
||||
|
|
@ -543,26 +504,6 @@ static ExPolygons inner_brim_area(const Print& print, const ConstPrintObjectPtrs
|
|||
no_brim_area_support.emplace_back(support_contour);
|
||||
}
|
||||
}
|
||||
|
||||
// BBS
|
||||
if (!object->tree_support_layers().empty()) {
|
||||
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
|
||||
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) {
|
||||
if (!top_outer_brim)
|
||||
append(brim_area_support, diff_ex(offset_ex(ex_poly.contour, brim_width + brim_offset, jtRound, SCALED_RESOLUTION), offset_ex(ex_poly.contour, brim_offset)));
|
||||
}
|
||||
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner)
|
||||
append(brim_area_support, diff_ex(offset_ex(ex_poly.holes, -brim_offset), offset_ex(ex_poly.holes, -brim_width - brim_offset)));
|
||||
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim)
|
||||
append(no_brim_area_support, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly.holes));
|
||||
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
|
||||
append(no_brim_area_support, offset_ex(ex_poly.holes, -no_brim_offset));
|
||||
append(holes_support, ex_poly.holes);
|
||||
if (brim_type != BrimType::btNoBrim)
|
||||
append(no_brim_area_support, offset_ex(ex_poly.contour, 0));
|
||||
no_brim_area_support.emplace_back(ex_poly.contour);
|
||||
}
|
||||
}
|
||||
}
|
||||
brimToWrite.at(object->id()).sup = false;
|
||||
for (const PrintInstance& instance : object->instances()) {
|
||||
|
|
@ -927,7 +868,6 @@ static ExPolygons outer_inner_brim_area(const Print& print,
|
|||
double brimWidthRaw = configBrimWidthByVolumeGroups(adhension, maxSpeed, groupVolumePtrs, volumeGroup.slices, groupHeight);
|
||||
brim_width = scale_(floor(brimWidthRaw / flowWidth / 2) * flowWidth * 2);
|
||||
}
|
||||
|
||||
for (const ExPolygon& ex_poly : volumeGroup.slices) {
|
||||
// BBS: additional brim width will be added if part's adhension area is too small and brim is not generated
|
||||
float brim_width_mod;
|
||||
|
|
@ -986,7 +926,7 @@ static ExPolygons outer_inner_brim_area(const Print& print,
|
|||
support_material_extruder = printExtruders.front() + 1;
|
||||
}
|
||||
if (support_material_extruder == extruderNo && brimToWrite.at(object->id()).sup) {
|
||||
if (!object->support_layers().empty()) {
|
||||
if (!object->support_layers().empty() && object->support_layers().front()->support_type==stInnerNormal) {
|
||||
for (const Polygon& support_contour : object->support_layers().front()->support_fills.polygons_covered_by_spacing()) {
|
||||
// Brim will not be generated for supports
|
||||
/*
|
||||
|
|
@ -1000,8 +940,8 @@ static ExPolygons outer_inner_brim_area(const Print& print,
|
|||
}
|
||||
}
|
||||
// BBS
|
||||
if (!object->tree_support_layers().empty()) {
|
||||
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
|
||||
if (!object->support_layers().empty() && object->support_layers().front()->support_type == stInnerTree) {
|
||||
for (const ExPolygon &ex_poly : object->support_layers().front()->lslices) {
|
||||
// BBS: additional brim width will be added if adhension area is too small without brim
|
||||
float brim_width_mod = ex_poly.area() / ex_poly.contour.length() < scaled_half_min_adh_length
|
||||
&& brim_width < scaled_flow_width ? brim_width + scaled_additional_brim_width : brim_width;
|
||||
|
|
@ -1036,11 +976,16 @@ static ExPolygons outer_inner_brim_area(const Print& print,
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!bedExPoly.empty())
|
||||
if (!bedExPoly.empty()){
|
||||
auto plateOffset = print.get_plate_origin();
|
||||
bedExPoly.front().translate(scale_(plateOffset.x()), scale_(plateOffset.y()));
|
||||
no_brim_area.push_back(bedExPoly.front());
|
||||
for (const PrintObject* object : print.objects())
|
||||
if (brimAreaMap.find(object->id()) != brimAreaMap.end()) {
|
||||
}
|
||||
for (const PrintObject* object : print.objects()) {
|
||||
if (brimAreaMap.find(object->id()) != brimAreaMap.end())
|
||||
{
|
||||
brimAreaMap[object->id()] = diff_ex(brimAreaMap[object->id()], no_brim_area);
|
||||
}
|
||||
|
||||
if (supportBrimAreaMap.find(object->id()) != supportBrimAreaMap.end())
|
||||
supportBrimAreaMap[object->id()] = diff_ex(supportBrimAreaMap[object->id()], no_brim_area);
|
||||
|
|
@ -1220,13 +1165,6 @@ static void make_inner_island_brim(const Print& print, const ConstPrintObjectPtr
|
|||
}
|
||||
}
|
||||
|
||||
if (!object->tree_support_layers().empty()) {
|
||||
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
|
||||
Polygon counter = ex_poly.contour;
|
||||
save_polygon_if_is_inner_island(holes_area, counter, hole_island_pair);
|
||||
}
|
||||
}
|
||||
|
||||
//BBS: 3 generate loops, only save part of loop which inside hole
|
||||
const float brim_offset = scale_(object->config().brim_object_gap.value);
|
||||
const float brim_width = scale_(object->config().brim_width.value);
|
||||
|
|
@ -1369,13 +1307,6 @@ static void make_inner_island_brim(const Print& print, const ConstPrintObjectPtr
|
|||
}
|
||||
}
|
||||
|
||||
if (!object->tree_support_layers().empty()) {
|
||||
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
|
||||
Polygon counter = ex_poly.contour;
|
||||
save_polygon_if_is_inner_island(holes_area, counter, hole_island_pair_supports);
|
||||
}
|
||||
}
|
||||
|
||||
//BBS: 3 generate loops, only save part of loop which inside hole
|
||||
const float brim_offset = scale_(object->config().brim_object_gap.value);
|
||||
const float brim_width = floor(scale_(object->config().brim_width.value) / 2 / flow.scaled_spacing()) * 2 * flow.scaled_spacing();
|
||||
|
|
@ -1587,10 +1518,10 @@ Polygons tryExPolygonOffset(const ExPolygons islandAreaEx, const Print& print)
|
|||
for (ExPolygon& poly_ex : islands_ex)
|
||||
poly_ex.douglas_peucker(resolution);
|
||||
polygons_append(loops, to_polygons(islands_ex));
|
||||
islands_ex = offset_ex(std::move(islands_ex), -1.4f*float(flow.scaled_spacing()), jtRound, resolution);
|
||||
islands_ex = offset_ex(std::move(islands_ex), -1.3f*float(flow.scaled_spacing()), jtRound, resolution);
|
||||
for (ExPolygon& poly_ex : islands_ex)
|
||||
poly_ex.douglas_peucker(resolution);
|
||||
islands_ex = offset_ex(std::move(islands_ex), 0.4f*float(flow.scaled_spacing()), jtRound, resolution);
|
||||
islands_ex = offset_ex(std::move(islands_ex), 0.3f*float(flow.scaled_spacing()), jtRound, resolution);
|
||||
}
|
||||
return loops;
|
||||
}
|
||||
|
|
@ -1663,13 +1594,6 @@ void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_
|
|||
ex_poly_translated.translate(instance.shift.x(), instance.shift.y());
|
||||
bbx.merge(get_extents(ex_poly_translated));
|
||||
}
|
||||
if (!object->tree_support_layers().empty())
|
||||
for (const Polygon& ex_poly : object->tree_support_layers().front()->support_fills.polygons_covered_by_spacing())
|
||||
for (const PrintInstance& instance : object->instances()) {
|
||||
auto ex_poly_translated = ex_poly;
|
||||
ex_poly_translated.translate(instance.shift.x(), instance.shift.y());
|
||||
bbx.merge(get_extents(ex_poly_translated));
|
||||
}
|
||||
if (supportBrimAreaMap.find(printObjID) != supportBrimAreaMap.end()) {
|
||||
for (const ExPolygon& ex_poly : supportBrimAreaMap.at(printObjID))
|
||||
bbx.merge(get_extents(ex_poly.contour));
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ project(libslic3r)
|
|||
|
||||
include(PrecompiledHeader)
|
||||
|
||||
string(TIMESTAMP COMPILE_TIME %Y%m%d-%H%M%S)
|
||||
set(SLIC3R_BUILD_TIME ${COMPILE_TIME})
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libslic3r_version.h.in ${CMAKE_CURRENT_BINARY_DIR}/libslic3r_version.h @ONLY)
|
||||
|
||||
if (MINGW)
|
||||
|
|
|
|||
|
|
@ -298,9 +298,16 @@ ConfigOption* ConfigOptionDef::create_default_option() const
|
|||
return new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt());
|
||||
|
||||
if (type == coEnums) {
|
||||
ConfigOptionEnumsGeneric* opt = dynamic_cast<ConfigOptionEnumsGeneric*>(this->default_value->clone());
|
||||
opt->keys_map = this->enum_keys_map;
|
||||
return opt;
|
||||
auto dft = this->default_value->clone();
|
||||
if (dft->nullable()) {
|
||||
ConfigOptionEnumsGenericNullable *opt = dynamic_cast<ConfigOptionEnumsGenericNullable *>(this->default_value->clone());
|
||||
opt->keys_map = this->enum_keys_map;
|
||||
return opt;
|
||||
} else {
|
||||
ConfigOptionEnumsGeneric *opt = dynamic_cast<ConfigOptionEnumsGeneric *>(this->default_value->clone());
|
||||
opt->keys_map = this->enum_keys_map;
|
||||
return opt;
|
||||
}
|
||||
}
|
||||
|
||||
return this->default_value->clone();
|
||||
|
|
@ -743,6 +750,9 @@ ConfigSubstitutions ConfigBase::load_from_json(const std::string &file, ForwardC
|
|||
int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContext& substitution_context, bool load_inherits_to_config, std::map<std::string, std::string>& key_values, std::string& reason)
|
||||
{
|
||||
json j;
|
||||
std::list<std::string> different_settings_append;
|
||||
std::string new_support_style;
|
||||
bool is_project_settings = false;
|
||||
try {
|
||||
boost::nowide::ifstream ifs(file);
|
||||
ifs >> j;
|
||||
|
|
@ -762,6 +772,8 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
|
|||
}
|
||||
else if (boost::iequals(it.key(), BBL_JSON_KEY_NAME)) {
|
||||
key_values.emplace(BBL_JSON_KEY_NAME, it.value());
|
||||
if (it.value() == "project_settings")
|
||||
is_project_settings = true;
|
||||
}
|
||||
else if (boost::iequals(it.key(), BBL_JSON_KEY_URL)) {
|
||||
key_values.emplace(BBL_JSON_KEY_URL, it.value());
|
||||
|
|
@ -791,6 +803,15 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
|
|||
if (it.value().is_string()) {
|
||||
//bool test1 = (it.key() == std::string("end_gcode"));
|
||||
this->set_deserialize(opt_key, it.value(), substitution_context);
|
||||
//some logic for special values
|
||||
if (opt_key == "support_type") {
|
||||
//std::string new_value = dynamic_cast<ConfigOptionString*>(this->option(opt_key))->value;
|
||||
if (it.value() == "hybrid(auto)") {
|
||||
different_settings_append.push_back(opt_key);
|
||||
different_settings_append.push_back("support_style");
|
||||
new_support_style = "tree_hybrid";
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (it.value().is_array()) {
|
||||
t_config_option_key opt_key_src = opt_key;
|
||||
|
|
@ -856,6 +877,62 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!different_settings_append.empty()) {
|
||||
if (!new_support_style.empty()) {
|
||||
ConfigOptionEnum<SupportMaterialStyle>* opt = this->option<ConfigOptionEnum<SupportMaterialStyle>>("support_style", true);
|
||||
opt->value = smsTreeHybrid;
|
||||
}
|
||||
if (is_project_settings) {
|
||||
std::vector<std::string>& different_settings = this->option<ConfigOptionStrings>("different_settings_to_system", true)->values;
|
||||
size_t size = different_settings.size();
|
||||
if (size == 0) {
|
||||
size = this->option<ConfigOptionStrings>("filament_settings_id")->values.size() + 2;
|
||||
different_settings.resize(size);
|
||||
}
|
||||
|
||||
std::vector<bool> is_first(size, false);
|
||||
std::vector<std::vector<std::string>> original_diffs(size);
|
||||
for (int index = 0; index < size; index++)
|
||||
{
|
||||
if (different_settings[index].empty()) {
|
||||
is_first[index] = true;
|
||||
}
|
||||
else {
|
||||
Slic3r::unescape_strings_cstyle(different_settings[index], original_diffs[index]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto diff_key : different_settings_append)
|
||||
{
|
||||
//get the index in the group
|
||||
int index = 0;
|
||||
bool need_insert = true;
|
||||
if (diff_key == "support_type")
|
||||
index = 0;
|
||||
else if (diff_key == "support_style")
|
||||
index = 0;
|
||||
|
||||
//check whether exist firstly
|
||||
if (!original_diffs[index].empty()) {
|
||||
for (int j = 0; j < original_diffs[index].size(); j++) {
|
||||
if (original_diffs[index][j] == diff_key) {
|
||||
need_insert = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!need_insert)
|
||||
continue;
|
||||
|
||||
//insert this key
|
||||
if (!is_first[index])
|
||||
different_settings[index] += ";";
|
||||
else
|
||||
is_first[index] = false;
|
||||
different_settings[index] += diff_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
catch (const std::ifstream::failure &err) {
|
||||
|
|
|
|||
|
|
@ -771,6 +771,8 @@ public:
|
|||
ConfigOptionIntsTempl() : ConfigOptionVector<int>() {}
|
||||
explicit ConfigOptionIntsTempl(size_t n, int value) : ConfigOptionVector<int>(n, value) {}
|
||||
explicit ConfigOptionIntsTempl(std::initializer_list<int> il) : ConfigOptionVector<int>(std::move(il)) {}
|
||||
explicit ConfigOptionIntsTempl(const std::vector<int> &vec) : ConfigOptionVector<int>(vec) {}
|
||||
explicit ConfigOptionIntsTempl(std::vector<int> &&vec) : ConfigOptionVector<int>(std::move(vec)) {}
|
||||
|
||||
static ConfigOptionType static_type() { return coInts; }
|
||||
ConfigOptionType type() const override { return static_type(); }
|
||||
|
|
@ -1640,33 +1642,37 @@ private:
|
|||
};
|
||||
|
||||
// BBS
|
||||
class ConfigOptionEnumsGeneric : public ConfigOptionInts
|
||||
template <bool NULLABLE>
|
||||
class ConfigOptionEnumsGenericTempl : public ConfigOptionInts
|
||||
{
|
||||
public:
|
||||
ConfigOptionEnumsGeneric(const t_config_enum_values* keys_map = nullptr) : keys_map(keys_map) {}
|
||||
explicit ConfigOptionEnumsGeneric(const t_config_enum_values* keys_map, size_t size, int value) : ConfigOptionInts(size, value), keys_map(keys_map) {}
|
||||
explicit ConfigOptionEnumsGeneric(std::initializer_list<int> il) : ConfigOptionInts(std::move(il)), keys_map(keys_map) {}
|
||||
ConfigOptionEnumsGenericTempl(const t_config_enum_values *keys_map = nullptr) : keys_map(keys_map) {}
|
||||
explicit ConfigOptionEnumsGenericTempl(const t_config_enum_values *keys_map, size_t size, int value) : ConfigOptionInts(size, value), keys_map(keys_map) {}
|
||||
explicit ConfigOptionEnumsGenericTempl(std::initializer_list<int> il) : ConfigOptionInts(std::move(il)), keys_map(keys_map) {}
|
||||
explicit ConfigOptionEnumsGenericTempl(const std::vector<int> &vec) : ConfigOptionInts(vec) {}
|
||||
explicit ConfigOptionEnumsGenericTempl(std::vector<int> &&vec) : ConfigOptionInts(std::move(vec)) {}
|
||||
|
||||
const t_config_enum_values* keys_map = nullptr;
|
||||
|
||||
static ConfigOptionType static_type() { return coEnums; }
|
||||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionEnumsGeneric(*this); }
|
||||
ConfigOptionEnumsGeneric& operator= (const ConfigOption* opt) { this->set(opt); return *this; }
|
||||
bool operator< (const ConfigOptionIntsTempl& rhs) const throw() { return this->values < rhs.values; }
|
||||
ConfigOption* clone() const override { return new ConfigOptionEnumsGenericTempl(*this); }
|
||||
ConfigOptionEnumsGenericTempl& operator= (const ConfigOption* opt) { this->set(opt); return *this; }
|
||||
bool operator< (const ConfigOptionInts& rhs) const throw() { return this->values < rhs.values; }
|
||||
|
||||
bool operator==(const ConfigOptionIntsTempl& rhs) const throw()
|
||||
bool operator==(const ConfigOptionInts& rhs) const throw()
|
||||
{
|
||||
if (rhs.type() != this->type())
|
||||
throw ConfigurationError("ConfigOptionEnumsGeneric: Comparing incompatible types");
|
||||
return this->values == rhs.values;
|
||||
}
|
||||
bool nullable() const override { return NULLABLE; }
|
||||
|
||||
void set(const ConfigOption* rhs) override {
|
||||
if (rhs->type() != this->type())
|
||||
throw ConfigurationError("ConfigOptionEnumGeneric: Assigning an incompatible type");
|
||||
// rhs could be of the following type: ConfigOptionEnumsGeneric
|
||||
this->values = dynamic_cast<const ConfigOptionEnumsGeneric*>(rhs)->values;
|
||||
this->values = dynamic_cast<const ConfigOptionEnumsGenericTempl *>(rhs)->values;
|
||||
}
|
||||
|
||||
std::string serialize() const override
|
||||
|
|
@ -1701,7 +1707,10 @@ public:
|
|||
while (std::getline(is, item_str, ',')) {
|
||||
boost::trim(item_str);
|
||||
if (item_str == "nil") {
|
||||
return false;
|
||||
if (NULLABLE)
|
||||
this->values.push_back(nil_value());
|
||||
else
|
||||
throw ConfigurationError("Deserializing nil into a non-nullable object");
|
||||
}
|
||||
else {
|
||||
auto it = this->keys_map->find(item_str);
|
||||
|
|
@ -1716,15 +1725,26 @@ public:
|
|||
private:
|
||||
void serialize_single_value(std::ostringstream& ss, const int v) const
|
||||
{
|
||||
for (const auto& kvp : *this->keys_map)
|
||||
if (kvp.second == v)
|
||||
ss << kvp.first;
|
||||
if (v == nil_value()) {
|
||||
if (NULLABLE)
|
||||
ss << "nil";
|
||||
else
|
||||
throw ConfigurationError("Serializing NaN");
|
||||
}
|
||||
else {
|
||||
for (const auto& kvp : *this->keys_map)
|
||||
if (kvp.second == v)
|
||||
ss << kvp.first;
|
||||
}
|
||||
}
|
||||
|
||||
friend class cereal::access;
|
||||
template<class Archive> void serialize(Archive& ar) { ar(cereal::base_class<ConfigOptionVector<int>>(this)); }
|
||||
};
|
||||
|
||||
using ConfigOptionEnumsGeneric = ConfigOptionEnumsGenericTempl<false>;
|
||||
using ConfigOptionEnumsGenericNullable = ConfigOptionEnumsGenericTempl<true>;
|
||||
|
||||
// Definition of a configuration value for the purpose of GUI presentation, editing, value mapping and config file handling.
|
||||
class ConfigOptionDef
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1367,61 +1367,61 @@ ModelVolumeType type_from_string(const std::string &s)
|
|||
|
||||
void _3MF_Importer::_extract_custom_gcode_per_print_z_from_archive(::mz_zip_archive &archive, const mz_zip_archive_file_stat &stat)
|
||||
{
|
||||
if (stat.m_uncomp_size > 0) {
|
||||
std::string buffer((size_t)stat.m_uncomp_size, 0);
|
||||
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
|
||||
if (res == 0) {
|
||||
add_error("Error while reading custom Gcodes per height data to buffer");
|
||||
return;
|
||||
}
|
||||
//if (stat.m_uncomp_size > 0) {
|
||||
// std::string buffer((size_t)stat.m_uncomp_size, 0);
|
||||
// mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
|
||||
// if (res == 0) {
|
||||
// add_error("Error while reading custom Gcodes per height data to buffer");
|
||||
// return;
|
||||
// }
|
||||
|
||||
std::istringstream iss(buffer); // wrap returned xml to istringstream
|
||||
pt::ptree main_tree;
|
||||
pt::read_xml(iss, main_tree);
|
||||
// std::istringstream iss(buffer); // wrap returned xml to istringstream
|
||||
// pt::ptree main_tree;
|
||||
// pt::read_xml(iss, main_tree);
|
||||
|
||||
if (main_tree.front().first != "custom_gcodes_per_print_z")
|
||||
return;
|
||||
pt::ptree code_tree = main_tree.front().second;
|
||||
// if (main_tree.front().first != "custom_gcodes_per_print_z")
|
||||
// return;
|
||||
// pt::ptree code_tree = main_tree.front().second;
|
||||
|
||||
m_model->custom_gcode_per_print_z.gcodes.clear();
|
||||
// m_model->custom_gcode_per_print_z.gcodes.clear();
|
||||
|
||||
for (const auto& code : code_tree) {
|
||||
if (code.first == "mode") {
|
||||
pt::ptree tree = code.second;
|
||||
std::string mode = tree.get<std::string>("<xmlattr>.value");
|
||||
m_model->custom_gcode_per_print_z.mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder :
|
||||
mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle :
|
||||
CustomGCode::Mode::MultiExtruder;
|
||||
}
|
||||
if (code.first != "code")
|
||||
continue;
|
||||
// for (const auto& code : code_tree) {
|
||||
// if (code.first == "mode") {
|
||||
// pt::ptree tree = code.second;
|
||||
// std::string mode = tree.get<std::string>("<xmlattr>.value");
|
||||
// m_model->custom_gcode_per_print_z.mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder :
|
||||
// mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle :
|
||||
// CustomGCode::Mode::MultiExtruder;
|
||||
// }
|
||||
// if (code.first != "code")
|
||||
// continue;
|
||||
|
||||
pt::ptree tree = code.second;
|
||||
double print_z = tree.get<double> ("<xmlattr>.print_z" );
|
||||
int extruder = tree.get<int> ("<xmlattr>.extruder");
|
||||
std::string color = tree.get<std::string> ("<xmlattr>.color" );
|
||||
// pt::ptree tree = code.second;
|
||||
// double print_z = tree.get<double> ("<xmlattr>.print_z" );
|
||||
// int extruder = tree.get<int> ("<xmlattr>.extruder");
|
||||
// std::string color = tree.get<std::string> ("<xmlattr>.color" );
|
||||
|
||||
CustomGCode::Type type;
|
||||
std::string extra;
|
||||
pt::ptree attr_tree = tree.find("<xmlattr>")->second;
|
||||
if (attr_tree.find("type") == attr_tree.not_found()) {
|
||||
// It means that data was saved in old version (2.2.0 and older) of PrusaSlicer
|
||||
// read old data ...
|
||||
std::string gcode = tree.get<std::string> ("<xmlattr>.gcode");
|
||||
// ... and interpret them to the new data
|
||||
type = gcode == "M600" ? CustomGCode::ColorChange :
|
||||
gcode == "M601" ? CustomGCode::PausePrint :
|
||||
gcode == "tool_change" ? CustomGCode::ToolChange : CustomGCode::Custom;
|
||||
extra = type == CustomGCode::PausePrint ? color :
|
||||
type == CustomGCode::Custom ? gcode : "";
|
||||
}
|
||||
else {
|
||||
type = static_cast<CustomGCode::Type>(tree.get<int>("<xmlattr>.type"));
|
||||
extra = tree.get<std::string>("<xmlattr>.extra");
|
||||
}
|
||||
m_model->custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, type, extruder, color, extra}) ;
|
||||
}
|
||||
}
|
||||
// CustomGCode::Type type;
|
||||
// std::string extra;
|
||||
// pt::ptree attr_tree = tree.find("<xmlattr>")->second;
|
||||
// if (attr_tree.find("type") == attr_tree.not_found()) {
|
||||
// // It means that data was saved in old version (2.2.0 and older) of PrusaSlicer
|
||||
// // read old data ...
|
||||
// std::string gcode = tree.get<std::string> ("<xmlattr>.gcode");
|
||||
// // ... and interpret them to the new data
|
||||
// type = gcode == "M600" ? CustomGCode::ColorChange :
|
||||
// gcode == "M601" ? CustomGCode::PausePrint :
|
||||
// gcode == "tool_change" ? CustomGCode::ToolChange : CustomGCode::Custom;
|
||||
// extra = type == CustomGCode::PausePrint ? color :
|
||||
// type == CustomGCode::Custom ? gcode : "";
|
||||
// }
|
||||
// else {
|
||||
// type = static_cast<CustomGCode::Type>(tree.get<int>("<xmlattr>.type"));
|
||||
// extra = tree.get<std::string>("<xmlattr>.extra");
|
||||
// }
|
||||
// m_model->custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, type, extruder, color, extra}) ;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
void _3MF_Importer::_handle_start_model_xml_element(const char* name, const char** attributes)
|
||||
|
|
@ -3182,54 +3182,55 @@ ModelVolumeType type_from_string(const std::string &s)
|
|||
|
||||
bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config)
|
||||
{
|
||||
std::string out = "";
|
||||
|
||||
if (!model.custom_gcode_per_print_z.gcodes.empty()) {
|
||||
pt::ptree tree;
|
||||
pt::ptree& main_tree = tree.add("custom_gcodes_per_print_z", "");
|
||||
|
||||
for (const CustomGCode::Item& code : model.custom_gcode_per_print_z.gcodes) {
|
||||
pt::ptree& code_tree = main_tree.add("code", "");
|
||||
|
||||
// store data of custom_gcode_per_print_z
|
||||
code_tree.put("<xmlattr>.print_z" , code.print_z );
|
||||
code_tree.put("<xmlattr>.type" , static_cast<int>(code.type));
|
||||
code_tree.put("<xmlattr>.extruder" , code.extruder );
|
||||
code_tree.put("<xmlattr>.color" , code.color );
|
||||
code_tree.put("<xmlattr>.extra" , code.extra );
|
||||
|
||||
//BBS
|
||||
std::string gcode = //code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") :
|
||||
code.type == CustomGCode::PausePrint ? config->opt_string("machine_pause_gcode") :
|
||||
code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") :
|
||||
code.type == CustomGCode::ToolChange ? "tool_change" : code.extra;
|
||||
code_tree.put("<xmlattr>.gcode" , gcode );
|
||||
}
|
||||
|
||||
pt::ptree& mode_tree = main_tree.add("mode", "");
|
||||
// store mode of a custom_gcode_per_print_z
|
||||
mode_tree.put("<xmlattr>.value", model.custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
|
||||
model.custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode :
|
||||
CustomGCode::MultiExtruderMode);
|
||||
|
||||
if (!tree.empty()) {
|
||||
std::ostringstream oss;
|
||||
boost::property_tree::write_xml(oss, tree);
|
||||
out = oss.str();
|
||||
|
||||
// Post processing("beautification") of the output string
|
||||
boost::replace_all(out, "><", ">\n<");
|
||||
}
|
||||
}
|
||||
|
||||
if (!out.empty()) {
|
||||
if (!mz_zip_writer_add_mem(&archive, CUSTOM_GCODE_PER_PRINT_Z_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
|
||||
add_error("Unable to add custom Gcodes per print_z file to archive");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
//std::string out = "";
|
||||
|
||||
//if (!model.custom_gcode_per_print_z.gcodes.empty()) {
|
||||
// pt::ptree tree;
|
||||
// pt::ptree& main_tree = tree.add("custom_gcodes_per_print_z", "");
|
||||
|
||||
// for (const CustomGCode::Item& code : model.custom_gcode_per_print_z.gcodes) {
|
||||
// pt::ptree& code_tree = main_tree.add("code", "");
|
||||
|
||||
// // store data of custom_gcode_per_print_z
|
||||
// code_tree.put("<xmlattr>.print_z" , code.print_z );
|
||||
// code_tree.put("<xmlattr>.type" , static_cast<int>(code.type));
|
||||
// code_tree.put("<xmlattr>.extruder" , code.extruder );
|
||||
// code_tree.put("<xmlattr>.color" , code.color );
|
||||
// code_tree.put("<xmlattr>.extra" , code.extra );
|
||||
|
||||
// //BBS
|
||||
// std::string gcode = //code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") :
|
||||
// code.type == CustomGCode::PausePrint ? config->opt_string("machine_pause_gcode") :
|
||||
// code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") :
|
||||
// code.type == CustomGCode::ToolChange ? "tool_change" : code.extra;
|
||||
// code_tree.put("<xmlattr>.gcode" , gcode );
|
||||
// }
|
||||
|
||||
// pt::ptree& mode_tree = main_tree.add("mode", "");
|
||||
// // store mode of a custom_gcode_per_print_z
|
||||
// mode_tree.put("<xmlattr>.value", model.custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
|
||||
// model.custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode :
|
||||
// CustomGCode::MultiExtruderMode);
|
||||
|
||||
// if (!tree.empty()) {
|
||||
// std::ostringstream oss;
|
||||
// boost::property_tree::write_xml(oss, tree);
|
||||
// out = oss.str();
|
||||
|
||||
// // Post processing("beautification") of the output string
|
||||
// boost::replace_all(out, "><", ">\n<");
|
||||
// }
|
||||
//}
|
||||
|
||||
//if (!out.empty()) {
|
||||
// if (!mz_zip_writer_add_mem(&archive, CUSTOM_GCODE_PER_PRINT_Z_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION)) {
|
||||
// add_error("Unable to add custom Gcodes per print_z file to archive");
|
||||
// return false;
|
||||
// }
|
||||
//}
|
||||
|
||||
//return true;
|
||||
}
|
||||
|
||||
// Perform conversions based on the config values available.
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@
|
|||
#include "TopExp_Explorer.hxx"
|
||||
#include "BRep_Tool.hxx"
|
||||
|
||||
const double STEP_TRANS_CHORD_ERROR = 0.005;
|
||||
const double STEP_TRANS_ANGLE_RES = 1;
|
||||
const double STEP_TRANS_CHORD_ERROR = 0.0025;
|
||||
const double STEP_TRANS_ANGLE_RES = 0.5;
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
|
@ -210,13 +210,15 @@ static void getNamedSolids(const TopLoc_Location& location, const std::string& p
|
|||
}
|
||||
}
|
||||
|
||||
bool load_step(const char *path, Model *model, ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn)
|
||||
bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgressFn stepFn, StepIsUtf8Fn isUtf8Fn)
|
||||
{
|
||||
bool cb_cancel = false;
|
||||
if (stepFn) {
|
||||
stepFn(LOAD_STEP_STAGE_READ_FILE, 0, 1, cb_cancel);
|
||||
if (cb_cancel)
|
||||
is_cancel = cb_cancel;
|
||||
if (cb_cancel) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!StepPreProcessor::isUtf8File(path) && isUtf8Fn)
|
||||
|
|
@ -248,6 +250,7 @@ bool load_step(const char *path, Model *model, ImportStepProgressFn stepFn, Step
|
|||
if (stepFn) {
|
||||
if ((iLabel % stage_unit2) == 0) {
|
||||
stepFn(LOAD_STEP_STAGE_GET_SOLID, iLabel, topShapeLength, cb_cancel);
|
||||
is_cancel = cb_cancel;
|
||||
}
|
||||
if (cb_cancel) {
|
||||
shapeTool.reset(nullptr);
|
||||
|
|
@ -345,6 +348,7 @@ bool load_step(const char *path, Model *model, ImportStepProgressFn stepFn, Step
|
|||
if (stepFn) {
|
||||
if ((i % stage_unit3) == 0) {
|
||||
stepFn(LOAD_STEP_STAGE_GET_MESH, i, stl.size(), cb_cancel);
|
||||
is_cancel = cb_cancel;
|
||||
}
|
||||
if (cb_cancel) {
|
||||
model->delete_object(new_object);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ typedef std::function<void(int load_stage, int current, int total, bool& cancel)
|
|||
typedef std::function<void(bool isUtf8)> StepIsUtf8Fn;
|
||||
|
||||
//BBS: Load an step file into a provided model.
|
||||
extern bool load_step(const char *path, Model *model, ImportStepProgressFn proFn = nullptr, StepIsUtf8Fn isUtf8Fn = nullptr);
|
||||
extern bool load_step(const char *path, Model *model, bool& is_cancel, ImportStepProgressFn proFn = nullptr, StepIsUtf8Fn isUtf8Fn = nullptr);
|
||||
|
||||
//BBS: Used to detect what kind of encoded type is used in name field of step
|
||||
// If is encoded in UTF8, the file don't need to be handled, then return the original path directly.
|
||||
|
|
|
|||
|
|
@ -240,6 +240,7 @@ static constexpr const char* LAST_TRIANGLE_ID_ATTR = "lastid";
|
|||
static constexpr const char* SUBTYPE_ATTR = "subtype";
|
||||
static constexpr const char* LOCK_ATTR = "locked";
|
||||
static constexpr const char* BED_TYPE_ATTR = "bed_type";
|
||||
static constexpr const char* PRINT_SEQUENCE_ATTR = "print_sequence";
|
||||
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";
|
||||
|
|
@ -1776,6 +1777,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
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;
|
||||
plate_data_list[it->first-1]->config = it->second->config;
|
||||
|
||||
current_plate_data = plate_data_list[it->first - 1];
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", plate %1%, thumbnail_file=%2%")%it->first %plate_data_list[it->first-1]->thumbnail_file;
|
||||
it++;
|
||||
|
|
@ -2499,6 +2501,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
|
||||
void _BBS_3MF_Importer::_extract_custom_gcode_per_print_z_from_archive(::mz_zip_archive &archive, const mz_zip_archive_file_stat &stat)
|
||||
{
|
||||
//BBS: add plate tree related logic
|
||||
if (stat.m_uncomp_size > 0) {
|
||||
std::string buffer((size_t)stat.m_uncomp_size, 0);
|
||||
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
|
||||
|
|
@ -2513,45 +2516,71 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
|
||||
if (main_tree.front().first != "custom_gcodes_per_layer")
|
||||
return;
|
||||
pt::ptree code_tree = main_tree.front().second;
|
||||
|
||||
m_model->custom_gcode_per_print_z.gcodes.clear();
|
||||
auto extract_code = [this](int plate_id, pt::ptree code_tree) {
|
||||
for (const auto& code : code_tree) {
|
||||
if (code.first == "mode") {
|
||||
pt::ptree tree = code.second;
|
||||
std::string mode = tree.get<std::string>("<xmlattr>.value");
|
||||
m_model->plates_custom_gcodes[plate_id - 1].mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder :
|
||||
mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle :
|
||||
CustomGCode::Mode::MultiExtruder;
|
||||
}
|
||||
if (code.first == "layer") {
|
||||
pt::ptree tree = code.second;
|
||||
double print_z = tree.get<double>("<xmlattr>.top_z");
|
||||
int extruder = tree.get<int>("<xmlattr>.extruder");
|
||||
std::string color = tree.get<std::string>("<xmlattr>.color");
|
||||
|
||||
for (const auto& code : code_tree) {
|
||||
if (code.first == "mode") {
|
||||
pt::ptree tree = code.second;
|
||||
std::string mode = tree.get<std::string>("<xmlattr>.value");
|
||||
m_model->custom_gcode_per_print_z.mode = mode == CustomGCode::SingleExtruderMode ? CustomGCode::Mode::SingleExtruder :
|
||||
mode == CustomGCode::MultiAsSingleMode ? CustomGCode::Mode::MultiAsSingle :
|
||||
CustomGCode::Mode::MultiExtruder;
|
||||
CustomGCode::Type type;
|
||||
std::string extra;
|
||||
pt::ptree attr_tree = tree.find("<xmlattr>")->second;
|
||||
if (attr_tree.find("type") == attr_tree.not_found()) {
|
||||
// It means that data was saved in old version (2.2.0 and older) of PrusaSlicer
|
||||
// read old data ...
|
||||
std::string gcode = tree.get<std::string>("<xmlattr>.gcode");
|
||||
// ... and interpret them to the new data
|
||||
type = gcode == "M600" ? CustomGCode::ColorChange :
|
||||
gcode == "M601" ? CustomGCode::PausePrint :
|
||||
gcode == "tool_change" ? CustomGCode::ToolChange : CustomGCode::Custom;
|
||||
extra = type == CustomGCode::PausePrint ? color :
|
||||
type == CustomGCode::Custom ? gcode : "";
|
||||
}
|
||||
else {
|
||||
type = static_cast<CustomGCode::Type>(tree.get<int>("<xmlattr>.type"));
|
||||
extra = tree.get<std::string>("<xmlattr>.extra");
|
||||
}
|
||||
m_model->plates_custom_gcodes[plate_id - 1].gcodes.push_back(CustomGCode::Item{ print_z, type, extruder, color, extra });
|
||||
}
|
||||
}
|
||||
if (code.first != "layer")
|
||||
continue;
|
||||
};
|
||||
|
||||
pt::ptree tree = code.second;
|
||||
double print_z = tree.get<double> ("<xmlattr>.top_z" );
|
||||
int extruder = tree.get<int> ("<xmlattr>.extruder");
|
||||
std::string color = tree.get<std::string> ("<xmlattr>.color" );
|
||||
m_model->plates_custom_gcodes.clear();
|
||||
|
||||
CustomGCode::Type type;
|
||||
std::string extra;
|
||||
pt::ptree attr_tree = tree.find("<xmlattr>")->second;
|
||||
if (attr_tree.find("type") == attr_tree.not_found()) {
|
||||
// It means that data was saved in old version (2.2.0 and older) of PrusaSlicer
|
||||
// read old data ...
|
||||
std::string gcode = tree.get<std::string> ("<xmlattr>.gcode");
|
||||
// ... and interpret them to the new data
|
||||
type = gcode == "M600" ? CustomGCode::ColorChange :
|
||||
gcode == "M601" ? CustomGCode::PausePrint :
|
||||
gcode == "tool_change" ? CustomGCode::ToolChange : CustomGCode::Custom;
|
||||
extra = type == CustomGCode::PausePrint ? color :
|
||||
type == CustomGCode::Custom ? gcode : "";
|
||||
bool has_plate_info = false;
|
||||
for (const auto& element : main_tree.front().second) {
|
||||
if (element.first == "plate") {
|
||||
has_plate_info = true;
|
||||
|
||||
int plate_id = -1;
|
||||
pt::ptree code_tree = element.second;
|
||||
for (const auto& code : code_tree) {
|
||||
if (code.first == "plate_info") {
|
||||
plate_id = code.second.get<int>("<xmlattr>.id");
|
||||
}
|
||||
|
||||
}
|
||||
if (plate_id == -1)
|
||||
continue;
|
||||
|
||||
extract_code(plate_id, code_tree);
|
||||
}
|
||||
else {
|
||||
type = static_cast<CustomGCode::Type>(tree.get<int>("<xmlattr>.type"));
|
||||
extra = tree.get<std::string>("<xmlattr>.extra");
|
||||
}
|
||||
m_model->custom_gcode_per_print_z.gcodes.push_back(CustomGCode::Item{print_z, type, extruder, color, extra}) ;
|
||||
}
|
||||
|
||||
if (!has_plate_info) {
|
||||
int plate_id = 1;
|
||||
pt::ptree code_tree = main_tree.front().second;
|
||||
extract_code(plate_id, code_tree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3444,6 +3473,12 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
ConfigOptionEnum<BedType>::from_string(value, bed_type);
|
||||
m_curr_plater->config.set_key_value("curr_bed_type", new ConfigOptionEnum<BedType>(bed_type));
|
||||
}
|
||||
else if (key == PRINT_SEQUENCE_ATTR)
|
||||
{
|
||||
PrintSequence print_sequence = PrintSequence::ByLayer;
|
||||
ConfigOptionEnum<PrintSequence>::from_string(value, print_sequence);
|
||||
m_curr_plater->config.set_key_value("print_sequence", new ConfigOptionEnum<PrintSequence>(print_sequence));
|
||||
}
|
||||
else if (key == GCODE_FILE_ATTR)
|
||||
{
|
||||
m_curr_plater->gcode_file = value;
|
||||
|
|
@ -3805,6 +3840,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
//add the shared mesh logic
|
||||
shared_mesh_id = ::atoi(metadata.value.c_str());
|
||||
found_count++;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": line %1%, shared_mesh_id %2%")%__LINE__%shared_mesh_id;
|
||||
}
|
||||
|
||||
if (found_count >= 2)
|
||||
|
|
@ -3822,6 +3858,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
std::map<int, ModelVolume*>::iterator iter = m_shared_meshes.find(shared_mesh_id);
|
||||
if (iter != m_shared_meshes.end()) {
|
||||
shared_volume = iter->second;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": line %1%, found shared mesh, id %2%, mesh %3%")%__LINE__%shared_mesh_id%shared_volume;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3874,11 +3911,17 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
|
||||
volume = object.add_volume(std::move(triangle_mesh));
|
||||
|
||||
m_shared_meshes[sub_object->id] = volume;
|
||||
if (shared_mesh_id != -1)
|
||||
//for some cases the shared mesh is in other plate and not loaded in cli slicing
|
||||
//we need to use the first one in the same plate instead
|
||||
m_shared_meshes[shared_mesh_id] = volume;
|
||||
else
|
||||
m_shared_meshes[sub_object->id] = volume;
|
||||
}
|
||||
else {
|
||||
//create volume to use shared mesh
|
||||
volume = object.add_volume_with_shared_mesh(*shared_volume);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": line %1%, create volume using shared_mesh %2%")%__LINE__%shared_volume;
|
||||
}
|
||||
// stores the volume matrix taken from the metadata, if present
|
||||
if (has_transform)
|
||||
|
|
@ -5347,6 +5390,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
bool sub_model = !objects_data.empty();
|
||||
bool write_object = sub_model || !m_split_model;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", filename %1%, m_split_model %2%, sub_model %3%")%filename % m_split_model % sub_model;
|
||||
|
||||
#if WRITE_ZIP_LANGUAGE_ENCODING
|
||||
auto & zip_filename = filename;
|
||||
#else
|
||||
|
|
@ -6388,6 +6433,11 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
|||
if (bed_type_opt != nullptr && bed_type_names.size() > bed_type_opt->getInt())
|
||||
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << BED_TYPE_ATTR << "\" " << VALUE_ATTR << "=\"" << bed_type_names[bed_type_opt->getInt()] << "\"/>\n";
|
||||
|
||||
ConfigOption* print_sequence_opt = plate_data->config.option("print_sequence");
|
||||
t_config_enum_names print_sequence_names = ConfigOptionEnum<PrintSequence>::get_enum_names();
|
||||
if (print_sequence_opt != nullptr && print_sequence_names.size() > print_sequence_opt->getInt())
|
||||
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PRINT_SEQUENCE_ATTR << "\" " << VALUE_ATTR << "=\"" << print_sequence_names[print_sequence_opt->getInt()] << "\"/>\n";
|
||||
|
||||
if (save_gcode)
|
||||
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << GCODE_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << xml_escape(plate_data->gcode_file) << "\"/>\n";
|
||||
if (!plate_data->gcode_file.empty()) {
|
||||
|
|
@ -6621,46 +6671,50 @@ bool _BBS_3MF_Exporter::_add_gcode_file_to_archive(mz_zip_archive& archive, cons
|
|||
return result;
|
||||
}
|
||||
|
||||
bool _BBS_3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config)
|
||||
bool _BBS_3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model, const DynamicPrintConfig* config)
|
||||
{
|
||||
//BBS: add plate tree related logic
|
||||
std::string out = "";
|
||||
|
||||
if (!model.custom_gcode_per_print_z.gcodes.empty()) {
|
||||
pt::ptree tree;
|
||||
pt::ptree& main_tree = tree.add("custom_gcodes_per_layer", "");
|
||||
|
||||
for (const CustomGCode::Item& code : model.custom_gcode_per_print_z.gcodes) {
|
||||
pt::ptree& code_tree = main_tree.add("layer", "");
|
||||
bool has_custom_gcode = false;
|
||||
pt::ptree tree;
|
||||
pt::ptree& main_tree = tree.add("custom_gcodes_per_layer", "");
|
||||
for (auto custom_gcodes : model.plates_custom_gcodes) {
|
||||
has_custom_gcode = true;
|
||||
pt::ptree& plate_tree = main_tree.add("plate", "");
|
||||
pt::ptree& plate_idx_tree = plate_tree.add("plate_info", "");
|
||||
plate_idx_tree.put("<xmlattr>.id", custom_gcodes.first + 1);
|
||||
|
||||
// store data of custom_gcode_per_print_z
|
||||
code_tree.put("<xmlattr>.top_z" , code.print_z );
|
||||
code_tree.put("<xmlattr>.type" , static_cast<int>(code.type));
|
||||
code_tree.put("<xmlattr>.extruder" , code.extruder );
|
||||
code_tree.put("<xmlattr>.color" , code.color );
|
||||
code_tree.put("<xmlattr>.extra" , code.extra );
|
||||
for (const CustomGCode::Item& code : custom_gcodes.second.gcodes) {
|
||||
pt::ptree& code_tree = plate_tree.add("layer", "");
|
||||
code_tree.put("<xmlattr>.top_z", code.print_z);
|
||||
code_tree.put("<xmlattr>.type", static_cast<int>(code.type));
|
||||
code_tree.put("<xmlattr>.extruder", code.extruder);
|
||||
code_tree.put("<xmlattr>.color", code.color);
|
||||
code_tree.put("<xmlattr>.extra", code.extra);
|
||||
|
||||
//BBS
|
||||
std::string gcode = //code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") :
|
||||
code.type == CustomGCode::PausePrint ? config->opt_string("machine_pause_gcode") :
|
||||
code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") :
|
||||
code.type == CustomGCode::ToolChange ? "tool_change" : code.extra;
|
||||
code_tree.put("<xmlattr>.gcode" , gcode );
|
||||
}
|
||||
//BBS
|
||||
std::string gcode = //code.type == CustomGCode::ColorChange ? config->opt_string("color_change_gcode") :
|
||||
code.type == CustomGCode::PausePrint ? config->opt_string("machine_pause_gcode") :
|
||||
code.type == CustomGCode::Template ? config->opt_string("template_custom_gcode") :
|
||||
code.type == CustomGCode::ToolChange ? "tool_change" : code.extra;
|
||||
code_tree.put("<xmlattr>.gcode", gcode);
|
||||
}
|
||||
|
||||
pt::ptree& mode_tree = main_tree.add("mode", "");
|
||||
// store mode of a custom_gcode_per_print_z
|
||||
mode_tree.put("<xmlattr>.value", model.custom_gcode_per_print_z.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
|
||||
model.custom_gcode_per_print_z.mode == CustomGCode::Mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode :
|
||||
CustomGCode::MultiExtruderMode);
|
||||
pt::ptree& mode_tree = plate_tree.add("mode", "");
|
||||
// store mode of a custom_gcode_per_print_z
|
||||
mode_tree.put("<xmlattr>.value", custom_gcodes.second.mode == CustomGCode::Mode::SingleExtruder ? CustomGCode::SingleExtruderMode :
|
||||
custom_gcodes.second.mode == CustomGCode::Mode::MultiAsSingle ? CustomGCode::MultiAsSingleMode :
|
||||
CustomGCode::MultiExtruderMode);
|
||||
|
||||
if (!tree.empty()) {
|
||||
std::ostringstream oss;
|
||||
boost::property_tree::write_xml(oss, tree);
|
||||
out = oss.str();
|
||||
}
|
||||
if (has_custom_gcode) {
|
||||
std::ostringstream oss;
|
||||
boost::property_tree::write_xml(oss, tree);
|
||||
out = oss.str();
|
||||
|
||||
// Post processing("beautification") of the output string
|
||||
boost::replace_all(out, "><", ">\n<");
|
||||
}
|
||||
// Post processing("beautification") of the output string
|
||||
boost::replace_all(out, "><", ">\n<");
|
||||
}
|
||||
|
||||
if (!out.empty()) {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ struct ThumbnailData;
|
|||
#define EMBEDDED_FILAMENT_FILE_FORMAT "Metadata/filament_settings_%1%.config"
|
||||
#define EMBEDDED_PRINTER_FILE_FORMAT "Metadata/machine_settings_%1%.config"
|
||||
|
||||
#define BBL_DESIGNER_PROFILE_ID_TAG "DesignProfileId"
|
||||
#define BBL_DESIGNER_MODEL_ID_TAG "DesignModelId"
|
||||
|
||||
|
||||
//BBS: define assistant struct to store temporary variable during exporting 3mf
|
||||
class PackingTemporaryData
|
||||
|
|
|
|||
|
|
@ -82,6 +82,132 @@ static const int g_max_flush_count = 4;
|
|||
|
||||
bool GCode::gcode_label_objects = true;
|
||||
|
||||
Vec2d travel_point_1;
|
||||
Vec2d travel_point_2;
|
||||
Vec2d travel_point_3;
|
||||
|
||||
static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
{
|
||||
// give safe value in case there is no start_end_points in config
|
||||
std::vector<Vec2d> out_points;
|
||||
out_points.emplace_back(Vec2d(54, 0));
|
||||
out_points.emplace_back(Vec2d(54, 0));
|
||||
out_points.emplace_back(Vec2d(54, 245));
|
||||
|
||||
// get the start_end_points from config (20, -3) (54, 245)
|
||||
Pointfs points = print.config().start_end_points.values;
|
||||
if (points.size() != 2)
|
||||
return out_points;
|
||||
|
||||
Vec2d start_point = points[0];
|
||||
Vec2d end_point = points[1];
|
||||
|
||||
// the cutter area size(18, 28)
|
||||
Pointfs excluse_area = print.config().bed_exclude_area.values;
|
||||
if (excluse_area.size() != 4)
|
||||
return out_points;
|
||||
|
||||
double cutter_area_x = excluse_area[2].x() + 2;
|
||||
double cutter_area_y = excluse_area[2].y() + 2;
|
||||
|
||||
double start_x_position = start_point.x();
|
||||
double end_x_position = end_point.x();
|
||||
double end_y_position = end_point.y();
|
||||
|
||||
bool can_travel_form_left = true;
|
||||
|
||||
// step 1: get the x-range intervals of all objects
|
||||
std::vector<std::pair<double, double>> object_intervals;
|
||||
for (PrintObject *print_object : print.objects()) {
|
||||
const PrintInstances &print_instances = print_object->instances();
|
||||
BoundingBoxf3 bounding_box = print_instances[0].model_instance->get_object()->bounding_box();
|
||||
|
||||
if (bounding_box.min.x() < start_x_position && bounding_box.min.y() < cutter_area_y)
|
||||
can_travel_form_left = false;
|
||||
|
||||
std::pair<double, double> object_scope = std::make_pair(bounding_box.min.x() - 2, bounding_box.max.x() + 2);
|
||||
if (object_intervals.empty())
|
||||
object_intervals.push_back(object_scope);
|
||||
else {
|
||||
std::vector<std::pair<double, double>> new_object_intervals;
|
||||
bool intervals_intersect = false;
|
||||
std::pair<double, double> new_merged_scope;
|
||||
for (auto object_interval : object_intervals) {
|
||||
if (object_interval.second >= object_scope.first && object_interval.first <= object_scope.second) {
|
||||
if (intervals_intersect) {
|
||||
new_merged_scope = std::make_pair(std::min(object_interval.first, new_merged_scope.first), std::max(object_interval.second, new_merged_scope.second));
|
||||
} else { // it is the first intersection
|
||||
new_merged_scope = std::make_pair(std::min(object_interval.first, object_scope.first), std::max(object_interval.second, object_scope.second));
|
||||
}
|
||||
intervals_intersect = true;
|
||||
} else {
|
||||
new_object_intervals.push_back(object_interval);
|
||||
}
|
||||
}
|
||||
|
||||
if (intervals_intersect) {
|
||||
new_object_intervals.push_back(new_merged_scope);
|
||||
object_intervals = new_object_intervals;
|
||||
} else
|
||||
object_intervals.push_back(object_scope);
|
||||
}
|
||||
}
|
||||
|
||||
// step 2: get the available x-range
|
||||
std::sort(object_intervals.begin(), object_intervals.end(),
|
||||
[](const std::pair<double, double> &left, const std::pair<double, double> &right) {
|
||||
return left.first < right.first;
|
||||
});
|
||||
std::vector<std::pair<double, double>> available_intervals;
|
||||
double start_position = 0;
|
||||
for (auto object_interval : object_intervals) {
|
||||
if (object_interval.first > start_position)
|
||||
available_intervals.push_back(std::make_pair(start_position, object_interval.first));
|
||||
start_position = object_interval.second;
|
||||
}
|
||||
available_intervals.push_back(std::make_pair(start_position, 255));
|
||||
|
||||
// step 3: get the nearest path
|
||||
double new_path = 255;
|
||||
for (auto available_interval : available_intervals) {
|
||||
if (available_interval.first > end_x_position) {
|
||||
double distance = available_interval.first - end_x_position;
|
||||
new_path = abs(end_x_position - new_path) < distance ? new_path : available_interval.first;
|
||||
break;
|
||||
} else {
|
||||
if (available_interval.second >= end_x_position) {
|
||||
new_path = end_x_position;
|
||||
break;
|
||||
} else if (!can_travel_form_left && available_interval.second < start_x_position) {
|
||||
continue;
|
||||
} else {
|
||||
new_path = available_interval.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step 4: generate path points (new_path == start_x_position means not need to change path)
|
||||
Vec2d out_point_1;
|
||||
Vec2d out_point_2;
|
||||
Vec2d out_point_3;
|
||||
if (new_path < start_x_position) {
|
||||
out_point_1 = Vec2d(start_x_position, cutter_area_y);
|
||||
out_point_2 = Vec2d(new_path, cutter_area_y);
|
||||
out_point_3 = Vec2d(new_path, end_y_position);
|
||||
} else {
|
||||
out_point_1 = Vec2d(new_path, 0);
|
||||
out_point_2 = Vec2d(new_path, 0);
|
||||
out_point_3 = Vec2d(new_path, end_y_position);
|
||||
}
|
||||
|
||||
out_points.clear();
|
||||
out_points.emplace_back(out_point_1);
|
||||
out_points.emplace_back(out_point_2);
|
||||
out_points.emplace_back(out_point_3);
|
||||
|
||||
return out_points;
|
||||
}
|
||||
|
||||
// Only add a newline in case the current G-code does not end with a newline.
|
||||
static inline void check_add_eol(std::string& gcode)
|
||||
{
|
||||
|
|
@ -378,6 +504,12 @@ bool GCode::gcode_label_objects = true;
|
|||
config.set_key_value("second_flush_volume", new ConfigOptionFloat(purge_length / 2.f));
|
||||
config.set_key_value("old_filament_e_feedrate", new ConfigOptionInt(old_filament_e_feedrate));
|
||||
config.set_key_value("new_filament_e_feedrate", new ConfigOptionInt(new_filament_e_feedrate));
|
||||
config.set_key_value("travel_point_1_x", new ConfigOptionFloat(float(travel_point_1.x())));
|
||||
config.set_key_value("travel_point_1_y", new ConfigOptionFloat(float(travel_point_1.y())));
|
||||
config.set_key_value("travel_point_2_x", new ConfigOptionFloat(float(travel_point_2.x())));
|
||||
config.set_key_value("travel_point_2_y", new ConfigOptionFloat(float(travel_point_2.y())));
|
||||
config.set_key_value("travel_point_3_x", new ConfigOptionFloat(float(travel_point_3.x())));
|
||||
config.set_key_value("travel_point_3_y", new ConfigOptionFloat(float(travel_point_3.y())));
|
||||
|
||||
int flush_count = std::min(g_max_flush_count, (int)std::round(purge_volume / g_purge_volume_one_time));
|
||||
float flush_unit = purge_length / flush_count;
|
||||
|
|
@ -624,7 +756,7 @@ bool GCode::gcode_label_objects = true;
|
|||
std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObject& object)
|
||||
{
|
||||
std::vector<GCode::LayerToPrint> layers_to_print;
|
||||
layers_to_print.reserve(object.layers().size() + object.support_layers().size() + object.tree_support_layers().size());
|
||||
layers_to_print.reserve(object.layers().size() + object.support_layers().size());
|
||||
|
||||
/*
|
||||
// Calculate a minimum support layer height as a minimum over all extruders, but not smaller than 10um.
|
||||
|
|
@ -647,9 +779,8 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
|
|||
// Pair the object layers with the support layers by z.
|
||||
size_t idx_object_layer = 0;
|
||||
size_t idx_support_layer = 0;
|
||||
size_t idx_tree_support_layer = 0;
|
||||
const LayerToPrint* last_extrusion_layer = nullptr;
|
||||
while (idx_object_layer < object.layers().size() || idx_support_layer < object.support_layers().size() || idx_tree_support_layer < object.tree_support_layers().size()) {
|
||||
while (idx_object_layer < object.layers().size() || idx_support_layer < object.support_layers().size()) {
|
||||
LayerToPrint layer_to_print;
|
||||
double print_z_min = std::numeric_limits<double>::max();
|
||||
if (idx_object_layer < object.layers().size()) {
|
||||
|
|
@ -662,11 +793,6 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
|
|||
print_z_min = std::min(print_z_min, layer_to_print.support_layer->print_z);
|
||||
}
|
||||
|
||||
if (idx_tree_support_layer < object.tree_support_layers().size()) {
|
||||
layer_to_print.tree_support_layer = object.tree_support_layers()[idx_tree_support_layer++];
|
||||
print_z_min = std::min(print_z_min, layer_to_print.tree_support_layer->print_z);
|
||||
}
|
||||
|
||||
if (layer_to_print.object_layer && layer_to_print.object_layer->print_z > print_z_min + EPSILON) {
|
||||
layer_to_print.object_layer = nullptr;
|
||||
--idx_object_layer;
|
||||
|
|
@ -677,17 +803,11 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
|
|||
--idx_support_layer;
|
||||
}
|
||||
|
||||
if (layer_to_print.tree_support_layer && layer_to_print.tree_support_layer->print_z > print_z_min + EPSILON) {
|
||||
layer_to_print.tree_support_layer = nullptr;
|
||||
--idx_tree_support_layer;
|
||||
}
|
||||
|
||||
layer_to_print.original_object = &object;
|
||||
layers_to_print.push_back(layer_to_print);
|
||||
|
||||
bool has_extrusions = (layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
|
||||
|| (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions()
|
||||
|| (layer_to_print.tree_support_layer && layer_to_print.tree_support_layer->has_extrusions()));
|
||||
|| (layer_to_print.support_layer && layer_to_print.support_layer->has_extrusions());
|
||||
|
||||
// Check that there are extrusions on the very first layer. The case with empty
|
||||
// first layer may result in skirt/brim in the air and maybe other issues.
|
||||
|
|
@ -699,13 +819,12 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
|
|||
// In case there are extrusions on this layer, check there is a layer to lay it on.
|
||||
if ((layer_to_print.object_layer && layer_to_print.object_layer->has_extrusions())
|
||||
// Allow empty support layers, as the support generator may produce no extrusions for non-empty support regions.
|
||||
|| (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)
|
||||
|| (layer_to_print.tree_support_layer)) {
|
||||
|| (layer_to_print.support_layer /* && layer_to_print.support_layer->has_extrusions() */)) {
|
||||
double top_cd = object.config().support_top_z_distance;
|
||||
//double bottom_cd = object.config().support_bottom_z_distance == 0. ? top_cd : object.config().support_bottom_z_distance;
|
||||
double bottom_cd = top_cd;
|
||||
|
||||
double extra_gap = ((layer_to_print.support_layer || layer_to_print.tree_support_layer) ? bottom_cd : top_cd);
|
||||
double extra_gap = (layer_to_print.support_layer ? bottom_cd : top_cd);
|
||||
|
||||
double maximal_print_z = (last_extrusion_layer ? last_extrusion_layer->print_z() : 0.)
|
||||
+ layer_to_print.layer()->height
|
||||
|
|
@ -914,6 +1033,9 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
|||
{
|
||||
PROFILE_CLEAR();
|
||||
|
||||
// BBS
|
||||
m_curr_print = print;
|
||||
|
||||
CNumericLocalesSetter locales_setter;
|
||||
|
||||
// Does the file exist? If so, we hope that it is still valid.
|
||||
|
|
@ -1333,7 +1455,14 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
for (auto layer : object->support_layers())
|
||||
zs.push_back((coord_t)(layer->print_z / EPSILON));
|
||||
std::sort(zs.begin(), zs.end());
|
||||
m_layer_count += (unsigned int)(object->instances().size() * (std::unique(zs.begin(), zs.end()) - zs.begin()));
|
||||
//BBS: merge numerically very close Z values.
|
||||
auto end_it = std::unique(zs.begin(), zs.end());
|
||||
unsigned int temp_layer_count = (unsigned int)(end_it - zs.begin());
|
||||
for (auto it = zs.begin(); it != end_it - 1; it++) {
|
||||
if (abs(*it - *(it + 1)) < EPSILON)
|
||||
temp_layer_count--;
|
||||
}
|
||||
m_layer_count += (unsigned int)(object->instances().size() * temp_layer_count);
|
||||
}
|
||||
} else {
|
||||
// Print all objects with the same print_z together.
|
||||
|
|
@ -1347,7 +1476,13 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
zs.push_back((coord_t)(layer->print_z / EPSILON));
|
||||
}
|
||||
std::sort(zs.begin(), zs.end());
|
||||
m_layer_count = (unsigned int)(std::unique(zs.begin(), zs.end()) - zs.begin());
|
||||
//BBS: merge numerically very close Z values.
|
||||
auto end_it = std::unique(zs.begin(), zs.end());
|
||||
m_layer_count = (unsigned int)(end_it - zs.begin());
|
||||
for (auto it = zs.begin(); it != end_it - 1; it++) {
|
||||
if (abs(*it - *(it + 1)) < EPSILON)
|
||||
m_layer_count--;
|
||||
}
|
||||
}
|
||||
print.throw_if_canceled();
|
||||
|
||||
|
|
@ -1374,6 +1509,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
//BBS: total estimated printing time
|
||||
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str());
|
||||
//BBS: total layer number
|
||||
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Total_Layer_Number_Placeholder).c_str());
|
||||
file.write_format("; total layer number: %d\n", m_layer_count);
|
||||
file.write_format("; HEADER_BLOCK_END\n\n");
|
||||
|
||||
|
|
@ -1450,6 +1586,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
// For a print by objects, find the 1st printing object.
|
||||
ToolOrdering tool_ordering;
|
||||
unsigned int initial_extruder_id = (unsigned int)-1;
|
||||
//BBS: first non-support filament extruder
|
||||
unsigned int initial_non_support_extruder_id;
|
||||
unsigned int final_extruder_id = (unsigned int)-1;
|
||||
bool has_wipe_tower = false;
|
||||
std::vector<const PrintInstance*> print_object_instances_ordering;
|
||||
|
|
@ -1462,8 +1600,33 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
print_object_instance_sequential_active = print_object_instances_ordering.begin();
|
||||
for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
|
||||
tool_ordering = ToolOrdering(*(*print_object_instance_sequential_active)->print_object, initial_extruder_id);
|
||||
if ((initial_extruder_id = tool_ordering.first_extruder()) != static_cast<unsigned int>(-1))
|
||||
if ((initial_extruder_id = tool_ordering.first_extruder()) != static_cast<unsigned int>(-1)) {
|
||||
//BBS: try to find the non-support filament extruder if is multi color and initial_extruder is support filament
|
||||
initial_non_support_extruder_id = initial_extruder_id;
|
||||
if (tool_ordering.all_extruders().size() > 1 && print.config().filament_is_support.get_at(initial_extruder_id)) {
|
||||
bool has_non_support_filament = false;
|
||||
for (unsigned int extruder : tool_ordering.all_extruders()) {
|
||||
if (!print.config().filament_is_support.get_at(extruder)) {
|
||||
has_non_support_filament = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//BBS: find the non-support filament extruder of object
|
||||
if (has_non_support_filament)
|
||||
for (LayerTools layer_tools : tool_ordering.layer_tools()) {
|
||||
if (!layer_tools.has_object)
|
||||
continue;
|
||||
for (unsigned int extruder : layer_tools.extruders) {
|
||||
if (print.config().filament_is_support.get_at(extruder))
|
||||
continue;
|
||||
initial_non_support_extruder_id = extruder;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (initial_extruder_id == static_cast<unsigned int>(-1))
|
||||
// No object to print was found, cancel the G-code export.
|
||||
|
|
@ -1471,6 +1634,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
// We don't allow switching of extruders per layer by Model::custom_gcode_per_print_z in sequential mode.
|
||||
// Use the extruder IDs collected from Regions.
|
||||
this->set_extruders(print.extruders());
|
||||
|
||||
has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower();
|
||||
} else {
|
||||
// Find tool ordering for all the objects at once, and the initial extruder ID.
|
||||
// If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it.
|
||||
|
|
@ -1490,6 +1655,32 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
#else
|
||||
initial_extruder_id = tool_ordering.first_extruder();
|
||||
#endif
|
||||
//BBS: try to find the non-support filament extruder if is multi color and initial_extruder is support filament
|
||||
if (initial_extruder_id != static_cast<unsigned int>(-1)) {
|
||||
initial_non_support_extruder_id = initial_extruder_id;
|
||||
if (tool_ordering.all_extruders().size() > 1 && print.config().filament_is_support.get_at(initial_extruder_id)) {
|
||||
bool has_non_support_filament = false;
|
||||
for (unsigned int extruder : tool_ordering.all_extruders()) {
|
||||
if (!print.config().filament_is_support.get_at(extruder)) {
|
||||
has_non_support_filament = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//BBS: find the non-support filament extruder of object
|
||||
if (has_non_support_filament)
|
||||
for (LayerTools layer_tools : tool_ordering.layer_tools()) {
|
||||
if (!layer_tools.has_object)
|
||||
continue;
|
||||
for (unsigned int extruder : layer_tools.extruders) {
|
||||
if (print.config().filament_is_support.get_at(extruder))
|
||||
continue;
|
||||
initial_non_support_extruder_id = extruder;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In non-sequential print, the printing extruders may have been modified by the extruder switches stored in Model::custom_gcode_per_print_z.
|
||||
// Therefore initialize the printing extruders from there.
|
||||
this->set_extruders(tool_ordering.all_extruders());
|
||||
|
|
@ -1499,6 +1690,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
if (initial_extruder_id == (unsigned int)-1) {
|
||||
// Nothing to print!
|
||||
initial_extruder_id = 0;
|
||||
initial_non_support_extruder_id = 0;
|
||||
final_extruder_id = 0;
|
||||
} else {
|
||||
final_extruder_id = tool_ordering.last_extruder();
|
||||
|
|
@ -1522,6 +1714,9 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
// Let the start-up script prime the 1st printing tool.
|
||||
m_placeholder_parser.set("initial_tool", initial_extruder_id);
|
||||
m_placeholder_parser.set("initial_extruder", initial_extruder_id);
|
||||
//BBS
|
||||
m_placeholder_parser.set("initial_no_support_tool", initial_non_support_extruder_id);
|
||||
m_placeholder_parser.set("initial_no_support_extruder", initial_non_support_extruder_id);
|
||||
m_placeholder_parser.set("current_extruder", initial_extruder_id);
|
||||
//Set variable for total layer count so it can be used in custom gcode.
|
||||
m_placeholder_parser.set("total_layer_count", m_layer_count);
|
||||
|
|
@ -1531,10 +1726,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
m_placeholder_parser.set("has_wipe_tower", has_wipe_tower);
|
||||
//m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming);
|
||||
m_placeholder_parser.set("total_toolchanges", std::max(0, print.wipe_tower_data().number_of_toolchanges)); // Check for negative toolchanges (single extruder mode) and set to 0 (no tool change).
|
||||
Vec2f plate_offset = m_writer.get_xy_offset();
|
||||
{
|
||||
BoundingBoxf bbox(print.config().printable_area.values);
|
||||
m_placeholder_parser.set("print_bed_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() }));
|
||||
m_placeholder_parser.set("print_bed_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() }));
|
||||
m_placeholder_parser.set("print_bed_min", new ConfigOptionFloats({ bbox.min.x() - plate_offset.x(), bbox.min.y() - plate_offset.y() }));
|
||||
m_placeholder_parser.set("print_bed_max", new ConfigOptionFloats({ bbox.max.x() - plate_offset.x(), bbox.max.y() - plate_offset.y() }));
|
||||
m_placeholder_parser.set("print_bed_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
|
||||
}
|
||||
{
|
||||
|
|
@ -1549,8 +1745,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
pts->values.emplace_back(unscale(pt) - Vec2d(print.get_plate_origin().x(), print.get_plate_origin().y()));
|
||||
BoundingBoxf bbox(pts->values);
|
||||
m_placeholder_parser.set("first_layer_print_convex_hull", pts.release());
|
||||
m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({ bbox.min.x(), bbox.min.y() }));
|
||||
m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({ bbox.max.x(), bbox.max.y() }));
|
||||
m_placeholder_parser.set("first_layer_print_min", new ConfigOptionFloats({bbox.min.x() - plate_offset.x(), bbox.min.y() - plate_offset.y()}));
|
||||
m_placeholder_parser.set("first_layer_print_max", new ConfigOptionFloats({bbox.max.x() - plate_offset.x(), bbox.max.y() - plate_offset.y()}));
|
||||
m_placeholder_parser.set("first_layer_print_size", new ConfigOptionFloats({ bbox.size().x(), bbox.size().y() }));
|
||||
}
|
||||
float outer_wall_volumetric_speed = 0.0f;
|
||||
|
|
@ -1575,13 +1771,13 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
|
||||
//BBS: calculate the volumetric speed of outer wall. Ignore pre-object setting and multi-filament, and just use the default setting
|
||||
{
|
||||
float filament_max_volumetric_speed = m_config.option<ConfigOptionFloats>("filament_max_volumetric_speed")->get_at(initial_extruder_id);
|
||||
float filament_max_volumetric_speed = m_config.option<ConfigOptionFloats>("filament_max_volumetric_speed")->get_at(initial_non_support_extruder_id);
|
||||
float outer_wall_line_width = print.default_region_config().outer_wall_line_width.value;
|
||||
if (outer_wall_line_width == 0.0) {
|
||||
float default_line_width = print.default_object_config().line_width.value;
|
||||
outer_wall_line_width = default_line_width == 0.0 ? m_config.nozzle_diameter.get_at(initial_extruder_id) : default_line_width;
|
||||
outer_wall_line_width = default_line_width == 0.0 ? m_config.nozzle_diameter.get_at(initial_non_support_extruder_id) : default_line_width;
|
||||
}
|
||||
Flow outer_wall_flow = Flow(outer_wall_line_width, m_config.layer_height, m_config.nozzle_diameter.get_at(initial_extruder_id));
|
||||
Flow outer_wall_flow = Flow(outer_wall_line_width, m_config.layer_height, m_config.nozzle_diameter.get_at(initial_non_support_extruder_id));
|
||||
float outer_wall_speed = print.default_region_config().outer_wall_speed.value;
|
||||
outer_wall_volumetric_speed = outer_wall_speed * outer_wall_flow.mm3_per_mm();
|
||||
if (outer_wall_volumetric_speed > filament_max_volumetric_speed)
|
||||
|
|
@ -1637,7 +1833,17 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
std::function<void(void)> throw_if_canceled_func = [&print]() { print.throw_if_canceled(); };
|
||||
m_seam_placer.init(print, throw_if_canceled_func);
|
||||
|
||||
// BBS: priming logic is removed, always set first extruder here.
|
||||
// BBS: get path for change filament
|
||||
if (m_writer.multiple_extruders) {
|
||||
std::vector<Vec2d> points = get_path_of_change_filament(print);
|
||||
if (points.size() == 3) {
|
||||
travel_point_1 = points[0];
|
||||
travel_point_2 = points[1];
|
||||
travel_point_3 = points[2];
|
||||
}
|
||||
}
|
||||
|
||||
// BBS: priming logic is removed, always set first extruer here.
|
||||
//if (! (has_wipe_tower && print.config().single_extruder_multi_material_priming))
|
||||
{
|
||||
// Set initial extruder only after custom start G-code.
|
||||
|
|
@ -1676,8 +1882,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
gcode += pa_test.generate_test(params.start, params.step, std::llround(std::ceil((params.end - params.start) / params.step)));
|
||||
|
||||
file.write(gcode);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
//BBS: open spaghetti detector
|
||||
if (is_bbl_printers) {
|
||||
// if (print.config().spaghetti_detector.value)
|
||||
|
|
@ -1685,11 +1890,11 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
}
|
||||
|
||||
// Do all objects for each layer.
|
||||
if (print.config().print_sequence == PrintSequence::ByObject) {
|
||||
if (print.config().print_sequence == PrintSequence::ByObject && !has_wipe_tower) {
|
||||
size_t finished_objects = 0;
|
||||
const PrintObject* prev_object = (*print_object_instance_sequential_active)->print_object;
|
||||
for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++print_object_instance_sequential_active) {
|
||||
const PrintObject& object = *(*print_object_instance_sequential_active)->print_object;
|
||||
const PrintObject *prev_object = (*print_object_instance_sequential_active)->print_object;
|
||||
for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++ print_object_instance_sequential_active) {
|
||||
const PrintObject &object = *(*print_object_instance_sequential_active)->print_object;
|
||||
if (&object != prev_object || tool_ordering.first_extruder() != final_extruder_id) {
|
||||
tool_ordering = ToolOrdering(object, final_extruder_id);
|
||||
unsigned int new_extruder_id = tool_ordering.first_extruder();
|
||||
|
|
@ -1697,7 +1902,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
// Skip this object.
|
||||
continue;
|
||||
initial_extruder_id = new_extruder_id;
|
||||
final_extruder_id = tool_ordering.last_extruder();
|
||||
final_extruder_id = tool_ordering.last_extruder();
|
||||
assert(final_extruder_id != (unsigned int)-1);
|
||||
}
|
||||
print.throw_if_canceled();
|
||||
|
|
@ -1751,34 +1956,33 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
file.write("M1003 S0\n");
|
||||
}
|
||||
}
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
if (m_pressure_equalizer)
|
||||
file.write(m_pressure_equalizer->process("", true));
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
++finished_objects;
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
++ finished_objects;
|
||||
// Flag indicating whether the nozzle temperature changes from 1st to 2nd layer were performed.
|
||||
// Reset it when starting another object from 1st layer.
|
||||
m_second_layer_things_done = false;
|
||||
prev_object = &object;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Sort layers by Z.
|
||||
// All extrusion moves with the same top layer height are extruded uninterrupted.
|
||||
std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> layers_to_print = collect_layers_to_print(print);
|
||||
// Prusa Multi-Material wipe tower.
|
||||
if (has_wipe_tower && !layers_to_print.empty()) {
|
||||
m_wipe_tower.reset(new WipeTowerIntegration(print.config(), print.get_plate_index(), print.get_plate_origin(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get()));
|
||||
if (has_wipe_tower && ! layers_to_print.empty()) {
|
||||
m_wipe_tower.reset(new WipeTowerIntegration(print.config(), print.get_plate_index(), print.get_plate_origin(), * print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get()));
|
||||
//BBS
|
||||
//file.write(m_writer.travel_to_z(initial_layer_print_height + m_config.z_offset.value, "Move to the first layer height"));
|
||||
file.write(m_writer.travel_to_z(initial_layer_print_height, "Move to the first layer height"));
|
||||
#if 0
|
||||
#if 0
|
||||
if (print.config().single_extruder_multi_material_priming) {
|
||||
file.write(m_wipe_tower->prime(*this));
|
||||
// Verify, whether the print overaps the priming extrusions.
|
||||
BoundingBoxf bbox_print(get_print_extrusions_extents(print));
|
||||
coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
|
||||
for (const PrintObject* print_object : print.objects())
|
||||
for (const PrintObject *print_object : print.objects())
|
||||
bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
|
||||
bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
|
||||
BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
|
||||
|
|
@ -1791,8 +1995,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
if (overlap) {
|
||||
// Wait for the user to remove the priming extrusions.
|
||||
file.write("M1 Remove priming towers and click button.\n");
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Just wait for a bit to let the user check, that the priming succeeded.
|
||||
//TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
|
||||
file.write("M1 S10\n");
|
||||
|
|
@ -1810,7 +2013,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
//}
|
||||
//}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
print.throw_if_canceled();
|
||||
}
|
||||
// Process all layers of all objects (non-sequential mode) with a parallel pipeline:
|
||||
|
|
@ -1824,10 +2027,10 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||
file.write("M1003 S0\n");
|
||||
}
|
||||
}
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
#ifdef HAS_PRESSURE_EQUALIZER
|
||||
if (m_pressure_equalizer)
|
||||
file.write(m_pressure_equalizer->process("", true));
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
#endif /* HAS_PRESSURE_EQUALIZER */
|
||||
if (m_wipe_tower)
|
||||
// Purge the extruder, pull out the active filament.
|
||||
file.write(m_wipe_tower->finalize(*this));
|
||||
|
|
@ -2502,7 +2705,6 @@ GCode::LayerResult GCode::process_layer(
|
|||
// First object, support and raft layer, if available.
|
||||
const Layer *object_layer = nullptr;
|
||||
const SupportLayer *support_layer = nullptr;
|
||||
const TreeSupportLayer* tree_support_layer = nullptr;
|
||||
const SupportLayer *raft_layer = nullptr;
|
||||
for (const LayerToPrint &l : layers) {
|
||||
if (l.object_layer && ! object_layer)
|
||||
|
|
@ -2513,16 +2715,6 @@ GCode::LayerResult GCode::process_layer(
|
|||
if (! raft_layer && support_layer->id() < support_layer->object()->slicing_parameters().raft_layers())
|
||||
raft_layer = support_layer;
|
||||
}
|
||||
|
||||
if (l.tree_support_layer) {
|
||||
if (!tree_support_layer)
|
||||
tree_support_layer = l.tree_support_layer;
|
||||
// BBS: to be checked.
|
||||
#if 0
|
||||
if (!raft_layer && tree_support_layer->id() < tree_support_layer->object()->slicing_parameters().raft_layers())
|
||||
raft_layer = tree_support_layer;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
const Layer* layer_ptr = nullptr;
|
||||
|
|
@ -2530,8 +2722,6 @@ GCode::LayerResult GCode::process_layer(
|
|||
layer_ptr = object_layer;
|
||||
else if (support_layer != nullptr)
|
||||
layer_ptr = support_layer;
|
||||
else
|
||||
layer_ptr = tree_support_layer;
|
||||
const Layer& layer = *layer_ptr;
|
||||
GCode::LayerResult result { {}, layer.id(), false, last_layer };
|
||||
if (layer_tools.extruders.empty())
|
||||
|
|
@ -2552,7 +2742,7 @@ GCode::LayerResult GCode::process_layer(
|
|||
// Check whether it is possible to apply the spiral vase logic for this layer.
|
||||
// Just a reminder: A spiral vase mode is allowed for a single object, single material print only.
|
||||
m_enable_loop_clipping = true;
|
||||
if (m_spiral_vase && layers.size() == 1 && support_layer == nullptr && tree_support_layer == nullptr) {
|
||||
if (m_spiral_vase && layers.size() == 1 && support_layer == nullptr) {
|
||||
bool enable = (layer.id() > 0 || !print.has_brim()) && (layer.id() >= (size_t)print.config().skirt_height.value && ! print.has_infinite_skirt());
|
||||
if (enable) {
|
||||
for (const LayerRegion *layer_region : layer.regions())
|
||||
|
|
@ -2790,90 +2980,6 @@ GCode::LayerResult GCode::process_layer(
|
|||
}
|
||||
}
|
||||
|
||||
// BBS
|
||||
if (layer_to_print.tree_support_layer != nullptr) {
|
||||
const TreeSupportLayer& tree_support_layer = *layer_to_print.tree_support_layer;
|
||||
const PrintObject& object = *layer_to_print.original_object;
|
||||
if (!tree_support_layer.support_fills.entities.empty()) {
|
||||
ExtrusionRole role = tree_support_layer.support_fills.role();
|
||||
bool has_support = role == erMixed || role == erSupportMaterial || role == erSupportTransition;
|
||||
bool has_interface = role == erMixed || role == erSupportMaterialInterface;
|
||||
// Extruder ID of the support base. -1 if "don't care".
|
||||
unsigned int support_extruder = object.config().support_filament.value - 1;
|
||||
// Shall the support be printed with the active extruder, preferably with non-soluble, to avoid tool changes?
|
||||
bool support_dontcare = object.config().support_filament.value == 0;
|
||||
// Extruder ID of the support interface. -1 if "don't care".
|
||||
unsigned int interface_extruder = object.config().support_interface_filament.value - 1;
|
||||
// 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;
|
||||
for (unsigned int extruder_id : layer_tools.extruders) {
|
||||
if (print.config().filament_soluble.get_at(extruder_id)) continue;
|
||||
|
||||
if (extruder_id == interface_extruder) continue;
|
||||
|
||||
dontcare_extruder = extruder_id;
|
||||
break;
|
||||
}
|
||||
|
||||
if (support_dontcare) support_extruder = dontcare_extruder;
|
||||
}
|
||||
else 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?
|
||||
unsigned int dontcare_extruder = first_extruder_id;
|
||||
if (print.config().filament_soluble.get_at(dontcare_extruder)) {
|
||||
// The last extruder printed on the previous layer extrudes soluble filament.
|
||||
// Try to find a non-soluble extruder on the same layer.
|
||||
for (unsigned int extruder_id : layer_tools.extruders)
|
||||
if (!print.config().filament_soluble.get_at(extruder_id)) {
|
||||
dontcare_extruder = extruder_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (support_dontcare)
|
||||
support_extruder = dontcare_extruder;
|
||||
if (interface_dontcare)
|
||||
interface_extruder = dontcare_extruder;
|
||||
}
|
||||
// Both the support and the support interface are printed with the same extruder, therefore
|
||||
// the interface may be interleaved with the support base.
|
||||
bool single_extruder = !has_support || support_extruder == interface_extruder;
|
||||
|
||||
// Assign an extruder to the base.
|
||||
ObjectByExtruder& obj = object_by_extruder(by_extruder, has_support ? support_extruder : interface_extruder, &layer_to_print - layers.data(), layers.size());
|
||||
obj.support = &tree_support_layer.support_fills;
|
||||
obj.support_extrusion_role = single_extruder ? erMixed : erSupportMaterial;
|
||||
if (!single_extruder && has_interface) {
|
||||
ObjectByExtruder& obj_interface = object_by_extruder(by_extruder, interface_extruder, &layer_to_print - layers.data(), layers.size());
|
||||
obj_interface.support = &tree_support_layer.support_fills;
|
||||
obj_interface.support_extrusion_role = erSupportMaterialInterface;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (layer_to_print.object_layer != nullptr) {
|
||||
const Layer &layer = *layer_to_print.object_layer;
|
||||
// We now define a strategy for building perimeters and fills. The separation
|
||||
|
|
@ -3024,8 +3130,9 @@ GCode::LayerResult GCode::process_layer(
|
|||
// BBS: ordering instances by extruder
|
||||
std::vector<InstanceToPrint> instances_to_print;
|
||||
bool has_prime_tower = print.config().enable_prime_tower
|
||||
&& print.config().print_sequence == PrintSequence::ByLayer
|
||||
&& print.extruders().size() > 1;
|
||||
&& print.extruders().size() > 1
|
||||
&& (print.config().print_sequence == PrintSequence::ByLayer
|
||||
|| (print.config().print_sequence == PrintSequence::ByObject && print.objects().size() == 1));
|
||||
if (has_prime_tower) {
|
||||
int plate_idx = print.get_plate_index();
|
||||
Point wt_pos(print.config().wipe_tower_x.get_at(plate_idx), print.config().wipe_tower_y.get_at(plate_idx));
|
||||
|
|
@ -3097,12 +3204,7 @@ GCode::LayerResult GCode::process_layer(
|
|||
m_last_obj_copy = this_object_copy;
|
||||
this->set_origin(unscale(offset));
|
||||
if (instance_to_print.object_by_extruder.support != nullptr) {
|
||||
if (layers[instance_to_print.layer_id].support_layer) {
|
||||
m_layer = layers[instance_to_print.layer_id].support_layer;
|
||||
}
|
||||
else {
|
||||
m_layer = layers[instance_to_print.layer_id].tree_support_layer;
|
||||
}
|
||||
m_layer = layers[instance_to_print.layer_id].support_layer;
|
||||
m_object_layer_over_raft = false;
|
||||
|
||||
//BBS: print supports' brims first
|
||||
|
|
@ -3303,8 +3405,11 @@ std::string GCode::change_layer(coordf_t print_z, bool lazy_raise)
|
|||
//BBS
|
||||
//coordf_t z = print_z + m_config.z_offset.value; // in unscaled coordinates
|
||||
coordf_t z = print_z; // in unscaled coordinates
|
||||
if (EXTRUDER_CONFIG(retract_when_changing_layer) && m_writer.will_move_z(z))
|
||||
gcode += this->retract();
|
||||
if (EXTRUDER_CONFIG(retract_when_changing_layer) && m_writer.will_move_z(z)) {
|
||||
LiftType lift_type = this->to_lift_type(ZHopType(EXTRUDER_CONFIG(z_hop_types)));
|
||||
//BBS: force to use SpiralLift when change layer if lift type is auto
|
||||
gcode += this->retract(false, false, ZHopType(EXTRUDER_CONFIG(z_hop_types)) == ZHopType::zhtAuto ? LiftType::SpiralLift : lift_type);
|
||||
}
|
||||
|
||||
if (!lazy_raise) {
|
||||
std::ostringstream comment;
|
||||
|
|
@ -3421,6 +3526,12 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
|
|||
if (was_clockwise) {
|
||||
// swap points
|
||||
Point c = a; a = b; b = c;
|
||||
|
||||
// double angle = paths.front().first_point().ccw_angle(a, b) / 3;
|
||||
|
||||
// // turn left if contour, turn right if hole
|
||||
// if (was_clockwise) angle *= -1;
|
||||
|
||||
}
|
||||
|
||||
double angle = paths.front().first_point().ccw_angle(a, b) / 3;
|
||||
|
|
@ -4067,7 +4178,8 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
|
|||
Polyline travel { this->last_pos(), point };
|
||||
|
||||
// check whether a straight travel move would need retraction
|
||||
bool needs_retraction = this->needs_retraction(travel, role);
|
||||
LiftType lift_type = LiftType::SpiralLift;
|
||||
bool needs_retraction = this->needs_retraction(travel, role, lift_type);
|
||||
// check whether wipe could be disabled without causing visible stringing
|
||||
bool could_be_wipe_disabled = false;
|
||||
// Save state of use_external_mp_once for the case that will be needed to call twice m_avoid_crossing_perimeters.travel_to.
|
||||
|
|
@ -4104,10 +4216,12 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
|
|||
// multi-hop travel path inside the configuration space
|
||||
if (needs_retraction
|
||||
&& m_config.reduce_crossing_wall
|
||||
&& ! m_avoid_crossing_perimeters.disabled_once()) {
|
||||
&& ! m_avoid_crossing_perimeters.disabled_once()
|
||||
//BBS: don't generate detour travel paths when current position is unclear
|
||||
&& m_writer.is_current_position_clear()) {
|
||||
travel = m_avoid_crossing_perimeters.travel_to(*this, point, &could_be_wipe_disabled);
|
||||
// check again whether the new travel path still needs a retraction
|
||||
needs_retraction = this->needs_retraction(travel, role);
|
||||
needs_retraction = this->needs_retraction(travel, role, lift_type);
|
||||
//if (needs_retraction && m_layer_index > 1) exit(0);
|
||||
}
|
||||
|
||||
|
|
@ -4120,7 +4234,7 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
|
|||
m_wipe.reset_path();
|
||||
|
||||
Point last_post_before_retract = this->last_pos();
|
||||
gcode += this->retract();
|
||||
gcode += this->retract(false, false, lift_type);
|
||||
// When "Wipe while retracting" is enabled, then extruder moves to another position, and travel from this position can cross perimeters.
|
||||
// Because of it, it is necessary to call avoid crossing perimeters again with new starting point after calling retraction()
|
||||
// FIXME Lukas H.: Try to predict if this second calling of avoid crossing perimeters will be needed or not. It could save computations.
|
||||
|
|
@ -4155,36 +4269,102 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
|
|||
return gcode;
|
||||
}
|
||||
|
||||
bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role)
|
||||
//BBS
|
||||
LiftType GCode::to_lift_type(ZHopType z_hop_types) {
|
||||
switch (z_hop_types)
|
||||
{
|
||||
case ZHopType::zhtNormal:
|
||||
return LiftType::NormalLift;
|
||||
case ZHopType::zhtSlope:
|
||||
return LiftType::LazyLift;
|
||||
case ZHopType::zhtSpiral:
|
||||
return LiftType::SpiralLift;
|
||||
default:
|
||||
// if no corresponding lift type, use normal lift
|
||||
return LiftType::NormalLift;
|
||||
}
|
||||
};
|
||||
|
||||
bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role, LiftType& lift_type)
|
||||
{
|
||||
if (travel.length() < scale_(EXTRUDER_CONFIG(retraction_minimum_travel))) {
|
||||
// skip retraction if the move is shorter than the configured threshold
|
||||
return false;
|
||||
}
|
||||
|
||||
auto is_through_overhang = [this](const Polyline& travel) {
|
||||
const float protect_z_scaled = scale_(0.4);
|
||||
std::pair<float, float> z_range;
|
||||
z_range.second = m_layer ? m_layer->print_z : 0.f;
|
||||
z_range.first = std::max(0.f, z_range.second - protect_z_scaled);
|
||||
for (auto object : m_curr_print->objects()) {
|
||||
BoundingBox obj_bbox = object->bounding_box();
|
||||
BoundingBox travel_bbox = get_extents(travel);
|
||||
obj_bbox.offset(scale_(EPSILON));
|
||||
if (!obj_bbox.overlap(travel_bbox))
|
||||
continue;
|
||||
|
||||
for (auto layer : object->layers()) {
|
||||
if (layer->print_z < z_range.first)
|
||||
continue;
|
||||
|
||||
if (layer->print_z > z_range.second + EPSILON)
|
||||
break;
|
||||
|
||||
for (ExPolygon& overhang : layer->loverhangs) {
|
||||
if (overhang.contains(travel))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
float max_z_hop = 0.f;
|
||||
for (int i = 0; i < m_config.z_hop.size(); i++)
|
||||
max_z_hop = std::max(max_z_hop, (float)m_config.z_hop.get_at(i));
|
||||
float travel_len_thresh = max_z_hop / tan(GCodeWriter::slope_threshold);
|
||||
float accum_len = 0.f;
|
||||
Polyline clipped_travel;
|
||||
for (auto line : travel.lines()) {
|
||||
if (accum_len + line.length() > travel_len_thresh + EPSILON) {
|
||||
Point end_pnt = line.a + line.normal() * (travel_len_thresh - accum_len);
|
||||
clipped_travel.append(Polyline(line.a, end_pnt));
|
||||
break;
|
||||
}
|
||||
else {
|
||||
clipped_travel.append(Polyline(line.a, line.b));
|
||||
accum_len += line.length();
|
||||
}
|
||||
}
|
||||
|
||||
//BBS: force to retract when leave from external perimeter for a long travel
|
||||
//Better way is judging whether the travel move direction is same with last extrusion move.
|
||||
if (is_perimeter(m_last_processor_extrusion_role) && m_last_processor_extrusion_role != erPerimeter)
|
||||
if (is_perimeter(m_last_processor_extrusion_role) && m_last_processor_extrusion_role != erPerimeter) {
|
||||
if (ZHopType(EXTRUDER_CONFIG(z_hop_types)) == ZHopType::zhtAuto) {
|
||||
lift_type = is_through_overhang(clipped_travel) ? LiftType::SpiralLift : LiftType::LazyLift;
|
||||
}
|
||||
else {
|
||||
lift_type = to_lift_type(ZHopType(EXTRUDER_CONFIG(z_hop_types)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (role == erSupportMaterial || role == erSupportTransition) {
|
||||
const SupportLayer* support_layer = dynamic_cast<const SupportLayer*>(m_layer);
|
||||
|
||||
//FIXME support_layer->support_islands.contains should use some search structure!
|
||||
if (support_layer != NULL && support_layer->support_islands.contains(travel))
|
||||
// skip retraction if this is a travel move inside a support material island
|
||||
//FIXME not retracting over a long path may cause oozing, which in turn may result in missing material
|
||||
// at the end of the extrusion path!
|
||||
return false;
|
||||
|
||||
//reduce the retractions in lightning infills for tree support
|
||||
const TreeSupportLayer* ts_layer = dynamic_cast<const TreeSupportLayer*>(m_layer);
|
||||
if (ts_layer != NULL)
|
||||
for (auto& area : ts_layer->base_areas)
|
||||
if(area.contains(travel))
|
||||
if (support_layer != NULL && support_layer->support_type==stInnerTree)
|
||||
for (auto &area : support_layer->base_areas)
|
||||
if (area.contains(travel))
|
||||
return false;
|
||||
}
|
||||
|
||||
//BBS: need retract when long moving to print perimeter to avoid dropping of material
|
||||
if (!is_perimeter(role) && m_config.reduce_infill_retraction && m_layer != nullptr &&
|
||||
m_config.sparse_infill_density.value > 0 && m_layer->any_internal_region_slice_contains(travel))
|
||||
|
|
@ -4194,10 +4374,16 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role)
|
|||
return false;
|
||||
|
||||
// retract if reduce_infill_retraction is disabled or doesn't apply when role is perimeter
|
||||
if (ZHopType(EXTRUDER_CONFIG(z_hop_types)) == ZHopType::zhtAuto) {
|
||||
lift_type = is_through_overhang(clipped_travel) ? LiftType::SpiralLift : LiftType::LazyLift;
|
||||
}
|
||||
else {
|
||||
lift_type = to_lift_type(ZHopType(EXTRUDER_CONFIG(z_hop_types)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GCode::retract(bool toolchange, bool is_last_retraction)
|
||||
std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType lift_type)
|
||||
{
|
||||
std::string gcode;
|
||||
|
||||
|
|
@ -4221,11 +4407,7 @@ std::string GCode::retract(bool toolchange, bool is_last_retraction)
|
|||
if (m_writer.extruder()->retraction_length() > 0 || m_config.use_firmware_retraction) {
|
||||
// BBS: don't do lazy_lift when enable spiral vase
|
||||
size_t extruder_id = m_writer.extruder()->id();
|
||||
auto _lift = m_config.z_lift_type.value;
|
||||
if(m_spiral_vase)
|
||||
_lift = NormalLift;
|
||||
|
||||
gcode += m_writer.lift(_lift);
|
||||
gcode += m_writer.lift(!m_spiral_vase ? lift_type : LiftType::NormalLift);
|
||||
}
|
||||
|
||||
return gcode;
|
||||
|
|
@ -4340,6 +4522,12 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
|
|||
dyn_config.set_key_value("second_flush_volume", new ConfigOptionFloat(wipe_length / 2.f));
|
||||
dyn_config.set_key_value("old_filament_e_feedrate", new ConfigOptionInt(old_filament_e_feedrate));
|
||||
dyn_config.set_key_value("new_filament_e_feedrate", new ConfigOptionInt(new_filament_e_feedrate));
|
||||
dyn_config.set_key_value("travel_point_1_x", new ConfigOptionFloat(float(travel_point_1.x())));
|
||||
dyn_config.set_key_value("travel_point_1_y", new ConfigOptionFloat(float(travel_point_1.y())));
|
||||
dyn_config.set_key_value("travel_point_2_x", new ConfigOptionFloat(float(travel_point_2.x())));
|
||||
dyn_config.set_key_value("travel_point_2_y", new ConfigOptionFloat(float(travel_point_2.y())));
|
||||
dyn_config.set_key_value("travel_point_3_x", new ConfigOptionFloat(float(travel_point_3.x())));
|
||||
dyn_config.set_key_value("travel_point_3_y", new ConfigOptionFloat(float(travel_point_3.y())));
|
||||
|
||||
int flush_count = std::min(g_max_flush_count, (int)std::round(wipe_volume / g_purge_volume_one_time));
|
||||
float flush_unit = wipe_length / flush_count;
|
||||
|
|
|
|||
|
|
@ -197,8 +197,8 @@ public:
|
|||
void apply_print_config(const PrintConfig &print_config);
|
||||
|
||||
std::string travel_to(const Point& point, ExtrusionRole role, std::string comment);
|
||||
bool needs_retraction(const Polyline& travel, ExtrusionRole role = erNone);
|
||||
std::string retract(bool toolchange = false, bool is_last_retraction = false);
|
||||
bool needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type);
|
||||
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::SpiralLift);
|
||||
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
|
||||
std::string set_extruder(unsigned int extruder_id, double print_z);
|
||||
|
||||
|
|
@ -209,10 +209,9 @@ public:
|
|||
// public, so that it could be accessed by free helper functions from GCode.cpp
|
||||
struct LayerToPrint
|
||||
{
|
||||
LayerToPrint() : object_layer(nullptr), support_layer(nullptr), tree_support_layer(nullptr), original_object(nullptr) {}
|
||||
LayerToPrint() : object_layer(nullptr), support_layer(nullptr), original_object(nullptr) {}
|
||||
const Layer* object_layer;
|
||||
const SupportLayer* support_layer;
|
||||
const TreeSupportLayer* tree_support_layer;
|
||||
const PrintObject* original_object; //BBS: used for shared object logic
|
||||
const Layer* layer() const
|
||||
{
|
||||
|
|
@ -222,9 +221,6 @@ public:
|
|||
if (support_layer != nullptr)
|
||||
return support_layer;
|
||||
|
||||
if (tree_support_layer != nullptr)
|
||||
return tree_support_layer;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -246,10 +242,6 @@ public:
|
|||
count++;
|
||||
}
|
||||
|
||||
if (tree_support_layer != nullptr) {
|
||||
sum_z += tree_support_layer->print_z;
|
||||
count++;
|
||||
}
|
||||
return sum_z / count;
|
||||
}
|
||||
};
|
||||
|
|
@ -409,6 +401,10 @@ private:
|
|||
std::string extrude_perimeters(const Print& print, const std::vector<ObjectByExtruder::Island::Region>& by_region);
|
||||
std::string extrude_infill(const Print& print, const std::vector<ObjectByExtruder::Island::Region>& by_region, bool ironing);
|
||||
std::string extrude_support(const ExtrusionEntityCollection& support_fills);
|
||||
|
||||
// BBS
|
||||
LiftType to_lift_type(ZHopType z_hop_types);
|
||||
|
||||
std::set<ObjectID> m_objsWithBrim; // indicates the objs with brim
|
||||
std::set<ObjectID> m_objSupportsWithBrim; // indicates the objs' supports with brim
|
||||
// Cache for custom seam enforcers/blockers for each layer.
|
||||
|
|
@ -491,6 +487,7 @@ private:
|
|||
GCodeProcessor m_processor;
|
||||
|
||||
// BBS
|
||||
Print* m_curr_print = nullptr;
|
||||
unsigned int m_toolchange_count;
|
||||
coordf_t m_nominal_z;
|
||||
bool m_need_change_layer_lift_z = false;
|
||||
|
|
|
|||
|
|
@ -1135,8 +1135,7 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
|
|||
|
||||
const ExPolygons &lslices = gcodegen.layer()->lslices;
|
||||
const std::vector<BoundingBox> &lslices_bboxes = gcodegen.layer()->lslices_bboxes;
|
||||
bool is_support_layer = (dynamic_cast<const SupportLayer *>(gcodegen.layer()) != nullptr) ||
|
||||
(dynamic_cast<const TreeSupportLayer *>(gcodegen.layer()) != nullptr);
|
||||
bool is_support_layer = (dynamic_cast<const SupportLayer *>(gcodegen.layer()) != nullptr);
|
||||
if (!use_external && (is_support_layer || (!lslices.empty() && !any_expolygon_contains(lslices, lslices_bboxes, m_grid_lslice, travel)))) {
|
||||
// Initialize m_internal only when it is necessary.
|
||||
if (m_internal.boundaries.empty())
|
||||
|
|
|
|||
|
|
@ -244,22 +244,22 @@ float new_feedrate_to_reach_time_stretch(
|
|||
{
|
||||
float new_feedrate = min_feedrate;
|
||||
for (size_t iter = 0; iter < max_iter; ++ iter) {
|
||||
float nomin = 0;
|
||||
float denom = time_stretch;
|
||||
double nomin = 0;
|
||||
double denom = time_stretch;
|
||||
for (auto it = it_begin; it != it_end; ++ it) {
|
||||
assert((*it)->slow_down_min_speed < min_feedrate + EPSILON);
|
||||
for (size_t i = 0; i < (*it)->n_lines_adjustable; ++i) {
|
||||
const CoolingLine &line = (*it)->lines[i];
|
||||
if (line.feedrate > min_feedrate) {
|
||||
nomin += line.time * line.feedrate;
|
||||
denom += line.time;
|
||||
nomin += (double)line.time * (double)line.feedrate;
|
||||
denom += (double)line.time;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(denom > 0);
|
||||
if (denom < 0)
|
||||
return min_feedrate;
|
||||
new_feedrate = nomin / denom;
|
||||
new_feedrate = (float)(nomin / denom);
|
||||
assert(new_feedrate > min_feedrate - EPSILON);
|
||||
if (new_feedrate < min_feedrate + EPSILON)
|
||||
goto finished;
|
||||
|
|
|
|||
|
|
@ -55,7 +55,8 @@ const std::vector<std::string> GCodeProcessor::Reserved_Tags = {
|
|||
" CUSTOM_GCODE",
|
||||
"_GP_FIRST_LINE_M73_PLACEHOLDER",
|
||||
"_GP_LAST_LINE_M73_PLACEHOLDER",
|
||||
"_GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER"
|
||||
"_GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER",
|
||||
"_GP_TOTAL_LAYER_NUMBER_PLACEHOLDER"
|
||||
};
|
||||
|
||||
const std::vector<std::string> GCodeProcessor::Reserved_Tags_compatible = {
|
||||
|
|
@ -227,6 +228,7 @@ void GCodeProcessor::TimeMachine::reset()
|
|||
std::fill(moves_time.begin(), moves_time.end(), 0.0f);
|
||||
std::fill(roles_time.begin(), roles_time.end(), 0.0f);
|
||||
layers_time = std::vector<float>();
|
||||
prepare_time = 0.0f;
|
||||
}
|
||||
|
||||
void GCodeProcessor::TimeMachine::simulate_st_synchronize(float additional_time)
|
||||
|
|
@ -334,7 +336,9 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks, floa
|
|||
|
||||
time += block_time;
|
||||
gcode_time.cache += block_time;
|
||||
moves_time[static_cast<size_t>(block.move_type)] += block_time;
|
||||
//BBS: don't calculate travel of start gcode into travel time
|
||||
if (!block.flags.prepare_stage || block.move_type != EMoveType::Travel)
|
||||
moves_time[static_cast<size_t>(block.move_type)] += block_time;
|
||||
roles_time[static_cast<size_t>(block.role)] += block_time;
|
||||
if (block.layer_id >= layers_time.size()) {
|
||||
const size_t curr_size = layers_time.size();
|
||||
|
|
@ -344,6 +348,9 @@ void GCodeProcessor::TimeMachine::calculate_time(size_t keep_last_n_blocks, floa
|
|||
}
|
||||
}
|
||||
layers_time[block.layer_id - 1] += block_time;
|
||||
//BBS
|
||||
if (block.flags.prepare_stage)
|
||||
prepare_time += block_time;
|
||||
g1_times_cache.push_back({ block.g1_line_id, time });
|
||||
// update times for remaining time to printer stop placeholders
|
||||
auto it_stop_time = std::lower_bound(stop_times.begin(), stop_times.end(), block.g1_line_id,
|
||||
|
|
@ -371,7 +378,7 @@ void GCodeProcessor::TimeProcessor::reset()
|
|||
machines[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].enabled = true;
|
||||
}
|
||||
|
||||
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends)
|
||||
void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends, size_t total_layer_num)
|
||||
{
|
||||
FilePtr in{ boost::nowide::fopen(filename.c_str(), "rb") };
|
||||
if (in.f == nullptr)
|
||||
|
|
@ -480,14 +487,20 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
|
|||
//sprintf(buf, "; estimated printing time (%s mode) = %s\n",
|
||||
// (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent",
|
||||
// get_time_dhms(machine.time).c_str());
|
||||
sprintf(buf, "; model printing time: %s; total estimated time: %s\n",
|
||||
get_time_dhms(machine.time - machine.roles_time[ExtrusionRole::erCustom]).c_str(),
|
||||
get_time_dhms(machine.time).c_str());
|
||||
sprintf(buf, "; model printing time: %s; total estimated time: %s\n",
|
||||
get_time_dhms(machine.time - machine.prepare_time).c_str(),
|
||||
get_time_dhms(machine.time).c_str());
|
||||
}
|
||||
ret += buf;
|
||||
}
|
||||
}
|
||||
}
|
||||
//BBS: write total layer number
|
||||
else if (line == reserved_tag(ETags::Total_Layer_Number_Placeholder)) {
|
||||
char buf[128];
|
||||
sprintf(buf, "; total layer number: %zd\n", total_layer_num);
|
||||
ret += buf;
|
||||
}
|
||||
}
|
||||
|
||||
if (! ret.empty())
|
||||
|
|
@ -783,6 +796,7 @@ void GCodeProcessorResult::reset() {
|
|||
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
|
||||
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
|
||||
custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
|
||||
spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>();
|
||||
time = 0;
|
||||
|
||||
//BBS: add mutex for protection of gcode result
|
||||
|
|
@ -808,6 +822,7 @@ void GCodeProcessorResult::reset() {
|
|||
required_nozzle_HRC = std::vector<int>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_HRC);
|
||||
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
|
||||
custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
|
||||
spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>();
|
||||
warnings.clear();
|
||||
|
||||
//BBS: add mutex for protection of gcode result
|
||||
|
|
@ -962,6 +977,10 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
|||
m_first_layer_height = std::abs(initial_layer_print_height->value);
|
||||
|
||||
m_result.printable_height = config.printable_height;
|
||||
|
||||
const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_mode");
|
||||
if (spiral_vase != nullptr)
|
||||
m_spiral_vase_active = spiral_vase->value;
|
||||
}
|
||||
|
||||
void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
||||
|
|
@ -1224,6 +1243,10 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
const ConfigOptionFloat* printable_height = config.option<ConfigOptionFloat>("printable_height");
|
||||
if (printable_height != nullptr)
|
||||
m_result.printable_height = printable_height->value;
|
||||
|
||||
const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_mode");
|
||||
if (spiral_vase != nullptr)
|
||||
m_spiral_vase_active = spiral_vase->value;
|
||||
}
|
||||
|
||||
void GCodeProcessor::enable_stealth_time_estimator(bool enabled)
|
||||
|
|
@ -1294,6 +1317,8 @@ void GCodeProcessor::reset()
|
|||
|
||||
m_options_z_corrector.reset();
|
||||
|
||||
m_spiral_vase_active = false;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
m_mm3_per_mm_compare.reset();
|
||||
m_height_compare.reset();
|
||||
|
|
@ -1449,7 +1474,7 @@ void GCodeProcessor::finalize(bool post_process)
|
|||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
if (post_process)
|
||||
m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends);
|
||||
m_time_processor.post_process(m_result.filename, m_result.moves, m_result.lines_ends, m_layer_id);
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
m_result.time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_start_time).count();
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
|
@ -1462,6 +1487,11 @@ float GCodeProcessor::get_time(PrintEstimatedStatistics::ETimeMode mode) const
|
|||
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast<size_t>(mode)].time : 0.0f;
|
||||
}
|
||||
|
||||
float GCodeProcessor::get_prepare_time(PrintEstimatedStatistics::ETimeMode mode) const
|
||||
{
|
||||
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? m_time_processor.machines[static_cast<size_t>(mode)].prepare_time : 0.0f;
|
||||
}
|
||||
|
||||
std::string GCodeProcessor::get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const
|
||||
{
|
||||
return (mode < PrintEstimatedStatistics::ETimeMode::Count) ? short_time(get_time_dhms(m_time_processor.machines[static_cast<size_t>(mode)].time)) : std::string("N/A");
|
||||
|
|
@ -2044,6 +2074,18 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
|
|||
// layer change tag
|
||||
if (comment == reserved_tag(ETags::Layer_Change)) {
|
||||
++m_layer_id;
|
||||
if (m_spiral_vase_active) {
|
||||
if (m_result.moves.empty() || m_result.spiral_vase_layers.empty())
|
||||
// add a placeholder for layer height. the actual value will be set inside process_G1() method
|
||||
m_result.spiral_vase_layers.push_back({ FLT_MAX, { 0, 0 } });
|
||||
else {
|
||||
const size_t move_id = m_result.moves.size() - 1;
|
||||
if (!m_result.spiral_vase_layers.empty())
|
||||
m_result.spiral_vase_layers.back().second.second = move_id;
|
||||
// add a placeholder for layer height. the actual value will be set inside process_G1() method
|
||||
m_result.spiral_vase_layers.push_back({ FLT_MAX, { move_id, move_id } });
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2703,10 +2745,12 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
|
||||
TimeBlock block;
|
||||
block.move_type = type;
|
||||
block.role = m_extrusion_role;
|
||||
//BBS: don't calculate travel time into extrusion path, except travel inside start and end gcode.
|
||||
block.role = (type != EMoveType::Travel || m_extrusion_role == erCustom) ? m_extrusion_role : erNone;
|
||||
block.distance = distance;
|
||||
block.g1_line_id = m_g1_line_id;
|
||||
block.layer_id = std::max<unsigned int>(1, m_layer_id);
|
||||
block.flags.prepare_stage = m_processing_start_custom_gcode;
|
||||
|
||||
//BBS: limite the cruise according to centripetal acceleration
|
||||
//Only need to handle when both prev and curr segment has movement in x-y plane
|
||||
|
|
@ -2924,6 +2968,14 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset);
|
||||
}
|
||||
|
||||
if (m_spiral_vase_active && !m_result.spiral_vase_layers.empty()) {
|
||||
if (m_result.spiral_vase_layers.back().first == FLT_MAX && delta_pos[Z] > 0.0)
|
||||
// replace layer height placeholder with correct value
|
||||
m_result.spiral_vase_layers.back().first = static_cast<float>(m_end_position[Z]);
|
||||
if (!m_result.moves.empty())
|
||||
m_result.spiral_vase_layers.back().second.second = m_result.moves.size() - 1;
|
||||
}
|
||||
|
||||
// store move
|
||||
store_move_vertex(type);
|
||||
}
|
||||
|
|
@ -3125,10 +3177,12 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
|
|||
|
||||
TimeBlock block;
|
||||
block.move_type = type;
|
||||
block.role = m_extrusion_role;
|
||||
//BBS: don't calculate travel time into extrusion path, except travel inside start and end gcode.
|
||||
block.role = (type != EMoveType::Travel || m_extrusion_role == erCustom) ? m_extrusion_role : erNone;
|
||||
block.distance = delta_xyz;
|
||||
block.g1_line_id = m_g1_line_id;
|
||||
block.layer_id = std::max<unsigned int>(1, m_layer_id);
|
||||
block.flags.prepare_stage = m_processing_start_custom_gcode;
|
||||
|
||||
// BBS: calculates block cruise feedrate
|
||||
// For arc move, we need to limite the cruise according to centripetal acceleration which is
|
||||
|
|
@ -4103,6 +4157,7 @@ void GCodeProcessor::update_estimated_times_stats()
|
|||
auto update_mode = [this](PrintEstimatedStatistics::ETimeMode mode) {
|
||||
PrintEstimatedStatistics::Mode& data = m_result.print_statistics.modes[static_cast<size_t>(mode)];
|
||||
data.time = get_time(mode);
|
||||
data.prepare_time = get_prepare_time(mode);
|
||||
data.custom_gcode_times = get_custom_gcode_times(mode, true);
|
||||
data.moves_times = get_moves_time(mode);
|
||||
data.roles_times = get_roles_time(mode);
|
||||
|
|
@ -4176,4 +4231,3 @@ void GCodeProcessor::update_slice_warnings()
|
|||
}
|
||||
|
||||
} /* namespace Slic3r */
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ namespace Slic3r {
|
|||
struct Mode
|
||||
{
|
||||
float time;
|
||||
float prepare_time;
|
||||
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> custom_gcode_times;
|
||||
std::vector<std::pair<EMoveType, float>> moves_times;
|
||||
std::vector<std::pair<ExtrusionRole, float>> roles_times;
|
||||
|
|
@ -56,6 +57,7 @@ namespace Slic3r {
|
|||
|
||||
void reset() {
|
||||
time = 0.0f;
|
||||
prepare_time = 0.0f;
|
||||
custom_gcode_times.clear();
|
||||
moves_times.clear();
|
||||
roles_times.clear();
|
||||
|
|
@ -163,6 +165,7 @@ namespace Slic3r {
|
|||
std::vector<int> filament_vitrification_temperature;
|
||||
PrintEstimatedStatistics print_statistics;
|
||||
std::vector<CustomGCode::Item> custom_gcode_per_print_z;
|
||||
std::vector<std::pair<float, std::pair<size_t, size_t>>> spiral_vase_layers;
|
||||
//BBS
|
||||
std::vector<SliceWarning> warnings;
|
||||
int nozzle_hrc;
|
||||
|
|
@ -191,6 +194,7 @@ namespace Slic3r {
|
|||
filament_densities = other.filament_densities;
|
||||
print_statistics = other.print_statistics;
|
||||
custom_gcode_per_print_z = other.custom_gcode_per_print_z;
|
||||
spiral_vase_layers = other.spiral_vase_layers;
|
||||
warnings = other.warnings;
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
time = other.time;
|
||||
|
|
@ -223,7 +227,8 @@ namespace Slic3r {
|
|||
Custom_Code,
|
||||
First_Line_M73_Placeholder,
|
||||
Last_Line_M73_Placeholder,
|
||||
Estimated_Printing_Time_Placeholder
|
||||
Estimated_Printing_Time_Placeholder,
|
||||
Total_Layer_Number_Placeholder
|
||||
};
|
||||
|
||||
static const std::string& reserved_tag(ETags tag) { return s_IsBBLPrinter ? Reserved_Tags[static_cast<unsigned char>(tag)] : Reserved_Tags_compatible[static_cast<unsigned char>(tag)]; }
|
||||
|
|
@ -301,6 +306,7 @@ namespace Slic3r {
|
|||
{
|
||||
bool recalculate{ false };
|
||||
bool nominal_length{ false };
|
||||
bool prepare_stage{ false };
|
||||
};
|
||||
|
||||
EMoveType move_type{ EMoveType::Noop };
|
||||
|
|
@ -384,6 +390,8 @@ namespace Slic3r {
|
|||
std::array<float, static_cast<size_t>(EMoveType::Count)> moves_time;
|
||||
std::array<float, static_cast<size_t>(ExtrusionRole::erCount)> roles_time;
|
||||
std::vector<float> layers_time;
|
||||
//BBS: prepare stage time before print model, including start gcode time and mostly same with start gcode time
|
||||
float prepare_time;
|
||||
|
||||
void reset();
|
||||
|
||||
|
|
@ -420,7 +428,7 @@ namespace Slic3r {
|
|||
|
||||
// post process the file with the given filename to add remaining time lines M73
|
||||
// and updates moves' gcode ids accordingly
|
||||
void post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends);
|
||||
void post_process(const std::string& filename, std::vector<GCodeProcessorResult::MoveVertex>& moves, std::vector<size_t>& lines_ends, size_t total_layer_num);
|
||||
};
|
||||
|
||||
struct UsedFilaments // filaments per ColorChange
|
||||
|
|
@ -627,6 +635,7 @@ namespace Slic3r {
|
|||
SeamsDetector m_seams_detector;
|
||||
OptionsZCorrector m_options_z_corrector;
|
||||
size_t m_last_default_color_id;
|
||||
bool m_spiral_vase_active;
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
|
@ -684,6 +693,7 @@ namespace Slic3r {
|
|||
void finalize(bool post_process);
|
||||
|
||||
float get_time(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
float get_prepare_time(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
std::string get_time_dhm(PrintEstimatedStatistics::ETimeMode mode) const;
|
||||
std::vector<std::pair<CustomGCode::Type, std::pair<float, float>>> get_custom_gcode_times(PrintEstimatedStatistics::ETimeMode mode, bool include_remaining) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -467,6 +467,8 @@ void process_perimeter_polygon(
|
|||
if (orig_point) {
|
||||
Vec3f pos_of_next = orig_polygon_points.empty() ? first : orig_polygon_points.front();
|
||||
float distance_to_next = (position - pos_of_next).norm();
|
||||
if (distance_to_next > perimeter.flow_width * perimeter.flow_width * 4)
|
||||
oversampled_points.push((position + pos_of_next) / 2);
|
||||
if (global_model_info.is_enforced(position, distance_to_next)) {
|
||||
Vec3f vec_to_next = (pos_of_next - position).normalized();
|
||||
float step_size = SeamPlacer::enforcer_oversampling_distance;
|
||||
|
|
|
|||
|
|
@ -175,10 +175,6 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
|
|||
for (auto layer : object->support_layers())
|
||||
zs.emplace_back(layer->print_z);
|
||||
|
||||
// BBS
|
||||
for (auto layer : object->tree_support_layers())
|
||||
zs.emplace_back(layer->print_z);
|
||||
|
||||
// Find first object layer that is not empty and save its print_z
|
||||
for (const Layer* layer : object->layers())
|
||||
if (layer->has_extrusions()) {
|
||||
|
|
@ -199,10 +195,11 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
|
|||
// BBS
|
||||
if (auto num_filaments = unsigned(print.config().filament_diameter.size());
|
||||
num_filaments > 1 && print.object_extruders().size() == 1 && // the current Print's configuration is CustomGCode::MultiAsSingle
|
||||
print.model().custom_gcode_per_print_z.mode == CustomGCode::MultiAsSingle) {
|
||||
//BBS: replace model custom gcode with current plate custom gcode
|
||||
print.model().get_curr_plate_custom_gcodes().mode == CustomGCode::MultiAsSingle) {
|
||||
// Printing a single extruder platter on a printer with more than 1 extruder (or single-extruder multi-material).
|
||||
// There may be custom per-layer tool changes available at the model.
|
||||
per_layer_extruder_switches = custom_tool_changes(print.model().custom_gcode_per_print_z, num_filaments);
|
||||
per_layer_extruder_switches = custom_tool_changes(print.model().get_curr_plate_custom_gcodes(), num_filaments);
|
||||
}
|
||||
|
||||
// Collect extruders reuqired to print the layers.
|
||||
|
|
@ -348,24 +345,6 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
|
|||
}
|
||||
}
|
||||
|
||||
// BBS
|
||||
for (auto tree_support_layer : object.tree_support_layers()) {
|
||||
LayerTools &layer_tools = this->tools_for_layer(tree_support_layer->print_z);
|
||||
ExtrusionRole role = tree_support_layer->support_fills.role();
|
||||
bool has_support = role == erMixed || role == erSupportMaterial || role == erSupportTransition;;
|
||||
bool has_interface = role == erMixed || role == erSupportMaterialInterface;
|
||||
unsigned int extruder_support = object.config().support_filament.value;
|
||||
unsigned int extruder_interface = object.config().support_interface_filament.value;
|
||||
if (has_support)
|
||||
layer_tools.extruders.push_back(extruder_support);
|
||||
if (has_interface)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Extruder overrides are ordered by print_z.
|
||||
std::vector<std::pair<double, unsigned int>>::const_iterator it_per_layer_extruder_override;
|
||||
it_per_layer_extruder_override = per_layer_extruder_switches.begin();
|
||||
|
|
@ -781,12 +760,15 @@ void ToolOrdering::mark_skirt_layers(const PrintConfig &config, coordf_t max_lay
|
|||
// Assign a pointer to a custom G-code to the respective ToolOrdering::LayerTools.
|
||||
// Ignore color changes, which are performed on a layer and for such an extruder, that the extruder will not be printing above that layer.
|
||||
// If multiple events are planned over a span of a single layer, use the last one.
|
||||
|
||||
// BBS: replace model custom gcode with current plate custom gcode
|
||||
static CustomGCode::Info custom_gcode_per_print_z;
|
||||
void ToolOrdering::assign_custom_gcodes(const Print &print)
|
||||
{
|
||||
// Only valid for non-sequential print.
|
||||
assert(print.config().print_sequence == PrintSequence::ByLayer);
|
||||
|
||||
const CustomGCode::Info &custom_gcode_per_print_z = print.model().custom_gcode_per_print_z;
|
||||
custom_gcode_per_print_z = print.model().get_curr_plate_custom_gcodes();
|
||||
if (custom_gcode_per_print_z.gcodes.empty())
|
||||
return;
|
||||
|
||||
|
|
@ -795,7 +777,7 @@ void ToolOrdering::assign_custom_gcodes(const Print &print)
|
|||
CustomGCode::Mode mode =
|
||||
(num_filaments == 1) ? CustomGCode::SingleExtruder :
|
||||
print.object_extruders().size() == 1 ? CustomGCode::MultiAsSingle : CustomGCode::MultiExtruder;
|
||||
CustomGCode::Mode model_mode = print.model().custom_gcode_per_print_z.mode;
|
||||
CustomGCode::Mode model_mode = print.model().get_curr_plate_custom_gcodes().mode;
|
||||
std::vector<unsigned char> extruder_printing_above(num_filaments, false);
|
||||
auto custom_gcode_it = custom_gcode_per_print_z.gcodes.rbegin();
|
||||
// Tool changes and color changes will be ignored, if the model's tool/color changes were entered in mm mode and the print is in non mm mode
|
||||
|
|
@ -890,7 +872,7 @@ int WipingExtrusions::first_nonsoluble_extruder_on_layer(const PrintConfig& prin
|
|||
{
|
||||
const LayerTools& lt = *m_layer_tools;
|
||||
for (auto extruders_it = lt.extruders.begin(); extruders_it != lt.extruders.end(); ++extruders_it)
|
||||
if (!print_config.filament_soluble.get_at(*extruders_it))
|
||||
if (!print_config.filament_soluble.get_at(*extruders_it) && !print_config.filament_is_support.get_at(*extruders_it))
|
||||
return (*extruders_it);
|
||||
|
||||
return (-1);
|
||||
|
|
@ -901,7 +883,7 @@ int WipingExtrusions::last_nonsoluble_extruder_on_layer(const PrintConfig& print
|
|||
{
|
||||
const LayerTools& lt = *m_layer_tools;
|
||||
for (auto extruders_it = lt.extruders.rbegin(); extruders_it != lt.extruders.rend(); ++extruders_it)
|
||||
if (!print_config.filament_soluble.get_at(*extruders_it))
|
||||
if (!print_config.filament_soluble.get_at(*extruders_it) && !print_config.filament_is_support.get_at(*extruders_it))
|
||||
return (*extruders_it);
|
||||
|
||||
return (-1);
|
||||
|
|
@ -1042,10 +1024,9 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
|
|||
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)
|
||||
if (this_support_layer == nullptr)
|
||||
break;
|
||||
|
||||
bool support_overriddable = object_config.support_filament == 0;
|
||||
|
|
@ -1053,7 +1034,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
|
|||
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;
|
||||
auto &entities = this_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) {
|
||||
|
|
|
|||
|
|
@ -398,14 +398,26 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co
|
|||
slop_move = w0.string();
|
||||
}
|
||||
}
|
||||
|
||||
std::string xy_z_move;
|
||||
{
|
||||
GCodeG1Formatter w0;
|
||||
if (this->is_current_position_clear()) {
|
||||
w0.emit_xyz(target);
|
||||
w0.emit_f(this->config.travel_speed.value * 60.0);
|
||||
w0.emit_comment(GCodeWriter::full_gcode_comment, comment);
|
||||
xy_z_move = w0.string();
|
||||
}
|
||||
else {
|
||||
w0.emit_xy(Vec2d(target.x(), target.y()));
|
||||
w0.emit_f(this->config.travel_speed.value * 60.0);
|
||||
w0.emit_comment(GCodeWriter::full_gcode_comment, comment);
|
||||
xy_z_move = w0.string() + _travel_to_z(target.z(), comment);
|
||||
}
|
||||
}
|
||||
m_pos = dest_point;
|
||||
this->set_current_position_clear(true);
|
||||
GCodeG1Formatter w1;
|
||||
w1.emit_xyz(target);
|
||||
w1.emit_f(this->config.travel_speed.value * 60.0);
|
||||
//BBS
|
||||
w1.emit_comment(GCodeWriter::full_gcode_comment, comment);
|
||||
return slop_move + w1.string();
|
||||
return slop_move + xy_z_move;
|
||||
}
|
||||
else if (!this->will_move_z(point(2))) {
|
||||
double nominal_z = m_pos(2) - m_lifted;
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ public:
|
|||
|
||||
//BBS: set offset for gcode writer
|
||||
void set_xy_offset(double x, double y) { m_x_offset = x; m_y_offset = y; }
|
||||
|
||||
Vec2f get_xy_offset() { return Vec2f{m_x_offset, m_y_offset}; };
|
||||
// To be called by the CoolingBuffer from another thread.
|
||||
static std::string set_fan(const GCodeFlavor gcode_flavor, unsigned int speed);
|
||||
// To be called by the main thread. It always emits the G-code, it does not remember the previous state.
|
||||
|
|
@ -93,7 +93,8 @@ public:
|
|||
bool is_current_position_clear() const { return m_is_current_pos_clear; };
|
||||
//BBS:
|
||||
static const bool full_gcode_comment;
|
||||
|
||||
//Radian threshold of slope for lazy lift and spiral lift;
|
||||
static const double slope_threshold;
|
||||
//SoftFever
|
||||
void set_is_bbl_machine(bool bval) {m_is_bbl_printers = bval;}
|
||||
const bool is_bbl_printers() const {return m_is_bbl_printers;}
|
||||
|
|
@ -134,9 +135,6 @@ private:
|
|||
double m_x_offset{ 0 };
|
||||
double m_y_offset{ 0 };
|
||||
|
||||
//Radian threshold of slope for lazy lift and spiral lift;
|
||||
static const double slope_threshold;
|
||||
|
||||
//SoftFever
|
||||
bool m_is_bbl_printers = false;
|
||||
double m_current_speed;
|
||||
|
|
|
|||
|
|
@ -352,6 +352,50 @@ void Layer::export_region_fill_surfaces_to_svg_debug(const char *name) const
|
|||
this->export_region_fill_surfaces_to_svg(debug_out_path("Layer-fill_surfaces-%s-%d.svg", name, idx ++).c_str());
|
||||
}
|
||||
|
||||
coordf_t Layer::get_sparse_infill_max_void_area()
|
||||
{
|
||||
double max_void_area = 0.;
|
||||
for (auto layerm : m_regions) {
|
||||
Flow flow = layerm->flow(frInfill);
|
||||
float density = layerm->region().config().sparse_infill_density;
|
||||
InfillPattern pattern = layerm->region().config().sparse_infill_pattern;
|
||||
if (density == 0.)
|
||||
return -1;
|
||||
|
||||
//BBS: rough estimation and need to be optimized
|
||||
double spacing = flow.scaled_spacing() * (100 - density) / density;
|
||||
switch (pattern) {
|
||||
case ipConcentric:
|
||||
case ipRectilinear:
|
||||
case ipLine:
|
||||
case ipGyroid:
|
||||
case ipAlignedRectilinear:
|
||||
case ipOctagramSpiral:
|
||||
case ipHilbertCurve:
|
||||
case ip3DHoneycomb:
|
||||
case ipArchimedeanChords:
|
||||
max_void_area = std::max(max_void_area, spacing * spacing);
|
||||
break;
|
||||
case ipGrid:
|
||||
case ipHoneycomb:
|
||||
case ipLightning:
|
||||
max_void_area = std::max(max_void_area, 4.0 * spacing * spacing);
|
||||
break;
|
||||
case ipCubic:
|
||||
case ipAdaptiveCubic:
|
||||
case ipTriangles:
|
||||
case ipStars:
|
||||
case ipSupportCubic:
|
||||
max_void_area = std::max(max_void_area, 4.5 * spacing * spacing);
|
||||
break;
|
||||
default:
|
||||
max_void_area = std::max(max_void_area, spacing * spacing);
|
||||
break;
|
||||
}
|
||||
};
|
||||
return max_void_area;
|
||||
}
|
||||
|
||||
BoundingBox get_extents(const LayerRegion &layer_region)
|
||||
{
|
||||
BoundingBox bbox;
|
||||
|
|
|
|||
|
|
@ -143,6 +143,9 @@ public:
|
|||
ExPolygons lslices;
|
||||
std::vector<BoundingBox> lslices_bboxes;
|
||||
|
||||
// BBS
|
||||
ExPolygons loverhangs;
|
||||
|
||||
size_t region_count() const { return m_regions.size(); }
|
||||
const LayerRegion* get_region(int idx) const { return m_regions[idx]; }
|
||||
LayerRegion* get_region(int idx) { return m_regions[idx]; }
|
||||
|
|
@ -184,6 +187,8 @@ public:
|
|||
|
||||
//BBS
|
||||
void simplify_extrusion_path() { for (auto layerm : m_regions) layerm->simplify_extrusion_entity();}
|
||||
//BBS: this function calculate the maximum void grid area of sparse infill of this layer. Just estimated value
|
||||
coordf_t get_sparse_infill_max_void_area();
|
||||
|
||||
protected:
|
||||
friend class PrintObject;
|
||||
|
|
@ -209,6 +214,11 @@ private:
|
|||
LayerRegionPtrs m_regions;
|
||||
};
|
||||
|
||||
enum SupportInnerType {
|
||||
stInnerNormal,
|
||||
stInnerTree
|
||||
};
|
||||
|
||||
class SupportLayer : public Layer
|
||||
{
|
||||
public:
|
||||
|
|
@ -217,6 +227,10 @@ public:
|
|||
ExPolygonCollection support_islands;
|
||||
// Extrusion paths for the support base and for the support interface and contacts.
|
||||
ExtrusionEntityCollection support_fills;
|
||||
SupportInnerType support_type = stInnerNormal;
|
||||
|
||||
// for tree supports
|
||||
ExPolygons base_areas;
|
||||
|
||||
|
||||
// Is there any valid extrusion assigned to this LayerRegion?
|
||||
|
|
@ -229,33 +243,23 @@ public:
|
|||
|
||||
protected:
|
||||
friend class PrintObject;
|
||||
friend class TreeSupport;
|
||||
|
||||
// The constructor has been made public to be able to insert additional support layers for the skirt or a wipe tower
|
||||
// between the raft and the object first layer.
|
||||
SupportLayer(size_t id, size_t interface_id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) :
|
||||
Layer(id, object, height, print_z, slice_z), m_interface_id(interface_id) {}
|
||||
Layer(id, object, height, print_z, slice_z), m_interface_id(interface_id), support_type(stInnerNormal) {}
|
||||
virtual ~SupportLayer() = default;
|
||||
|
||||
size_t m_interface_id;
|
||||
};
|
||||
|
||||
class TreeSupportLayer : public Layer
|
||||
{
|
||||
public:
|
||||
ExtrusionEntityCollection support_fills;
|
||||
ExPolygons overhang_areas;
|
||||
ExPolygons roof_areas;
|
||||
ExPolygons roof_1st_layer; // the layer just below roof. When working with PolySupport, this layer should be printed with regular material
|
||||
ExPolygons floor_areas;
|
||||
ExPolygons base_areas;
|
||||
ExPolygons roof_gap_areas; // the areas in the gap between support roof and overhang
|
||||
|
||||
enum AreaType {
|
||||
BaseType=0,
|
||||
RoofType=1,
|
||||
FloorType=2,
|
||||
Roof1stLayer=3
|
||||
};
|
||||
// for tree support
|
||||
ExPolygons overhang_areas;
|
||||
ExPolygons roof_areas;
|
||||
ExPolygons roof_1st_layer; // the layer just below roof. When working with PolySupport, this layer should be printed with regular material
|
||||
ExPolygons floor_areas;
|
||||
ExPolygons roof_gap_areas; // the areas in the gap between support roof and overhang
|
||||
enum AreaType { BaseType = 0, RoofType = 1, FloorType = 2, Roof1stLayer = 3 };
|
||||
struct AreaGroup
|
||||
{
|
||||
ExPolygon *area;
|
||||
|
|
@ -263,23 +267,9 @@ public:
|
|||
coordf_t dist_to_top; // mm dist to top
|
||||
AreaGroup(ExPolygon *a, int t, coordf_t d) : area(a), type(t), dist_to_top(d) {}
|
||||
};
|
||||
std::vector<AreaGroup> area_groups;
|
||||
|
||||
enum OverhangType {
|
||||
Detected=0,
|
||||
Enforced
|
||||
};
|
||||
enum OverhangType { Detected = 0, Enforced };
|
||||
std::vector<AreaGroup> area_groups;
|
||||
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);}
|
||||
|
||||
protected:
|
||||
friend class PrintObject;
|
||||
TreeSupportLayer(size_t id, PrintObject* object, coordf_t height, coordf_t print_z, coordf_t slice_z) :
|
||||
Layer(id, object, height, print_z, slice_z) {}
|
||||
virtual ~TreeSupportLayer() = default;
|
||||
};
|
||||
|
||||
template<typename LayerContainer>
|
||||
|
|
|
|||
|
|
@ -158,11 +158,20 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
|||
{
|
||||
// Voids are sparse infills if infill rate is zero.
|
||||
Polygons voids;
|
||||
|
||||
double max_grid_area = -1;
|
||||
if (this->layer()->lower_layer != nullptr)
|
||||
max_grid_area = this->layer()->lower_layer->get_sparse_infill_max_void_area();
|
||||
for (const Surface &surface : this->fill_surfaces.surfaces) {
|
||||
if (surface.is_top()) {
|
||||
// Collect the top surfaces, inflate them and trim them by the bottom surfaces.
|
||||
// This gives the priority to bottom surfaces.
|
||||
surfaces_append(top, offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface);
|
||||
if (max_grid_area < 0 || surface.expolygon.area() < max_grid_area)
|
||||
surfaces_append(top, offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface);
|
||||
else
|
||||
//BBS: Don't need to expand too much in this situation. Expand 3mm to eliminate hole and 1mm for contour
|
||||
surfaces_append(top, intersection_ex(offset(surface.expolygon.contour, margin / 3.0, EXTERNAL_SURFACES_OFFSET_PARAMETERS),
|
||||
offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS)), surface);
|
||||
} else if (surface.surface_type == stBottom || (surface.surface_type == stBottomBridge && lower_layer == nullptr)) {
|
||||
// Grown by 3mm.
|
||||
surfaces_append(bottom, offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface);
|
||||
|
|
|
|||
|
|
@ -63,7 +63,9 @@ Model& Model::assign_copy(const Model &rhs)
|
|||
}
|
||||
|
||||
// copy custom code per height
|
||||
this->custom_gcode_per_print_z = rhs.custom_gcode_per_print_z;
|
||||
// BBS
|
||||
this->plates_custom_gcodes = rhs.plates_custom_gcodes;
|
||||
this->curr_plate_index = rhs.curr_plate_index;
|
||||
|
||||
// BBS: for design info
|
||||
this->design_info = rhs.design_info;
|
||||
|
|
@ -89,7 +91,9 @@ Model& Model::assign_copy(Model &&rhs)
|
|||
rhs.objects.clear();
|
||||
|
||||
// copy custom code per height
|
||||
this->custom_gcode_per_print_z = std::move(rhs.custom_gcode_per_print_z);
|
||||
// BBS
|
||||
this->plates_custom_gcodes = std::move(rhs.plates_custom_gcodes);
|
||||
this->curr_plate_index = rhs.curr_plate_index;
|
||||
|
||||
//BBS: add auxiliary path logic
|
||||
// BBS: backup, all in one temp dir
|
||||
|
|
@ -161,10 +165,11 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
|
|||
file_version = &temp_version;
|
||||
|
||||
bool result = false;
|
||||
bool is_cb_cancel = false;
|
||||
std::string message;
|
||||
if (boost::algorithm::iends_with(input_file, ".stp") ||
|
||||
boost::algorithm::iends_with(input_file, ".step"))
|
||||
result = load_step(input_file.c_str(), &model, stepFn, stepIsUtf8Fn);
|
||||
result = load_step(input_file.c_str(), &model, is_cb_cancel, stepFn, stepIsUtf8Fn);
|
||||
else if (boost::algorithm::iends_with(input_file, ".stl"))
|
||||
result = load_stl(input_file.c_str(), &model, nullptr, stlFn);
|
||||
else if (boost::algorithm::iends_with(input_file, ".obj"))
|
||||
|
|
@ -185,6 +190,11 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
|
|||
else
|
||||
throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .amf(.xml) extension.");
|
||||
|
||||
if (is_cb_cancel) {
|
||||
Model empty_model;
|
||||
return empty_model;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
if (message.empty())
|
||||
throw Slic3r::RuntimeError("Loading of a model file failed.");
|
||||
|
|
@ -203,7 +213,9 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
|
|||
|
||||
//BBS
|
||||
//CustomGCode::update_custom_gcode_per_print_z_from_config(model.custom_gcode_per_print_z, config);
|
||||
CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
|
||||
//BBS
|
||||
for (auto& plate_gcodes : model.plates_custom_gcodes)
|
||||
CustomGCode::check_mode_for_custom_gcode_per_print_z(plate_gcodes.second);
|
||||
|
||||
sort_remove_duplicates(config_substitutions->substitutions);
|
||||
return model;
|
||||
|
|
@ -277,7 +289,9 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
|
|||
throw Slic3r::RuntimeError("Canceled");
|
||||
}
|
||||
|
||||
CustomGCode::check_mode_for_custom_gcode_per_print_z(model.custom_gcode_per_print_z);
|
||||
//BBS
|
||||
for (auto& plate_gcodes : model.plates_custom_gcodes)
|
||||
CustomGCode::check_mode_for_custom_gcode_per_print_z(plate_gcodes.second);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_STAGE_CHECK_MODE_GCODE\n");
|
||||
if (proFn) {
|
||||
|
|
@ -2352,6 +2366,12 @@ bool ModelVolume::is_splittable() const
|
|||
// BBS
|
||||
std::vector<int> ModelVolume::get_extruders() const
|
||||
{
|
||||
if (m_type == ModelVolumeType::INVALID
|
||||
|| m_type == ModelVolumeType::NEGATIVE_VOLUME
|
||||
|| m_type == ModelVolumeType::SUPPORT_BLOCKER
|
||||
|| m_type == ModelVolumeType::SUPPORT_ENFORCER)
|
||||
return std::vector<int>();
|
||||
|
||||
if (mmu_segmentation_facets.timestamp() != mmuseg_ts) {
|
||||
std::vector<indexed_triangle_set> its_per_type;
|
||||
mmuseg_extruders.clear();
|
||||
|
|
@ -2564,6 +2584,7 @@ size_t ModelVolume::split(unsigned int max_extruders)
|
|||
if (idx == 0) {
|
||||
this->set_mesh(std::move(mesh));
|
||||
this->calculate_convex_hull();
|
||||
this->invalidate_convex_hull_2d();
|
||||
// Assign a new unique ID, so that a new GLVolume will be generated.
|
||||
this->set_new_unique_id();
|
||||
// reset the source to disable reload from disk
|
||||
|
|
|
|||
|
|
@ -650,6 +650,38 @@ private:
|
|||
friend class ModelVolume;
|
||||
};
|
||||
|
||||
struct RaycastResult
|
||||
{
|
||||
Vec2d mouse_position = Vec2d::Zero();
|
||||
int mesh_id = -1;
|
||||
Vec3f hit = Vec3f::Zero();
|
||||
Vec3f normal = Vec3f::Zero();
|
||||
|
||||
template<typename Archive> void serialize(Archive &ar) { ar(mouse_position, mesh_id, hit, normal); }
|
||||
};
|
||||
|
||||
struct TextInfo
|
||||
{
|
||||
std::string m_font_name;
|
||||
float m_font_size = 16.f;
|
||||
int m_curr_font_idx = 0;
|
||||
bool m_bold = true;
|
||||
bool m_italic = false;
|
||||
float m_thickness = 2.f;
|
||||
float m_embeded_depth = 0.f;
|
||||
float m_rotate_angle = 0;
|
||||
float m_text_gap = 0.f;
|
||||
bool m_is_surface_text = false;
|
||||
bool m_keep_horizontal = false;
|
||||
std::string m_text;
|
||||
|
||||
RaycastResult m_rr;
|
||||
|
||||
template<typename Archive> void serialize(Archive &ar) {
|
||||
ar(m_font_name, m_font_size, m_curr_font_idx, m_bold, m_italic, m_thickness, m_embeded_depth, m_rotate_angle, m_text_gap, m_is_surface_text, m_keep_horizontal, m_text, m_rr);
|
||||
}
|
||||
};
|
||||
|
||||
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
|
||||
// ModelVolume instances are owned by a ModelObject.
|
||||
class ModelVolume final : public ObjectBase
|
||||
|
|
@ -756,6 +788,10 @@ public:
|
|||
const std::shared_ptr<const TriangleMesh>& get_convex_hull_shared_ptr() const { return m_convex_hull; }
|
||||
//BBS: add convex_hell_2d related logic
|
||||
const Polygon& get_convex_hull_2d(const Transform3d &trafo_instance) const;
|
||||
void invalidate_convex_hull_2d()
|
||||
{
|
||||
m_convex_hull_2d.clear();
|
||||
}
|
||||
|
||||
// Get count of errors in the mesh
|
||||
int get_repaired_errors_count() const;
|
||||
|
|
@ -795,6 +831,9 @@ public:
|
|||
void convert_from_imperial_units();
|
||||
void convert_from_meters();
|
||||
|
||||
void set_text_info(const TextInfo& text_info) { m_text_info = text_info; }
|
||||
const TextInfo& get_text_info() const { return m_text_info; }
|
||||
|
||||
const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); }
|
||||
|
||||
void set_new_unique_id() {
|
||||
|
|
@ -839,6 +878,8 @@ private:
|
|||
mutable Polygon m_cached_2d_polygon; //BBS, used for convex_hell_2d acceleration
|
||||
Geometry::Transformation m_transformation;
|
||||
|
||||
TextInfo m_text_info;
|
||||
|
||||
//BBS: add convex_hell_2d related logic
|
||||
void calculate_convex_hull_2d(const Geometry::Transformation &transformation) const;
|
||||
|
||||
|
|
@ -892,7 +933,8 @@ private:
|
|||
ObjectBase(other),
|
||||
name(other.name), source(other.source), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull),
|
||||
config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation),
|
||||
supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets)
|
||||
supported_facets(other.supported_facets), seam_facets(other.seam_facets), mmu_segmentation_facets(other.mmu_segmentation_facets),
|
||||
m_text_info(other.m_text_info)
|
||||
{
|
||||
assert(this->id().valid());
|
||||
assert(this->config.id().valid());
|
||||
|
|
@ -957,7 +999,7 @@ private:
|
|||
// BBS: add backup, check modify
|
||||
bool mesh_changed = false;
|
||||
auto tr = m_transformation;
|
||||
ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull);
|
||||
ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull, m_text_info);
|
||||
mesh_changed |= !(tr == m_transformation);
|
||||
if (mesh_changed) m_transformation.get_matrix(true, true, true, true); // force dirty
|
||||
auto t = supported_facets.timestamp();
|
||||
|
|
@ -983,7 +1025,7 @@ private:
|
|||
}
|
||||
template<class Archive> void save(Archive &ar) const {
|
||||
bool has_convex_hull = m_convex_hull.get() != nullptr;
|
||||
ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull);
|
||||
ar(name, source, m_mesh, m_type, m_material_id, m_transformation, m_is_splittable, has_convex_hull, m_text_info);
|
||||
cereal::save_by_value(ar, supported_facets);
|
||||
cereal::save_by_value(ar, seam_facets);
|
||||
cereal::save_by_value(ar, mmu_segmentation_facets);
|
||||
|
|
@ -1280,7 +1322,17 @@ public:
|
|||
}
|
||||
|
||||
// Extensions for color print
|
||||
CustomGCode::Info custom_gcode_per_print_z;
|
||||
// CustomGCode::Info custom_gcode_per_print_z;
|
||||
//BBS: replace model custom gcode with current plate custom gcode
|
||||
int curr_plate_index{ 0 };
|
||||
std::map<int, CustomGCode::Info> plates_custom_gcodes; //map<plate_index, CustomGCode::Info>
|
||||
|
||||
const CustomGCode::Info get_curr_plate_custom_gcodes() const {
|
||||
if (plates_custom_gcodes.find(curr_plate_index) != plates_custom_gcodes.end()) {
|
||||
return plates_custom_gcodes.at(curr_plate_index);
|
||||
}
|
||||
return CustomGCode::Info();
|
||||
}
|
||||
|
||||
// Default constructor assigns a new ID to the model.
|
||||
Model() { assert(this->id().valid()); }
|
||||
|
|
|
|||
|
|
@ -1136,21 +1136,8 @@ static std::vector<std::vector<const MMU_Graph::Arc *>> get_all_next_arcs(
|
|||
continue;
|
||||
|
||||
Vec2d arc_line = graph.nodes[arc.to_idx].point - graph.nodes[arc.from_idx].point;
|
||||
if (arc_line.norm() < 5) { // two points whose distance is less than 5 are considered as one point
|
||||
Linef process_line_1(graph.nodes[arc.from_idx].point, graph.nodes[arc.to_idx].point);
|
||||
std::vector<std::vector<const MMU_Graph::Arc *>> next_arcs = get_all_next_arcs(graph, used_arcs, process_line_1, arc, color);
|
||||
if (next_arcs.empty())
|
||||
continue;
|
||||
|
||||
for (std::vector<const MMU_Graph::Arc *> &next_arc : next_arcs) {
|
||||
next_continue_arc.emplace_back(&arc);
|
||||
next_continue_arc.insert(next_continue_arc.end(), next_arc.begin(), next_arc.end());
|
||||
all_next_arcs.emplace_back(next_continue_arc);
|
||||
}
|
||||
} else {
|
||||
next_continue_arc.emplace_back(&arc);
|
||||
all_next_arcs.emplace_back(next_continue_arc);
|
||||
}
|
||||
next_continue_arc.emplace_back(&arc);
|
||||
all_next_arcs.emplace_back(next_continue_arc);
|
||||
}
|
||||
return all_next_arcs;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -965,26 +965,19 @@ void PerimeterGenerator::process_classic()
|
|||
if (is_outer_wall_first ||
|
||||
//BBS: always print outer wall first when there indeed has brim.
|
||||
(this->layer_id == 0 &&
|
||||
this->object_config->brim_type == BrimType::btOuterOnly &&
|
||||
this->object_config->brim_width.value > 0))
|
||||
{
|
||||
if (this->config->wall_infill_order == WallInfillOrder::InnerOuterInnerInfill) {
|
||||
if (entities.entities.size() > 1) {
|
||||
std::vector<int> extPs;
|
||||
for (int i = 0; i < entities.entities.size(); ++i) {
|
||||
if (entities.entities[i]->role() == erExternalPerimeter)
|
||||
extPs.push_back(i);
|
||||
this->object_config->brim_type == BrimType::btOuterOnly &&
|
||||
this->object_config->brim_width.value > 0))
|
||||
entities.reverse();
|
||||
else if (this->config->wall_infill_order == WallInfillOrder::InnerOuterInnerInfill)
|
||||
if (entities.entities.size() > 1){
|
||||
int last_outer=0;
|
||||
int outer = 0;
|
||||
for (; outer < entities.entities.size(); ++outer)
|
||||
if (entities.entities[outer]->role() == erExternalPerimeter && outer - last_outer > 1) {
|
||||
std::swap(entities.entities[outer], entities.entities[outer - 1]);
|
||||
last_outer = outer;
|
||||
}
|
||||
for (int i = 0; i < extPs.size(); ++i) {
|
||||
if (extPs[i] == 0 || (i > 0 && extPs[i] - 1 == extPs[i - 1]))
|
||||
continue;
|
||||
std::swap(entities.entities[extPs[i]], entities.entities[extPs[i] - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
entities.reverse();
|
||||
}
|
||||
// append perimeters for this slice as a collection
|
||||
if (! entities.empty())
|
||||
this->loops->append(entities);
|
||||
|
|
@ -1327,6 +1320,17 @@ void PerimeterGenerator::process_arachne()
|
|||
}
|
||||
}
|
||||
}
|
||||
// BBS. adjust wall generate seq
|
||||
if (this->config->wall_infill_order == WallInfillOrder::InnerOuterInnerInfill)
|
||||
if (ordered_extrusions.size() > 1) {
|
||||
int last_outer = 0;
|
||||
int outer = 0;
|
||||
for (; outer < ordered_extrusions.size(); ++outer)
|
||||
if (ordered_extrusions[outer].extrusion->inset_idx == 0 && outer - last_outer > 1) {
|
||||
std::swap(ordered_extrusions[outer], ordered_extrusions[outer - 1]);
|
||||
last_outer = outer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (this->config->wall_infill_order == WallInfillOrder::InnerOuterInnerInfill) {
|
||||
|
|
|
|||
|
|
@ -161,6 +161,21 @@ public:
|
|||
Point& operator-=(const Point& rhs) { this->x() -= rhs.x(); this->y() -= rhs.y(); return *this; }
|
||||
Point& operator*=(const double &rhs) { this->x() = coord_t(this->x() * rhs); this->y() = coord_t(this->y() * rhs); return *this; }
|
||||
Point operator*(const double &rhs) { return Point(this->x() * rhs, this->y() * rhs); }
|
||||
bool both_comp(const Point &rhs, const std::string& op) {
|
||||
if (op == ">")
|
||||
return this->x() > rhs.x() && this->y() > rhs.y();
|
||||
else if (op == "<")
|
||||
return this->x() < rhs.x() && this->y() < rhs.y();
|
||||
return false;
|
||||
}
|
||||
bool any_comp(const Point &rhs, const std::string &op)
|
||||
{
|
||||
if (op == ">")
|
||||
return this->x() > rhs.x() || this->y() > rhs.y();
|
||||
else if (op == "<")
|
||||
return this->x() < rhs.x() || this->y() < rhs.y();
|
||||
return false;
|
||||
}
|
||||
|
||||
void rotate(double angle) { this->rotate(std::cos(angle), std::sin(angle)); }
|
||||
void rotate(double cos_a, double sin_a) {
|
||||
|
|
|
|||
|
|
@ -771,7 +771,7 @@ static std::vector<std::string> s_Preset_filament_options {
|
|||
"fan_max_speed", "enable_overhang_bridge_fan", "overhang_fan_speed", "overhang_fan_threshold", "close_fan_the_first_x_layers", "full_fan_speed_layer", "fan_cooling_layer_time", "slow_down_layer_time", "slow_down_min_speed",
|
||||
"filament_start_gcode", "filament_end_gcode",
|
||||
// Retract overrides
|
||||
"filament_retraction_length", "filament_z_hop", "filament_retraction_speed", "filament_deretraction_speed", "filament_retract_restart_extra", "filament_retraction_minimum_travel",
|
||||
"filament_retraction_length", "filament_z_hop", "filament_z_hop_types", "filament_retraction_speed", "filament_deretraction_speed", "filament_retract_restart_extra", "filament_retraction_minimum_travel",
|
||||
"filament_retract_when_changing_layer", "filament_wipe", "filament_retract_before_wipe",
|
||||
// Profile compatibility
|
||||
"filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits",
|
||||
|
|
@ -799,7 +799,7 @@ static std::vector<std::string> s_Preset_printer_options {
|
|||
"silent_mode",
|
||||
// BBS
|
||||
"scan_first_layer", "machine_load_filament_time", "machine_unload_filament_time", "machine_pause_gcode", "template_custom_gcode",
|
||||
"nozzle_type", "nozzle_hrc","auxiliary_fan", "nozzle_volume","upward_compatible_machine",
|
||||
"nozzle_type", "nozzle_hrc","auxiliary_fan", "nozzle_volume","upward_compatible_machine", "z_hop_types",
|
||||
//SoftFever
|
||||
"host_type", "print_host", "printhost_apikey",
|
||||
"printhost_cafile","printhost_port","printhost_authorization_type",
|
||||
|
|
@ -1128,7 +1128,7 @@ void PresetCollection::load_presets(
|
|||
std::sort(m_presets.begin() + m_num_default_presets, m_presets.end());
|
||||
//BBS: add config related logs
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": loaded %1% presets from %2%, type %3%")%presets_loaded.size() %dir %Preset::get_type_string(m_type);
|
||||
this->select_preset(first_visible_idx());
|
||||
//this->select_preset(first_visible_idx());
|
||||
if (! errors_cummulative.empty())
|
||||
throw Slic3r::RuntimeError(errors_cummulative);
|
||||
}
|
||||
|
|
@ -1555,6 +1555,10 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
|
|||
BOOST_LOG_TRIVIAL(error) << "Error in a preset file: The preset \"" <<
|
||||
name << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
|
||||
if (need_update) {
|
||||
if (iter->name == m_edited_preset.name && iter->is_dirty) {
|
||||
// Keep modifies when update from remote
|
||||
new_config.apply_only(m_edited_preset.config, m_edited_preset.config.diff(iter->config));
|
||||
}
|
||||
iter->config = new_config;
|
||||
iter->updated_time = cloud_update_time;
|
||||
iter->version = cloud_version.value();
|
||||
|
|
|
|||
|
|
@ -241,44 +241,17 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward
|
|||
//BBS: change system config to json
|
||||
std::tie(substitutions, errors_cummulative) = this->load_system_presets_from_json(substitution_rule);
|
||||
|
||||
//BBS load preset from user's folder, load system default if
|
||||
//BBS: change directories by design
|
||||
std::string dir_user_presets;
|
||||
if (!config.get("preset_folder").empty()) {
|
||||
dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/" + config.get("preset_folder");
|
||||
// Load default user presets always
|
||||
load_user_presets(DEFAULT_USER_FOLDER_NAME, substitution_rule);
|
||||
// BBS load preset from user's folder, load system default if
|
||||
// BBS: change directories by design
|
||||
std::string dir_user_presets = config.get("preset_folder");
|
||||
if (!dir_user_presets.empty()) {
|
||||
load_user_presets(dir_user_presets, substitution_rule);
|
||||
}
|
||||
else {
|
||||
dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/" + DEFAULT_USER_FOLDER_NAME;
|
||||
}
|
||||
fs::path user_folder(data_dir() + "/" + PRESET_USER_DIR);
|
||||
if (!fs::exists(user_folder))
|
||||
fs::create_directory(user_folder);
|
||||
|
||||
fs::path folder(dir_user_presets);
|
||||
if (!fs::exists(folder))
|
||||
fs::create_directory(folder);
|
||||
|
||||
// BBS do not load sla_print
|
||||
//BBS: change directoties by design
|
||||
try {
|
||||
this->prints.load_presets(dir_user_presets, PRESET_PRINT_NAME, substitutions, substitution_rule);
|
||||
} catch (const std::runtime_error &err) {
|
||||
errors_cummulative += err.what();
|
||||
}
|
||||
try {
|
||||
this->filaments.load_presets(dir_user_presets, PRESET_FILAMENT_NAME, substitutions, substitution_rule);
|
||||
} catch (const std::runtime_error &err) {
|
||||
errors_cummulative += err.what();
|
||||
}
|
||||
try {
|
||||
this->printers.load_presets(dir_user_presets, PRESET_PRINTER_NAME, substitutions, substitution_rule);
|
||||
} catch (const std::runtime_error &err) {
|
||||
errors_cummulative += err.what();
|
||||
}
|
||||
this->update_multi_material_filament_presets();
|
||||
this->update_compatible(PresetSelectCompatibleType::Never);
|
||||
if (! errors_cummulative.empty())
|
||||
throw Slic3r::RuntimeError(errors_cummulative);
|
||||
|
||||
this->load_selections(config, preferred_selection);
|
||||
|
||||
|
|
@ -534,7 +507,44 @@ std::string PresetBundle::get_hotend_model_for_printer_model(std::string model_n
|
|||
return out;
|
||||
}
|
||||
|
||||
PresetsConfigSubstitutions PresetBundle::load_user_presets(AppConfig &config, std::map<std::string, std::map<std::string, std::string>>& my_presets, ForwardCompatibilitySubstitutionRule substitution_rule)
|
||||
PresetsConfigSubstitutions PresetBundle::load_user_presets(std::string user, ForwardCompatibilitySubstitutionRule substitution_rule)
|
||||
{
|
||||
PresetsConfigSubstitutions substitutions;
|
||||
std::string errors_cummulative;
|
||||
|
||||
fs::path user_folder(data_dir() + "/" + PRESET_USER_DIR);
|
||||
if (!fs::exists(user_folder)) fs::create_directory(user_folder);
|
||||
|
||||
std::string dir_user_presets = data_dir() + "/" + PRESET_USER_DIR + "/" + user;
|
||||
fs::path folder(user_folder / user);
|
||||
if (!fs::exists(folder)) fs::create_directory(folder);
|
||||
|
||||
// BBS do not load sla_print
|
||||
// BBS: change directoties by design
|
||||
try {
|
||||
this->prints.load_presets(dir_user_presets, PRESET_PRINT_NAME, substitutions, substitution_rule);
|
||||
} catch (const std::runtime_error &err) {
|
||||
errors_cummulative += err.what();
|
||||
}
|
||||
try {
|
||||
this->filaments.load_presets(dir_user_presets, PRESET_FILAMENT_NAME, substitutions, substitution_rule);
|
||||
} catch (const std::runtime_error &err) {
|
||||
errors_cummulative += err.what();
|
||||
}
|
||||
try {
|
||||
this->printers.load_presets(dir_user_presets, PRESET_PRINTER_NAME, substitutions, substitution_rule);
|
||||
} catch (const std::runtime_error &err) {
|
||||
errors_cummulative += err.what();
|
||||
}
|
||||
if (!errors_cummulative.empty()) throw Slic3r::RuntimeError(errors_cummulative);
|
||||
this->update_multi_material_filament_presets();
|
||||
this->update_compatible(PresetSelectCompatibleType::Never);
|
||||
return PresetsConfigSubstitutions();
|
||||
}
|
||||
|
||||
PresetsConfigSubstitutions PresetBundle::load_user_presets(AppConfig & config,
|
||||
std::map<std::string, std::map<std::string, std::string>> &my_presets,
|
||||
ForwardCompatibilitySubstitutionRule substitution_rule)
|
||||
{
|
||||
// First load the vendor specific system presets.
|
||||
PresetsConfigSubstitutions substitutions;
|
||||
|
|
@ -545,6 +555,10 @@ PresetsConfigSubstitutions PresetBundle::load_user_presets(AppConfig &config, st
|
|||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" print's selected_idx %1%, selected_name %2%") %prints.get_selected_idx() %prints.get_selected_preset_name();
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" filament's selected_idx %1%, selected_name %2%") %filaments.get_selected_idx() %filaments.get_selected_preset_name();
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" printers's selected_idx %1%, selected_name %2%") %printers.get_selected_idx() %printers.get_selected_preset_name();
|
||||
|
||||
// Sync removing
|
||||
remove_users_preset(config, &my_presets);
|
||||
|
||||
std::map<std::string, std::map<std::string, std::string>>::iterator it;
|
||||
for (it = my_presets.begin(); it != my_presets.end(); it++) {
|
||||
std::string name = it->first;
|
||||
|
|
@ -830,13 +844,26 @@ bool PresetBundle::validate_printers(const std::string &name, DynamicPrintConfig
|
|||
#endif
|
||||
}
|
||||
|
||||
void PresetBundle::remove_users_preset(AppConfig& config)
|
||||
void PresetBundle::remove_users_preset(AppConfig &config, std::map<std::string, std::map<std::string, std::string>> *my_presets)
|
||||
{
|
||||
auto check_removed = [my_presets, this](Preset &preset) -> bool {
|
||||
if (my_presets == nullptr) return true;
|
||||
if (my_presets->find(preset.name) != my_presets->end()) return false;
|
||||
if (!preset.sync_info.empty()) return false; // syncing, not remove
|
||||
if (preset.setting_id.empty()) return false; // no id, not remove
|
||||
// Saved preset is removed by another session
|
||||
if (preset.is_dirty) {
|
||||
preset.setting_id.clear();
|
||||
return false;
|
||||
}
|
||||
preset.remove_files();
|
||||
return true;
|
||||
};
|
||||
std::string preset_folder_user_id = config.get("preset_folder");
|
||||
std::string printer_selected_preset_name = printers.get_selected_preset().name;
|
||||
bool need_reset_printer_preset = false;
|
||||
for (auto it = printers.begin(); it != printers.end();) {
|
||||
if (it->is_user() && !it->user_id.empty() && it->user_id.compare(preset_folder_user_id) == 0) {
|
||||
if (it->is_user() && !it->user_id.empty() && it->user_id.compare(preset_folder_user_id) == 0 && check_removed(*it)) {
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":printers erase %1%, type %2%, user_id %3%") % it->name % Preset::get_type_string(it->type) % it->user_id;
|
||||
if (it->name == printer_selected_preset_name)
|
||||
need_reset_printer_preset = true;
|
||||
|
|
@ -867,7 +894,7 @@ void PresetBundle::remove_users_preset(AppConfig& config)
|
|||
bool need_reset_print_preset = false;
|
||||
// remove preset if user_id is not current user
|
||||
for (auto it = prints.begin(); it != prints.end();) {
|
||||
if (it->is_user() && !it->user_id.empty() && it->user_id.compare(preset_folder_user_id) == 0) {
|
||||
if (it->is_user() && !it->user_id.empty() && it->user_id.compare(preset_folder_user_id) == 0 && check_removed(*it)) {
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":prints erase %1%, type %2%, user_id %3%")%it->name %Preset::get_type_string(it->type) %it->user_id;
|
||||
if (it->name == selected_print_name)
|
||||
need_reset_print_preset = true;
|
||||
|
|
@ -887,7 +914,7 @@ void PresetBundle::remove_users_preset(AppConfig& config)
|
|||
std::string selected_filament_name = filaments.get_selected_preset().name;
|
||||
bool need_reset_filament_preset = false;
|
||||
for (auto it = filaments.begin(); it != filaments.end();) {
|
||||
if (it->is_user() && !it->user_id.empty() && it->user_id.compare(preset_folder_user_id) == 0) {
|
||||
if (it->is_user() && !it->user_id.empty() && it->user_id.compare(preset_folder_user_id) == 0 && check_removed(*it)) {
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":filaments erase %1%, type %2%, user_id %3%")%it->name %Preset::get_type_string(it->type) %it->user_id;
|
||||
if (it->name == selected_filament_name)
|
||||
need_reset_filament_preset = true;
|
||||
|
|
@ -905,6 +932,8 @@ void PresetBundle::remove_users_preset(AppConfig& config)
|
|||
filaments.select_preset_by_name(selected_filament_name, false);
|
||||
}
|
||||
|
||||
update_compatible(PresetSelectCompatibleType::Always);
|
||||
|
||||
/* set selected preset */
|
||||
for (size_t i = 0; i < filament_presets.size(); ++i)
|
||||
{
|
||||
|
|
@ -1268,7 +1297,7 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
|
|||
this->filament_presets = { filaments.get_selected_preset_name() };
|
||||
for (unsigned int i = 1; i < 1000; ++ i) {
|
||||
char name[64];
|
||||
sprintf(name, "filament_%u", i);
|
||||
sprintf(name, "filament_%02u", i);
|
||||
if (! config.has("presets", name))
|
||||
break;
|
||||
this->filament_presets.emplace_back(remove_ini_suffix(config.get("presets", name)));
|
||||
|
|
@ -1276,8 +1305,9 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
|
|||
std::vector<std::string> filament_colors;
|
||||
if (config.has("presets", "filament_colors")) {
|
||||
boost::algorithm::split(filament_colors, config.get("presets", "filament_colors"), boost::algorithm::is_any_of(","));
|
||||
project_config.option<ConfigOptionStrings>("filament_colour")->values = filament_colors;
|
||||
}
|
||||
filament_colors.resize(filament_presets.size());
|
||||
project_config.option<ConfigOptionStrings>("filament_colour")->values = filament_colors;
|
||||
std::vector<std::string> matrix;
|
||||
if (config.has("presets", "flush_volumes_matrix")) {
|
||||
boost::algorithm::split(matrix, config.get("presets", "flush_volumes_matrix"), boost::algorithm::is_any_of("|"));
|
||||
|
|
@ -1340,13 +1370,14 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
|
|||
void PresetBundle::export_selections(AppConfig &config)
|
||||
{
|
||||
assert(this->printers.get_edited_preset().printer_technology() != ptFFF || filament_presets.size() >= 1);
|
||||
// assert(this->printers.get_edited_preset().printer_technology() != ptFFF || filament_presets.size() > 1 || filaments.get_selected_preset_name() == filament_presets.front());
|
||||
//assert(this->printers.get_edited_preset().printer_technology() != ptFFF || filament_presets.size() > 1 || filaments.get_selected_preset_name() == filament_presets.front());
|
||||
config.clear_section("presets");
|
||||
config.set("presets", PRESET_PRINT_NAME, prints.get_selected_preset_name());
|
||||
config.set("presets", PRESET_FILAMENT_NAME, filament_presets.front());
|
||||
for (unsigned i = 1; i < filament_presets.size(); ++i) {
|
||||
char name[64];
|
||||
sprintf(name, "filament_%u", i);
|
||||
assert(!filament_presets[i].empty());
|
||||
sprintf(name, "filament_%02u", i);
|
||||
config.set("presets", name, filament_presets[i]);
|
||||
}
|
||||
CNumericLocalesSetter locales_setter;
|
||||
|
|
@ -1402,6 +1433,13 @@ unsigned int PresetBundle::sync_ams_list(unsigned int &unknowns)
|
|||
for (auto &ams : filament_ams_list) {
|
||||
auto filament_id = ams.opt_string("filament_id", 0u);
|
||||
auto filament_color = ams.opt_string("filament_colour", 0u);
|
||||
auto filament_changed = !ams.has("filament_changed") || ams.opt_bool("filament_changed");
|
||||
if (filament_id.empty()) continue;
|
||||
if (!filament_changed && this->filament_presets.size() > filament_presets.size()) {
|
||||
filament_presets.push_back(this->filament_presets[filament_presets.size()]);
|
||||
filament_colors.push_back(filament_color);
|
||||
continue;
|
||||
}
|
||||
auto iter = std::find_if(filaments.begin(), filaments.end(), [&filament_id](auto &f) { return f.is_compatible && f.is_system && f.filament_id == filament_id; });
|
||||
if (iter == filaments.end()) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": filament_id %1% not found or system or compatible") % filament_id;
|
||||
|
|
@ -3473,9 +3511,9 @@ std::vector<std::string> PresetBundle::export_current_configs(const std::string
|
|||
std::string file = path + "/" + preset->name + ".json";
|
||||
if (boost::filesystem::exists(file) && overwrite < 2) {
|
||||
overwrite = override_confirm(preset->name);
|
||||
if (overwrite == 0 || overwrite == 2)
|
||||
continue;
|
||||
}
|
||||
if (overwrite == 0 || overwrite == 2)
|
||||
continue;
|
||||
preset->config.save_to_json(file, preset->name, "", preset->version.to_string());
|
||||
result.push_back(file);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,10 +47,11 @@ public:
|
|||
void load_selections(AppConfig &config, const PresetPreferences& preferred_selection = PresetPreferences());
|
||||
|
||||
// BBS Load user presets
|
||||
PresetsConfigSubstitutions load_user_presets(std::string user, ForwardCompatibilitySubstitutionRule rule);
|
||||
PresetsConfigSubstitutions load_user_presets(AppConfig &config, std::map<std::string, std::map<std::string, std::string>>& my_presets, ForwardCompatibilitySubstitutionRule rule);
|
||||
PresetsConfigSubstitutions import_presets(std::vector<std::string> &files, std::function<int(std::string const &)> override_confirm, ForwardCompatibilitySubstitutionRule rule);
|
||||
void save_user_presets(AppConfig& config, std::vector<std::string>& need_to_delete_list);
|
||||
void remove_users_preset(AppConfig &config);
|
||||
void remove_users_preset(AppConfig &config, std::map<std::string, std::map<std::string, std::string>> * my_presets = nullptr);
|
||||
void update_user_presets_directory(const std::string preset_folder);
|
||||
void remove_user_presets_directory(const std::string preset_folder);
|
||||
void update_system_preset_setting_ids(std::map<std::string, std::map<std::string, std::string>>& system_presets);
|
||||
|
|
|
|||
|
|
@ -246,6 +246,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||
osteps.emplace_back(posSimplifyPath);
|
||||
osteps.emplace_back(posSimplifySupportPath);
|
||||
steps.emplace_back(psSkirtBrim);
|
||||
}
|
||||
else if (opt_key == "z_hop_types") {
|
||||
osteps.emplace_back(posDetectOverhangsForLift);
|
||||
} else {
|
||||
// for legacy, if we can't handle this option let's invalidate all steps
|
||||
//FIXME invalidate all steps of all objects as well?
|
||||
|
|
@ -354,10 +357,21 @@ std::vector<unsigned int> Print::support_material_extruders() const
|
|||
}
|
||||
|
||||
// returns 0-based indices of used extruders
|
||||
std::vector<unsigned int> Print::extruders() const
|
||||
std::vector<unsigned int> Print::extruders(bool conside_custom_gcode) const
|
||||
{
|
||||
std::vector<unsigned int> extruders = this->object_extruders();
|
||||
append(extruders, this->support_material_extruders());
|
||||
|
||||
if (conside_custom_gcode) {
|
||||
//BBS
|
||||
for (auto plate_data : m_model.plates_custom_gcodes) {
|
||||
for (auto item : plate_data.second.gcodes) {
|
||||
if (item.type == CustomGCode::Type::ToolChange)
|
||||
extruders.push_back((unsigned int)item.extruder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort_remove_duplicates(extruders);
|
||||
return extruders;
|
||||
}
|
||||
|
|
@ -1135,34 +1149,35 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
const ConfigOptionDef* bed_type_def = print_config_def.get("curr_bed_type");
|
||||
assert(bed_type_def != nullptr);
|
||||
|
||||
if (is_BBL_printer()) {
|
||||
const t_config_enum_values* bed_type_keys_map = bed_type_def->enum_keys_map;
|
||||
for (unsigned int extruder_id : extruders) {
|
||||
const ConfigOptionInts* bed_temp_opt = m_config.option<ConfigOptionInts>(get_bed_temp_key(m_config.curr_bed_type));
|
||||
for (unsigned int extruder_id : extruders) {
|
||||
int curr_bed_temp = bed_temp_opt->get_at(extruder_id);
|
||||
if (curr_bed_temp == 0 && bed_type_keys_map != nullptr) {
|
||||
std::string bed_type_name;
|
||||
for (auto item : *bed_type_keys_map) {
|
||||
if (item.second == m_config.curr_bed_type) {
|
||||
bed_type_name = item.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_BBL_printer()) {
|
||||
const t_config_enum_values* bed_type_keys_map = bed_type_def->enum_keys_map;
|
||||
for (unsigned int extruder_id : extruders) {
|
||||
const ConfigOptionInts* bed_temp_opt = m_config.option<ConfigOptionInts>(get_bed_temp_key(m_config.curr_bed_type));
|
||||
for (unsigned int extruder_id : extruders) {
|
||||
int curr_bed_temp = bed_temp_opt->get_at(extruder_id);
|
||||
if (curr_bed_temp == 0 && bed_type_keys_map != nullptr) {
|
||||
std::string bed_type_name;
|
||||
for (auto item : *bed_type_keys_map) {
|
||||
if (item.second == m_config.curr_bed_type) {
|
||||
bed_type_name = item.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StringObjectException except;
|
||||
except.string = format(L("Plate %d: %s does not support filament %s"), this->get_plate_index() + 1, L(bed_type_name), extruder_id + 1);
|
||||
except.string += "\n";
|
||||
except.type = STRING_EXCEPT_FILAMENT_NOT_MATCH_BED_TYPE;
|
||||
except.params.push_back(std::to_string(this->get_plate_index() + 1));
|
||||
except.params.push_back(L(bed_type_name));
|
||||
except.params.push_back(std::to_string(extruder_id + 1));
|
||||
except.object = nullptr;
|
||||
return except;
|
||||
}
|
||||
StringObjectException except;
|
||||
except.string = format(L("Plate %d: %s does not support filament %s"), this->get_plate_index() + 1, L(bed_type_name), extruder_id + 1);
|
||||
except.string += "\n";
|
||||
except.type = STRING_EXCEPT_FILAMENT_NOT_MATCH_BED_TYPE;
|
||||
except.params.push_back(std::to_string(this->get_plate_index() + 1));
|
||||
except.params.push_back(L(bed_type_name));
|
||||
except.params.push_back(std::to_string(extruder_id+1));
|
||||
except.object = nullptr;
|
||||
return except;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1307,7 +1322,6 @@ void PrintObject::clear_shared_object()
|
|||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, clear previous shared object data %2%")%this %m_shared_object;
|
||||
m_layers.clear();
|
||||
m_support_layers.clear();
|
||||
m_tree_support_layers.clear();
|
||||
|
||||
m_shared_object = nullptr;
|
||||
|
||||
|
|
@ -1320,7 +1334,6 @@ void PrintObject::copy_layers_from_shared_object()
|
|||
if (m_shared_object) {
|
||||
m_layers.clear();
|
||||
m_support_layers.clear();
|
||||
m_tree_support_layers.clear();
|
||||
|
||||
firstLayerObjSliceByVolume.clear();
|
||||
firstLayerObjSliceByGroups.clear();
|
||||
|
|
@ -1328,7 +1341,6 @@ void PrintObject::copy_layers_from_shared_object()
|
|||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, copied layers from object %2%")%this%m_shared_object;
|
||||
m_layers = m_shared_object->layers();
|
||||
m_support_layers = m_shared_object->support_layers();
|
||||
m_tree_support_layers = m_shared_object->tree_support_layers();
|
||||
|
||||
firstLayerObjSliceByVolume = m_shared_object->firstLayerObjSlice();
|
||||
firstLayerObjSliceByGroups = m_shared_object->firstLayerObjGroups();
|
||||
|
|
@ -1660,6 +1672,17 @@ void Print::process(bool use_cache)
|
|||
}
|
||||
}
|
||||
|
||||
// BBS
|
||||
for (PrintObject* obj : m_objects) {
|
||||
if (need_slicing_objects.count(obj) != 0) {
|
||||
obj->detect_overhangs_for_lift();
|
||||
}
|
||||
else {
|
||||
if (obj->set_started(posDetectOverhangsForLift))
|
||||
obj->set_done(posDetectOverhangsForLift);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info();
|
||||
}
|
||||
|
||||
|
|
@ -1732,13 +1755,6 @@ void Print::_make_skirt()
|
|||
break;
|
||||
layer->support_fills.collect_points(object_points);
|
||||
}
|
||||
// BBS
|
||||
for (const TreeSupportLayer* layer : object->m_tree_support_layers) {
|
||||
if (layer->print_z > skirt_height_z)
|
||||
break;
|
||||
|
||||
layer->support_fills.collect_points(object_points);
|
||||
}
|
||||
|
||||
object_convex_hulls.insert({ object, Slic3r::Geometry::convex_hull(object_points) });
|
||||
|
||||
|
|
@ -1885,12 +1901,12 @@ Polygons Print::first_layer_islands() const
|
|||
Polygons object_islands;
|
||||
for (ExPolygon &expoly : object->m_layers.front()->lslices)
|
||||
object_islands.push_back(expoly.contour);
|
||||
if (! object->support_layers().empty())
|
||||
object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON));
|
||||
if (! object->tree_support_layers().empty()) {
|
||||
ExPolygons& expolys_first_layer = object->m_tree_support_layers.front()->lslices;
|
||||
for (ExPolygon &expoly : expolys_first_layer) {
|
||||
object_islands.push_back(expoly.contour);
|
||||
if (!object->support_layers().empty()) {
|
||||
if (object->support_layers().front()->support_type==stInnerNormal)
|
||||
object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON));
|
||||
else if(object->support_layers().front()->support_type==stInnerTree) {
|
||||
ExPolygons &expolys_first_layer = object->m_support_layers.front()->lslices;
|
||||
for (ExPolygon &expoly : expolys_first_layer) { object_islands.push_back(expoly.contour); }
|
||||
}
|
||||
}
|
||||
islands.reserve(islands.size() + object_islands.size() * object->instances().size());
|
||||
|
|
@ -2241,7 +2257,7 @@ std::string PrintStatistics::finalize_output_path(const std::string &path_in) co
|
|||
#define JSON_SUPPORT_LAYER_ISLANDS "support_islands"
|
||||
#define JSON_SUPPORT_LAYER_FILLS "support_fills"
|
||||
#define JSON_SUPPORT_LAYER_INTERFACE_ID "interface_id"
|
||||
|
||||
#define JSON_SUPPORT_LAYER_TYPE "support_type"
|
||||
|
||||
#define JSON_LAYER_REGION_CONFIG_HASH "config_hash"
|
||||
#define JSON_LAYER_REGION_SLICES "slices"
|
||||
|
|
@ -2872,6 +2888,7 @@ void extract_layer(const json& layer_json, Layer& layer) {
|
|||
void extract_support_layer(const json& support_layer_json, SupportLayer& support_layer) {
|
||||
extract_layer(support_layer_json, support_layer);
|
||||
|
||||
support_layer.support_type = support_layer_json[JSON_SUPPORT_LAYER_TYPE];
|
||||
//support_islands
|
||||
int islands_count = support_layer_json[JSON_SUPPORT_LAYER_ISLANDS].size();
|
||||
for (int islands_index = 0; islands_index < islands_count; islands_index++)
|
||||
|
|
@ -2900,27 +2917,6 @@ void extract_support_layer(const json& support_layer_json, SupportLayer& support
|
|||
return;
|
||||
}
|
||||
|
||||
void extract_tree_support_layer(const json& tree_support_layer_json, TreeSupportLayer& tree_support_layer) {
|
||||
extract_layer(tree_support_layer_json, tree_support_layer);
|
||||
|
||||
//support_fills
|
||||
tree_support_layer.support_fills.no_sort = tree_support_layer_json[JSON_SUPPORT_LAYER_FILLS][JSON_EXTRUSION_NO_SORT];
|
||||
int treesupport_fills_entities_count = tree_support_layer_json[JSON_SUPPORT_LAYER_FILLS][JSON_EXTRUSION_ENTITIES].size();
|
||||
for (int treesupport_fills_entities_index = 0; treesupport_fills_entities_index < treesupport_fills_entities_count; treesupport_fills_entities_index++)
|
||||
{
|
||||
const json& extrusion_entity_json = tree_support_layer_json[JSON_SUPPORT_LAYER_FILLS][JSON_EXTRUSION_ENTITIES][treesupport_fills_entities_index];
|
||||
bool ret = convert_extrusion_from_json(extrusion_entity_json, tree_support_layer.support_fills);
|
||||
if (!ret) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": error parsing fills found at tree_support_layer %1%, print_z %2%")%tree_support_layer.id() %tree_support_layer.print_z;
|
||||
char error_buf[1024];
|
||||
::sprintf(error_buf, "Error while parsing fills at tree_support_layer %d, print_z %f", tree_support_layer.id(), tree_support_layer.print_z);
|
||||
throw Slic3r::FileIOError(error_buf);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int Print::export_cached_data(const std::string& directory, bool with_space)
|
||||
{
|
||||
int ret = 0;
|
||||
|
|
@ -2986,7 +2982,7 @@ int Print::export_cached_data(const std::string& directory, bool with_space)
|
|||
std::string file_name = directory +"/obj_"+std::to_string(arrange_order)+".json";
|
||||
|
||||
try {
|
||||
json root_json, layers_json = json::array(), support_layers_json = json::array(), tree_support_layers_json = json::array();
|
||||
json root_json, layers_json = json::array(), support_layers_json = json::array();
|
||||
|
||||
root_json[JSON_OBJECT_NAME] = model_obj->name;
|
||||
root_json[JSON_ARRANGE_ORDER] = arrange_order;
|
||||
|
|
@ -3031,6 +3027,7 @@ int Print::export_cached_data(const std::string& directory, bool with_space)
|
|||
convert_layer_to_json(support_layer_json, support_layer);
|
||||
|
||||
support_layer_json[JSON_SUPPORT_LAYER_INTERFACE_ID] = support_layer->interface_id();
|
||||
support_layer_json[JSON_SUPPORT_LAYER_TYPE] = support_layer->support_type;
|
||||
|
||||
//support_islands
|
||||
for (const ExPolygon& support_island : support_layer->support_islands.expolygons) {
|
||||
|
|
@ -3094,136 +3091,6 @@ int Print::export_cached_data(const std::string& directory, bool with_space)
|
|||
} // for each layer*/
|
||||
root_json[JSON_SUPPORT_LAYERS] = std::move(support_layers_json);
|
||||
|
||||
//export the tree support layers
|
||||
std::vector<json> tree_support_layers_json_vector(obj->tree_support_layer_count());
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, obj->tree_support_layer_count()),
|
||||
[&tree_support_layers_json_vector, obj, convert_layer_to_json](const tbb::blocked_range<size_t>& tree_support_layer_range) {
|
||||
for (size_t ts_layer_index = tree_support_layer_range.begin(); ts_layer_index < tree_support_layer_range.end(); ++ ts_layer_index) {
|
||||
const TreeSupportLayer *tree_support_layer = obj->get_tree_support_layer(ts_layer_index);
|
||||
json treesupport_layer_json, treesupport_fills_json, treesupportfills_entities_json = json::array();
|
||||
//json overhang_areas_json = json::array(), roof_areas_json = json::array(), roof_1st_layer_json = json::array(), floor_areas_json = json::array(), base_areas_json = json::array();
|
||||
|
||||
convert_layer_to_json(treesupport_layer_json, tree_support_layer);
|
||||
|
||||
//tree_support_fills
|
||||
treesupport_fills_json[JSON_EXTRUSION_NO_SORT] = tree_support_layer->support_fills.no_sort;
|
||||
treesupport_fills_json[JSON_EXTRUSION_ENTITY_TYPE] = JSON_EXTRUSION_TYPE_COLLECTION;
|
||||
for (const ExtrusionEntity* extrusion_entity : tree_support_layer->support_fills.entities) {
|
||||
json treesupportfill_entity_json, treesupportfill_entity_paths_json = json::array();
|
||||
bool ret = convert_extrusion_to_json(treesupportfill_entity_json, treesupportfill_entity_paths_json, extrusion_entity);
|
||||
if (!ret)
|
||||
continue;
|
||||
|
||||
treesupportfills_entities_json.push_back(std::move(treesupportfill_entity_json));
|
||||
}
|
||||
treesupport_fills_json[JSON_EXTRUSION_ENTITIES] = std::move(treesupportfills_entities_json);
|
||||
treesupport_layer_json[JSON_SUPPORT_LAYER_FILLS] = std::move(treesupport_fills_json);
|
||||
|
||||
//following data are not needed in the later stage
|
||||
//overhang_areas
|
||||
/*for (const ExPolygon& overhang_area : tree_support_layer->overhang_areas) {
|
||||
json overhang_area_json = overhang_area;
|
||||
overhang_areas_json.push_back(std::move(overhang_area_json));
|
||||
}
|
||||
treesupport_layer_json["overhang_areas"] = std::move(overhang_areas_json);
|
||||
|
||||
//roof_areas
|
||||
for (const ExPolygon& roof_area : tree_support_layer->roof_areas) {
|
||||
json roof_area_json = roof_area;
|
||||
roof_areas_json.push_back(std::move(roof_area_json));
|
||||
}
|
||||
treesupport_layer_json["roof_areas"] = std::move(roof_areas_json);
|
||||
|
||||
//roof_1st_layer
|
||||
for (const ExPolygon& layer_poly : tree_support_layer->roof_1st_layer) {
|
||||
json layer_poly_json = layer_poly;
|
||||
roof_1st_layer_json.push_back(std::move(layer_poly_json));
|
||||
}
|
||||
treesupport_layer_json["roof_1st_layer"] = std::move(roof_1st_layer_json);
|
||||
|
||||
//floor_areas
|
||||
for (const ExPolygon& floor_area : tree_support_layer->floor_areas) {
|
||||
json floor_area_json = floor_area;
|
||||
floor_areas_json.push_back(std::move(floor_area_json));
|
||||
}
|
||||
treesupport_layer_json["floor_areas"] = std::move(floor_areas_json);
|
||||
|
||||
//base_areas
|
||||
for (const ExPolygon& base_area : tree_support_layer->base_areas) {
|
||||
json base_area_json = base_area;
|
||||
base_areas_json.push_back(std::move(base_area_json));
|
||||
}
|
||||
treesupport_layer_json["base_areas"] = std::move(base_areas_json);*/
|
||||
|
||||
tree_support_layers_json_vector[ts_layer_index] = std::move(treesupport_layer_json);
|
||||
}
|
||||
}
|
||||
);
|
||||
for (int ts_index = 0; ts_index < tree_support_layers_json_vector.size(); ts_index++) {
|
||||
tree_support_layers_json.push_back(std::move(tree_support_layers_json_vector[ts_index]));
|
||||
}
|
||||
tree_support_layers_json_vector.clear();
|
||||
#if 0
|
||||
for (const TreeSupportLayer *tree_support_layer : obj->tree_support_layers()) {
|
||||
json treesupport_layer_json, treesupport_fills_json, treesupportfills_entities_json = json::array();
|
||||
json overhang_areas_json = json::array(), roof_areas_json = json::array(), roof_1st_layer_json = json::array(), floor_areas_json = json::array(), base_areas_json = json::array();
|
||||
|
||||
convert_layer_to_json(treesupport_layer_json, tree_support_layer);
|
||||
|
||||
//tree_support_fills
|
||||
treesupport_fills_json[JSON_EXTRUSION_NO_SORT] = tree_support_layer->support_fills.no_sort;
|
||||
treesupport_fills_json[JSON_EXTRUSION_ENTITY_TYPE] = JSON_EXTRUSION_TYPE_COLLECTION;
|
||||
for (const ExtrusionEntity* extrusion_entity : tree_support_layer->support_fills.entities) {
|
||||
json treesupportfill_entity_json, treesupportfill_entity_paths_json = json::array();
|
||||
bool ret = convert_extrusion_to_json(treesupportfill_entity_json, treesupportfill_entity_paths_json, extrusion_entity);
|
||||
if (!ret)
|
||||
continue;
|
||||
|
||||
treesupportfills_entities_json.push_back(std::move(treesupportfill_entity_json));
|
||||
}
|
||||
treesupport_fills_json[JSON_EXTRUSION_ENTITIES] = std::move(treesupportfills_entities_json);
|
||||
treesupport_layer_json[JSON_SUPPORT_LAYER_FILLS] = std::move(treesupport_fills_json);
|
||||
|
||||
//overhang_areas
|
||||
/*for (const ExPolygon& overhang_area : tree_support_layer->overhang_areas) {
|
||||
json overhang_area_json = overhang_area;
|
||||
overhang_areas_json.push_back(std::move(overhang_area_json));
|
||||
}
|
||||
treesupport_layer_json["overhang_areas"] = std::move(overhang_areas_json);
|
||||
|
||||
//roof_areas
|
||||
for (const ExPolygon& roof_area : tree_support_layer->roof_areas) {
|
||||
json roof_area_json = roof_area;
|
||||
roof_areas_json.push_back(std::move(roof_area_json));
|
||||
}
|
||||
treesupport_layer_json["roof_areas"] = std::move(roof_areas_json);
|
||||
|
||||
//roof_1st_layer
|
||||
for (const ExPolygon& layer_poly : tree_support_layer->roof_1st_layer) {
|
||||
json layer_poly_json = layer_poly;
|
||||
roof_1st_layer_json.push_back(std::move(layer_poly_json));
|
||||
}
|
||||
treesupport_layer_json["roof_1st_layer"] = std::move(roof_1st_layer_json);
|
||||
|
||||
//floor_areas
|
||||
for (const ExPolygon& floor_area : tree_support_layer->floor_areas) {
|
||||
json floor_area_json = floor_area;
|
||||
floor_areas_json.push_back(std::move(floor_area_json));
|
||||
}
|
||||
treesupport_layer_json["floor_areas"] = std::move(floor_areas_json);
|
||||
|
||||
//base_areas
|
||||
for (const ExPolygon& base_area : tree_support_layer->base_areas) {
|
||||
json base_area_json = base_area;
|
||||
base_areas_json.push_back(std::move(base_area_json));
|
||||
}
|
||||
treesupport_layer_json["base_areas"] = std::move(base_areas_json);*/
|
||||
|
||||
tree_support_layers_json.push_back(std::move(treesupport_layer_json));
|
||||
} // for each layer
|
||||
#endif
|
||||
root_json[JSON_TREE_SUPPORT_LAYERS] = std::move(tree_support_layers_json);
|
||||
|
||||
filename_vector.push_back(file_name);
|
||||
json_vector.push_back(std::move(root_json));
|
||||
|
|
@ -3302,7 +3169,6 @@ int Print::load_cached_data(const std::string& directory)
|
|||
|
||||
obj->clear_layers();
|
||||
obj->clear_support_layers();
|
||||
obj->clear_tree_support_layers();
|
||||
|
||||
int arrange_order = model_instance->arrange_order;
|
||||
if (arrange_order <= 0) {
|
||||
|
|
@ -3354,13 +3220,12 @@ int Print::load_cached_data(const std::string& directory)
|
|||
|
||||
std::string name = root_json.at(JSON_OBJECT_NAME);
|
||||
int order = root_json.at(JSON_ARRANGE_ORDER);
|
||||
int layer_count = 0, support_layer_count = 0, treesupport_layer_count = 0;
|
||||
int layer_count = 0, support_layer_count = 0;
|
||||
|
||||
layer_count = root_json[JSON_LAYERS].size();
|
||||
support_layer_count = root_json[JSON_SUPPORT_LAYERS].size();
|
||||
treesupport_layer_count = root_json[JSON_TREE_SUPPORT_LAYERS].size();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<<boost::format(":will load %1%, arrange_order %2%, layer_count %3%, support_layer_count %4%, treesupport_layer_count %5%")%name %order %layer_count %support_layer_count %treesupport_layer_count;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<<boost::format(":will load %1%, arrange_order %2%, layer_count %3%, support_layer_count %4%")%name %order %layer_count %support_layer_count;
|
||||
|
||||
Layer* previous_layer = NULL;
|
||||
//create layer and layer regions
|
||||
|
|
@ -3441,35 +3306,6 @@ int Print::load_cached_data(const std::string& directory)
|
|||
}
|
||||
);
|
||||
|
||||
//tree support layers
|
||||
Layer* previous_tree_support_layer = NULL;
|
||||
//create tree_support_layers
|
||||
for (int index = 0; index < treesupport_layer_count; index++)
|
||||
{
|
||||
json& layer_json = root_json[JSON_TREE_SUPPORT_LAYERS][index];
|
||||
TreeSupportLayer* new_tree_support_layer = obj->add_tree_support_layer(layer_json[JSON_LAYER_ID], layer_json[JSON_LAYER_HEIGHT], layer_json[JSON_LAYER_PRINT_Z], layer_json[JSON_LAYER_SLICE_Z]);
|
||||
if (!new_tree_support_layer) {
|
||||
BOOST_LOG_TRIVIAL(error) <<__FUNCTION__<< boost::format(":add_support_layer failed, out of memory");
|
||||
return CLI_OUT_OF_MEMORY;
|
||||
}
|
||||
if (previous_tree_support_layer) {
|
||||
previous_tree_support_layer->upper_layer = new_tree_support_layer;
|
||||
new_tree_support_layer->lower_layer = previous_tree_support_layer;
|
||||
}
|
||||
previous_tree_support_layer = new_tree_support_layer;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": finished load support_layers, start to load treesupport_layers.");
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, obj->tree_support_layer_count()),
|
||||
[&root_json, &obj](const tbb::blocked_range<size_t>& tree_support_layer_range) {
|
||||
for (size_t layer_index = tree_support_layer_range.begin(); layer_index < tree_support_layer_range.end(); ++ layer_index) {
|
||||
const json& layer_json = root_json[JSON_TREE_SUPPORT_LAYERS][layer_index];
|
||||
TreeSupportLayer* tree_support_layer = obj->get_tree_support_layer(layer_index);
|
||||
extract_tree_support_layer(layer_json, *tree_support_layer);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
count ++;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": load object %1% from %2% successfully.")%count%object_filenames[obj_index].first;
|
||||
}
|
||||
|
|
@ -3489,4 +3325,4 @@ int Print::load_cached_data(const std::string& directory)
|
|||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
} // namespace Slic3r
|
||||
|
|
@ -32,7 +32,6 @@ class Print;
|
|||
class PrintObject;
|
||||
class SupportLayer;
|
||||
// BBS
|
||||
class TreeSupportLayer;
|
||||
class TreeSupportData;
|
||||
class TreeSupport;
|
||||
|
||||
|
|
@ -86,7 +85,10 @@ enum PrintStep {
|
|||
|
||||
enum PrintObjectStep {
|
||||
posSlice, posPerimeters, posPrepareInfill,
|
||||
posInfill, posIroning, posSupportMaterial, posSimplifyPath, posSimplifySupportPath, posCount,
|
||||
posInfill, posIroning, posSupportMaterial, posSimplifyPath, posSimplifySupportPath,
|
||||
// BBS
|
||||
posDetectOverhangsForLift,
|
||||
posCount,
|
||||
};
|
||||
|
||||
// A PrintRegion object represents a group of volumes to print
|
||||
|
|
@ -176,13 +178,6 @@ class ConstSupportLayerPtrsAdaptor : public ConstVectorOfPtrsAdaptor<SupportLaye
|
|||
ConstSupportLayerPtrsAdaptor(const SupportLayerPtrs *data) : ConstVectorOfPtrsAdaptor<SupportLayer>(data) {}
|
||||
};
|
||||
|
||||
// BBS
|
||||
typedef std::vector<TreeSupportLayer*> TreeSupportLayerPtrs;
|
||||
class ConstTreeSupportLayerPtrsAdaptor : public ConstVectorOfPtrsAdaptor<TreeSupportLayer> {
|
||||
friend PrintObject;
|
||||
ConstTreeSupportLayerPtrsAdaptor(const TreeSupportLayerPtrs* data) : ConstVectorOfPtrsAdaptor<TreeSupportLayer>(data) {}
|
||||
};
|
||||
|
||||
class BoundingBoxf3; // TODO: for temporary constructor parameter
|
||||
|
||||
// Single instance of a PrintObject.
|
||||
|
|
@ -297,14 +292,10 @@ public:
|
|||
Transform3d trafo_centered() const
|
||||
{ Transform3d t = this->trafo(); t.pretranslate(Vec3d(- unscale<double>(m_center_offset.x()), - unscale<double>(m_center_offset.y()), 0)); return t; }
|
||||
const PrintInstances& instances() const { return m_instances; }
|
||||
// BBS
|
||||
ConstTreeSupportLayerPtrsAdaptor tree_support_layers() const { return ConstTreeSupportLayerPtrsAdaptor(&m_tree_support_layers); }
|
||||
|
||||
// Whoever will get a non-const pointer to PrintObject will be able to modify its layers.
|
||||
LayerPtrs& layers() { return m_layers; }
|
||||
SupportLayerPtrs& support_layers() { return m_support_layers; }
|
||||
// BBS
|
||||
TreeSupportLayerPtrs& tree_support_layers() { return m_tree_support_layers; }
|
||||
|
||||
template<typename PolysType>
|
||||
static void remove_bridges_from_contacts(
|
||||
|
|
@ -327,7 +318,9 @@ public:
|
|||
// BBS
|
||||
void generate_support_preview();
|
||||
const std::vector<VolumeSlices>& firstLayerObjSlice() const { return firstLayerObjSliceByVolume; }
|
||||
std::vector<VolumeSlices>& firstLayerObjSliceMod() { return firstLayerObjSliceByVolume; }
|
||||
const std::vector<groupedVolumeSlices>& firstLayerObjGroups() const { return firstLayerObjSliceByGroups; }
|
||||
std::vector<groupedVolumeSlices>& firstLayerObjGroupsMod() { return firstLayerObjSliceByGroups; }
|
||||
|
||||
bool has_brim() const {
|
||||
return ((this->config().brim_type != btNoBrim && this->config().brim_width.value > 0.) || this->config().brim_type == btAutoBrim)
|
||||
|
|
@ -365,12 +358,7 @@ public:
|
|||
Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
|
||||
|
||||
// BBS
|
||||
TreeSupportLayer* get_tree_support_layer(int idx) { return m_tree_support_layers[idx]; }
|
||||
const TreeSupportLayer* get_tree_support_layer_at_printz(coordf_t print_z, coordf_t epsilon) const;
|
||||
TreeSupportLayer* get_tree_support_layer_at_printz(coordf_t print_z, coordf_t epsilon);
|
||||
TreeSupportLayer* add_tree_support_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
|
||||
void clear_tree_support_layers();
|
||||
size_t tree_support_layer_count() const { return m_tree_support_layers.size(); }
|
||||
SupportLayer* add_tree_support_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
|
||||
std::shared_ptr<TreeSupportData> alloc_tree_support_preview_cache();
|
||||
void clear_tree_support_preview_cache() { m_tree_support_preview_cache.reset(); }
|
||||
|
||||
|
|
@ -381,7 +369,6 @@ public:
|
|||
SupportLayer* get_support_layer_at_printz(coordf_t print_z, coordf_t epsilon);
|
||||
SupportLayer* add_support_layer(int id, int interface_id, coordf_t height, coordf_t print_z);
|
||||
SupportLayerPtrs::iterator insert_support_layer(SupportLayerPtrs::iterator pos, size_t id, size_t interface_id, coordf_t height, coordf_t print_z, coordf_t slice_z);
|
||||
void delete_support_layer(int idx);
|
||||
|
||||
// Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters.
|
||||
// Returns true, if the layer_height_profile was changed.
|
||||
|
|
@ -464,6 +451,10 @@ private:
|
|||
void slice_volumes();
|
||||
//BBS
|
||||
ExPolygons _shrink_contour_holes(double contour_delta, double hole_delta, const ExPolygons& polys) const;
|
||||
// BBS
|
||||
void detect_overhangs_for_lift();
|
||||
void clear_overhangs_for_lift();
|
||||
|
||||
// Has any support (not counting the raft).
|
||||
void detect_surfaces_type();
|
||||
void process_external_surfaces();
|
||||
|
|
@ -498,7 +489,6 @@ private:
|
|||
LayerPtrs m_layers;
|
||||
SupportLayerPtrs m_support_layers;
|
||||
// BBS
|
||||
TreeSupportLayerPtrs m_tree_support_layers;
|
||||
std::shared_ptr<TreeSupportData> m_tree_support_preview_cache;
|
||||
|
||||
// this is set to true when LayerRegion->slices is split in top/internal/bottom
|
||||
|
|
@ -661,7 +651,7 @@ public:
|
|||
|
||||
std::vector<unsigned int> object_extruders() const;
|
||||
std::vector<unsigned int> support_material_extruders() const;
|
||||
std::vector<unsigned int> extruders() const;
|
||||
std::vector<unsigned int> extruders(bool conside_custom_gcode = false) const;
|
||||
double max_allowed_layer_height() const;
|
||||
bool has_support_material() const;
|
||||
// Make sure the background processing has no access to this model_object during this call!
|
||||
|
|
|
|||
|
|
@ -1020,10 +1020,11 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
new_full_config.option("filament_settings_id", true);
|
||||
new_full_config.option("printer_settings_id", true);
|
||||
// BBS
|
||||
int used_filaments = this->extruders().size();
|
||||
int used_filaments = this->extruders(true).size();
|
||||
|
||||
//new_full_config.normalize_fdm(used_filaments);
|
||||
new_full_config.normalize_fdm_1();
|
||||
t_config_option_keys changed_keys = new_full_config.normalize_fdm_2(used_filaments);
|
||||
t_config_option_keys changed_keys = new_full_config.normalize_fdm_2(objects().size(), used_filaments);
|
||||
if (changed_keys.size() > 0) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", got changed_keys, size=%1%")%changed_keys.size();
|
||||
for (int i = 0; i < changed_keys.size(); i++)
|
||||
|
|
@ -1117,17 +1118,19 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
for (const ModelObject *model_object : m_model.objects)
|
||||
model_object_status_db.add(*model_object, ModelObjectStatus::New);
|
||||
} else {
|
||||
if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
|
||||
update_apply_status(num_extruders_changed ||
|
||||
// Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering.
|
||||
//FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable
|
||||
// to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same.
|
||||
(num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.custom_gcode_per_print_z.gcodes, model.custom_gcode_per_print_z.gcodes)) ?
|
||||
// The Tool Ordering and the Wipe Tower are no more valid.
|
||||
this->invalidate_steps({ psWipeTower, psGCodeExport }) :
|
||||
// There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.
|
||||
this->invalidate_step(psGCodeExport));
|
||||
m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
|
||||
//BBS: replace model custom gcode with current plate custom gcode
|
||||
m_model.curr_plate_index = model.curr_plate_index;
|
||||
if (m_model.get_curr_plate_custom_gcodes() != model.get_curr_plate_custom_gcodes()) {
|
||||
update_apply_status(num_extruders_changed ||
|
||||
// Tool change G-codes are applied as color changes for a single extruder printer, no need to invalidate tool ordering.
|
||||
//FIXME The tool ordering may be invalidated unnecessarily if the custom_gcode_per_print_z.mode is not applicable
|
||||
// to the active print / model state, and then it is reset, so it is being applicable, but empty, thus the effect is the same.
|
||||
(num_extruders > 1 && custom_per_printz_gcodes_tool_changes_differ(m_model.get_curr_plate_custom_gcodes().gcodes, model.get_curr_plate_custom_gcodes().gcodes)) ?
|
||||
// The Tool Ordering and the Wipe Tower are no more valid.
|
||||
this->invalidate_steps({ psWipeTower, psGCodeExport }) :
|
||||
// There is no change in Tool Changes stored in custom_gcode_per_print_z, therefore there is no need to update Tool Ordering.
|
||||
this->invalidate_step(psGCodeExport));
|
||||
m_model.plates_custom_gcodes[m_model.curr_plate_index] = model.get_curr_plate_custom_gcodes();
|
||||
}
|
||||
if (model_object_list_equal(m_model, model)) {
|
||||
// The object list did not change.
|
||||
|
|
@ -1413,8 +1416,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
|
|||
}
|
||||
|
||||
//BBS: check the config again
|
||||
int new_used_filaments = this->extruders().size();
|
||||
t_config_option_keys new_changed_keys = new_full_config.normalize_fdm_2(new_used_filaments);
|
||||
int new_used_filaments = this->extruders(true).size();
|
||||
t_config_option_keys new_changed_keys = new_full_config.normalize_fdm_2(objects().size(), new_used_filaments);
|
||||
if (new_changed_keys.size() > 0) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", got new_changed_keys, size=%1%")%new_changed_keys.size();
|
||||
for (int i = 0; i < new_changed_keys.size(); i++)
|
||||
|
|
|
|||
|
|
@ -159,7 +159,8 @@ static t_config_enum_values s_keys_map_WallInfillOrder {
|
|||
{ "outer wall/inner wall/infill", int(WallInfillOrder::OuterInnerInfill) },
|
||||
{ "inner-outer-inner wall/infill", int(WallInfillOrder::InnerOuterInnerInfill) },
|
||||
{ "infill/inner wall/outer wall", int(WallInfillOrder::InfillInnerOuter) },
|
||||
{ "infill/outer wall/inner wall", int(WallInfillOrder::InfillOuterInner) }
|
||||
{ "infill/outer wall/inner wall", int(WallInfillOrder::InfillOuterInner) },
|
||||
{ "inner-outer-inner wall/infill", int(WallInfillOrder::InnerOuterInnerInfill)}
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(WallInfillOrder)
|
||||
|
||||
|
|
@ -303,6 +304,14 @@ static t_config_enum_values s_keys_map_PerimeterGeneratorType{
|
|||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PerimeterGeneratorType)
|
||||
|
||||
static const t_config_enum_values s_keys_map_ZHopType = {
|
||||
{ "Auto Lift", zhtAuto },
|
||||
{ "Normal Lift", zhtNormal },
|
||||
{ "Slope Lift", zhtSlope },
|
||||
{ "Spiral Lift", zhtSpiral }
|
||||
};
|
||||
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(ZHopType)
|
||||
|
||||
static void assign_printer_technology_to_unknown(t_optiondef_map &options, PrinterTechnology printer_technology)
|
||||
{
|
||||
for (std::pair<const t_config_option_key, ConfigOptionDef> &kvp : options)
|
||||
|
|
@ -813,8 +822,8 @@ void PrintConfigDef::init_fff_params()
|
|||
def = this->add("brim_type", coEnum);
|
||||
def->label = L("Brim type");
|
||||
def->category = L("Support");
|
||||
def->tooltip = L("This controls brim position including outer side of models, inner side of holes or both. "
|
||||
"Auto means both the brim position and brim width is analysed and calculated automatically");
|
||||
def->tooltip = L("This controls the generation of the brim at outer side of models. "
|
||||
"Auto means the brim width is analysed and calculated automatically.");
|
||||
def->enum_keys_map = &ConfigOptionEnum<BrimType>::get_enum_values();
|
||||
def->enum_values.emplace_back("auto_brim");
|
||||
def->enum_values.emplace_back("outer_only");
|
||||
|
|
@ -1325,6 +1334,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_values.push_back("PA-CF");
|
||||
def->enum_values.push_back("PLA-CF");
|
||||
def->enum_values.push_back("PET-CF");
|
||||
def->enum_values.push_back("PETG-CF");
|
||||
def->enum_values.push_back("PVA");
|
||||
def->mode = comSimple;
|
||||
def->set_default_value(new ConfigOptionStrings { "PLA" });
|
||||
|
|
@ -2149,6 +2159,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->tooltip = L("Diameter of nozzle");
|
||||
def->sidetext = L("mm");
|
||||
def->mode = comAdvanced;
|
||||
def->max = 1.0;
|
||||
def->set_default_value(new ConfigOptionFloats { 0.4 });
|
||||
|
||||
def = this->add("host_type", coEnum);
|
||||
|
|
@ -2183,6 +2194,14 @@ void PrintConfigDef::init_fff_params()
|
|||
def->readonly = true;
|
||||
def->set_default_value(new ConfigOptionFloat { 0.0 });
|
||||
|
||||
def = this->add("start_end_points", coPoints);
|
||||
def->label = L("Start end points");
|
||||
def->tooltip = L("The start and end points which is from cutter area to garbage can.");
|
||||
def->mode = comDevelop;
|
||||
def->readonly = true;
|
||||
// start and end point is from the change_filament_gcode
|
||||
def->set_default_value(new ConfigOptionPoints{Vec2d(30, -3), Vec2d(54, 245)});
|
||||
|
||||
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. "
|
||||
|
|
@ -2397,6 +2416,21 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comSimple;
|
||||
def->set_default_value(new ConfigOptionFloats { 0.4 });
|
||||
|
||||
def = this->add("z_hop_types", coEnums);
|
||||
def->label = L("Z Hop Type");
|
||||
def->tooltip = L("");
|
||||
def->enum_keys_map = &ConfigOptionEnum<ZHopType>::get_enum_values();
|
||||
def->enum_values.push_back("Auto Lift");
|
||||
def->enum_values.push_back("Normal Lift");
|
||||
def->enum_values.push_back("Slope Lift");
|
||||
def->enum_values.push_back("Spiral Lift");
|
||||
def->enum_labels.push_back(L("Auto"));
|
||||
def->enum_labels.push_back(L("Normal"));
|
||||
def->enum_labels.push_back(L("Slope"));
|
||||
def->enum_labels.push_back(L("Spiral"));
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnumsGeneric{ ZHopType::zhtSpiral });
|
||||
|
||||
def = this->add("retract_restart_extra", coFloats);
|
||||
def->label = L("Extra length on restart");
|
||||
def->tooltip = L("When the retraction is compensated after the travel move, the extruder will push "
|
||||
|
|
@ -2495,11 +2529,11 @@ void PrintConfigDef::init_fff_params()
|
|||
def->set_default_value(new ConfigOptionFloat(2));
|
||||
|
||||
def = this->add("skirt_height", coInt);
|
||||
//def->label = L("Skirt height");
|
||||
def->label = "Skirt height";
|
||||
//def->tooltip = L("How many layers of skirt. Usually only one layer");
|
||||
def->label = L("Skirt height");
|
||||
//def->label = "Skirt height";
|
||||
def->tooltip = L("How many layers of skirt. Usually only one layer");
|
||||
def->sidetext = L("layers");
|
||||
def->mode = comAdvanced;
|
||||
def->mode = comSimple;
|
||||
def->max = 10000;
|
||||
def->set_default_value(new ConfigOptionInt(1));
|
||||
|
||||
|
|
@ -3226,7 +3260,7 @@ void PrintConfigDef::init_fff_params()
|
|||
//def->sidetext = L("mm");
|
||||
def->mode = comDevelop;
|
||||
// BBS: change data type to floats to add partplate logic
|
||||
def->set_default_value(new ConfigOptionFloats{ 240. });
|
||||
def->set_default_value(new ConfigOptionFloats{ 220. });
|
||||
|
||||
def = this->add("prime_tower_width", coFloat);
|
||||
def->label = L("Width");
|
||||
|
|
@ -3409,7 +3443,7 @@ void PrintConfigDef::init_fff_params()
|
|||
// Declare retract values for filament profile, overriding the printer's extruder profile.
|
||||
for (const char *opt_key : {
|
||||
// floats
|
||||
"retraction_length", "z_hop", "retraction_speed", "deretraction_speed", "retract_restart_extra", "retraction_minimum_travel",
|
||||
"retraction_length", "z_hop", "z_hop_types", "retraction_speed", "deretraction_speed", "retract_restart_extra", "retraction_minimum_travel",
|
||||
// BBS: floats
|
||||
"wipe_distance",
|
||||
// bools
|
||||
|
|
@ -3423,6 +3457,9 @@ void PrintConfigDef::init_fff_params()
|
|||
def->full_label = it_opt->second.full_label;
|
||||
def->tooltip = it_opt->second.tooltip;
|
||||
def->sidetext = it_opt->second.sidetext;
|
||||
def->enum_keys_map = it_opt->second.enum_keys_map;
|
||||
def->enum_labels = it_opt->second.enum_labels;
|
||||
def->enum_values = it_opt->second.enum_values;
|
||||
//BBS: shown specific filament retract config because we hide the machine retract into comDevelop mode
|
||||
if ((strcmp(opt_key, "retraction_length") == 0) ||
|
||||
(strcmp(opt_key, "z_hop") == 0))
|
||||
|
|
@ -3433,6 +3470,7 @@ void PrintConfigDef::init_fff_params()
|
|||
case coFloats : def->set_default_value(new ConfigOptionFloatsNullable (static_cast<const ConfigOptionFloats* >(it_opt->second.default_value.get())->values)); break;
|
||||
case coPercents : def->set_default_value(new ConfigOptionPercentsNullable(static_cast<const ConfigOptionPercents*>(it_opt->second.default_value.get())->values)); break;
|
||||
case coBools : def->set_default_value(new ConfigOptionBoolsNullable (static_cast<const ConfigOptionBools* >(it_opt->second.default_value.get())->values)); break;
|
||||
case coEnums : def->set_default_value(new ConfigOptionEnumsGenericNullable(static_cast<const ConfigOptionEnumsGeneric* >(it_opt->second.default_value.get())->values)); break;
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
|
|
@ -3452,7 +3490,7 @@ void PrintConfigDef::init_extruder_option_keys()
|
|||
// ConfigOptionFloats, ConfigOptionPercents, ConfigOptionBools, ConfigOptionStrings
|
||||
m_extruder_option_keys = {
|
||||
"nozzle_diameter", "min_layer_height", "max_layer_height", "extruder_offset",
|
||||
"retraction_length", "z_hop", "retraction_speed", "deretraction_speed",
|
||||
"retraction_length", "z_hop", "z_hop_types", "retraction_speed", "deretraction_speed",
|
||||
"retract_before_wipe", "retract_restart_extra", "retraction_minimum_travel", "wipe", "wipe_distance",
|
||||
"retract_when_changing_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour",
|
||||
"default_filament_profile"
|
||||
|
|
@ -3468,7 +3506,8 @@ void PrintConfigDef::init_extruder_option_keys()
|
|||
"retraction_speed",
|
||||
"wipe",
|
||||
"wipe_distance",
|
||||
"z_hop"
|
||||
"z_hop",
|
||||
"z_hop_types"
|
||||
};
|
||||
assert(std::is_sorted(m_extruder_retract_keys.begin(), m_extruder_retract_keys.end()));
|
||||
}
|
||||
|
|
@ -3477,7 +3516,7 @@ void PrintConfigDef::init_filament_option_keys()
|
|||
{
|
||||
m_filament_option_keys = {
|
||||
"filament_diameter", "min_layer_height", "max_layer_height",
|
||||
"retraction_length", "z_hop", "retraction_speed", "deretraction_speed",
|
||||
"retraction_length", "z_hop", "z_hop_types", "retraction_speed", "deretraction_speed",
|
||||
"retract_before_wipe", "retract_restart_extra", "retraction_minimum_travel", "wipe", "wipe_distance",
|
||||
"retract_when_changing_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "filament_colour",
|
||||
"default_filament_profile"/*,"filament_seam_gap"*/
|
||||
|
|
@ -3493,7 +3532,8 @@ void PrintConfigDef::init_filament_option_keys()
|
|||
"retraction_speed",
|
||||
"wipe",
|
||||
"wipe_distance",
|
||||
"z_hop"
|
||||
"z_hop",
|
||||
"z_hop_types"
|
||||
};
|
||||
assert(std::is_sorted(m_filament_retract_keys.begin(), m_filament_retract_keys.end()));
|
||||
}
|
||||
|
|
@ -4219,7 +4259,8 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
|||
"support_closing_radius",
|
||||
"remove_freq_sweep", "remove_bed_leveling", "remove_extrusion_calibration",
|
||||
"support_transition_line_width", "support_transition_speed", "bed_temperature", "bed_temperature_initial_layer",
|
||||
"can_switch_nozzle_type", "can_add_auxiliary_fan", "extra_flush_volume", "spaghetti_detector", "adaptive_layer_height"
|
||||
"can_switch_nozzle_type", "can_add_auxiliary_fan", "extra_flush_volume", "spaghetti_detector", "adaptive_layer_height",
|
||||
"z_hop_type"
|
||||
};
|
||||
|
||||
if (ignore.find(opt_key) != ignore.end()) {
|
||||
|
|
@ -4394,7 +4435,7 @@ void DynamicPrintConfig::normalize_fdm_1()
|
|||
return;
|
||||
}
|
||||
|
||||
t_config_option_keys DynamicPrintConfig::normalize_fdm_2(int used_filaments)
|
||||
t_config_option_keys DynamicPrintConfig::normalize_fdm_2(int num_objects, int used_filaments)
|
||||
{
|
||||
t_config_option_keys changed_keys;
|
||||
ConfigOptionBool* ept_opt = this->option<ConfigOptionBool>("enable_prime_tower");
|
||||
|
|
@ -4405,7 +4446,7 @@ t_config_option_keys DynamicPrintConfig::normalize_fdm_2(int used_filaments)
|
|||
|
||||
ConfigOptionEnum<TimelapseType>* timelapse_opt = this->option<ConfigOptionEnum<TimelapseType>>("timelapse_type");
|
||||
bool is_smooth_timelapse = timelapse_opt != nullptr && timelapse_opt->value == TimelapseType::tlSmooth;
|
||||
if (!is_smooth_timelapse && (used_filaments == 1 || ps_opt->value == PrintSequence::ByObject)) {
|
||||
if (!is_smooth_timelapse && (used_filaments == 1 || (ps_opt->value == PrintSequence::ByObject && num_objects > 1))) {
|
||||
if (ept_opt->value) {
|
||||
ept_opt->value = false;
|
||||
changed_keys.push_back("enable_prime_tower");
|
||||
|
|
@ -4498,7 +4539,8 @@ void DynamicPrintConfig::set_num_filaments(unsigned int num_filaments)
|
|||
}
|
||||
}
|
||||
|
||||
std::string DynamicPrintConfig::validate()
|
||||
//BBS: pass map to recording all invalid valies
|
||||
std::map<std::string, std::string> DynamicPrintConfig::validate(bool under_cli)
|
||||
{
|
||||
// Full print config is initialized from the defaults.
|
||||
const ConfigOption *opt = this->option("printer_technology", false);
|
||||
|
|
@ -4509,11 +4551,11 @@ std::string DynamicPrintConfig::validate()
|
|||
FullPrintConfig fpc;
|
||||
fpc.apply(*this, true);
|
||||
// Verify this print options through the FullPrintConfig.
|
||||
return Slic3r::validate(fpc);
|
||||
return Slic3r::validate(fpc, under_cli);
|
||||
}
|
||||
default:
|
||||
//FIXME no validation on SLA data?
|
||||
return std::string();
|
||||
return std::map<std::string, std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4584,38 +4626,50 @@ bool DynamicPrintConfig::is_custom_defined()
|
|||
return false;
|
||||
}
|
||||
|
||||
//BBS: pass map to recording all invalid valies
|
||||
//FIXME localize this function.
|
||||
std::string validate(const FullPrintConfig &cfg)
|
||||
std::map<std::string, std::string> validate(const FullPrintConfig &cfg, bool under_cli)
|
||||
{
|
||||
std::map<std::string, std::string> error_message;
|
||||
// --layer-height
|
||||
if (cfg.get_abs_value("layer_height") <= 0)
|
||||
return "Invalid value for --layer-height";
|
||||
if (fabs(fmod(cfg.get_abs_value("layer_height"), SCALING_FACTOR)) > 1e-4)
|
||||
return "--layer-height must be a multiple of print resolution";
|
||||
if (cfg.get_abs_value("layer_height") <= 0) {
|
||||
error_message.emplace("layer_height", L("invalid value ") + std::to_string(cfg.get_abs_value("layer_height")));
|
||||
}
|
||||
else if (fabs(fmod(cfg.get_abs_value("layer_height"), SCALING_FACTOR)) > 1e-4) {
|
||||
error_message.emplace("layer_height", L("invalid value ") + std::to_string(cfg.get_abs_value("layer_height")));
|
||||
}
|
||||
|
||||
// --first-layer-height
|
||||
if (cfg.initial_layer_print_height.value <= 0)
|
||||
return "Invalid value for --first-layer-height";
|
||||
if (cfg.initial_layer_print_height.value <= 0) {
|
||||
error_message.emplace("initial_layer_print_height", L("invalid value ") + std::to_string(cfg.initial_layer_print_height.value));
|
||||
}
|
||||
|
||||
// --filament-diameter
|
||||
for (double fd : cfg.filament_diameter.values)
|
||||
if (fd < 1)
|
||||
return "Invalid value for --filament-diameter";
|
||||
if (fd < 1) {
|
||||
error_message.emplace("filament_diameter", L("invalid value ") + cfg.filament_diameter.serialize());
|
||||
break;
|
||||
}
|
||||
|
||||
// --nozzle-diameter
|
||||
for (double nd : cfg.nozzle_diameter.values)
|
||||
if (nd < 0.005)
|
||||
return "Invalid value for --nozzle-diameter";
|
||||
if (nd < 0.005) {
|
||||
error_message.emplace("nozzle_diameter", L("invalid value ") + cfg.nozzle_diameter.serialize());
|
||||
break;
|
||||
}
|
||||
|
||||
// --perimeters
|
||||
if (cfg.wall_loops.value < 0)
|
||||
return "Invalid value for --wall_loops";
|
||||
if (cfg.wall_loops.value < 0) {
|
||||
error_message.emplace("wall_loops", L("invalid value ") + std::to_string(cfg.wall_loops.value));
|
||||
}
|
||||
|
||||
// --solid-layers
|
||||
if (cfg.top_shell_layers < 0)
|
||||
return "Invalid value for --top-solid-layers";
|
||||
if (cfg.bottom_shell_layers < 0)
|
||||
return "Invalid value for --bottom-solid-layers";
|
||||
if (cfg.top_shell_layers < 0) {
|
||||
error_message.emplace("top_shell_layers", L("invalid value ") + std::to_string(cfg.top_shell_layers));
|
||||
}
|
||||
if (cfg.bottom_shell_layers < 0) {
|
||||
error_message.emplace("bottom_shell_layers", L("invalid value ") + std::to_string(cfg.bottom_shell_layers));
|
||||
}
|
||||
|
||||
if (cfg.use_firmware_retraction.value &&
|
||||
cfg.gcode_flavor.value != gcfKlipper &&
|
||||
|
|
@ -4626,69 +4680,96 @@ std::string validate(const FullPrintConfig &cfg)
|
|||
cfg.gcode_flavor.value != gcfMarlinFirmware &&
|
||||
cfg.gcode_flavor.value != gcfMachinekit &&
|
||||
cfg.gcode_flavor.value != gcfRepetier)
|
||||
return "--use-firmware-retraction is only supported by Klipper, Marlin, Smoothie, RepRapFirmware, Repetier and Machinekit firmware";
|
||||
error_message.emplace("use_firmware_retraction","--use-firmware-retraction is only supported by Klipper, Marlin, Smoothie, RepRapFirmware, Repetier and Machinekit firmware");
|
||||
|
||||
if (cfg.use_firmware_retraction.value)
|
||||
for (unsigned char wipe : cfg.wipe.values)
|
||||
if (wipe)
|
||||
return "--use-firmware-retraction is not compatible with --wipe";
|
||||
error_message.emplace("use_firmware_retraction", "--use-firmware-retraction is not compatible with --wipe");
|
||||
|
||||
// --gcode-flavor
|
||||
if (! print_config_def.get("gcode_flavor")->has_enum_value(cfg.gcode_flavor.serialize()))
|
||||
return "Invalid value for --gcode-flavor";
|
||||
if (! print_config_def.get("gcode_flavor")->has_enum_value(cfg.gcode_flavor.serialize())) {
|
||||
error_message.emplace("gcode_flavor", L("invalid value ") + cfg.gcode_flavor.serialize());
|
||||
}
|
||||
|
||||
// --fill-pattern
|
||||
if (! print_config_def.get("sparse_infill_pattern")->has_enum_value(cfg.sparse_infill_pattern.serialize()))
|
||||
return "Invalid value for --fill-pattern";
|
||||
if (! print_config_def.get("sparse_infill_pattern")->has_enum_value(cfg.sparse_infill_pattern.serialize())) {
|
||||
error_message.emplace("sparse_infill_pattern", L("invalid value ") + cfg.sparse_infill_pattern.serialize());
|
||||
}
|
||||
|
||||
// --top-fill-pattern
|
||||
if (! print_config_def.get("top_surface_pattern")->has_enum_value(cfg.top_surface_pattern.serialize()))
|
||||
return "Invalid value for --top-fill-pattern";
|
||||
if (! print_config_def.get("top_surface_pattern")->has_enum_value(cfg.top_surface_pattern.serialize())) {
|
||||
error_message.emplace("top_surface_pattern", L("invalid value ") + cfg.top_surface_pattern.serialize());
|
||||
}
|
||||
|
||||
// --bottom-fill-pattern
|
||||
if (! print_config_def.get("bottom_surface_pattern")->has_enum_value(cfg.bottom_surface_pattern.serialize()))
|
||||
return "Invalid value for --bottom-fill-pattern";
|
||||
if (! print_config_def.get("bottom_surface_pattern")->has_enum_value(cfg.bottom_surface_pattern.serialize())) {
|
||||
error_message.emplace("bottom_surface_pattern", L("invalid value ") + cfg.bottom_surface_pattern.serialize());
|
||||
}
|
||||
|
||||
// --fill-density
|
||||
if (fabs(cfg.sparse_infill_density.value - 100.) < EPSILON &&
|
||||
! print_config_def.get("top_surface_pattern")->has_enum_value(cfg.sparse_infill_pattern.serialize()))
|
||||
return "The selected fill pattern is not supposed to work at 100% density";
|
||||
! print_config_def.get("top_surface_pattern")->has_enum_value(cfg.sparse_infill_pattern.serialize())) {
|
||||
error_message.emplace("sparse_infill_pattern", cfg.sparse_infill_pattern.serialize() + L(" doesn't work at 100%% density "));
|
||||
}
|
||||
|
||||
// --skirt-height
|
||||
if (cfg.skirt_height < 0)
|
||||
return "Invalid value for --skirt-height";
|
||||
if (cfg.skirt_height < 0) {
|
||||
error_message.emplace("skirt_height", L("invalid value ") + std::to_string(cfg.skirt_height));
|
||||
}
|
||||
|
||||
// --bridge-flow-ratio
|
||||
if (cfg.bridge_flow <= 0)
|
||||
return "Invalid value for --bridge-flow-ratio";
|
||||
if (cfg.bridge_flow <= 0) {
|
||||
error_message.emplace("bridge_flow", L("invalid value ") + std::to_string(cfg.bridge_flow));
|
||||
}
|
||||
|
||||
// extruder clearance
|
||||
if (cfg.extruder_clearance_radius <= 0)
|
||||
return "Invalid value for --extruder-clearance-radius";
|
||||
if (cfg.extruder_clearance_height_to_rod <= 0)
|
||||
return "Invalid value for --extruder-clearance-height-to-rod";
|
||||
if (cfg.extruder_clearance_height_to_lid <= 0)
|
||||
return "Invalid value for --extruder-clearance-height-to-lid";
|
||||
if (cfg.extruder_clearance_radius <= 0) {
|
||||
error_message.emplace("extruder_clearance_radius", L("invalid value ") + std::to_string(cfg.extruder_clearance_radius));
|
||||
}
|
||||
if (cfg.extruder_clearance_height_to_rod <= 0) {
|
||||
error_message.emplace("extruder_clearance_height_to_rod", L("invalid value ") + std::to_string(cfg.extruder_clearance_height_to_rod));
|
||||
}
|
||||
if (cfg.extruder_clearance_height_to_lid <= 0) {
|
||||
error_message.emplace("extruder_clearance_height_to_lid", L("invalid value ") + std::to_string(cfg.extruder_clearance_height_to_lid));
|
||||
}
|
||||
|
||||
// --extrusion-multiplier
|
||||
for (double em : cfg.filament_flow_ratio.values)
|
||||
if (em <= 0)
|
||||
return "Invalid value for --filament-flow-ratio";
|
||||
if (em <= 0) {
|
||||
error_message.emplace("filament_flow_ratio", L("invalid value ") + cfg.filament_flow_ratio.serialize());
|
||||
break;
|
||||
}
|
||||
|
||||
// --spiral-vase
|
||||
if (cfg.spiral_mode) {
|
||||
//for non-cli case, we will popup dialog for spiral mode correction
|
||||
if (cfg.spiral_mode && under_cli) {
|
||||
// Note that we might want to have more than one perimeter on the bottom
|
||||
// solid layers.
|
||||
if (cfg.wall_loops > 1)
|
||||
return "Can't make more than one perimeter when spiral vase mode is enabled";
|
||||
else if (cfg.wall_loops < 1)
|
||||
return "Can't make less than one perimeter when spiral vase mode is enabled";
|
||||
if (cfg.sparse_infill_density > 0)
|
||||
return "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0";
|
||||
if (cfg.top_shell_layers > 0)
|
||||
return "Spiral vase mode is not compatible with top solid layers";
|
||||
if (cfg.enable_support || cfg.enforce_support_layers > 0)
|
||||
return "Spiral vase mode is not compatible with support";
|
||||
if (cfg.wall_loops != 1) {
|
||||
error_message.emplace("wall_loops", L("Invalid value when spiral vase mode is enabled: ") + std::to_string(cfg.wall_loops));
|
||||
//return "Can't make more than one perimeter when spiral vase mode is enabled";
|
||||
//return "Can't make less than one perimeter when spiral vase mode is enabled";
|
||||
}
|
||||
|
||||
if (cfg.sparse_infill_density > 0) {
|
||||
error_message.emplace("sparse_infill_density", L("Invalid value when spiral vase mode is enabled: ") + std::to_string(cfg.sparse_infill_density));
|
||||
//return "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0";
|
||||
}
|
||||
|
||||
if (cfg.top_shell_layers > 0) {
|
||||
error_message.emplace("top_shell_layers", L("Invalid value when spiral vase mode is enabled: ") + std::to_string(cfg.top_shell_layers));
|
||||
//return "Spiral vase mode is not compatible with top solid layers";
|
||||
}
|
||||
|
||||
if (cfg.enable_support ) {
|
||||
error_message.emplace("enable_support", L("Invalid value when spiral vase mode is enabled: ") + std::to_string(cfg.enable_support));
|
||||
//return "Spiral vase mode is not compatible with support";
|
||||
}
|
||||
if (cfg.enforce_support_layers > 0) {
|
||||
error_message.emplace("enforce_support_layers", L("Invalid value when spiral vase mode is enabled: ") + std::to_string(cfg.enforce_support_layers));
|
||||
//return "Spiral vase mode is not compatible with support";
|
||||
}
|
||||
}
|
||||
|
||||
// extrusion widths
|
||||
|
|
@ -4706,8 +4787,10 @@ std::string validate(const FullPrintConfig &cfg)
|
|||
"initial_layer_line_width" };
|
||||
for (size_t i = 0; i < sizeof(widths) / sizeof(widths[i]); ++ i) {
|
||||
std::string key(widths[i]);
|
||||
if (cfg.get_abs_value(key) > 2.5 * max_nozzle_diameter)
|
||||
return std::string("Too Large line width: ") + key;
|
||||
if (cfg.get_abs_value(key) > 2.5 * max_nozzle_diameter) {
|
||||
error_message.emplace(key, L("too large line width ") + std::to_string(cfg.get_abs_value(key)));
|
||||
//return std::string("Too Large line width: ") + key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4750,12 +4833,15 @@ std::string validate(const FullPrintConfig &cfg)
|
|||
break;
|
||||
default:;
|
||||
}
|
||||
if (out_of_range)
|
||||
return std::string("Value out of range: " + opt_key);
|
||||
if (out_of_range) {
|
||||
if (error_message.find(opt_key) == error_message.end())
|
||||
error_message.emplace(opt_key, opt->serialize() + L(" not in range ") +"[" + std::to_string(optdef->min) + "," + std::to_string(optdef->max) + "]");
|
||||
//return std::string("Value out of range: " + opt_key);
|
||||
}
|
||||
}
|
||||
|
||||
// The configuration is valid.
|
||||
return "";
|
||||
return error_message;
|
||||
}
|
||||
|
||||
// Declare and initialize static caches of StaticPrintConfig derived classes.
|
||||
|
|
@ -5127,6 +5213,26 @@ Points get_bed_shape(const PrintConfig &cfg)
|
|||
|
||||
Points get_bed_shape(const SLAPrinterConfig &cfg) { return to_points(cfg.printable_area.values); }
|
||||
|
||||
Polygon get_bed_shape_with_excluded_area(const PrintConfig& cfg)
|
||||
{
|
||||
Polygon bed_poly;
|
||||
bed_poly.points = get_bed_shape(cfg);
|
||||
|
||||
Points excluse_area_points = to_points(cfg.bed_exclude_area.values);
|
||||
Polygons exclude_polys;
|
||||
Polygon exclude_poly;
|
||||
for (int i = 0; i < excluse_area_points.size(); i++) {
|
||||
auto pt = excluse_area_points[i];
|
||||
exclude_poly.points.emplace_back(pt);
|
||||
if (i % 4 == 3) { // exclude areas are always rectangle
|
||||
exclude_polys.push_back(exclude_poly);
|
||||
exclude_poly.points.clear();
|
||||
}
|
||||
}
|
||||
auto tmp = diff({ bed_poly }, exclude_polys);
|
||||
if (!tmp.empty()) bed_poly = tmp[0];
|
||||
return bed_poly;
|
||||
}
|
||||
} // namespace Slic3r
|
||||
|
||||
#include <cereal/types/polymorphic.hpp>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#include "libslic3r.h"
|
||||
#include "Config.hpp"
|
||||
|
||||
#include "Polygon.hpp"
|
||||
#include <boost/preprocessor/facilities/empty.hpp>
|
||||
#include <boost/preprocessor/punctuation/comma_if.hpp>
|
||||
#include <boost/preprocessor/seq/for_each.hpp>
|
||||
|
|
@ -78,6 +78,7 @@ enum class WallInfillOrder {
|
|||
enum class PrintSequence {
|
||||
ByLayer,
|
||||
ByObject,
|
||||
ByDefault,
|
||||
Count,
|
||||
};
|
||||
|
||||
|
|
@ -207,6 +208,15 @@ enum NozzleType {
|
|||
ntCount
|
||||
};
|
||||
|
||||
// BBS
|
||||
enum ZHopType {
|
||||
zhtAuto = 0,
|
||||
zhtNormal,
|
||||
zhtSlope,
|
||||
zhtSpiral,
|
||||
zhtCount
|
||||
};
|
||||
|
||||
static std::string bed_type_to_gcode_string(const BedType type)
|
||||
{
|
||||
std::string type_str;
|
||||
|
|
@ -368,15 +378,16 @@ public:
|
|||
void normalize_fdm(int used_filaments = 0);
|
||||
void normalize_fdm_1();
|
||||
//return the changed param set
|
||||
t_config_option_keys normalize_fdm_2(int used_filaments = 0);
|
||||
t_config_option_keys normalize_fdm_2(int num_objects, int used_filaments = 0);
|
||||
|
||||
void set_num_extruders(unsigned int num_extruders);
|
||||
|
||||
// BBS
|
||||
void set_num_filaments(unsigned int num_filaments);
|
||||
|
||||
//BBS
|
||||
// Validate the PrintConfig. Returns an empty string on success, otherwise an error message is returned.
|
||||
std::string validate();
|
||||
std::map<std::string, std::string> validate(bool under_cli = false);
|
||||
|
||||
// Verify whether the opt_key has not been obsoleted or renamed.
|
||||
// Both opt_key and value may be modified by handle_legacy().
|
||||
|
|
@ -842,7 +853,8 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||
((ConfigOptionFloats, retraction_length))
|
||||
((ConfigOptionFloats, retract_length_toolchange))
|
||||
((ConfigOptionFloats, z_hop))
|
||||
((ConfigOptionEnum<LiftType>, z_lift_type))
|
||||
// BBS
|
||||
((ConfigOptionEnumsGeneric, z_hop_types))
|
||||
((ConfigOptionFloats, retract_restart_extra))
|
||||
((ConfigOptionFloats, retract_restart_extra_toolchange))
|
||||
((ConfigOptionFloats, retraction_speed))
|
||||
|
|
@ -978,6 +990,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
|||
// BBS: not in any preset, calculated before slicing
|
||||
((ConfigOptionBool, has_prime_tower))
|
||||
((ConfigOptionFloat, nozzle_volume))
|
||||
((ConfigOptionPoints, start_end_points))
|
||||
((ConfigOptionEnum<TimelapseType>, timelapse_type))
|
||||
((ConfigOptionPoints, thumbnails))
|
||||
// BBS: move from PrintObjectConfig
|
||||
|
|
@ -994,7 +1007,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE0(
|
|||
)
|
||||
|
||||
// Validate the FullPrintConfig. Returns an empty string on success, otherwise an error message is returned.
|
||||
std::string validate(const FullPrintConfig &config);
|
||||
std::map<std::string, std::string> validate(const FullPrintConfig &config, bool under_cli = false);
|
||||
|
||||
PRINT_CONFIG_CLASS_DEFINE(
|
||||
SLAPrintConfig,
|
||||
|
|
@ -1282,6 +1295,7 @@ private:
|
|||
Points get_bed_shape(const DynamicPrintConfig &cfg);
|
||||
Points get_bed_shape(const PrintConfig &cfg);
|
||||
Points get_bed_shape(const SLAPrinterConfig &cfg);
|
||||
Slic3r::Polygon get_bed_shape_with_excluded_area(const PrintConfig& cfg);
|
||||
|
||||
// ModelConfig is a wrapper around DynamicPrintConfig with an addition of a timestamp.
|
||||
// Each change of ModelConfig is tracked by assigning a new timestamp from a global counter.
|
||||
|
|
|
|||
|
|
@ -91,7 +91,6 @@ PrintObject::~PrintObject()
|
|||
if (m_shared_regions && -- m_shared_regions->m_ref_cnt == 0) delete m_shared_regions;
|
||||
clear_layers();
|
||||
clear_support_layers();
|
||||
clear_tree_support_layers();
|
||||
}
|
||||
|
||||
PrintBase::ApplyStatus PrintObject::set_instances(PrintInstances &&instances)
|
||||
|
|
@ -410,11 +409,49 @@ void PrintObject::ironing()
|
|||
}
|
||||
}
|
||||
|
||||
// BBS
|
||||
void PrintObject::clear_overhangs_for_lift()
|
||||
{
|
||||
if (!m_shared_object) {
|
||||
for (Layer* l : m_layers)
|
||||
l->loverhangs.clear();
|
||||
}
|
||||
}
|
||||
|
||||
static const float g_min_overhang_percent_for_lift = 0.3f;
|
||||
|
||||
void PrintObject::detect_overhangs_for_lift()
|
||||
{
|
||||
if (this->set_started(posDetectOverhangsForLift)) {
|
||||
const float min_overlap = m_config.line_width * g_min_overhang_percent_for_lift;
|
||||
size_t num_layers = this->layer_count();
|
||||
size_t num_raft_layers = m_slicing_params.raft_layers();
|
||||
|
||||
m_print->set_status(78, L("Detect overhangs for auto-lift"));
|
||||
|
||||
this->clear_overhangs_for_lift();
|
||||
|
||||
tbb::spin_mutex layer_storage_mutex;
|
||||
tbb::parallel_for(tbb::blocked_range<size_t>(num_raft_layers + 1, num_layers),
|
||||
[this, min_overlap](const tbb::blocked_range<size_t>& range)
|
||||
{
|
||||
for (size_t layer_id = range.begin(); layer_id < range.end(); ++layer_id) {
|
||||
Layer& layer = *m_layers[layer_id];
|
||||
Layer& lower_layer = *layer.lower_layer;
|
||||
|
||||
ExPolygons overhangs = diff_ex(layer.lslices, offset_ex(lower_layer.lslices, scale_(min_overlap)));
|
||||
layer.loverhangs = std::move(offset2_ex(overhangs, -0.1f * scale_(m_config.line_width), 0.1f * scale_(m_config.line_width)));
|
||||
}
|
||||
});
|
||||
|
||||
this->set_done(posDetectOverhangsForLift);
|
||||
}
|
||||
}
|
||||
|
||||
void PrintObject::generate_support_material()
|
||||
{
|
||||
if (this->set_started(posSupportMaterial)) {
|
||||
this->clear_support_layers();
|
||||
this->clear_tree_support_layers();
|
||||
|
||||
if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && ! m_layers.empty())) {
|
||||
m_print->set_status(50, L("Generating support"));
|
||||
|
|
@ -483,16 +520,6 @@ void PrintObject::simplify_extrusion_path()
|
|||
}
|
||||
);
|
||||
m_print->throw_if_canceled();
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(0, m_tree_support_layers.size()),
|
||||
[this](const tbb::blocked_range<size_t>& range) {
|
||||
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
|
||||
m_print->throw_if_canceled();
|
||||
m_tree_support_layers[layer_idx]->simplify_support_extrusion_path();
|
||||
}
|
||||
}
|
||||
);
|
||||
m_print->throw_if_canceled();
|
||||
BOOST_LOG_TRIVIAL(debug) << "Simplify extrusion path of support in parallel - end";
|
||||
this->set_done(posSimplifySupportPath);
|
||||
}
|
||||
|
|
@ -564,19 +591,6 @@ Layer* PrintObject::add_layer(int id, coordf_t height, coordf_t print_z, coordf_
|
|||
return m_layers.back();
|
||||
}
|
||||
|
||||
// BBS
|
||||
const TreeSupportLayer* PrintObject::get_tree_support_layer_at_printz(coordf_t print_z, coordf_t epsilon) const
|
||||
{
|
||||
coordf_t limit = print_z - epsilon;
|
||||
auto it = Slic3r::lower_bound_by_predicate(m_tree_support_layers.begin(), m_tree_support_layers.end(), [limit](const TreeSupportLayer* layer) { return layer->print_z < limit; });
|
||||
return (it == m_tree_support_layers.end() || (*it)->print_z > print_z + epsilon) ? nullptr : *it;
|
||||
}
|
||||
|
||||
TreeSupportLayer* PrintObject::get_tree_support_layer_at_printz(coordf_t print_z, coordf_t epsilon)
|
||||
{
|
||||
return const_cast<TreeSupportLayer*>(std::as_const(*this).get_tree_support_layer_at_printz(print_z, epsilon));
|
||||
}
|
||||
|
||||
const SupportLayer* PrintObject::get_support_layer_at_printz(coordf_t print_z, coordf_t epsilon) const
|
||||
{
|
||||
coordf_t limit = print_z - epsilon;
|
||||
|
|
@ -589,12 +603,17 @@ SupportLayer* PrintObject::get_support_layer_at_printz(coordf_t print_z, coordf_
|
|||
return const_cast<SupportLayer*>(std::as_const(*this).get_support_layer_at_printz(print_z, epsilon));
|
||||
}
|
||||
|
||||
void PrintObject::clear_tree_support_layers()
|
||||
void PrintObject::clear_support_layers()
|
||||
{
|
||||
if (!m_shared_object) {
|
||||
for (TreeSupportLayer* l : m_tree_support_layers)
|
||||
for (SupportLayer* l : m_support_layers)
|
||||
delete l;
|
||||
m_tree_support_layers.clear();
|
||||
m_support_layers.clear();
|
||||
for (auto l : m_layers) {
|
||||
l->sharp_tails.clear();
|
||||
l->sharp_tails_height.clear();
|
||||
l->cantilevers.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -614,19 +633,11 @@ std::shared_ptr<TreeSupportData> PrintObject::alloc_tree_support_preview_cache()
|
|||
return m_tree_support_preview_cache;
|
||||
}
|
||||
|
||||
TreeSupportLayer* PrintObject::add_tree_support_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z)
|
||||
SupportLayer* PrintObject::add_tree_support_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z)
|
||||
{
|
||||
m_tree_support_layers.emplace_back(new TreeSupportLayer(id, this, height, print_z, slice_z));
|
||||
return m_tree_support_layers.back();
|
||||
}
|
||||
|
||||
void PrintObject::clear_support_layers()
|
||||
{
|
||||
if (!m_shared_object) {
|
||||
for (Layer *l : m_support_layers)
|
||||
delete l;
|
||||
m_support_layers.clear();
|
||||
}
|
||||
m_support_layers.emplace_back(new SupportLayer(id, 0, this, height, print_z, slice_z));
|
||||
m_support_layers.back()->support_type = stInnerTree;
|
||||
return m_support_layers.back();
|
||||
}
|
||||
|
||||
SupportLayer* PrintObject::add_support_layer(int id, int interface_id, coordf_t height, coordf_t print_z)
|
||||
|
|
@ -717,7 +728,14 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||
|| opt_key == "support_top_z_distance"
|
||||
|| opt_key == "support_bottom_z_distance"
|
||||
|| opt_key == "xy_hole_compensation"
|
||||
|| opt_key == "xy_contour_compensation") {
|
||||
|| opt_key == "xy_contour_compensation"
|
||||
//BBS: [Arthur] the following params affect bottomBridge surface type detection
|
||||
|| opt_key == "support_type"
|
||||
|| opt_key == "bridge_no_support"
|
||||
|| opt_key == "max_bridge_length"
|
||||
|| opt_key == "support_interface_top_layers"
|
||||
|| opt_key == "support_critical_regions_only"
|
||||
) {
|
||||
steps.emplace_back(posSlice);
|
||||
} else if (opt_key == "enable_support") {
|
||||
steps.emplace_back(posSupportMaterial);
|
||||
|
|
@ -762,9 +780,23 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||
|| opt_key == "tree_support_branch_angle"
|
||||
|| opt_key == "tree_support_wall_count") {
|
||||
steps.emplace_back(posSupportMaterial);
|
||||
} else if (opt_key == "bottom_shell_layers") {
|
||||
} else if (
|
||||
opt_key == "bottom_shell_layers"
|
||||
|| opt_key == "top_shell_layers") {
|
||||
|
||||
steps.emplace_back(posPrepareInfill);
|
||||
if (m_print->config().spiral_mode) {
|
||||
|
||||
const auto *old_shell_layers = old_config.option<ConfigOptionInt>(opt_key);
|
||||
const auto *new_shell_layers = new_config.option<ConfigOptionInt>(opt_key);
|
||||
assert(old_shell_layers && new_shell_layers);
|
||||
|
||||
bool value_changed = (old_shell_layers->value == 0 && new_shell_layers->value > 0) ||
|
||||
(old_shell_layers->value > 0 && new_shell_layers->value == 0);
|
||||
|
||||
if (value_changed && this->object_extruders().size() > 1) {
|
||||
steps.emplace_back(posSlice);
|
||||
}
|
||||
else if (m_print->config().spiral_mode && opt_key == "bottom_shell_layers") {
|
||||
// Changing the number of bottom layers when a spiral vase is enabled requires re-slicing the object again.
|
||||
// Otherwise, holes in the bottom layers could be filled, as is reported in GH #5528.
|
||||
steps.emplace_back(posSlice);
|
||||
|
|
@ -773,7 +805,6 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||
opt_key == "interface_shells"
|
||||
|| opt_key == "infill_combination"
|
||||
|| opt_key == "bottom_shell_thickness"
|
||||
|| opt_key == "top_shell_layers"
|
||||
|| opt_key == "top_shell_thickness"
|
||||
|| opt_key == "minimum_sparse_infill_area"
|
||||
|| opt_key == "sparse_infill_filament"
|
||||
|
|
@ -971,9 +1002,17 @@ void PrintObject::detect_surfaces_type()
|
|||
[this, region_id, interface_shells, &surfaces_new](const tbb::blocked_range<size_t>& range) {
|
||||
// If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating
|
||||
// the support from the print.
|
||||
SurfaceType surface_type_bottom_other =
|
||||
(this->has_support() && m_config.support_top_z_distance.value == 0) ?
|
||||
stBottom : stBottomBridge;
|
||||
// BBS: the above logic only applys for normal(auto) support. Complete logic:
|
||||
// 1. has support, top z distance=0 (soluble material), auto support
|
||||
// 2. for normal(auto), bridge_no_support is off
|
||||
// 3. for tree(auto), interface top layers=0, max bridge length=0, support_critical_regions_only=false (only in this way the bridge is fully supported)
|
||||
bool bottom_is_fully_supported = this->has_support() && m_config.support_top_z_distance.value == 0 && is_auto(m_config.support_type.value);
|
||||
if (m_config.support_type.value == stNormalAuto)
|
||||
bottom_is_fully_supported &= !m_config.bridge_no_support.value;
|
||||
else if (m_config.support_type.value == stTreeAuto) {
|
||||
bottom_is_fully_supported &= (m_config.support_interface_top_layers.value > 0 && m_config.max_bridge_length.value == 0 && m_config.support_critical_regions_only.value==false);
|
||||
}
|
||||
SurfaceType surface_type_bottom_other = bottom_is_fully_supported ? stBottom : stBottomBridge;
|
||||
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++ idx_layer) {
|
||||
m_print->throw_if_canceled();
|
||||
// BOOST_LOG_TRIVIAL(trace) << "Detecting solid surfaces for region " << region_id << " and layer " << layer->print_z;
|
||||
|
|
@ -2389,7 +2428,7 @@ void PrintObject::_generate_support_material()
|
|||
support_material.generate(*this);
|
||||
|
||||
TreeSupport tree_support(*this, m_slicing_params);
|
||||
tree_support.generate_support_areas();
|
||||
tree_support.generate();
|
||||
}
|
||||
|
||||
// BBS
|
||||
|
|
@ -2545,6 +2584,7 @@ template void PrintObject::remove_bridges_from_contacts<Polygons>(
|
|||
|
||||
SupportNecessaryType PrintObject::is_support_necessary()
|
||||
{
|
||||
#if 0
|
||||
static const double super_overhang_area_threshold = SQ(scale_(5.0));
|
||||
|
||||
double threshold_rad = (m_config.support_threshold_angle.value < EPSILON ? 30 : m_config.support_threshold_angle.value + 1) * M_PI / 180.;
|
||||
|
|
@ -2627,7 +2667,16 @@ SupportNecessaryType PrintObject::is_support_necessary()
|
|||
if (!exceed_overhang.empty())
|
||||
return LargeOverhang;
|
||||
}
|
||||
|
||||
#else
|
||||
TreeSupport tree_support(*this, m_slicing_params);
|
||||
tree_support.support_type = SupportType::stTreeAuto; // need to set support type to fully utilize the power of feature detection
|
||||
tree_support.detect_overhangs();
|
||||
this->clear_support_layers();
|
||||
if (tree_support.has_sharp_tails)
|
||||
return SharpTail;
|
||||
else if (tree_support.has_cantilever)
|
||||
return LargeOverhang;
|
||||
#endif
|
||||
return NoNeedSupp;
|
||||
}
|
||||
|
||||
|
|
@ -2820,10 +2869,6 @@ static void project_triangles_to_slabs(ConstLayerPtrsAdaptor layers, const index
|
|||
void PrintObject::project_and_append_custom_facets(
|
||||
bool seam, EnforcerBlockerType type, std::vector<Polygons>& out) const
|
||||
{
|
||||
// BBS: Approve adding enforcer support on vertical faces
|
||||
SlabSlicingConfig config;
|
||||
config.isVertical = type == EnforcerBlockerType::ENFORCER ? true : false;
|
||||
|
||||
for (const ModelVolume* mv : this->model_object()->volumes)
|
||||
if (mv->is_model_part()) {
|
||||
const indexed_triangle_set custom_facets = seam
|
||||
|
|
@ -2837,7 +2882,7 @@ void PrintObject::project_and_append_custom_facets(
|
|||
else {
|
||||
std::vector<Polygons> projected;
|
||||
// Support blockers or enforcers. Project downward facing painted areas upwards to their respective slicing plane.
|
||||
slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, [](){}, config);
|
||||
slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, [](){});
|
||||
// Merge these projections with the output, layer by layer.
|
||||
assert(! projected.empty());
|
||||
assert(out.empty() || out.size() == projected.size());
|
||||
|
|
|
|||
|
|
@ -458,6 +458,151 @@ static std::vector<std::vector<ExPolygons>> slices_to_regions(
|
|||
return slices_by_region;
|
||||
}
|
||||
|
||||
//BBS: justify whether a volume is connected to another one
|
||||
bool doesVolumeIntersect(VolumeSlices& vs1, VolumeSlices& vs2)
|
||||
{
|
||||
if (vs1.volume_id == vs2.volume_id) return true;
|
||||
if (vs1.slices.size() != vs2.slices.size()) return false;
|
||||
|
||||
for (int i = 0; i != vs1.slices.size(); ++i) {
|
||||
|
||||
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, double resolution, int firstLayerReplacedBy)
|
||||
{
|
||||
std::vector<int> groupIndex(objSliceByVolume.size(), -1);
|
||||
double offsetValue = 0.05 / 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;
|
||||
}
|
||||
for (int j = i + 1; j != objSliceByVolume.size(); ++j) {
|
||||
if (doesVolumeIntersect(objSliceByVolume[i], objSliceByVolume[j])) {
|
||||
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]);
|
||||
for (int k = 0; k != objSliceByVolume.size(); ++k) {
|
||||
if (groupIndex[k] == cover) groupIndex[k] = retain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> groupVector{};
|
||||
for (int gi : groupIndex) {
|
||||
bool exist = false;
|
||||
for (int gv : groupVector) {
|
||||
if (gv == gi) {
|
||||
exist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exist) groupVector.push_back(gi);
|
||||
}
|
||||
|
||||
// group volumes and their slices according to the grouping Vector
|
||||
groups.clear();
|
||||
|
||||
for (int gv : groupVector) {
|
||||
groupedVolumeSlices gvs;
|
||||
gvs.groupId = gv;
|
||||
for (int i = 0; i != objSliceByVolume.size(); ++i) {
|
||||
if (groupIndex[i] == gv) {
|
||||
gvs.volume_ids.push_back(objSliceByVolume[i].volume_id);
|
||||
append(gvs.slices, objSliceByVolume[i].slices[firstLayerReplacedBy]);
|
||||
}
|
||||
}
|
||||
|
||||
// the slices of a group should be unioned
|
||||
gvs.slices = offset_ex(union_ex(gvs.slices), -offsetValue);
|
||||
for (ExPolygon& poly_ex : gvs.slices)
|
||||
poly_ex.douglas_peucker(resolution);
|
||||
|
||||
groups.push_back(gvs);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//BBS: filter the members of "objSliceByVolume" such that only "model_part" are included
|
||||
std::vector<VolumeSlices> findPartVolumes(const std::vector<VolumeSlices>& objSliceByVolume, ModelVolumePtrs model_volumes) {
|
||||
std::vector<VolumeSlices> outPut;
|
||||
for (const auto& vs : objSliceByVolume) {
|
||||
for (const auto& mv : model_volumes) {
|
||||
if (vs.volume_id == mv->id() && mv->is_model_part()) outPut.push_back(vs);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void reGroupingLayerPolygons(std::vector<groupedVolumeSlices>& gvss, ExPolygons eps)
|
||||
{
|
||||
std::vector<int> epsIndex;
|
||||
epsIndex.resize(eps.size(), -1);
|
||||
for (int ie = 0; ie != eps.size(); ie++) {
|
||||
for (int iv = 0; iv != gvss.size(); iv++) {
|
||||
auto clipedExPolys = diff_ex(eps[ie], gvss[iv].slices);
|
||||
double area = 0;
|
||||
for (const auto& ce : clipedExPolys) {
|
||||
area += ce.area();
|
||||
}
|
||||
if (eps[ie].area() > 0 && area / eps[ie].area() < 0.3) {
|
||||
epsIndex[ie] = iv;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int iv = 0; iv != gvss.size(); iv++)
|
||||
gvss[iv].slices.clear();
|
||||
|
||||
for (int ie = 0; ie != eps.size(); ie++) {
|
||||
if (epsIndex[ie] >= 0)
|
||||
gvss[epsIndex[ie]].slices.push_back(eps[ie]);
|
||||
}
|
||||
}
|
||||
|
||||
std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std::function<void()> &throw_if_canceled)
|
||||
{
|
||||
std::string error_msg;//BBS
|
||||
|
|
@ -567,6 +712,21 @@ std::string fix_slicing_errors(PrintObject* object, LayerPtrs &layers, const std
|
|||
//BBS
|
||||
if(error_msg.empty() && !buggy_layers.empty())
|
||||
error_msg = L("The model has too many empty layers.");
|
||||
|
||||
// BBS: first layer slices are sorted by volume group, if the first layer is empty and replaced by the 2nd layer
|
||||
// the later will be stored in "object->firstLayerObjGroupsMod()"
|
||||
int firstLayerReplacedBy = 0;
|
||||
if (!buggy_layers.empty() && buggy_layers.front() == 0)
|
||||
firstLayerReplacedBy = 1;
|
||||
|
||||
const auto scaled_resolution = scaled<double>(object->print()->config().resolution.value);
|
||||
auto partsObjSliceByVolume = findPartVolumes(object->firstLayerObjSliceMod(), object->model_object()->volumes);
|
||||
groupingVolumes(partsObjSliceByVolume, object->firstLayerObjGroupsMod(), scaled_resolution, firstLayerReplacedBy);
|
||||
applyNegtiveVolumes(object->model_object()->volumes, object->firstLayerObjSliceMod(), object->firstLayerObjGroupsMod(), scaled_resolution);
|
||||
|
||||
// BBS: the actual first layer slices stored in layers are re-sorted by volume group and will be used to generate brim
|
||||
reGroupingLayerPolygons(object->firstLayerObjGroupsMod(), layers.front()->lslices);
|
||||
|
||||
return error_msg;
|
||||
}
|
||||
|
||||
|
|
@ -616,7 +776,7 @@ void PrintObject::slice()
|
|||
}
|
||||
});
|
||||
if (m_layers.empty())
|
||||
throw Slic3r::SlicingError("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n");
|
||||
throw Slic3r::SlicingError(L("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n"));
|
||||
|
||||
// BBS
|
||||
this->set_done(posSlice);
|
||||
|
|
@ -749,127 +909,7 @@ static inline void apply_mm_segmentation(PrintObject &print_object, ThrowOnCance
|
|||
});
|
||||
}
|
||||
|
||||
//BBS: justify whether a volume is connected to another one
|
||||
bool doesVolumeIntersect(VolumeSlices& vs1, VolumeSlices& vs2)
|
||||
{
|
||||
if (vs1.volume_id == vs2.volume_id) return true;
|
||||
if (vs1.slices.size() != vs2.slices.size()) return false;
|
||||
|
||||
for (int i = 0; i != vs1.slices.size(); ++i) {
|
||||
|
||||
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, double resolution)
|
||||
{
|
||||
int existGroups = 0;
|
||||
std::vector<int> groupIndex(objSliceByVolume.size(), -1);
|
||||
|
||||
double offsetValue = 0.15 / 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;
|
||||
++existGroups;
|
||||
}
|
||||
for (int j = i + 1; j != objSliceByVolume.size(); ++j) {
|
||||
if (doesVolumeIntersect(objSliceByVolume[i], objSliceByVolume[j])) {
|
||||
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]);
|
||||
for (int k = 0; k != objSliceByVolume.size(); ++k) {
|
||||
if (groupIndex[k] == cover) groupIndex[k] = retain;
|
||||
}
|
||||
--existGroups;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> groupVector{};
|
||||
for (int gi : groupIndex) {
|
||||
bool exist = false;
|
||||
for (int gv : groupVector) {
|
||||
if (gv == gi) {
|
||||
exist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exist) groupVector.push_back(gi);
|
||||
}
|
||||
|
||||
// group volumes and their slices according to the grouping Vector
|
||||
groups.clear();
|
||||
|
||||
for (int gv : groupVector) {
|
||||
groupedVolumeSlices gvs;
|
||||
gvs.groupId = gv;
|
||||
for (int i = 0; i != objSliceByVolume.size(); ++i) {
|
||||
if (groupIndex[i] == gv) {
|
||||
gvs.volume_ids.push_back(objSliceByVolume[i].volume_id);
|
||||
append(gvs.slices, objSliceByVolume[i].slices.front());
|
||||
}
|
||||
}
|
||||
|
||||
// the slices of a group should be unioned
|
||||
gvs.slices = offset_ex(union_ex(gvs.slices), -offsetValue);
|
||||
for (ExPolygon& poly_ex : gvs.slices)
|
||||
poly_ex.douglas_peucker(resolution);
|
||||
|
||||
groups.push_back(gvs);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//BBS: filter the members of "objSliceByVolume" such that only "model_part" are included
|
||||
std::vector<VolumeSlices> findPartVolumes(const std::vector<VolumeSlices>& objSliceByVolume, ModelVolumePtrs model_volumes) {
|
||||
std::vector<VolumeSlices> outPut;
|
||||
for (const auto& vs : objSliceByVolume) {
|
||||
for (const auto& mv : model_volumes) {
|
||||
if (vs.volume_id == mv->id() && mv->is_model_part()) outPut.push_back(vs);
|
||||
}
|
||||
}
|
||||
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
|
||||
|
|
@ -897,14 +937,8 @@ void PrintObject::slice_volumes()
|
|||
for (const std::unique_ptr<PrintRegion> &pr : m_shared_regions->all_regions)
|
||||
layer->m_regions.emplace_back(new LayerRegion(layer, pr.get()));
|
||||
}
|
||||
// BBS: first layer slices are sorted by volume
|
||||
std::vector<float> slice_zs = zs_from_layers(m_layers);
|
||||
if (!slice_zs.empty()) {
|
||||
firstLayerObjSliceByVolume = slice_volumes_inner(
|
||||
print->config(), this->config(), this->trafo_centered(),
|
||||
this->model_object()->volumes, m_shared_regions->layer_ranges, {slice_zs.front()}, throw_on_cancel_callback);
|
||||
}
|
||||
|
||||
std::vector<float> slice_zs = zs_from_layers(m_layers);
|
||||
std::vector<VolumeSlices> objSliceByVolume;
|
||||
if (!slice_zs.empty()) {
|
||||
objSliceByVolume = slice_volumes_inner(
|
||||
|
|
@ -913,10 +947,11 @@ 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, scaled_resolution);
|
||||
applyNegtiveVolumes(this->model_object()->volumes, objSliceByVolume, firstLayerObjSliceByGroups, scaled_resolution);
|
||||
//const auto scaled_resolution = scaled<double>(print->config().resolution.value);
|
||||
//firstLayerObjSliceByVolume = findPartVolumes(objSliceByVolume, this->model_object()->volumes);
|
||||
//groupingVolumes(objSliceByVolumeParts, firstLayerObjSliceByGroups, scaled_resolution);
|
||||
//applyNegtiveVolumes(this->model_object()->volumes, objSliceByVolume, firstLayerObjSliceByGroups, scaled_resolution);
|
||||
firstLayerObjSliceByVolume = objSliceByVolume;
|
||||
|
||||
std::vector<std::vector<ExPolygons>> region_slices =
|
||||
slices_to_regions(print->config(), *this, this->model_object()->volumes, *m_shared_regions, slice_zs,
|
||||
|
|
@ -949,10 +984,10 @@ void PrintObject::slice_volumes()
|
|||
// If XY Size compensation is also enabled, notify the user that XY Size compensation
|
||||
// would not be used because the object is multi-material painted.
|
||||
if (m_config.xy_hole_compensation.value != 0.f || m_config.xy_contour_compensation.value != 0.f) {
|
||||
//this->active_step_add_warning(
|
||||
// PrintStateBase::WarningLevel::CRITICAL,
|
||||
// L("An object has enabled XY Size compensation which will not be used because it is also multi-material painted.\nXY Size "
|
||||
// "compensation cannot be combined with multi-material painting."));
|
||||
this->active_step_add_warning(
|
||||
PrintStateBase::WarningLevel::CRITICAL,
|
||||
L("An object's XY size compensation will not be used because it is also color-painted.\nXY Size "
|
||||
"compensation can not be combined with color-painting."));
|
||||
BOOST_LOG_TRIVIAL(info) << "xy compensation will not work for object " << this->model_object()->name << " for multi filament.";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1482,7 +1482,7 @@ static const double length_thresh_well_supported = scale_(6); // min: 6mm
|
|||
static const double area_thresh_well_supported = SQ(length_thresh_well_supported); // min: 6x6=36mm^2
|
||||
static const double sharp_tail_xy_gap = 0.2f;
|
||||
static const double no_overlap_xy_gap = 0.2f;
|
||||
static const double sharp_tail_max_support_height = 8.f;
|
||||
static const double sharp_tail_max_support_height = 16.f;
|
||||
|
||||
// Tuple: overhang_polygons, contact_polygons, enforcer_polygons, no_interface_offset
|
||||
// no_interface_offset: minimum of external perimeter widths
|
||||
|
|
@ -1598,7 +1598,7 @@ static inline Polygons detect_overhangs(
|
|||
// Check whether this is a sharp tail region.
|
||||
// Should use lower_layer_expolys without any offset. Otherwise, it may missing sharp tails near the main body.
|
||||
if (intersection_ex({ expoly }, lower_layer_expolys).empty()) {
|
||||
is_sharp_tail = expoly.area() < area_thresh_well_supported;
|
||||
is_sharp_tail = expoly.area() < area_thresh_well_supported && !offset_ex(expoly,-0.5*fw).empty();
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1646,7 +1646,8 @@ static inline Polygons detect_overhangs(
|
|||
// 2.4 if the area grows fast than threshold, it get connected to other part or
|
||||
// it has a sharp slop and will be auto supported.
|
||||
ExPolygons new_overhang_expolys = diff_ex({ expoly }, lower_layer_sharptails);
|
||||
if (!offset_ex(new_overhang_expolys, -5.0 * fw).empty()) {
|
||||
Point size_diff = get_extents(new_overhang_expolys).size() - get_extents(lower_layer_sharptails).size();
|
||||
if (size_diff.both_comp(Point(scale_(5),scale_(5)),">") || !offset_ex(new_overhang_expolys, -5.0 * fw).empty()) {
|
||||
is_sharp_tail = false;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,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
|
||||
{
|
||||
|
|
@ -285,7 +285,7 @@ static void draw_layer_mst
|
|||
svg.draw(spanning_tree.vertices(), "black", coord_t(scale_(0.1)));
|
||||
}
|
||||
|
||||
static void draw_two_overhangs_to_svg(TreeSupportLayer* ts_layer, const ExPolygons& overhangs1, const ExPolygons& overhangs2)
|
||||
static void draw_two_overhangs_to_svg(SupportLayer* ts_layer, const ExPolygons& overhangs1, const ExPolygons& overhangs2)
|
||||
{
|
||||
if (overhangs1.empty() && overhangs2.empty())
|
||||
return;
|
||||
|
|
@ -300,7 +300,7 @@ static void draw_two_overhangs_to_svg(TreeSupportLayer* ts_layer, const ExPolygo
|
|||
svg.draw(union_ex(overhangs2), "red");
|
||||
}
|
||||
|
||||
static void draw_polylines(TreeSupportLayer* ts_layer, Polylines& polylines)
|
||||
static void draw_polylines(SupportLayer* ts_layer, Polylines& polylines)
|
||||
{
|
||||
if (polylines.empty())
|
||||
return;
|
||||
|
|
@ -698,32 +698,41 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p
|
|||
ipConcentric :
|
||||
(m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase);
|
||||
m_support_params.support_extrusion_width = m_object_config->support_line_width.value > 0 ? m_object_config->support_line_width : m_object_config->line_width;
|
||||
is_slim = is_tree_slim(m_object_config->support_type, m_object_config->support_style);
|
||||
MAX_BRANCH_RADIUS = is_slim ? 5.0 : 10.0;
|
||||
support_type = m_object_config->support_type;
|
||||
is_slim = is_tree_slim(support_type, m_object_config->support_style);
|
||||
MAX_BRANCH_RADIUS = 10.0;
|
||||
tree_support_branch_diameter_angle = 5.0;//is_slim ? 10.0 : 5.0;
|
||||
// by default tree support needs no infill, unless it's tree hybrid which contains normal nodes.
|
||||
with_infill = support_pattern != smpNone && support_pattern != smpDefault;
|
||||
const PrintConfig& print_config = m_object->print()->config();
|
||||
m_machine_border.contour = get_bed_shape_with_excluded_area(print_config);
|
||||
Vec3d plate_offset = m_object->print()->get_plate_origin();
|
||||
// align with the centered object in current plate (may not be the 1st plate, so need to add the plate offset)
|
||||
m_machine_border.translate(Point(scale_(plate_offset(0)), scale_(plate_offset(1))) - m_object->instances().front().shift);
|
||||
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
|
||||
SVG svg("SVG/machine_boarder.svg", m_object->bounding_box());
|
||||
if (svg.is_opened()) svg.draw(m_machine_border, "yellow");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0.
|
||||
void TreeSupport::detect_object_overhangs()
|
||||
void TreeSupport::detect_overhangs()
|
||||
{
|
||||
// overhangs are already detected
|
||||
if (m_object->tree_support_layer_count() >= m_object->layer_count())
|
||||
if (m_object->support_layer_count() >= m_object->layer_count())
|
||||
return;
|
||||
|
||||
// Clear and create Tree Support Layers
|
||||
m_object->clear_tree_support_layers();
|
||||
m_object->clear_support_layers();
|
||||
m_object->clear_tree_support_preview_cache();
|
||||
|
||||
create_tree_support_layers();
|
||||
m_ts_data = m_object->alloc_tree_support_preview_cache();
|
||||
m_ts_data->is_slim = is_slim;
|
||||
|
||||
|
||||
const PrintObjectConfig& config = m_object->config();
|
||||
SupportType stype = config.support_type.value;
|
||||
const coordf_t radius_sample_resolution = m_ts_data->m_radius_sample_resolution;
|
||||
SupportType stype = support_type;
|
||||
const coordf_t radius_sample_resolution = g_config_tree_support_collision_resolution;
|
||||
const coordf_t extrusion_width = config.line_width.value;
|
||||
const coordf_t extrusion_width_scaled = scale_(extrusion_width);
|
||||
const coordf_t max_bridge_length = scale_(config.max_bridge_length.value);
|
||||
|
|
@ -732,7 +741,7 @@ void TreeSupport::detect_object_overhangs()
|
|||
const int enforce_support_layers = config.enforce_support_layers.value;
|
||||
const double area_thresh_well_supported = SQ(scale_(6));
|
||||
const double length_thresh_well_supported = scale_(6);
|
||||
static const double sharp_tail_max_support_height = 8.f;
|
||||
static const double sharp_tail_max_support_height = 16.f;
|
||||
// a region is considered well supported if the number of layers below it exceeds this threshold
|
||||
const int thresh_layers_below = 10 / config.layer_height;
|
||||
double obj_height = m_object->size().z();
|
||||
|
|
@ -922,9 +931,9 @@ void TreeSupport::detect_object_overhangs()
|
|||
float accum_height = layer->height;
|
||||
do {
|
||||
// 1. nothing below
|
||||
// check whether this is a sharp tail region
|
||||
// this is a sharp tail region if it's small but non-ignorable
|
||||
if (intersection_ex({expoly}, lower_polys).empty()) {
|
||||
is_sharp_tail = expoly.area() < area_thresh_well_supported;
|
||||
is_sharp_tail = expoly.area() < area_thresh_well_supported && !offset_ex(expoly,-0.5*extrusion_width_scaled).empty();
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -968,7 +977,7 @@ void TreeSupport::detect_object_overhangs()
|
|||
// 2.4 if the area grows fast than threshold, it get connected to other part or
|
||||
// it has a sharp slop and will be auto supported.
|
||||
ExPolygons new_overhang_expolys = diff_ex({expoly}, lower_layer_sharptails);
|
||||
if (!offset_ex(new_overhang_expolys, -5.0 * extrusion_width_scaled).empty()) {
|
||||
if ((get_extents(new_overhang_expolys).size()-get_extents(lower_layer_sharptails).size()).both_comp(Point(scale_(5),scale_(5)),">") || !offset_ex(new_overhang_expolys, -5.0 * extrusion_width_scaled).empty()) {
|
||||
is_sharp_tail = false;
|
||||
break;
|
||||
}
|
||||
|
|
@ -982,6 +991,9 @@ void TreeSupport::detect_object_overhangs()
|
|||
layer->sharp_tails.push_back(expoly);
|
||||
layer->sharp_tails_height.insert({ &expoly, accum_height });
|
||||
append(overhang_areas, overhang);
|
||||
|
||||
if (!overhang.empty())
|
||||
has_sharp_tails = true;
|
||||
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
|
||||
SVG svg(get_svg_filename(std::to_string(layer->print_z), "sharp_tail"), m_object->bounding_box());
|
||||
if (svg.is_opened()) svg.draw(overhang, "yellow");
|
||||
|
|
@ -992,11 +1004,13 @@ 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, true);
|
||||
if (max_bridge_length > 0 && overhang_areas.size()>0) {
|
||||
// do not break bridge for normal part in TreeHybrid
|
||||
bool break_bridge = !(config.support_style == smsTreeHybrid && area(overhang_areas) > m_support_params.thresh_big_overhang);
|
||||
m_object->remove_bridges_from_contacts(lower_layer, layer, extrusion_width_scaled, &overhang_areas, max_bridge_length, break_bridge);
|
||||
}
|
||||
|
||||
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
|
||||
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
|
||||
for (ExPolygon& poly : overhang_areas) {
|
||||
if (!offset_ex(poly, -0.1 * extrusion_width_scaled).empty())
|
||||
ts_layer->overhang_areas.emplace_back(poly);
|
||||
|
|
@ -1146,9 +1160,9 @@ void TreeSupport::detect_object_overhangs()
|
|||
if (m_object->print()->canceled())
|
||||
break;
|
||||
|
||||
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
|
||||
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
|
||||
auto layer = m_object->get_layer(layer_nr);
|
||||
if (support_critical_regions_only) {
|
||||
auto layer = m_object->get_layer(layer_nr);
|
||||
auto lower_layer = layer->lower_layer;
|
||||
if (lower_layer == nullptr)
|
||||
ts_layer->overhang_areas = layer->sharp_tails;
|
||||
|
|
@ -1164,7 +1178,7 @@ void TreeSupport::detect_object_overhangs()
|
|||
}
|
||||
|
||||
for (auto &area : ts_layer->overhang_areas) {
|
||||
ts_layer->overhang_types.emplace(&area, TreeSupportLayer::Detected);
|
||||
ts_layer->overhang_types.emplace(&area, SupportLayer::Detected);
|
||||
}
|
||||
// enforcers
|
||||
if (layer_nr < enforcers.size()) {
|
||||
|
|
@ -1175,15 +1189,16 @@ void TreeSupport::detect_object_overhangs()
|
|||
enforcer = offset(enforcer, 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);
|
||||
ts_layer->overhang_types.emplace(&ts_layer->overhang_areas.back(), SupportLayer::Enforced);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ts_layer->overhang_areas.empty()) has_overhangs = true;
|
||||
if (!layer->cantilevers.empty()) has_cantilever = true;
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
|
||||
for (const TreeSupportLayer* layer : m_object->tree_support_layers()) {
|
||||
for (const SupportLayer* layer : m_object->support_layers()) {
|
||||
if (layer->overhang_areas.empty())
|
||||
continue;
|
||||
|
||||
|
|
@ -1220,9 +1235,9 @@ void TreeSupport::create_tree_support_layers()
|
|||
}
|
||||
|
||||
for (Layer *layer : m_object->layers()) {
|
||||
TreeSupportLayer* ts_layer = m_object->add_tree_support_layer(layer->id(), layer->height, layer->print_z, layer->slice_z);
|
||||
SupportLayer* ts_layer = m_object->add_tree_support_layer(layer->id(), layer->height, layer->print_z, layer->slice_z);
|
||||
if (ts_layer->id() > m_raft_layers) {
|
||||
TreeSupportLayer* lower_layer = m_object->get_tree_support_layer(ts_layer->id() - 1);
|
||||
SupportLayer* lower_layer = m_object->get_support_layer(ts_layer->id() - 1);
|
||||
lower_layer->upper_layer = ts_layer;
|
||||
ts_layer->lower_layer = lower_layer;
|
||||
}
|
||||
|
|
@ -1434,7 +1449,7 @@ void TreeSupport::generate_toolpaths()
|
|||
const coordf_t branch_radius = object_config.tree_support_branch_diameter.value / 2;
|
||||
const coordf_t branch_radius_scaled = scale_(branch_radius);
|
||||
|
||||
if (m_object->tree_support_layers().empty())
|
||||
if (m_object->support_layers().empty())
|
||||
return;
|
||||
|
||||
// calculate fill areas for raft layers
|
||||
|
|
@ -1446,8 +1461,8 @@ void TreeSupport::generate_toolpaths()
|
|||
}
|
||||
}
|
||||
|
||||
if (m_object->tree_support_layer_count() > m_raft_layers) {
|
||||
const TreeSupportLayer *ts_layer = m_object->get_tree_support_layer(m_raft_layers);
|
||||
if (m_object->support_layer_count() > m_raft_layers) {
|
||||
const SupportLayer *ts_layer = m_object->get_support_layer(m_raft_layers);
|
||||
for (const ExPolygon expoly : ts_layer->floor_areas)
|
||||
raft_areas.push_back(expoly);
|
||||
for (const ExPolygon expoly : ts_layer->roof_areas)
|
||||
|
|
@ -1462,7 +1477,7 @@ void TreeSupport::generate_toolpaths()
|
|||
if (m_raft_layers > 0)
|
||||
{
|
||||
ExtrusionRole raft_contour_er = m_slicing_params.base_raft_layers > 0 ? erSupportMaterial : erSupportMaterialInterface;
|
||||
TreeSupportLayer *ts_layer = m_object->tree_support_layers().front();
|
||||
SupportLayer *ts_layer = m_object->support_layers().front();
|
||||
Flow flow = m_object->print()->brim_flow();
|
||||
|
||||
Polygons loops;
|
||||
|
|
@ -1476,7 +1491,7 @@ void TreeSupport::generate_toolpaths()
|
|||
}
|
||||
|
||||
for (size_t layer_nr = 0; layer_nr < m_slicing_params.base_raft_layers; layer_nr++) {
|
||||
TreeSupportLayer *ts_layer = m_object->get_tree_support_layer(layer_nr);
|
||||
SupportLayer *ts_layer = m_object->get_support_layer(layer_nr);
|
||||
coordf_t expand_offset = (layer_nr == 0 ? 0. : -1.);
|
||||
|
||||
Flow support_flow = layer_nr == 0 ? m_object->print()->brim_flow() : Flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
|
||||
|
|
@ -1496,7 +1511,7 @@ void TreeSupport::generate_toolpaths()
|
|||
layer_nr < m_slicing_params.base_raft_layers + m_slicing_params.interface_raft_layers;
|
||||
layer_nr++)
|
||||
{
|
||||
TreeSupportLayer *ts_layer = m_object->get_tree_support_layer(layer_nr);
|
||||
SupportLayer *ts_layer = m_object->get_support_layer(layer_nr);
|
||||
coordf_t expand_offset = (layer_nr == 0 ? 0. : -1.);
|
||||
|
||||
Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
|
||||
|
|
@ -1528,7 +1543,7 @@ void TreeSupport::generate_toolpaths()
|
|||
|
||||
// generate tree support tool paths
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<size_t>(m_raft_layers, m_object->tree_support_layer_count()),
|
||||
tbb::blocked_range<size_t>(m_raft_layers, m_object->support_layer_count()),
|
||||
[&](const tbb::blocked_range<size_t>& range)
|
||||
{
|
||||
for (size_t layer_id = range.begin(); layer_id < range.end(); layer_id++) {
|
||||
|
|
@ -1537,7 +1552,7 @@ void TreeSupport::generate_toolpaths()
|
|||
|
||||
m_object->print()->set_status(70, (boost::format(_L("Support: generate toolpath at layer %d")) % layer_id).str());
|
||||
|
||||
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_id);
|
||||
SupportLayer* ts_layer = m_object->get_support_layer(layer_id);
|
||||
Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
|
||||
coordf_t support_spacing = object_config.support_base_pattern_spacing.value + support_flow.spacing();
|
||||
coordf_t support_density = std::min(1., support_flow.spacing() / support_spacing);
|
||||
|
|
@ -1548,14 +1563,14 @@ void TreeSupport::generate_toolpaths()
|
|||
ExPolygon& poly = *area_group.area;
|
||||
ExPolygons polys;
|
||||
FillParams fill_params;
|
||||
if (area_group.type != TreeSupportLayer::BaseType) {
|
||||
if (area_group.type != SupportLayer::BaseType) {
|
||||
// interface
|
||||
if (layer_id == 0) {
|
||||
Flow flow = m_raft_layers == 0 ? m_object->print()->brim_flow() : support_flow;
|
||||
make_perimeter_and_inner_brim(ts_layer->support_fills.entities, poly, wall_count, flow,
|
||||
area_group.type == TreeSupportLayer::RoofType ? erSupportMaterialInterface : erSupportMaterial);
|
||||
area_group.type == SupportLayer::RoofType ? erSupportMaterialInterface : erSupportMaterial);
|
||||
polys = std::move(offset_ex(poly, -flow.scaled_spacing()));
|
||||
} else if (area_group.type == TreeSupportLayer::Roof1stLayer) {
|
||||
} else if (area_group.type == SupportLayer::Roof1stLayer) {
|
||||
polys = std::move(offset_ex(poly, 0.5*support_flow.scaled_width()));
|
||||
}
|
||||
else {
|
||||
|
|
@ -1564,7 +1579,7 @@ void TreeSupport::generate_toolpaths()
|
|||
fill_params.density = interface_density;
|
||||
fill_params.dont_adjust = true;
|
||||
}
|
||||
if (area_group.type == TreeSupportLayer::Roof1stLayer) {
|
||||
if (area_group.type == SupportLayer::Roof1stLayer) {
|
||||
// roof_1st_layer
|
||||
fill_params.density = interface_density;
|
||||
// Note: spacing means the separation between two lines as if they are tightly extruded
|
||||
|
|
@ -1578,13 +1593,13 @@ void TreeSupport::generate_toolpaths()
|
|||
ts_layer->support_fills.entities.push_back(temp_support_fills);
|
||||
else
|
||||
delete temp_support_fills;
|
||||
} else if (area_group.type == TreeSupportLayer::FloorType) {
|
||||
} else if (area_group.type == SupportLayer::FloorType) {
|
||||
// floor_areas
|
||||
fill_params.density = bottom_interface_density;
|
||||
filler_interface->spacing = m_support_material_interface_flow.spacing();
|
||||
fill_expolygons_generate_paths(ts_layer->support_fills.entities, std::move(polys),
|
||||
filler_interface.get(), fill_params, erSupportMaterialInterface, m_support_material_interface_flow);
|
||||
} else if (area_group.type == TreeSupportLayer::RoofType) {
|
||||
} else if (area_group.type == SupportLayer::RoofType) {
|
||||
// roof_areas
|
||||
fill_params.density = interface_density;
|
||||
filler_interface->spacing = m_support_material_interface_flow.spacing();
|
||||
|
|
@ -1886,7 +1901,7 @@ Polygons TreeSupport::contact_nodes_to_polygon(const std::vector<Node*>& contact
|
|||
}
|
||||
|
||||
|
||||
void TreeSupport::generate_support_areas()
|
||||
void TreeSupport::generate()
|
||||
{
|
||||
bool tree_support_enable = m_object_config->enable_support.value && is_tree(m_object_config->support_type.value);
|
||||
if (!tree_support_enable)
|
||||
|
|
@ -1899,9 +1914,14 @@ void TreeSupport::generate_support_areas()
|
|||
// Generate overhang areas
|
||||
profiler.stage_start(STAGE_DETECT_OVERHANGS);
|
||||
m_object->print()->set_status(55, _L("Support: detect overhangs"));
|
||||
detect_object_overhangs();
|
||||
detect_overhangs();
|
||||
profiler.stage_finish(STAGE_DETECT_OVERHANGS);
|
||||
|
||||
if (!has_overhangs) return;
|
||||
|
||||
m_ts_data = m_object->alloc_tree_support_preview_cache();
|
||||
m_ts_data->is_slim = is_slim;
|
||||
|
||||
// Generate contact points of tree support
|
||||
profiler.stage_start(STAGE_GENERATE_CONTACT_NODES);
|
||||
m_object->print()->set_status(56, _L("Support: generate contact points"));
|
||||
|
|
@ -2071,7 +2091,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
break;
|
||||
|
||||
const std::vector<Node*>& curr_layer_nodes = contact_nodes[layer_nr];
|
||||
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
|
||||
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
|
||||
assert(ts_layer != nullptr);
|
||||
|
||||
// skip if current layer has no points. This fixes potential crash in get_collision (see jira BBL001-355)
|
||||
|
|
@ -2201,18 +2221,20 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
//roof_areas = std::move(diff_ex(roof_areas, avoid_region_interface));
|
||||
//roof_1st_layer = std::move(diff_ex(roof_1st_layer, avoid_region_interface));
|
||||
roof_areas = avoid_object_remove_extra_small_parts(roof_areas, avoid_region_interface);
|
||||
roof_areas = intersection_ex(roof_areas, m_machine_border);
|
||||
roof_1st_layer = avoid_object_remove_extra_small_parts(roof_1st_layer, avoid_region_interface);
|
||||
|
||||
// roof_1st_layer and roof_areas may intersect, so need to subtract roof_areas from roof_1st_layer
|
||||
roof_1st_layer = std::move(diff_ex(roof_1st_layer, roof_areas));
|
||||
roof_1st_layer = intersection_ex(roof_1st_layer, m_machine_border);
|
||||
|
||||
// let supports touch objects when brim is on
|
||||
auto avoid_region = m_ts_data->get_collision((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr);
|
||||
// base_areas = std::move(diff_ex(base_areas, avoid_region));
|
||||
base_areas = avoid_object_remove_extra_small_parts(base_areas, avoid_region);
|
||||
base_areas = std::move(diff_ex(base_areas, roof_areas));
|
||||
base_areas = std::move(diff_ex(base_areas, roof_1st_layer));
|
||||
base_areas = std::move(diff_ex(base_areas, roof_gap_areas));
|
||||
base_areas = intersection_ex(base_areas, m_machine_border);
|
||||
|
||||
if (SQUARE_SUPPORT) {
|
||||
// simplify support contours
|
||||
|
|
@ -2247,10 +2269,10 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
}
|
||||
}
|
||||
auto &area_groups = ts_layer->area_groups;
|
||||
for (auto &area : ts_layer->base_areas) area_groups.emplace_back(&area, TreeSupportLayer::BaseType, max_layers_above_base);
|
||||
for (auto &area : ts_layer->roof_areas) area_groups.emplace_back(&area, TreeSupportLayer::RoofType, max_layers_above_roof);
|
||||
for (auto &area : ts_layer->floor_areas) area_groups.emplace_back(&area, TreeSupportLayer::FloorType, 10000);
|
||||
for (auto &area : ts_layer->roof_1st_layer) area_groups.emplace_back(&area, TreeSupportLayer::Roof1stLayer, max_layers_above_roof1);
|
||||
for (auto &area : ts_layer->base_areas) area_groups.emplace_back(&area, SupportLayer::BaseType, max_layers_above_base);
|
||||
for (auto &area : ts_layer->roof_areas) area_groups.emplace_back(&area, SupportLayer::RoofType, max_layers_above_roof);
|
||||
for (auto &area : ts_layer->floor_areas) area_groups.emplace_back(&area, SupportLayer::FloorType, 10000);
|
||||
for (auto &area : ts_layer->roof_1st_layer) area_groups.emplace_back(&area, SupportLayer::Roof1stLayer, max_layers_above_roof1);
|
||||
|
||||
for (auto &area_group : area_groups) {
|
||||
auto& expoly = area_group.area;
|
||||
|
|
@ -2275,7 +2297,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
for (int layer_nr = 1; layer_nr < m_object->layer_count(); layer_nr++) {
|
||||
if (print->canceled()) break;
|
||||
const std::vector<Node*>& curr_layer_nodes = contact_nodes[layer_nr];
|
||||
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
|
||||
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
|
||||
assert(ts_layer != nullptr);
|
||||
|
||||
// skip if current layer has no points. This fixes potential crash in get_collision (see jira BBL001-355)
|
||||
|
|
@ -2287,10 +2309,10 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
|
||||
int layer_nr_lower = layer_nr - 1;
|
||||
for (layer_nr_lower; layer_nr_lower >= 0; layer_nr_lower--) {
|
||||
if (!m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break;
|
||||
if (!m_object->get_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break;
|
||||
}
|
||||
TreeSupportLayer* lower_layer = m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers);
|
||||
ExPolygons& base_areas_lower = m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->base_areas;
|
||||
SupportLayer* lower_layer = m_object->get_support_layer(layer_nr_lower + m_raft_layers);
|
||||
ExPolygons& base_areas_lower = m_object->get_support_layer(layer_nr_lower + m_raft_layers)->base_areas;
|
||||
|
||||
ExPolygons overhang;
|
||||
|
||||
|
|
@ -2324,7 +2346,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
printZ_to_lightninglayer[lower_layer->print_z] = overhangs.size() - 1;
|
||||
|
||||
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
|
||||
draw_two_overhangs_to_svg(m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers), base_areas_lower, to_expolygons(overhangs.back()));
|
||||
draw_two_overhangs_to_svg(m_object->get_support_layer(layer_nr_lower + m_raft_layers), base_areas_lower, to_expolygons(overhangs.back()));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -2364,7 +2386,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
m_object->print()->set_status(66, (boost::format(_L("Support: fix holes at layer %d")) % layer_nr).str());
|
||||
|
||||
const std::vector<Node*>& curr_layer_nodes = contact_nodes[layer_nr];
|
||||
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
|
||||
SupportLayer* ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
|
||||
assert(ts_layer != nullptr);
|
||||
|
||||
// skip if current layer has no points. This fixes potential crash in get_collision (see jira BBL001-355)
|
||||
|
|
@ -2374,18 +2396,18 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
|
||||
int layer_nr_lower = layer_nr - 1;
|
||||
for (layer_nr_lower; layer_nr_lower >= 0; layer_nr_lower--) {
|
||||
if (!m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break;
|
||||
if (!m_object->get_support_layer(layer_nr_lower + m_raft_layers)->area_groups.empty()) break;
|
||||
}
|
||||
if (layer_nr_lower < 0) continue;
|
||||
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_support_layer(layer_nr_lower + m_raft_layers)->area_groups;
|
||||
|
||||
for (const auto& area_group : ts_layer->area_groups) {
|
||||
if (area_group.type != TreeSupportLayer::BaseType) continue;
|
||||
if (area_group.type != SupportLayer::BaseType) continue;
|
||||
const auto& area = area_group.area;
|
||||
for (const auto& hole : area->holes) {
|
||||
// auto hole_bbox = get_extents(hole).polygon();
|
||||
for (auto& area_group_lower : area_groups_lower) {
|
||||
if (area_group.type != TreeSupportLayer::BaseType) continue;
|
||||
if (area_group.type != SupportLayer::BaseType) continue;
|
||||
auto& base_area_lower = *area_group_lower.area;
|
||||
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
|
||||
|
|
@ -2450,8 +2472,8 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
#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);
|
||||
for (int layer_nr = m_object->layer_count() - 1; layer_nr >= 0; layer_nr--) {
|
||||
SupportLayer* ts_layer = m_object->get_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;
|
||||
|
|
@ -2466,7 +2488,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
draw_circles_layer_out.open("./SVG/layer_heights_draw_circles.txt");
|
||||
if (draw_circles_layer_out.is_open()) {
|
||||
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);
|
||||
SupportLayer* ts_layer = m_object->get_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;
|
||||
|
|
@ -2477,8 +2499,8 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
|
|||
}
|
||||
#endif // SUPPORT_TREE_DEBUG_TO_SVG
|
||||
|
||||
TreeSupportLayerPtrs& ts_layers = m_object->tree_support_layers();
|
||||
auto iter = std::remove_if(ts_layers.begin(), ts_layers.end(), [](TreeSupportLayer* ts_layer) { return ts_layer->height < EPSILON; });
|
||||
SupportLayerPtrs& ts_layers = m_object->support_layers();
|
||||
auto iter = std::remove_if(ts_layers.begin(), ts_layers.end(), [](SupportLayer* ts_layer) { return ts_layer->height < EPSILON; });
|
||||
ts_layers.erase(iter, ts_layers.end());
|
||||
for (int layer_nr = 0; layer_nr < ts_layers.size(); layer_nr++) {
|
||||
ts_layers[layer_nr]->upper_layer = layer_nr != ts_layers.size() - 1 ? ts_layers[layer_nr + 1] : nullptr;
|
||||
|
|
@ -2527,7 +2549,8 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
|
|||
return move_dist;
|
||||
};
|
||||
|
||||
std::vector<std::pair<coordf_t, coordf_t>> layer_heights = plan_layer_heights(contact_nodes);
|
||||
m_ts_data->layer_heights = plan_layer_heights(contact_nodes);
|
||||
std::vector<LayerHeightData> &layer_heights = m_ts_data->layer_heights;
|
||||
if (layer_heights.empty()) return;
|
||||
|
||||
std::unordered_set<Node*> to_free_node_set;
|
||||
|
|
@ -2547,9 +2570,10 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
|
|||
for (Node *p_node : contact_nodes[layer_nr]) {
|
||||
layer_node_dist.emplace(p_node->dist_mm_to_top);
|
||||
}
|
||||
if (layer_nr < m_highest_overhang_layer && layer_heights[layer_nr].second>0) {
|
||||
for (auto node_dist : all_layer_node_dist[layer_nr + 1])
|
||||
layer_node_dist.emplace(node_dist + layer_heights[layer_nr].second);
|
||||
size_t layer_nr_next = layer_heights[layer_nr].next_layer_nr;
|
||||
if (layer_nr < m_highest_overhang_layer && layer_heights[layer_nr].height>0) {
|
||||
for (auto node_dist : all_layer_node_dist[layer_nr_next])
|
||||
layer_node_dist.emplace(node_dist + layer_heights[layer_nr].height);
|
||||
}
|
||||
for (auto node_dist : layer_node_dist) {
|
||||
layer_radius.emplace(calc_branch_radius(branch_radius, node_dist, diameter_angle_scale_factor));
|
||||
|
|
@ -2577,14 +2601,12 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
|
|||
if (layer_contact_nodes.empty())
|
||||
continue;
|
||||
|
||||
int layer_nr_next = layer_nr - 1;
|
||||
while (layer_nr_next>=0 && layer_heights[layer_nr_next].second < EPSILON)
|
||||
layer_nr_next--;
|
||||
coordf_t print_z_next = layer_heights[layer_nr_next].first;
|
||||
coordf_t height_next = layer_heights[layer_nr_next].second;
|
||||
int layer_nr_next = layer_heights[layer_nr].next_layer_nr;
|
||||
coordf_t print_z_next = layer_heights[layer_nr_next].print_z;
|
||||
coordf_t height_next = layer_heights[layer_nr_next].height;
|
||||
|
||||
std::deque<std::pair<size_t, Node*>> unsupported_branch_leaves; // All nodes that are leaves on this layer that would result in unsupported ('mid-air') branches.
|
||||
const Layer* ts_layer = m_object->get_tree_support_layer(layer_nr);
|
||||
const Layer* ts_layer = m_object->get_support_layer(layer_nr);
|
||||
|
||||
m_object->print()->set_status(60, (boost::format(_L("Support: propagate branches at layer %d")) % layer_nr).str());
|
||||
|
||||
|
|
@ -2620,7 +2642,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
|
|||
|
||||
if (node.distance_to_top < 0) {
|
||||
// gap nodes do not merge or move
|
||||
Node* next_node = new Node(p_node->position, p_node->distance_to_top + 1, p_node->skin_direction, p_node->support_roof_layers_below - 1, p_node->to_buildplate, p_node,
|
||||
Node* next_node = new Node(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, p_node->to_buildplate, p_node,
|
||||
print_z_next, height_next);
|
||||
get_max_move_dist(next_node);
|
||||
next_node->is_merged = false;
|
||||
|
|
@ -2755,8 +2777,8 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
|
|||
parent = neighbour->parent;
|
||||
|
||||
const bool to_buildplate = !is_inside_ex(m_ts_data->get_avoidance(0, layer_nr_next), next_position);
|
||||
Node * next_node = new Node(next_position, new_distance_to_top, node.skin_direction, new_support_roof_layers_below, to_buildplate, p_node,
|
||||
layer_heights[layer_nr_next].first, layer_heights[layer_nr_next].second, new_dist_mm_to_top);
|
||||
Node * next_node = new Node(next_position, new_distance_to_top, layer_nr_next, new_support_roof_layers_below, to_buildplate, p_node,
|
||||
print_z_next, height_next, new_dist_mm_to_top);
|
||||
next_node->movement = next_position - node.position;
|
||||
get_max_move_dist(next_node);
|
||||
next_node->is_merged = true;
|
||||
|
|
@ -2801,7 +2823,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
|
|||
if (node.type == ePolygon) {
|
||||
// polygon node do not merge or move
|
||||
const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], p_node->position);
|
||||
Node * next_node = new Node(p_node->position, p_node->distance_to_top + 1, p_node->skin_direction, p_node->support_roof_layers_below - 1, to_buildplate,
|
||||
Node * next_node = new Node(p_node->position, p_node->distance_to_top + 1, layer_nr_next, p_node->support_roof_layers_below - 1, to_buildplate,
|
||||
p_node, print_z_next, height_next);
|
||||
next_node->max_move_dist = 0;
|
||||
next_node->is_merged = false;
|
||||
|
|
@ -2868,13 +2890,13 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
|
|||
|
||||
if (is_line_cut_by_contour(node.position, neighbour)) continue;
|
||||
|
||||
if (is_slim)
|
||||
if (/*is_slim*/1)
|
||||
sum_direction += direction * (1 / dist2_to_neighbor);
|
||||
else
|
||||
sum_direction += direction;
|
||||
}
|
||||
|
||||
if (is_slim)
|
||||
if (/*is_slim*/1)
|
||||
move_to_neighbor_center = sum_direction;
|
||||
else {
|
||||
if (vsize2_with_unscale(sum_direction) <= max_move_distance2) {
|
||||
|
|
@ -2945,7 +2967,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
|
|||
}
|
||||
|
||||
const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[layer_nr], next_layer_vertex);// !is_inside_ex(m_ts_data->get_avoidance(m_ts_data->m_xy_distance, layer_nr - 1), next_layer_vertex);
|
||||
Node * next_node = new Node(next_layer_vertex, node.distance_to_top + 1, node.skin_direction, node.support_roof_layers_below - 1, to_buildplate, p_node,
|
||||
Node * next_node = new Node(next_layer_vertex, node.distance_to_top + 1, layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node,
|
||||
print_z_next, height_next);
|
||||
next_node->movement = movement;
|
||||
get_max_move_dist(next_node);
|
||||
|
|
@ -2974,12 +2996,17 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
|
|||
{
|
||||
const auto& entry = unsupported_branch_leaves.back();
|
||||
Node* i_node = entry.second;
|
||||
for (size_t i_layer = entry.first; i_node != nullptr; ++i_layer, i_node = i_node->parent)
|
||||
for (; i_node != nullptr; i_node = i_node->parent)
|
||||
{
|
||||
size_t i_layer = i_node->obj_layer_nr;
|
||||
std::vector<Node*>::iterator to_erase = std::find(contact_nodes[i_layer].begin(), contact_nodes[i_layer].end(), i_node);
|
||||
if (to_erase != contact_nodes[i_layer].end())
|
||||
{
|
||||
to_free_node_set.insert(*to_erase);
|
||||
// update the parent-child chain
|
||||
if(i_node->parent)
|
||||
i_node->parent->child = i_node->child;
|
||||
if(i_node->child)
|
||||
i_node->child->parent = i_node->parent;
|
||||
contact_nodes[i_layer].erase(to_erase);
|
||||
to_free_node_set.insert(i_node);
|
||||
|
||||
|
|
@ -3191,7 +3218,7 @@ void TreeSupport::adjust_layer_heights(std::vector<std::vector<Node*>>& contact_
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<coordf_t, coordf_t>> TreeSupport::plan_layer_heights(std::vector<std::vector<Node*>>& contact_nodes)
|
||||
std::vector<LayerHeightData> TreeSupport::plan_layer_heights(std::vector<std::vector<Node *>> &contact_nodes)
|
||||
{
|
||||
const PrintObjectConfig& config = m_object->config();
|
||||
const PrintConfig & print_config = m_object->print()->config();
|
||||
|
|
@ -3205,21 +3232,19 @@ std::vector<std::pair<coordf_t, coordf_t>> TreeSupport::plan_layer_heights(std::
|
|||
}
|
||||
const size_t support_roof_layers = config.support_interface_top_layers.value;
|
||||
const int z_distance_top_layers = round_up_divide(scale_(z_distance_top), scale_(layer_height)) + 1;
|
||||
std::vector<std::pair<coordf_t, coordf_t>> layer_heights(contact_nodes.size(), std::pair<coordf_t, coordf_t>(0.0, 0.0));
|
||||
std::vector<LayerHeightData> layer_heights(contact_nodes.size());
|
||||
std::vector<int> bounds;
|
||||
|
||||
if (!config.tree_support_adaptive_layer_height || layer_height == max_layer_height || !print_config.independent_support_layer_height) {
|
||||
for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) {
|
||||
layer_heights[layer_nr].first = m_object->get_layer(layer_nr)->print_z;
|
||||
layer_heights[layer_nr].second = m_object->get_layer(layer_nr)->height;
|
||||
layer_heights[layer_nr] = {m_object->get_layer(layer_nr)->print_z, m_object->get_layer(layer_nr)->height, layer_nr > 0 ? size_t(layer_nr - 1) : 0};
|
||||
}
|
||||
return layer_heights;
|
||||
}
|
||||
|
||||
bounds.push_back(0);
|
||||
// Keep first layer still
|
||||
layer_heights[0].first = m_object->get_layer(0)->print_z;
|
||||
layer_heights[0].second = m_object->get_layer(0)->height;
|
||||
layer_heights[0] = {m_object->get_layer(0)->print_z, m_object->get_layer(0)->height, 0};
|
||||
// Collect top contact layers
|
||||
for (int layer_nr = 1; layer_nr < contact_nodes.size(); layer_nr++)
|
||||
{
|
||||
|
|
@ -3227,8 +3252,8 @@ std::vector<std::pair<coordf_t, coordf_t>> TreeSupport::plan_layer_heights(std::
|
|||
for (int i = 0; i < support_roof_layers + z_distance_top_layers + 1; i++) {
|
||||
if (layer_nr - i > 0) {
|
||||
bounds.push_back(layer_nr - i);
|
||||
layer_heights[layer_nr - i].first = m_object->get_layer(layer_nr - i)->print_z;
|
||||
layer_heights[layer_nr - i].second = m_object->get_layer(layer_nr - i)->height;
|
||||
layer_heights[layer_nr - i].print_z = m_object->get_layer(layer_nr - i)->print_z;
|
||||
layer_heights[layer_nr - i].height = m_object->get_layer(layer_nr - i)->height;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
|
|
@ -3259,19 +3284,29 @@ std::vector<std::pair<coordf_t, coordf_t>> TreeSupport::plan_layer_heights(std::
|
|||
for (int layer_nr = extr1_layer_nr + 1; layer_nr < extr2_layer_nr; layer_nr++) {
|
||||
// if (curr_layer_nodes.empty()) continue;
|
||||
if (std::abs(print_z - m_object->get_layer(layer_nr)->print_z) < step / 2 + EPSILON || extr_layers_left < 1) {
|
||||
layer_heights[layer_nr].first = print_z;
|
||||
layer_heights[layer_nr].second = step;
|
||||
layer_heights[layer_nr].print_z = print_z;
|
||||
layer_heights[layer_nr].height = step;
|
||||
print_z += step;
|
||||
}
|
||||
else {
|
||||
// can't clear curr_layer_nodes, or the model will have empty layers
|
||||
layer_heights[layer_nr].first = 0.0;
|
||||
layer_heights[layer_nr].second = 0.0;
|
||||
layer_heights[layer_nr].print_z = 0.0;
|
||||
layer_heights[layer_nr].height = 0.0;
|
||||
extr_layers_left--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = layer_heights.size() - 1; i >= 0; i--) {
|
||||
if (layer_heights[i].height < EPSILON) continue;
|
||||
for (int j = i - 1; j >= 0; j--) {
|
||||
if (layer_heights[j].height > EPSILON) {
|
||||
layer_heights[i].next_layer_nr = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
|
||||
// check bounds
|
||||
if (1)
|
||||
|
|
@ -3285,7 +3320,7 @@ std::vector<std::pair<coordf_t, coordf_t>> TreeSupport::plan_layer_heights(std::
|
|||
}
|
||||
}
|
||||
#endif
|
||||
for (int i = 0; i < layer_heights.size(); i++) { BOOST_LOG_TRIVIAL(info) << "plan_layer_heights print_z, height: "<< layer_heights[i].first << " " << layer_heights[i].second << std::endl; }
|
||||
for (int i = 0; i < layer_heights.size(); i++) { BOOST_LOG_TRIVIAL(info) << "plan_layer_heights print_z, height: "<< layer_heights[i].print_z << " " << layer_heights[i].height << std::endl; }
|
||||
|
||||
return layer_heights;
|
||||
}
|
||||
|
|
@ -3299,12 +3334,11 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
|
|||
BoundingBox bounding_box = m_object->bounding_box();
|
||||
const Point bounding_box_size = bounding_box.max - bounding_box.min;
|
||||
constexpr double rotate_angle = 22.0 / 180.0 * M_PI;
|
||||
constexpr double thresh_big_overhang = SQ(scale_(10));
|
||||
|
||||
const auto center = bounding_box_middle(bounding_box);
|
||||
const auto sin_angle = std::sin(rotate_angle);
|
||||
const auto cos_angle = std::cos(rotate_angle);
|
||||
const auto rotated_dims = Point(
|
||||
const Point rotated_dims = Point(
|
||||
bounding_box_size(0) * cos_angle + bounding_box_size(1) * sin_angle,
|
||||
bounding_box_size(0) * sin_angle + bounding_box_size(1) * cos_angle) / 2;
|
||||
|
||||
|
|
@ -3346,7 +3380,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
|
|||
{
|
||||
if (m_object->print()->canceled())
|
||||
break;
|
||||
auto ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
|
||||
auto ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers);
|
||||
const ExPolygons &overhang = ts_layer->overhang_areas;
|
||||
auto & curr_nodes = contact_nodes[layer_nr];
|
||||
if (overhang.empty())
|
||||
|
|
@ -3359,11 +3393,11 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
|
|||
for (const ExPolygon &overhang_part : overhang)
|
||||
{
|
||||
BoundingBox overhang_bounds = get_extents(overhang_part);
|
||||
if (config.support_style.value==smsTreeHybrid && overhang_part.area() > thresh_big_overhang) {
|
||||
if (config.support_style.value==smsTreeHybrid && overhang_part.area() > m_support_params.thresh_big_overhang) {
|
||||
Point candidate = overhang_bounds.center();
|
||||
if (!overhang_part.contains(candidate))
|
||||
move_inside_expoly(overhang_part, candidate);
|
||||
Node *contact_node = new Node(candidate, -z_distance_top_layers, (layer_nr) % 2, support_roof_layers + z_distance_top_layers, true, Node::NO_PARENT, print_z,
|
||||
Node *contact_node = new Node(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, Node::NO_PARENT, print_z,
|
||||
height, z_distance_top);
|
||||
contact_node->type = ePolygon;
|
||||
contact_node->overhang = &overhang_part;
|
||||
|
|
@ -3391,7 +3425,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
|
|||
//if (!is_inside_ex(m_ts_data->get_collision(0, layer_nr), candidate))
|
||||
{
|
||||
constexpr bool to_buildplate = true;
|
||||
Node * contact_node = new Node(candidate, -z_distance_top_layers, (layer_nr) % 2, support_roof_layers + z_distance_top_layers, to_buildplate,
|
||||
Node * contact_node = new Node(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate,
|
||||
Node::NO_PARENT, print_z, height, z_distance_top);
|
||||
contact_node->overhang = &overhang_part;
|
||||
curr_nodes.emplace_back(contact_node);
|
||||
|
|
@ -3405,7 +3439,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
|
|||
{
|
||||
auto bbox = overhang_part.contour.bounding_box();
|
||||
Points candidates;
|
||||
if (ts_layer->overhang_types[&overhang_part] == TreeSupportLayer::Detected)
|
||||
if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected)
|
||||
candidates = {bbox.min, bounding_box_middle(bbox), bbox.max};
|
||||
else
|
||||
candidates = {bounding_box_middle(bbox)};
|
||||
|
|
@ -3414,13 +3448,13 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
|
|||
if (!overhang_part.contains(candidate))
|
||||
move_inside_expoly(overhang_part, candidate);
|
||||
constexpr bool to_buildplate = true;
|
||||
Node *contact_node = new Node(candidate, -z_distance_top_layers, layer_nr % 2, support_roof_layers + z_distance_top_layers, to_buildplate, Node::NO_PARENT,
|
||||
Node *contact_node = new Node(candidate, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, to_buildplate, Node::NO_PARENT,
|
||||
print_z, height, z_distance_top);
|
||||
contact_node->overhang = &overhang_part;
|
||||
curr_nodes.emplace_back(contact_node);
|
||||
}
|
||||
}
|
||||
if (ts_layer->overhang_types[&overhang_part] == TreeSupportLayer::Detected) {
|
||||
if (ts_layer->overhang_types[&overhang_part] == SupportLayer::Detected) {
|
||||
// add points at corners
|
||||
auto &points = overhang_part.contour.points;
|
||||
int nSize = points.size();
|
||||
|
|
@ -3429,7 +3463,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
|
|||
auto v1 = (pt - points[(i - 1 + nSize) % nSize]).cast<double>().normalized();
|
||||
auto v2 = (pt - points[(i + 1) % nSize]).cast<double>().normalized();
|
||||
if (v1.dot(v2) > -0.7) { // angle smaller than 135 degrees
|
||||
Node *contact_node = new Node(pt, -z_distance_top_layers, layer_nr % 2, support_roof_layers + z_distance_top_layers, true, Node::NO_PARENT, print_z,
|
||||
Node *contact_node = new Node(pt, -z_distance_top_layers, layer_nr, support_roof_layers + z_distance_top_layers, true, Node::NO_PARENT, print_z,
|
||||
height, z_distance_top);
|
||||
contact_node->overhang = &overhang_part;
|
||||
contact_node->is_corner = true;
|
||||
|
|
@ -3437,7 +3471,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
|
|||
}
|
||||
}
|
||||
}
|
||||
if(ts_layer->overhang_types[&overhang_part] == TreeSupportLayer::Enforced || is_slim){
|
||||
if(ts_layer->overhang_types[&overhang_part] == SupportLayer::Enforced || is_slim){
|
||||
// remove close points in Enforcers
|
||||
// auto above_nodes = contact_nodes[layer_nr - 1];
|
||||
if (!curr_nodes.empty() /*&& !above_nodes.empty()*/) {
|
||||
|
|
@ -3487,16 +3521,6 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
|
|||
|
||||
BOOST_LOG_TRIVIAL(info) << "avg_node_per_layer=" << avg_node_per_layer << ", nodes_angle=" << nodes_angle;
|
||||
}
|
||||
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
|
||||
std::ofstream contact_nodes_out;
|
||||
contact_nodes_out.open("./SVG/contact_nodes.txt");
|
||||
if (contact_nodes_out.is_open()) {
|
||||
for (int i = 0; i < contact_nodes.size(); i++) {
|
||||
if (!contact_nodes[i].empty())
|
||||
contact_nodes_out << i << std::endl;
|
||||
}
|
||||
}
|
||||
#endif // SUPPORT_TREE_DEBUG_TO_SVG
|
||||
}
|
||||
|
||||
void TreeSupport::insert_dropped_node(std::vector<Node*>& nodes_layer, Node* p_node)
|
||||
|
|
@ -3621,13 +3645,18 @@ const ExPolygons& TreeSupportData::calculate_avoidance(const RadiusLayerPair& ke
|
|||
// if the layer at 2N below the current one but we won't exceed our limit unless there are N*N uncalculated layers
|
||||
// below our current one.
|
||||
constexpr auto max_recursion_depth = 100;
|
||||
size_t layer_nr_next = layer_nr;
|
||||
for (int i = 0; i < max_recursion_depth && layer_nr_next>0; i++) {
|
||||
layer_nr_next = layer_heights[layer_nr_next].next_layer_nr;
|
||||
}
|
||||
// Check if we would exceed the recursion limit by trying to process this layer
|
||||
if (layer_nr >= max_recursion_depth && m_avoidance_cache.find({radius, layer_nr - max_recursion_depth}) == m_avoidance_cache.end()) {
|
||||
if (layer_nr >= max_recursion_depth && m_avoidance_cache.find({radius, layer_nr_next}) == m_avoidance_cache.end()) {
|
||||
// Force the calculation of the layer `max_recursion_depth` below our current one, ignoring the result.
|
||||
get_avoidance(radius, layer_nr - max_recursion_depth);
|
||||
get_avoidance(radius, layer_nr_next);
|
||||
}
|
||||
|
||||
ExPolygons avoidance_areas = std::move(offset_ex(get_avoidance(radius, layer_nr - 1), scale_(-m_max_move)));
|
||||
layer_nr_next = layer_heights[layer_nr].next_layer_nr;
|
||||
ExPolygons avoidance_areas = std::move(offset_ex(get_avoidance(radius, layer_nr_next), scale_(-m_max_move)));
|
||||
const ExPolygons &collision = get_collision(radius, layer_nr);
|
||||
avoidance_areas.insert(avoidance_areas.end(), collision.begin(), collision.end());
|
||||
avoidance_areas = std::move(union_ex(avoidance_areas));
|
||||
|
|
|
|||
|
|
@ -21,6 +21,15 @@ namespace Slic3r
|
|||
class PrintObject;
|
||||
class TreeSupport;
|
||||
|
||||
struct LayerHeightData
|
||||
{
|
||||
coordf_t print_z = 0;
|
||||
coordf_t height = 0;
|
||||
size_t next_layer_nr = 0;
|
||||
LayerHeightData() = default;
|
||||
LayerHeightData(coordf_t z, coordf_t h, size_t next_layer) : print_z(z), height(h), next_layer_nr(next_layer) {}
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Lazily generates tree guidance volumes.
|
||||
*
|
||||
|
|
@ -79,6 +88,8 @@ public:
|
|||
Polygons get_contours(size_t layer_nr) const;
|
||||
Polygons get_contours_with_holes(size_t layer_nr) const;
|
||||
|
||||
std::vector<LayerHeightData> layer_heights;
|
||||
|
||||
private:
|
||||
/*!
|
||||
* \brief Convenience typedef for the keys to the caches
|
||||
|
|
@ -114,11 +125,6 @@ private:
|
|||
*/
|
||||
const ExPolygons& calculate_avoidance(const RadiusLayerPair& key) const;
|
||||
|
||||
/*!
|
||||
* \brief Polygons representing the limits of the printable area of the
|
||||
* machine
|
||||
*/
|
||||
ExPolygon m_machine_border;
|
||||
|
||||
public:
|
||||
bool is_slim = false;
|
||||
|
|
@ -195,9 +201,9 @@ public:
|
|||
* \param storage The data storage where the mesh data is gotten from and
|
||||
* where the resulting support areas are stored.
|
||||
*/
|
||||
void generate_support_areas();
|
||||
void generate();
|
||||
|
||||
void detect_object_overhangs();
|
||||
void detect_overhangs();
|
||||
|
||||
enum NodeType {
|
||||
eCircle,
|
||||
|
|
@ -215,7 +221,7 @@ public:
|
|||
Node()
|
||||
: distance_to_top(0)
|
||||
, position(Point(0, 0))
|
||||
, skin_direction(false)
|
||||
, obj_layer_nr(0)
|
||||
, support_roof_layers_below(0)
|
||||
, support_floor_layers_above(0)
|
||||
, to_buildplate(true)
|
||||
|
|
@ -224,11 +230,11 @@ public:
|
|||
, height(0.0)
|
||||
{}
|
||||
|
||||
Node(const Point position, const int distance_to_top, const bool skin_direction, const int support_roof_layers_below, const bool to_buildplate, Node* parent,
|
||||
Node(const Point position, const int distance_to_top, const int obj_layer_nr, const int support_roof_layers_below, const bool to_buildplate, Node* parent,
|
||||
coordf_t print_z_, coordf_t height_, coordf_t dist_mm_to_top_=0)
|
||||
: distance_to_top(distance_to_top)
|
||||
, position(position)
|
||||
, skin_direction(skin_direction)
|
||||
, obj_layer_nr(obj_layer_nr)
|
||||
, support_roof_layers_below(support_roof_layers_below)
|
||||
, support_floor_layers_above(0)
|
||||
, to_buildplate(to_buildplate)
|
||||
|
|
@ -292,6 +298,7 @@ public:
|
|||
*/
|
||||
int support_roof_layers_below;
|
||||
int support_floor_layers_above;
|
||||
int obj_layer_nr;
|
||||
|
||||
/*!
|
||||
* \brief Whether to try to go towards the build plate.
|
||||
|
|
@ -356,11 +363,15 @@ public:
|
|||
InfillPattern interface_fill_pattern;
|
||||
InfillPattern contact_fill_pattern;
|
||||
bool with_sheath;
|
||||
const double thresh_big_overhang = SQ(scale_(10));
|
||||
};
|
||||
|
||||
int avg_node_per_layer = 0;
|
||||
float nodes_angle = 0;
|
||||
bool has_overhangs = false;
|
||||
bool has_sharp_tails = false;
|
||||
bool has_cantilever = false;
|
||||
SupportType support_type;
|
||||
|
||||
std::unique_ptr<FillLightning::Generator> generator;
|
||||
std::unordered_map<double, size_t> printZ_to_lightninglayer;
|
||||
|
|
@ -388,6 +399,12 @@ private:
|
|||
bool with_infill = false;
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Polygons representing the limits of the printable area of the
|
||||
* machine
|
||||
*/
|
||||
ExPolygon m_machine_border;
|
||||
|
||||
/*!
|
||||
* \brief Draws circles around each node of the tree into the final support.
|
||||
*
|
||||
|
|
@ -423,7 +440,7 @@ private:
|
|||
*
|
||||
*/
|
||||
|
||||
std::vector<std::pair<coordf_t, coordf_t>> plan_layer_heights(std::vector<std::vector<Node*>>& contact_nodes);
|
||||
std::vector<LayerHeightData> plan_layer_heights(std::vector<std::vector<Node *>> &contact_nodes);
|
||||
/*!
|
||||
* \brief Creates points where support contacts the model.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -753,9 +753,7 @@ inline std::pair<SlabLines, SlabLines> slice_slabs_make_lines(
|
|||
const std::vector<float> &zs,
|
||||
bool top,
|
||||
bool bottom,
|
||||
const ThrowOnCancel throw_on_cancel_fn,
|
||||
// BBS: solve conflicts (see declaration) and most elegant way I can get
|
||||
SlabSlicingConfig config)
|
||||
const ThrowOnCancel throw_on_cancel_fn)
|
||||
{
|
||||
std::pair<SlabLines, SlabLines> out;
|
||||
SlabLines &lines_top = out.first;
|
||||
|
|
@ -774,7 +772,7 @@ inline std::pair<SlabLines, SlabLines> slice_slabs_make_lines(
|
|||
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<int>(0, int(indices.size())),
|
||||
[&vertices, &indices, &face_neighbors, &face_edge_ids, num_edges, &face_orientation, &zs, top, bottom, &lines_top, &lines_bottom, &lines_mutex_top, &lines_mutex_bottom, throw_on_cancel_fn, &config]
|
||||
[&vertices, &indices, &face_neighbors, &face_edge_ids, num_edges, &face_orientation, &zs, top, bottom, &lines_top, &lines_bottom, &lines_mutex_top, &lines_mutex_bottom, throw_on_cancel_fn]
|
||||
(const tbb::blocked_range<int> &range) {
|
||||
for (int face_idx = range.begin(); face_idx < range.end(); ++ face_idx) {
|
||||
if ((face_idx & 0x0ffff) == 0)
|
||||
|
|
@ -793,7 +791,7 @@ inline std::pair<SlabLines, SlabLines> slice_slabs_make_lines(
|
|||
slice_facet_with_slabs<true>(vertices, indices, face_idx, neighbors, edge_ids, num_edges, zs, lines_top, lines_mutex_top);
|
||||
}
|
||||
// BBS: add vertical faces option
|
||||
if (bottom && (fo == FaceOrientation::Down || (config.isVertical && fo == FaceOrientation::Vertical) || fo == FaceOrientation::Degenerate)) {
|
||||
if (bottom && (fo == FaceOrientation::Down || fo == FaceOrientation::Degenerate)) {
|
||||
Vec3i neighbors = face_neighbors[face_idx];
|
||||
// Reset neighborship of this triangle in case the other triangle is oriented backwards from this one.
|
||||
for (int i = 0; i < 3; ++ i)
|
||||
|
|
@ -1898,8 +1896,7 @@ void slice_mesh_slabs(
|
|||
const Transform3d &trafo,
|
||||
std::vector<Polygons> *out_top,
|
||||
std::vector<Polygons> *out_bottom,
|
||||
std::function<void()> throw_on_cancel,
|
||||
SlabSlicingConfig config)
|
||||
std::function<void()> throw_on_cancel)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << "slice_mesh_slabs to polygons";
|
||||
|
||||
|
|
@ -1978,7 +1975,7 @@ void slice_mesh_slabs(
|
|||
std::vector<Vec3i> face_edge_ids = its_face_edge_ids(mesh, face_neighbors, true, &num_edges);
|
||||
std::pair<SlabLines, SlabLines> lines = slice_slabs_make_lines(
|
||||
vertices_transformed, mesh.indices, face_neighbors, face_edge_ids, num_edges, face_orientation, zs,
|
||||
out_top != nullptr, out_bottom != nullptr, throw_on_cancel, config);
|
||||
out_top != nullptr, out_bottom != nullptr, throw_on_cancel);
|
||||
|
||||
throw_on_cancel();
|
||||
|
||||
|
|
|
|||
|
|
@ -46,19 +46,6 @@ struct MeshSlicingParamsEx : public MeshSlicingParams
|
|||
double resolution { 0 };
|
||||
};
|
||||
|
||||
// BBS: MusangKing - NEW: add paint-on support on vertical-faces
|
||||
// this SlabSlicingConfig aiming to distinguish if slice_slabs_make_lines() outputs lines by slab_slicing on vertical faces
|
||||
// e.g., for support enforcer operation: isVertical = true; for other color painting operations: isVertical = false (default).
|
||||
// solve conflicts STUDIO-1183/970/1285
|
||||
struct SlabSlicingConfig
|
||||
{
|
||||
SlabSlicingConfig()
|
||||
: isVertical(false)
|
||||
{}
|
||||
|
||||
bool isVertical;
|
||||
};
|
||||
|
||||
// All the following slicing functions shall produce consistent results with the same mesh, same transformation matrix and slicing parameters.
|
||||
// Namely, slice_mesh_slabs() shall produce consistent results with slice_mesh() and slice_mesh_ex() in the sense, that projections made by
|
||||
// slice_mesh_slabs() shall fall onto slicing planes produced by slice_mesh().
|
||||
|
|
@ -120,9 +107,7 @@ void slice_mesh_slabs(
|
|||
const Transform3d &trafo,
|
||||
std::vector<Polygons> *out_top,
|
||||
std::vector<Polygons> *out_bottom,
|
||||
std::function<void()> throw_on_cancel,
|
||||
// BBS: MusangKing
|
||||
SlabSlicingConfig config = SlabSlicingConfig());
|
||||
std::function<void()> throw_on_cancel);
|
||||
|
||||
// Project mesh upwards pointing surfaces / downwards pointing surfaces into 2D polygons.
|
||||
void project_mesh(
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#define CLI_3MF_NOT_SUPPORT_MACHINE_CHANGE -15
|
||||
#define CLI_3MF_NEW_MACHINE_NOT_SUPPORTED -16
|
||||
#define CLI_PROCESS_NOT_COMPATIBLE -17
|
||||
#define CLI_INVALID_VALUES_IN_3MF -18
|
||||
|
||||
|
||||
#define CLI_NO_SUITABLE_OBJECTS -50
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#define SLIC3R_VERSION "@SLIC3R_VERSION@"
|
||||
#define SoftFever_VERSION "@SoftFever_VERSION@"
|
||||
#define SLIC3R_BUILD_ID "@SLIC3R_BUILD_ID@"
|
||||
#define SLIC3R_BUILD_TIME "@SLIC3R_BUILD_TIME@"
|
||||
//#define SLIC3R_RC_VERSION "@SLIC3R_VERSION@"
|
||||
#define BBL_RELEASE_TO_PUBLIC @BBL_RELEASE_TO_PUBLIC@
|
||||
#define BBL_INTERNAL_TESTING @BBL_INTERNAL_TESTING@
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue