Merge branch 'master' into SoftFever

# Conflicts:
#	bbl/i18n/zh_cn/BambuStudio_zh_CN.po
#	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/nl/BambuStudio.mo
#	resources/i18n/sv/BambuStudio.mo
#	resources/i18n/zh_cn/BambuStudio.mo
#	resources/profiles/Creality.json
#	resources/profiles/Voron.json
#	resources/web/guide/3/index.html
#	src/libslic3r/AppConfig.cpp
#	src/libslic3r/GCode.cpp
#	src/libslic3r/GCode/GCodeProcessor.cpp
#	src/libslic3r/LayerRegion.cpp
#	src/libslic3r/Preset.cpp
#	src/libslic3r/Print.cpp
#	src/libslic3r/PrintConfig.cpp
#	src/libslic3r/PrintConfig.hpp
#	src/libslic3r/PrintObject.cpp
#	src/slic3r/GUI/AboutDialog.cpp
#	src/slic3r/GUI/BBLTopbar.cpp
#	src/slic3r/GUI/ConfigManipulation.cpp
#	src/slic3r/GUI/ConfigWizard.cpp
#	src/slic3r/GUI/GCodeViewer.cpp
#	src/slic3r/GUI/GUI_App.cpp
#	src/slic3r/GUI/GUI_Factories.cpp
#	src/slic3r/GUI/MainFrame.cpp
#	src/slic3r/GUI/Plater.cpp
#	src/slic3r/GUI/Tab.cpp
#	version.inc
This commit is contained in:
SoftFever 2022-12-16 13:59:30 +08:00
commit bf8a9fee1f
689 changed files with 46784 additions and 10006 deletions

View file

@ -185,13 +185,13 @@ void AppConfig::set_defaults()
#ifdef SUPPORT_DARK_MODE
if (get("dark_color_mode").empty())
set_bool("dark_color_mode", false);
set("dark_color_mode", "0");
#endif
#ifdef SUPPORT_SYS_MENU
//#ifdef SUPPORT_SYS_MENU
if (get("sys_menu_enabled").empty())
set_bool("sys_menu_enabled", true);
#endif
set("sys_menu_enabled", "1");
//#endif
#endif // _WIN32
// BBS
@ -288,9 +288,9 @@ void AppConfig::set_defaults()
if (get("backup_interval").empty()) {
set("backup_interval", "10");
}
if (get("curr_bed_type").empty()) {
set("curr_bed_type", "0");
set("curr_bed_type", "1");
}
// #if BBL_RELEASE_TO_PUBLIC
@ -458,7 +458,6 @@ std::string AppConfig::load()
} else if (it.key() == "presets") {
for (auto iter = it.value().begin(); iter != it.value().end(); iter++) {
if (iter.key() == "filaments") {
// BBS: filament presets is now considered as project config instead of app config
int idx = 0;
for(auto& element: iter.value()) {
if (idx == 0)
@ -480,7 +479,14 @@ std::string AppConfig::load()
} else {
m_storage[it.key()][iter.key()] = "false";
}
} else {
} else if (iter.key() == "filament_presets") {
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())
m_storage[it.key()][iter.key()] = iter.value().get<std::string>();
else {
@ -531,7 +537,7 @@ void AppConfig::save()
{
// Returns "undefined" if the thread naming functionality is not supported by the operating system.
std::optional<std::string> current_thread_name = get_current_thread_name();
if (current_thread_name && *current_thread_name != "bambustu_main") {
if (current_thread_name && *current_thread_name != "bambustu_main" && *current_thread_name != "main") {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<<", current_thread_name is " << *current_thread_name;
throw CriticalException("Calling AppConfig::save() from a worker thread, thread name: " + *current_thread_name);
}
@ -563,6 +569,18 @@ void AppConfig::save()
j["app"][kvp.first] = kvp.second;
}
for (const auto &filament_preset : m_filament_presets) {
j["app"]["filament_presets"].push_back(filament_preset);
}
for (const auto &filament_color : m_filament_colors) {
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())
@ -577,7 +595,7 @@ void AppConfig::save()
} else if (category.first == "presets") {
json j_filament_array;
for(const auto& kvp : category.second) {
if (boost::starts_with(kvp.first, "filament")) {
if (boost::starts_with(kvp.first, "filament") && kvp.first != "filament_colors") {
j_filament_array.push_back(kvp.second);
} else {
j[category.first][kvp.first] = kvp.second;
@ -930,7 +948,9 @@ void AppConfig::set_recent_projects(const std::vector<std::string>& recent_proje
it->second.clear();
for (unsigned int i = 0; i < (unsigned int)recent_projects.size(); ++i)
{
it->second[std::to_string(i + 1)] = recent_projects[i];
auto n = std::to_string(i + 1);
if (n.length() == 1) n = "0" + n;
it->second[n] = recent_projects[i];
}
}

View file

@ -17,6 +17,9 @@ using namespace nlohmann;
#define ENV_PRE_HOST "2"
#define ENV_PRODUCT_HOST "3"
#define SUPPORT_DARK_MODE
//#define _MSW_DARK_MODE
namespace Slic3r {
@ -164,6 +167,22 @@ public:
void set_vendors(VendorMap &&vendors) { m_vendors = std::move(vendors); m_dirty = true; }
const VendorMap& vendors() const { return m_vendors; }
const std::vector<std::string> &get_filament_presets() const { return m_filament_presets; }
void set_filament_presets(const std::vector<std::string> &filament_presets){
m_filament_presets = filament_presets;
m_dirty = true;
}
const std::vector<std::string> &get_filament_colors() const { return m_filament_colors; }
void set_filament_colors(const std::vector<std::string> &filament_colors){
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;
void update_config_dir(const std::string &dir);
@ -259,6 +278,10 @@ private:
bool m_legacy_datadir;
std::string m_loading_path;
std::vector<std::string> m_filament_presets;
std::vector<std::string> m_filament_colors;
std::vector<float> m_flush_volumes_matrix;
};
} // namespace Slic3r

View file

@ -84,7 +84,7 @@ const double BIG_ITEM_TRESHOLD = 0.02;
template<class PConf>
void fill_config(PConf& pcfg, const ArrangeParams &params) {
if (params.is_seq_print) {
if (params.is_seq_print || params.excluded_regions.empty()==false) {
// Align the arranged pile into the center of the bin
pcfg.alignment = PConf::Alignment::CENTER;
// Start placing the items from the center of the print bed
@ -137,7 +137,7 @@ static double fixed_overfit(const std::tuple<double, Box>& result, const Box &bi
}
// useful for arranging big circle objects
static double fixed_overfit_topright_sliding(const std::tuple<double, Box>& result, const Box& binbb)
static double fixed_overfit_topright_sliding(const std::tuple<double, Box> &result, const Box &binbb, const std::vector<Box> &excluded_boxes)
{
double score = std::get<0>(result);
Box pilebb = std::get<1>(result);
@ -152,6 +152,12 @@ static double fixed_overfit_topright_sliding(const std::tuple<double, Box>& resu
auto diff = double(fullbb.area()) - binbb.area();
if (diff > 0) score += diff;
// excluded regions and nonprefered regions should not intersect the translated pilebb
for (auto &bb : excluded_boxes) {
auto area_ = pilebb.intersection(bb).area();
if (area_ > 0) score += area_;
}
return score;
}
@ -190,6 +196,7 @@ protected:
Box m_pilebb; // The bounding box of the merged pile.
ItemGroup m_remaining; // Remaining items
ItemGroup m_items; // allready packed items
std::vector<Box> m_excluded_and_extruCali_regions; // excluded and extrusion calib regions
size_t m_item_count = 0; // Number of all items to be packed
ArrangeParams params;
@ -202,14 +209,20 @@ protected:
// 1) Y distance of item corner to bed corner. Must be put above bed corner. (high weight)
// 2) X distance of item corner to bed corner (low weight)
// 3) item row occupancy (useful when rotation is enabled)
// 4需要允许往屏蔽区域的左边或下边去一点不然很多物体可能认为摆不进去实际上我们最后是可以做平移的
double dist_for_BOTTOM_LEFT(Box ibb, const ClipperLib::IntPoint& origin_pack)
{
double dist_corner_y = ibb.minCorner().y() - origin_pack.y();
double dist_corner_x = ibb.minCorner().x() - origin_pack.x();
if (dist_corner_y < 0 || dist_corner_x<0)
return LARGE_COST_TO_REJECT;
double bindist = norm(dist_corner_y + 1 * dist_corner_x
+ 1 * double(ibb.maxCorner().y() - ibb.minCorner().y())); // occupy as few rows as possible
// occupy as few rows as possible if we have rotations
double bindist = double(ibb.maxCorner().y() - ibb.minCorner().y());
if (dist_corner_x >= 0 && dist_corner_y >= 0)
bindist += dist_corner_y + 1 * dist_corner_x;
else {
if (dist_corner_x < 0) bindist += 10 * (-dist_corner_x);
if (dist_corner_y < 0) bindist += 10 * (-dist_corner_y);
}
bindist = norm(bindist);
return bindist;
}
@ -350,9 +363,10 @@ protected:
score = dist_for_BOTTOM_LEFT(ibb, origin_pack);
}
else {
score = 0.5 * norm(pl::distance(ibb.center(), origin_pack));
if (m_pilebb.defined)
score += 0.5 * norm(pl::distance(ibb.center(), m_pilebb.center()));
score = 0.5 * norm(pl::distance(ibb.center(), m_pilebb.center()));
else
score = 0.5 * norm(pl::distance(ibb.center(), origin_pack));
}
break;
}
@ -411,6 +425,7 @@ protected:
if (p.is_virt_object) continue;
score += lambda3 * (item.bed_temp - p.vitrify_temp > 0);
}
score += lambda3 * (item.bed_temp - item.vitrify_temp > 0);
score += lambda4 * hasRowHeightConflict + lambda4 * hasLidHeightConflict;
}
else {
@ -418,12 +433,20 @@ protected:
Item& p = m_items[i];
if (p.is_virt_object) {
// Better not put items above wipe tower
if (p.is_wipe_tower)
score += ibb.maxCorner().y() > p.boundingBox().maxCorner().y();
if (p.is_wipe_tower) {
if (ibb.maxCorner().y() > p.boundingBox().maxCorner().y())
score += 1;
else if(m_pilebb.defined)
score += norm(pl::distance(ibb.center(), m_pilebb.center()));
}
else
continue;
} else
} else {
// 高度接近的件尽量摆到一起
score += (1- std::abs(item.height - p.height) / params.printable_height)
* norm(pl::distance(ibb.center(), p.boundingBox().center()));
score += LARGE_COST_TO_REJECT * (item.bed_temp - p.bed_temp != 0);
}
}
}
@ -461,6 +484,14 @@ public:
m_norm = std::sqrt(m_bin_area);
fill_config(m_pconf, params);
this->params = params;
for (auto& region : m_pconf.m_excluded_regions) {
Box bb = region.boundingBox();
m_excluded_and_extruCali_regions.emplace_back(bb);
}
for (auto& region : m_pconf.m_nonprefered_regions) {
Box bb = region.boundingBox();
m_excluded_and_extruCali_regions.emplace_back(bb);
}
// Set up a callback that is called just before arranging starts
// This functionality is provided by the Nester class (m_pack).
@ -498,28 +529,19 @@ public:
m_pconf.object_function = get_objfn();
auto bbox2expoly = [](Box bb) {
ExPolygon bin_poly;
auto c0 = bb.minCorner();
auto c1 = bb.maxCorner();
bin_poly.contour.points.emplace_back(c0);
bin_poly.contour.points.emplace_back(c1.x(), c0.y());
bin_poly.contour.points.emplace_back(c1);
bin_poly.contour.points.emplace_back(c0.x(), c1.y());
return bin_poly;
};
// preload fixed items (and excluded regions) on plate
m_pconf.on_preload = [this](const ItemGroup &items, PConfig &cfg) {
if (items.empty()) return;
auto bb = sl::boundingBox(m_bin);
auto binbb = sl::boundingBox(m_bin);
// BBS: excluded region (virtual object but not wipe tower) should not affect final alignment
bool all_is_excluded_region = std::all_of(items.begin(), items.end(), [](Item &itm) { return itm.is_virt_object && !itm.is_wipe_tower; });
if (!all_is_excluded_region)
cfg.alignment = PConfig::Alignment::DONT_ALIGN;
else
cfg.alignment = PConfig::Alignment::CENTER;
auto starting_point = cfg.starting_point == PConfig::Alignment::BOTTOM_LEFT ? bb.minCorner() : bb.center();
auto starting_point = cfg.starting_point == PConfig::Alignment::BOTTOM_LEFT ? binbb.minCorner() : binbb.center();
// if we have wipe tower, items should be arranged around wipe tower
for (Item itm : items) {
if (itm.is_wipe_tower) {
@ -528,12 +550,16 @@ public:
}
}
cfg.object_function = [this, bb, starting_point](const Item& item, const ItemGroup& packed_items) {
bool packed_are_excluded_region = std::all_of(packed_items.begin(), packed_items.end(), [](Item& itm) { return itm.is_virt_object && !itm.is_wipe_tower; });
if(packed_are_excluded_region)
return fixed_overfit_topright_sliding(objfunc(item, starting_point), bb);
else
return fixed_overfit(objfunc(item, starting_point), bb);
cfg.object_function = [this, binbb, starting_point](const Item &item, const ItemGroup &packed_items) {
// 在我们的摆盘中,没有天然的固定对象。固定对象只有:屏蔽区域、挤出补偿区域、料塔。
// 对于屏蔽区域,摆入的对象仍然是可以向右上滑动的;
// 对挤出料塔,摆入的对象不能滑动(必须围绕料塔)
bool pack_around_wipe_tower = std::any_of(packed_items.begin(), packed_items.end(), [](Item& itm) { return itm.is_wipe_tower; });
//if(pack_around_wipe_tower)
return fixed_overfit(objfunc(item, starting_point), binbb);
//else {
// return fixed_overfit_topright_sliding(objfunc(item, starting_point), binbb, m_excluded_and_extruCali_regions);
//}
};
};
@ -611,16 +637,18 @@ template<> std::function<double(const Item&, const ItemGroup&)> AutoArranger<Box
double score = std::get<0>(result);
auto& fullbb = std::get<1>(result);
if (m_pconf.starting_point == PConfig::Alignment::BOTTOM_LEFT)
{
if (!sl::isInside(fullbb, m_bin))
score += LARGE_COST_TO_REJECT;
}
else
//if (m_pconf.starting_point == PConfig::Alignment::BOTTOM_LEFT)
//{
// if (!sl::isInside(fullbb, m_bin))
// score += LARGE_COST_TO_REJECT;
//}
//else
{
double miss = Placer::overfit(fullbb, m_bin);
miss = miss > 0 ? miss : 0;
score += miss * miss;
if (score > LARGE_COST_TO_REJECT)
score = 1.5 * LARGE_COST_TO_REJECT;
}
return score;
@ -821,7 +849,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.back();
item.extrude_id = arrpoly.extrude_ids.front();
item.height = arrpoly.height;
item.name = arrpoly.name;
//BBS: add virtual object logic

View file

@ -124,6 +124,7 @@ struct ArrangeParams {
float clearance_height_to_rod = 0;
float clearance_height_to_lid = 0;
float cleareance_radius = 0;
float printable_height = 256.0;
ArrangePolygons excluded_regions; // regions cant't be used
ArrangePolygons nonprefered_regions; // regions can be used but not prefered

View file

@ -122,6 +122,7 @@ bool BridgeDetector::detect_angle(double bridge_direction_override)
double max_length = 0;
{
Lines clipped_lines = intersection_ln(lines, clip_area);
size_t archored_line_num = 0;
for (size_t i = 0; i < clipped_lines.size(); ++i) {
const Line &line = clipped_lines[i];
if (expolygons_contain(this->_anchor_regions, line.a) && expolygons_contain(this->_anchor_regions, line.b)) {
@ -129,8 +130,12 @@ bool BridgeDetector::detect_angle(double bridge_direction_override)
double len = line.length();
total_length += len;
max_length = std::max(max_length, len);
archored_line_num++;
}
}
}
if (clipped_lines.size() > 0 && archored_line_num > 0) {
candidates[i_angle].archored_percent = (double)archored_line_num / (double)clipped_lines.size();
}
}
if (total_length == 0.)
continue;
@ -155,7 +160,7 @@ bool BridgeDetector::detect_angle(double bridge_direction_override)
// if any other direction is within extrusion width of coverage, prefer it if shorter
// TODO: There are two options here - within width of the angle with most coverage, or within width of the currently perferred?
size_t i_best = 0;
for (size_t i = 1; i < candidates.size() && candidates[i_best].coverage - candidates[i].coverage < this->spacing; ++ i)
for (size_t i = 1; i < candidates.size() && abs(candidates[i_best].archored_percent - candidates[i].archored_percent) < EPSILON; ++ i)
if (candidates[i].max_length < candidates[i_best].max_length)
i_best = i;

View file

@ -44,15 +44,16 @@ private:
void initialize();
struct BridgeDirection {
BridgeDirection(double a = -1.) : angle(a), coverage(0.), max_length(0.) {}
BridgeDirection(double a = -1.) : angle(a), coverage(0.), max_length(0.), archored_percent(0.){}
// the best direction is the one causing most lines to be bridged (thus most coverage)
bool operator<(const BridgeDirection &other) const {
// Initial sort by coverage only - comparator must obey strict weak ordering
return this->coverage > other.coverage;
return this->archored_percent > other.archored_percent;
};
double angle;
double coverage;
double max_length;
double archored_percent;
};
// Get possible briging direction candidates.

View file

@ -876,6 +876,8 @@ static ExPolygons outer_inner_brim_area(const Print& print,
brimToWrite.insert({ objectWithExtruder.first, {true,true} });
ExPolygons objectIslands;
auto bedPoly = Model::getBedPolygon();
auto bedExPoly = diff_ex((offset(bedPoly, scale_(30.), jtRound, SCALED_RESOLUTION)), { bedPoly });
for (unsigned int extruderNo : printExtruders) {
++extruderNo;
@ -1036,7 +1038,9 @@ static ExPolygons outer_inner_brim_area(const Print& print,
}
}
}
for (const PrintObject* object : print.objects())
if (!bedExPoly.empty())
no_brim_area.push_back(bedExPoly.front());
for (const PrintObject* object : print.objects())
if (brimAreaMap.find(object->id()) != brimAreaMap.end()) {
brimAreaMap[object->id()] = diff_ex(brimAreaMap[object->id()], no_brim_area);

View file

@ -35,6 +35,8 @@ set(lisbslic3r_sources
clipper.hpp
ClipperUtils.cpp
ClipperUtils.hpp
Clipper2Utils.cpp
Clipper2Utils.hpp
Config.cpp
Config.hpp
CurveAnalyzer.cpp
@ -108,6 +110,8 @@ set(lisbslic3r_sources
Format/STL.hpp
Format/SL1.hpp
Format/SL1.cpp
Format/svg.hpp
Format/svg.cpp
GCode/ThumbnailData.cpp
GCode/ThumbnailData.hpp
GCode/CoolingBuffer.cpp
@ -474,6 +478,7 @@ target_link_libraries(libslic3r
PNG::PNG
ZLIB::ZLIB
${OCCT_LIBS}
Clipper2
)
if(NOT WIN32)

View file

@ -0,0 +1,60 @@
#include "Clipper2Utils.hpp"
namespace Slic3r {
//BBS: FIXME
Slic3r::Polylines Paths64_to_polylines(const Clipper2Lib::Paths64& in)
{
Slic3r::Polylines out;
out.reserve(in.size());
for (const Clipper2Lib::Path64& path64 : in) {
Slic3r::Points points;
points.reserve(path64.size());
for (const Clipper2Lib::Point64& point64 : path64)
points.emplace_back(std::move(Slic3r::Point(point64.x, point64.y)));
out.emplace_back(std::move(Slic3r::Polyline(points)));
}
return out;
}
//BBS: FIXME
template <typename T>
Clipper2Lib::Paths64 Slic3rPoints_to_Paths64(const std::vector<T>& in)
{
Clipper2Lib::Paths64 out;
out.reserve(in.size());
for (const T item: in) {
Clipper2Lib::Path64 path;
path.reserve(item.size());
for (const Slic3r::Point& point : item.points)
path.emplace_back(std::move(Clipper2Lib::Point64(point.x(), point.y())));
out.emplace_back(std::move(path));
}
return out;
}
Polylines _clipper2_pl_open(Clipper2Lib::ClipType clipType, const Slic3r::Polylines& subject, const Slic3r::Polygons& clip)
{
Clipper2Lib::Clipper64 c;
c.AddOpenSubject(Slic3rPoints_to_Paths64(subject));
c.AddClip(Slic3rPoints_to_Paths64(clip));
Clipper2Lib::ClipType ct = clipType;
Clipper2Lib::FillRule fr = Clipper2Lib::FillRule::NonZero;
Clipper2Lib::Paths64 solution, solution_open;
c.Execute(ct, fr, solution, solution_open);
Slic3r::Polylines out;
out.reserve(solution.size() + solution_open.size());
polylines_append(out, std::move(Paths64_to_polylines(solution)));
polylines_append(out, std::move(Paths64_to_polylines(solution_open)));
return out;
}
Slic3r::Polylines intersection_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip)
{ return _clipper2_pl_open(Clipper2Lib::ClipType::Intersection, subject, clip); }
Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip)
{ return _clipper2_pl_open(Clipper2Lib::ClipType::Difference, subject, clip); }
}

View file

@ -0,0 +1,17 @@
#ifndef slic3r_Clipper2Utils_hpp_
#define slic3r_Clipper2Utils_hpp_
#include "libslic3r.h"
#include "clipper2/clipper.h"
#include "Polygon.hpp"
#include "Polyline.hpp"
namespace Slic3r {
Slic3r::Polylines intersection_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip);
Slic3r::Polylines diff_pl_2(const Slic3r::Polylines& subject, const Slic3r::Polygons& clip);
}
#endif

View file

@ -534,7 +534,7 @@ bool ConfigBase::set_deserialize_nothrow(const t_config_option_key &opt_key_src,
this->handle_legacy(opt_key, value);
if (opt_key.empty()) {
// Ignore the option.
//BBS: record these options, keep only one repeated opt_key
//BBS: record these options, keep only one repeated opt_key
auto iter = std::find(substitutions_ctxt.unrecogized_keys.begin(), substitutions_ctxt.unrecogized_keys.end(), opt_key_src);
if (iter == substitutions_ctxt.unrecogized_keys.end())
substitutions_ctxt.unrecogized_keys.push_back(opt_key_src);
@ -615,7 +615,7 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con
if (! success && substitutions_ctxt.rule != ForwardCompatibilitySubstitutionRule::Disable &&
// Only allow substitutions of an enum value by another enum value or a boolean value with an enum value.
// That means, we expect enum values being added in the future and possibly booleans being converted to enums.
(optdef->type == coEnum || optdef->type == coBool) && ConfigHelpers::looks_like_enum_value(value)) {
(optdef->type == coEnum || optdef->type == coEnums || optdef->type == coBool) /*&& ConfigHelpers::looks_like_enum_value(value)*/) {
// Deserialize failed, try to substitute with a default value.
//assert(substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::Enable || substitutions_ctxt.rule == ForwardCompatibilitySubstitutionRule::EnableSilent);
if (optdef->type == coBool)
@ -757,6 +757,9 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
if (boost::iequals(it.key(),BBL_JSON_KEY_VERSION)) {
key_values.emplace(BBL_JSON_KEY_VERSION, it.value());
}
else if (boost::iequals(it.key(), BBL_JSON_KEY_IS_CUSTOM)) {
key_values.emplace(BBL_JSON_KEY_IS_CUSTOM, it.value());
}
else if (boost::iequals(it.key(), BBL_JSON_KEY_NAME)) {
key_values.emplace(BBL_JSON_KEY_NAME, it.value());
}
@ -1209,14 +1212,15 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo
}
//BBS: add json support
void ConfigBase::save_to_json(const std::string &file, const std::string &name, const std::string &from, const std::string &version) const
void ConfigBase::save_to_json(const std::string &file, const std::string &name, const std::string &from, const std::string &version, const std::string is_custom) const
{
json j;
//record the headers
j[BBL_JSON_KEY_VERSION] = version;
j[BBL_JSON_KEY_NAME] = name;
j[BBL_JSON_KEY_FROM] = from;
if (!is_custom.empty())
j[BBL_JSON_KEY_IS_CUSTOM] = is_custom;
//record all the key-values
for (const std::string &opt_key : this->keys())

View file

@ -1701,12 +1701,12 @@ public:
while (std::getline(is, item_str, ',')) {
boost::trim(item_str);
if (item_str == "nil") {
throw ConfigurationError("Deserializing nil into a non-nullable object");
return false;
}
else {
auto it = this->keys_map->find(item_str);
if (it == this->keys_map->end())
throw ConfigurationError(std::string("Unknown enum type: ") + item_str);
return false;
this->values.push_back(it->second);
}
}
@ -2139,7 +2139,7 @@ public:
void save(const std::string &file) const;
//BBS: add json support
void save_to_json(const std::string &file, const std::string &name, const std::string &from, const std::string &version) const;
void save_to_json(const std::string &file, const std::string &name, const std::string &from, const std::string &version, const std::string is_custom = "") const;
// Set all the nullable values to nils.
void null_nullables();

View file

@ -92,6 +92,12 @@ bool ExPolygon::contains(const Line &line) const
bool ExPolygon::contains(const Polyline &polyline) const
{
BoundingBox bbox1 = get_extents(*this);
BoundingBox bbox2 = get_extents(polyline);
bbox2.inflated(1);
if (!bbox1.overlap(bbox2))
return false;
return diff_pl(polyline, *this).empty();
}

View file

@ -24,7 +24,17 @@ SLIC3R_DERIVE_EXCEPTION(HostNetworkError, IOError);
SLIC3R_DERIVE_EXCEPTION(ExportError, CriticalException);
SLIC3R_DERIVE_EXCEPTION(PlaceholderParserError, RuntimeError);
// Runtime exception produced by Slicer. Such exception cancels the slicing process and it shall be shown in notifications.
SLIC3R_DERIVE_EXCEPTION(SlicingError, Exception);
//SLIC3R_DERIVE_EXCEPTION(SlicingError, Exception);
class SlicingError : public Exception
{
public:
using Exception::Exception;
SlicingError(std::string const &msg, size_t objectId) : Exception(msg), objectId_(objectId) {}
size_t objectId() const { return objectId_; }
private:
size_t objectId_ = 0;
};
#undef SLIC3R_DERIVE_EXCEPTION
} // namespace Slic3r

View file

@ -147,6 +147,7 @@ public:
// Height of the extrusion, used for visualization purposes.
float height;
ExtrusionPath() : mm3_per_mm(-1), width(-1), height(-1), m_role(erNone), m_no_extrusion(false) {}
ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), m_role(role), m_no_extrusion(false) {}
ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height, bool no_extrusion = false) : mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role), m_no_extrusion(no_extrusion) {}
ExtrusionPath(int overhang_degree, int curve_degree, ExtrusionRole role, double mm3_per_mm, float width, float height) : overhang_degree(overhang_degree), curve_degree(curve_degree), mm3_per_mm(mm3_per_mm), width(width), height(height), m_role(role) {}
@ -235,6 +236,8 @@ public:
void simplify_by_fitting_arc(double tolerance);
//BBS:
bool is_force_no_extrusion() const { return m_no_extrusion; }
void set_force_no_extrusion(bool no_extrusion) { m_no_extrusion = no_extrusion; }
void set_extrusion_role(ExtrusionRole extrusion_role) { m_role = extrusion_role; }
private:
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
@ -251,7 +254,7 @@ class ExtrusionMultiPath : public ExtrusionEntity
{
public:
ExtrusionPaths paths;
ExtrusionMultiPath() {}
ExtrusionMultiPath(const ExtrusionMultiPath &rhs) : paths(rhs.paths) {}
ExtrusionMultiPath(ExtrusionMultiPath &&rhs) : paths(std::move(rhs.paths)) {}
@ -288,7 +291,7 @@ public:
double min_mm3_per_mm() const override;
Polyline as_polyline() const override;
void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
void collect_points(Points &dst) const override {
void collect_points(Points &dst) const override {
size_t n = std::accumulate(paths.begin(), paths.end(), 0, [](const size_t n, const ExtrusionPath &p){ return n + p.polyline.size(); });
dst.reserve(dst.size() + n);
for (const ExtrusionPath &p : this->paths)
@ -302,11 +305,11 @@ class ExtrusionLoop : public ExtrusionEntity
{
public:
ExtrusionPaths paths;
ExtrusionLoop(ExtrusionLoopRole role = elrDefault) : m_loop_role(role) {}
ExtrusionLoop(const ExtrusionPaths &paths, ExtrusionLoopRole role = elrDefault) : paths(paths), m_loop_role(role) {}
ExtrusionLoop(ExtrusionPaths &&paths, ExtrusionLoopRole role = elrDefault) : paths(std::move(paths)), m_loop_role(role) {}
ExtrusionLoop(const ExtrusionPath &path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role)
ExtrusionLoop(const ExtrusionPath &path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role)
{ this->paths.push_back(path); }
ExtrusionLoop(const ExtrusionPath &&path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role)
{ this->paths.emplace_back(std::move(path)); }
@ -339,6 +342,7 @@ public:
bool has_overhang_point(const Point &point) const;
ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
ExtrusionLoopRole loop_role() const { return m_loop_role; }
void set_loop_role(ExtrusionLoopRole role) { m_loop_role = role; }
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
// Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps.
void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override;
@ -354,7 +358,7 @@ public:
double min_mm3_per_mm() const override;
Polyline as_polyline() const override { return this->polygon().split_at_first_point(); }
void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); }
void collect_points(Points &dst) const override {
void collect_points(Points &dst) const override {
size_t n = std::accumulate(paths.begin(), paths.end(), 0, [](const size_t n, const ExtrusionPath &p){ return n + p.polyline.size(); });
dst.reserve(dst.size() + n);
for (const ExtrusionPath &p : this->paths)

View file

@ -46,6 +46,8 @@ struct SurfaceFillParams
// 1000mm is roughly the maximum length line that fits into a 32bit coord_t.
float anchor_length = 1000.f;
float anchor_length_max = 1000.f;
//BBS
bool with_loop = false;
// width, height of extrusion, nozzle diameter, is bridge
// For the output, for fill generator.
@ -77,6 +79,7 @@ struct SurfaceFillParams
// RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust);
RETURN_COMPARE_NON_EQUAL(anchor_length);
RETURN_COMPARE_NON_EQUAL(anchor_length_max);
RETURN_COMPARE_NON_EQUAL(with_loop);
RETURN_COMPARE_NON_EQUAL(flow.width());
RETURN_COMPARE_NON_EQUAL(flow.height());
RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter());
@ -97,6 +100,7 @@ struct SurfaceFillParams
// this->dont_adjust == rhs.dont_adjust &&
this->anchor_length == rhs.anchor_length &&
this->anchor_length_max == rhs.anchor_length_max &&
this->with_loop == rhs.with_loop &&
this->flow == rhs.flow &&
this->extrusion_role == rhs.extrusion_role;
}
@ -147,6 +151,8 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
params.extruder = layerm.region().extruder(extrusion_role);
params.pattern = region_config.sparse_infill_pattern.value;
params.density = float(region_config.sparse_infill_density);
//BBS
params.with_loop = surface.surface_type == stInternalWithLoop;
if (surface.is_solid()) {
params.density = 100.f;
@ -363,8 +369,8 @@ std::vector<SurfaceFill> group_fills(const Layer &layer)
surface_fills.back().region_id = surface_fills[i].region_id;
surface_fills.back().surface.surface_type = stInternalSolid;
surface_fills.back().surface.thickness = surface_fills[i].surface.thickness;
surface_fills.back().region_id_group = surface_fills[i].region_id_group;
surface_fills.back().no_overlap_expolygons = surface_fills[i].no_overlap_expolygons;
surface_fills.back().region_id_group = surface_fills[i].region_id_group;
surface_fills.back().no_overlap_expolygons = surface_fills[i].no_overlap_expolygons;
for (size_t j = 0; j < narrow_expolygons_index.size(); j++) {
// BBS: move the narrow expolygons to new surface_fills.back();
surface_fills.back().expolygons.emplace_back(std::move(surface_fills[i].expolygons[narrow_expolygons_index[j]]));
@ -478,11 +484,12 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.extrusion_role = surface_fill.params.extrusion_role;
params.using_internal_flow = using_internal_flow;
params.no_extrusion_overlap = surface_fill.params.overlap;
params.with_loop = surface_fill.params.with_loop;
LayerRegion* layerm = this->m_regions[surface_fill.region_id];
params.config = &layerm->region().config();
for (ExPolygon& expoly : surface_fill.expolygons) {
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly});
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly});
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
f->spacing = surface_fill.params.spacing;
surface_fill.surface.expolygon = std::move(expoly);

View file

@ -29,7 +29,7 @@
namespace Slic3r {
//BBS: 0% of sparse_infill_line_width, no anchor at the start of sparse infill
float Fill::infill_anchor = 0;
float Fill::infill_anchor = 400;
//BBS: 20mm
float Fill::infill_anchor_max = 20;
@ -54,9 +54,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
case ipAdaptiveCubic: return new FillAdaptive::Filler();
case ipSupportCubic: return new FillAdaptive::Filler();
case ipSupportBase: return new FillSupportBase();
#if HAS_LIGHTNING_INFILL
case ipLightning: return new FillLightning::Filler();
#endif // HAS_LIGHTNING_INFILL
// BBS: for internal solid infill only
case ipConcentricInternal: return new FillConcentricInternal();
// BBS: for bottom and top surface only
@ -122,13 +120,57 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para
{
Polylines polylines;
ThickPolylines thick_polylines;
try {
if (params.use_arachne)
thick_polylines = this->fill_surface_arachne(surface, params);
else
polylines = this->fill_surface(surface, params);
if (!params.with_loop) {
try {
if (params.use_arachne)
thick_polylines = this->fill_surface_arachne(surface, params);
else
polylines = this->fill_surface(surface, params);
}
catch (InfillFailedException&) {}
}
//BBS: add handling for infill pattern with loop
else {
Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(scale_(this->overlap - 0.5 * this->spacing)));
Polylines loop_polylines = to_polylines(expp);
{
//BBS: clip the loop
size_t j = 0;
for (size_t i = 0; i < loop_polylines.size(); ++i) {
loop_polylines[i].clip_end(this->loop_clipping);
if (loop_polylines[i].is_valid()) {
if (j < i)
loop_polylines[j] = std::move(loop_polylines[i]);
++j;
}
}
if (j < loop_polylines.size())
loop_polylines.erase(loop_polylines.begin() + int(j), loop_polylines.end());
}
if (!loop_polylines.empty()) {
if (params.use_arachne)
append(thick_polylines, to_thick_polylines(std::move(loop_polylines), scaled<coord_t>(this->spacing)));
else
append(polylines, std::move(loop_polylines));
expp = offset_ex(expp, float(scale_(0 - 0.5 * this->spacing)));
} else {
//BBS: the area is too narrow to place a loop, return to original expolygon
expp = { surface->expolygon };
}
Surface temp_surface = *surface;
for (ExPolygon& ex : expp) {
temp_surface.expolygon = ex;
try {
if (params.use_arachne)
append(thick_polylines, std::move(this->fill_surface_arachne(&temp_surface, params)));
else
append(polylines, std::move(this->fill_surface(&temp_surface, params)));
}
catch (InfillFailedException&) {}
}
}
catch (InfillFailedException&) {}
if (!polylines.empty() || !thick_polylines.empty()) {
// calculate actual flow from spacing (which might have been adjusted by the infill

View file

@ -67,6 +67,8 @@ struct FillParams
bool use_arachne{ false };
// Layer height for Concentric infill with Arachne.
coordf_t layer_height { 0.f };
//BBS
bool with_loop { false };
// BBS
Flow flow;

View file

@ -5,6 +5,7 @@
#include "Arachne/WallToolPaths.hpp"
#include "FillConcentric.hpp"
#include <libslic3r/ShortestPath.hpp>
namespace Slic3r {
@ -133,6 +134,8 @@ void FillConcentric::_fill_surface_single(const FillParams& params,
}
if (j < thick_polylines_out.size())
thick_polylines_out.erase(thick_polylines_out.begin() + int(j), thick_polylines_out.end());
reorder_by_shortest_traverse(thick_polylines_out);
}
else {
Polylines polylines;

View file

@ -5,6 +5,7 @@
#include "Arachne/WallToolPaths.hpp"
#include "FillConcentricInternal.hpp"
#include <libslic3r/ShortestPath.hpp>
namespace Slic3r {
@ -22,7 +23,7 @@ void FillConcentricInternal::fill_surface_extrusion(const Surface* surface, cons
coord_t min_spacing = params.flow.scaled_spacing();
coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / min_spacing + 1;
Polygons polygons = offset(expolygon, 0);
Polygons polygons = to_polygons(expolygon);
double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end());
Arachne::WallToolPathsParams input_params;
@ -76,6 +77,8 @@ void FillConcentricInternal::fill_surface_extrusion(const Surface* surface, cons
}
if (j < thick_polylines_out.size())
thick_polylines_out.erase(thick_polylines_out.begin() + int(j), thick_polylines_out.end());
reorder_by_shortest_traverse(thick_polylines_out);
}
ExtrusionEntityCollection *coll_nosort = new ExtrusionEntityCollection();

View file

@ -26,9 +26,9 @@ void GeneratorDeleter::operator()(Generator *p) {
delete p;
}
GeneratorPtr build_generator(const PrintObject &print_object)
GeneratorPtr build_generator(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback)
{
return GeneratorPtr(new Generator(print_object));
return GeneratorPtr(new Generator(print_object, throw_on_cancel_callback));
}
} // namespace Slic3r::FillAdaptive

View file

@ -3,13 +3,6 @@
#include "FillBase.hpp"
/*
* A few modifications based on dba1179(2022.06.10) from Prusa, mainly in Generator.hpp and .cpp:
* 1. delete the second parameter(a throw back function) of Generator(), since I didnt find corresponding throw back function in BBS code
* 2. those codes that call the functions above
* 3. add codes of generating lightning in TreeSupport.cpp
*/
namespace Slic3r {
class PrintObject;
@ -21,7 +14,7 @@ class Generator;
struct GeneratorDeleter { void operator()(Generator *p); };
using GeneratorPtr = std::unique_ptr<Generator, GeneratorDeleter>;
GeneratorPtr build_generator(const PrintObject &print_object);
GeneratorPtr build_generator(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback);
class Filler : public Slic3r::Fill
{

View file

@ -43,7 +43,8 @@ DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outl
m_supporting_radius2 = Slic3r::sqr(int64_t(radius));
// Sample source polygons with a regular grid sampling pattern.
const BoundingBox overhang_bbox = get_extents(current_overhang);
for (const ExPolygon &expoly : union_ex(current_overhang)) {
ExPolygons expolys = offset2_ex(union_ex(current_overhang), -m_cell_size / 2, m_cell_size / 2); // remove dangling lines which causes sample_grid_pattern crash (fails the OUTER_LOW assertions)
for (const ExPolygon &expoly : expolys) {
const Points sampled_points = sample_grid_pattern(expoly, m_cell_size, overhang_bbox);
const size_t unsupported_points_prev_size = m_unsupported_points.size();
m_unsupported_points.resize(unsupported_points_prev_size + sampled_points.size());
@ -94,10 +95,6 @@ DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outl
void DistanceField::update(const Point& to_node, const Point& added_leaf)
{
std::ofstream out1("z:/misc/lightning.txt", std::ios::app);
out1 << m_unsupported_points.size() << std::endl;
out1.close();
Vec2d v = (added_leaf - to_node).cast<double>();
auto l2 = v.squaredNorm();
Vec2d extent = Vec2d(-v.y(), v.x()) * m_supporting_radius / sqrt(l2);

View file

@ -63,7 +63,7 @@ Slic3r::SVG draw_two_overhangs_to_svg(size_t ts_layer, const ExPolygons& overhan
namespace Slic3r::FillLightning {
Generator::Generator(const PrintObject &print_object)
Generator::Generator(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback)
{
const PrintConfig &print_config = print_object.print()->config();
const PrintObjectConfig &object_config = print_object.config();
@ -87,11 +87,11 @@ Generator::Generator(const PrintObject &print_object)
m_prune_length = coord_t(layer_thickness * std::tan(lightning_infill_prune_angle));
m_straightening_max_distance = coord_t(layer_thickness * std::tan(lightning_infill_straightening_angle));
generateInitialInternalOverhangs(print_object);
generateTrees(print_object);
generateInitialInternalOverhangs(print_object, throw_on_cancel_callback);
generateTrees(print_object, throw_on_cancel_callback);
}
Generator::Generator(PrintObject* m_object, std::vector<Polygons>& contours, std::vector<Polygons>& overhangs, float density)
Generator::Generator(PrintObject* m_object, std::vector<Polygons>& contours, std::vector<Polygons>& overhangs, const std::function<void()> &throw_on_cancel_callback, float density)
{
const PrintConfig &print_config = m_object->print()->config();
const PrintObjectConfig &object_config = m_object->config();
@ -120,7 +120,7 @@ Generator::Generator(PrintObject* m_object, std::vector<Polygons>& contours, std
m_overhang_per_layer = overhangs;
generateTreesforSupport(contours);
generateTreesforSupport(contours, throw_on_cancel_callback);
//for (size_t i = 0; i < overhangs.size(); i++)
//{
@ -130,13 +130,14 @@ Generator::Generator(PrintObject* m_object, std::vector<Polygons>& contours, std
//}
}
void Generator::generateInitialInternalOverhangs(const PrintObject &print_object)
void Generator::generateInitialInternalOverhangs(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback)
{
m_overhang_per_layer.resize(print_object.layers().size());
Polygons infill_area_above;
//Iterate from top to bottom, to subtract the overhang areas above from the overhang areas on the layer below, to get only overhang in the top layer where it is overhanging.
for (int layer_nr = int(print_object.layers().size()) - 1; layer_nr >= 0; --layer_nr) {
throw_on_cancel_callback();
Polygons infill_area_here;
for (const LayerRegion* layerm : print_object.get_layer(layer_nr)->regions())
for (const Surface& surface : layerm->fill_surfaces.surfaces)
@ -157,7 +158,7 @@ const Layer& Generator::getTreesForLayer(const size_t& layer_id) const
return m_lightning_layers[layer_id];
}
void Generator::generateTrees(const PrintObject &print_object)
void Generator::generateTrees(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback)
{
m_lightning_layers.resize(print_object.layers().size());
bboxs.resize(print_object.layers().size());
@ -165,6 +166,7 @@ void Generator::generateTrees(const PrintObject &print_object)
// For-each layer from top to bottom:
for (int layer_id = int(print_object.layers().size()) - 1; layer_id >= 0; layer_id--) {
throw_on_cancel_callback();
for (const LayerRegion *layerm : print_object.get_layer(layer_id)->regions())
for (const Surface &surface : layerm->fill_surfaces.surfaces)
if (surface.surface_type == stInternal || surface.surface_type == stInternalVoid)
@ -178,6 +180,7 @@ void Generator::generateTrees(const PrintObject &print_object)
// For-each layer from top to bottom:
for (int layer_id = int(top_layer_id); layer_id >= 0; layer_id--) {
throw_on_cancel_callback();
Layer &current_lightning_layer = m_lightning_layers[layer_id];
const Polygons &current_outlines = infill_outlines[layer_id];
const BoundingBox &current_outlines_bbox = get_extents(current_outlines);
@ -187,7 +190,7 @@ void Generator::generateTrees(const PrintObject &print_object)
// register all trees propagated from the previous layer as to-be-reconnected
std::vector<NodeSPtr> to_be_reconnected_tree_roots = current_lightning_layer.tree_roots;
current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius);
current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius, throw_on_cancel_callback);
current_lightning_layer.reconnectRoots(to_be_reconnected_tree_roots, current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius);
// Initialize trees for next lower layer from the current one.
@ -211,8 +214,10 @@ void Generator::generateTrees(const PrintObject &print_object)
}
}
void Generator::generateTreesforSupport(std::vector<Polygons>& contours)
void Generator::generateTreesforSupport(std::vector<Polygons>& contours, const std::function<void()> &throw_on_cancel_callback)
{
if (contours.empty()) return;
m_lightning_layers.resize(contours.size());
bboxs.resize(contours.size());
@ -223,6 +228,7 @@ void Generator::generateTreesforSupport(std::vector<Polygons>& contours)
// For-each layer from top to bottom:
for (int layer_id = int(top_layer_id); layer_id >= 0; layer_id--) {
throw_on_cancel_callback();
Layer& current_lightning_layer = m_lightning_layers[layer_id];
const Polygons& current_outlines = contours[layer_id];
const BoundingBox& current_outlines_bbox = get_extents(current_outlines);
@ -232,7 +238,7 @@ void Generator::generateTreesforSupport(std::vector<Polygons>& contours)
// register all trees propagated from the previous layer as to-be-reconnected
std::vector<NodeSPtr> to_be_reconnected_tree_roots = current_lightning_layer.tree_roots;
current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius);
current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius, throw_on_cancel_callback);
current_lightning_layer.reconnectRoots(to_be_reconnected_tree_roots, current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius);
// Initialize trees for next lower layer from the current one.

View file

@ -44,7 +44,7 @@ public:
* Lightning Infill for the infill areas in that mesh. The infill areas must
* already be calculated at this point.
*/
explicit Generator(const PrintObject &print_object);
explicit Generator(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback);
/*!
* Get a tree of paths generated for a certain layer of the mesh.
@ -61,7 +61,7 @@ public:
float infilll_extrusion_width() const { return m_infill_extrusion_width; }
Generator(PrintObject* m_object, std::vector<Polygons>& contours, std::vector<Polygons>& overhangs, float density = 0.15);
Generator(PrintObject* m_object, std::vector<Polygons>& contours, std::vector<Polygons>& overhangs, const std::function<void()> &throw_on_cancel_callback, float density = 0.15);
protected:
/*!
@ -72,13 +72,13 @@ protected:
* only when support is generated. For this pattern, we also need to
* generate overhang areas for the inside of the model.
*/
void generateInitialInternalOverhangs(const PrintObject &print_object);
void generateInitialInternalOverhangs(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback);
/*!
* Calculate the tree structure of all layers.
*/
void generateTrees(const PrintObject &print_object);
void generateTreesforSupport(std::vector<Polygons>& contours);
void generateTrees(const PrintObject &print_object, const std::function<void()> &throw_on_cancel_callback);
void generateTreesforSupport(std::vector<Polygons>& contours, const std::function<void()> &throw_on_cancel_callback);
float m_infill_extrusion_width;

View file

@ -48,10 +48,12 @@ void Layer::generateNewTrees
const BoundingBox& current_outlines_bbox,
const EdgeGrid::Grid& outlines_locator,
const coord_t supporting_radius,
const coord_t wall_supporting_radius
const coord_t wall_supporting_radius,
const std::function<void()> &throw_on_cancel_callback
)
{
DistanceField distance_field(supporting_radius, current_outlines, current_outlines_bbox, current_overhang);
throw_on_cancel_callback();
SparseNodeGrid tree_node_locator;
fillLocator(tree_node_locator, current_outlines_bbox);
@ -61,6 +63,7 @@ void Layer::generateNewTrees
size_t unsupported_cell_idx = 0;
Point unsupported_location;
while (distance_field.tryGetNextPoint(&unsupported_location, &unsupported_cell_idx, unsupported_cell_idx)) {
throw_on_cancel_callback();
GroundingLocation grounding_loc = getBestGroundingLocation(
unsupported_location, current_outlines, current_outlines_bbox, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator);

View file

@ -44,7 +44,8 @@ public:
const BoundingBox& current_outlines_bbox,
const EdgeGrid::Grid& outline_locator,
coord_t supporting_radius,
coord_t wall_supporting_radius
coord_t wall_supporting_radius,
const std::function<void()> &throw_on_cancel_callback
);
/*! Determine & connect to connection point in tree/outline.

View file

@ -661,7 +661,7 @@ ModelVolumeType type_from_string(const std::string &s)
: m_version(0)
, m_check_version(false)
, m_xml_parser(nullptr)
, m_model(nullptr)
, m_model(nullptr)
, m_unit_factor(1.0f)
, m_curr_metadata_name("")
, m_curr_characters("")
@ -934,6 +934,18 @@ ModelVolumeType type_from_string(const std::string &s)
++object_idx;
}
//BBS: copy object isteadof instance
int object_size = model.objects.size();
for (int obj_index = 0; obj_index < object_size; obj_index ++) {
ModelObject* object = model.objects[obj_index];
while (object->instances.size() > 1) {
ModelObject* new_model_object = model.add_object(*object);
new_model_object->clear_instances();
new_model_object->add_instance(*object->instances.back());
object->delete_last_instance();
}
}
// // fixes the min z of the model if negative
// model.adjust_min_z();
@ -1005,8 +1017,8 @@ ModelVolumeType type_from_string(const std::string &s)
}
void _3MF_Importer::_extract_print_config_from_archive(
mz_zip_archive& archive, const mz_zip_archive_file_stat& stat,
DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions,
mz_zip_archive& archive, const mz_zip_archive_file_stat& stat,
DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions,
const std::string& archive_filename)
{
if (stat.m_uncomp_size > 0) {
@ -1227,7 +1239,7 @@ ModelVolumeType type_from_string(const std::string &s)
}
}
}
void _3MF_Importer::_extract_sla_drain_holes_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
{
if (stat.m_uncomp_size > 0) {
@ -1237,13 +1249,13 @@ ModelVolumeType type_from_string(const std::string &s)
add_error("Error while reading sla support points data to buffer");
return;
}
if (buffer.back() == '\n')
buffer.pop_back();
std::vector<std::string> objects;
boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off);
// Info on format versioning - see 3mf.hpp
int version = 0;
std::string key("drain_holes_format_version=");
@ -1252,38 +1264,38 @@ ModelVolumeType type_from_string(const std::string &s)
version = std::stoi(objects[0]);
objects.erase(objects.begin()); // pop the header
}
for (const std::string& object : objects) {
std::vector<std::string> object_data;
boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off);
if (object_data.size() != 2) {
add_error("Error while reading object data");
continue;
}
std::vector<std::string> object_data_id;
boost::split(object_data_id, object_data[0], boost::is_any_of("="), boost::token_compress_off);
if (object_data_id.size() != 2) {
add_error("Error while reading object id");
continue;
}
int object_id = std::atoi(object_data_id[1].c_str());
if (object_id == 0) {
add_error("Found invalid object id");
continue;
}
IdToSlaDrainHolesMap::iterator object_item = m_sla_drain_holes.find(object_id);
if (object_item != m_sla_drain_holes.end()) {
add_error("Found duplicated SLA drain holes");
continue;
}
std::vector<std::string> object_data_points;
boost::split(object_data_points, object_data[1], boost::is_any_of(" "), boost::token_compress_off);
sla::DrainHoles sla_drain_holes;
if (version == 1) {
@ -1306,7 +1318,7 @@ ModelVolumeType type_from_string(const std::string &s)
hole.pos += hole.normal.normalized();
hole.height -= 1.f;
}
if (!sla_drain_holes.empty())
m_sla_drain_holes.insert({ object_id, sla_drain_holes });
}
@ -1394,11 +1406,11 @@ ModelVolumeType type_from_string(const std::string &s)
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 ...
// 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 :
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 : "";
@ -2113,7 +2125,7 @@ ModelVolumeType type_from_string(const std::string &s)
tri_id -= min_id;
}
if (m_prusaslicer_generator_version &&
if (m_prusaslicer_generator_version &&
*m_prusaslicer_generator_version >= *Semver::parse("2.4.0-alpha1") &&
*m_prusaslicer_generator_version < *Semver::parse("2.4.0-alpha3"))
// PrusaSlicer 2.4.0-alpha2 contained a bug, where all vertices of a single object were saved for each volume the object contained.
@ -2227,7 +2239,7 @@ ModelVolumeType type_from_string(const std::string &s)
if (importer != nullptr)
importer->_handle_start_config_xml_element(name, attributes);
}
void XMLCALL _3MF_Importer::_handle_end_config_xml_element(void* userData, const char* name)
{
_3MF_Importer* importer = (_3MF_Importer*)userData;
@ -2340,7 +2352,7 @@ ModelVolumeType type_from_string(const std::string &s)
}
}
// Adds relationships file ("_rels/.rels").
// Adds relationships file ("_rels/.rels").
// The content of this file is the same for each PrusaSlicer 3mf.
// The relationshis file contains a reference to the geometry file "3D/3dmodel.model", the name was chosen to be compatible with CURA.
if (!_add_relationships_file_to_archive(archive)) {
@ -2384,13 +2396,13 @@ ModelVolumeType type_from_string(const std::string &s)
boost::filesystem::remove(filename);
return false;
}
if (!_add_sla_drain_holes_file_to_archive(archive, model)) {
close_zip_writer(&archive);
boost::filesystem::remove(filename);
return false;
}
// Adds custom gcode per height file ("Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml").
// All custom gcode per height of whole Model are stored here
@ -2502,11 +2514,11 @@ ModelVolumeType type_from_string(const std::string &s)
bool _3MF_Exporter::_add_model_file_to_archive(const std::string& filename, mz_zip_archive& archive, const Model& model, IdToObjectDataMap& objects_data)
{
mz_zip_writer_staged_context context;
if (!mz_zip_writer_add_staged_open(&archive, &context, MODEL_FILE.c_str(),
m_zip64 ?
if (!mz_zip_writer_add_staged_open(&archive, &context, MODEL_FILE.c_str(),
m_zip64 ?
// Maximum expected and allowed 3MF file size is 16GiB.
// This switches the ZIP file to a 64bit mode, which adds a tiny bit of overhead to file records.
(uint64_t(1) << 30) * 16 :
(uint64_t(1) << 30) * 16 :
// Maximum expected 3MF file size is 4GB-1. This is a workaround for interoperability with Windows 10 3D model fixing API, see
// GH issue #6193.
(uint64_t(1) << 32) - 1,
@ -2589,7 +2601,7 @@ ModelVolumeType type_from_string(const std::string &s)
}
stream << "</" << MODEL_TAG << ">\n";
std::string buf = stream.str();
if ((! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) ||
@ -2877,7 +2889,7 @@ ModelVolumeType type_from_string(const std::string &s)
sprintf(buffer, (i == 0) ? "%f" : ";%f", layer_height_profile[i]);
out += buffer;
}
out += "\n";
}
}
@ -2936,8 +2948,8 @@ ModelVolumeType type_from_string(const std::string &s)
boost::replace_all(out, "><option", ">\n <option");
boost::replace_all(out, "></range>", ">\n </range>");
boost::replace_all(out, "></object>", ">\n </object>");
// OR just
boost::replace_all(out, "><", ">\n<");
// OR just
boost::replace_all(out, "><", ">\n<");
}
if (!out.empty()) {
@ -2984,13 +2996,13 @@ ModelVolumeType type_from_string(const std::string &s)
}
return true;
}
bool _3MF_Exporter::_add_sla_drain_holes_file_to_archive(mz_zip_archive& archive, Model& model)
{
assert(is_decimal_separator_point());
const char *const fmt = "object_id=%d|";
std::string out;
unsigned int count = 0;
for (const ModelObject* object : model.objects) {
++count;
@ -3007,7 +3019,7 @@ ModelVolumeType type_from_string(const std::string &s)
if (!drain_holes.empty()) {
out += string_printf(fmt, count);
// Store the layer height profile as a single space separated list.
for (size_t i = 0; i < drain_holes.size(); ++i)
out += string_printf((i == 0 ? "%f %f %f %f %f %f %f %f" : " %f %f %f %f %f %f %f %f"),
@ -3019,15 +3031,15 @@ ModelVolumeType type_from_string(const std::string &s)
drain_holes[i].normal(2),
drain_holes[i].radius,
drain_holes[i].height);
out += "\n";
}
}
if (!out.empty()) {
// Adds version header at the beginning:
out = std::string("drain_holes_format_version=") + std::to_string(drain_holes_format_version) + std::string("\n") + out;
if (!mz_zip_writer_add_mem(&archive, SLA_DRAIN_HOLES_FILE.c_str(), static_cast<const void*>(out.data()), out.length(), mz_uint(MZ_DEFAULT_COMPRESSION))) {
add_error("Unable to add sla support points file to archive");
return false;
@ -3099,7 +3111,7 @@ ModelVolumeType type_from_string(const std::string &s)
if (volume->is_modifier())
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << MODIFIER_KEY << "\" " << VALUE_ATTR << "=\"1\"/>\n";
// stores volume's type (overrides the modifier field above)
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " <<
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << VOLUME_TYPE_KEY << "\" " <<
VALUE_ATTR << "=\"" << ModelVolume::type_to_string(volume->type()) << "\"/>\n";
// stores volume's local matrix
@ -3137,7 +3149,7 @@ ModelVolumeType type_from_string(const std::string &s)
for (const std::string& key : volume->config.keys()) {
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n";
}
// stores mesh's statistics
const RepairedMeshErrors& stats = volume->mesh().stats().repaired_errors;
stream << " <" << MESH_TAG << " ";
@ -3190,12 +3202,12 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
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.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
// 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);
@ -3208,7 +3220,7 @@ bool _3MF_Exporter::_add_custom_gcode_per_print_z_file_to_archive( mz_zip_archiv
// 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)) {

File diff suppressed because it is too large Load diff

View file

@ -57,6 +57,7 @@ struct PlateData
int plate_index;
std::vector<std::pair<int, int>> objects_and_instances;
std::map<int, std::pair<int, int>> obj_inst_map;
std::string gcode_file;
std::string gcode_file_md5;
std::string thumbnail_file;
@ -67,6 +68,8 @@ struct PlateData
std::string gcode_prediction;
std::string gcode_weight;
std::vector<FilamentInfo> slice_filaments_info;
DynamicPrintConfig config;
bool is_support_used {false};
bool is_sliced_valid = false;
bool toolpath_outside {false};
@ -202,7 +205,8 @@ struct StoreParams
//BBS: add plate data list related logic
// add restore logic
// Load the content of a 3mf file into the given model and preset bundle.
extern bool load_bbs_3mf(const char* path, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, Model* model, PlateDataPtrs* plate_data_list, std::vector<Preset*>* project_presets, bool* is_bbl_3mf, Semver* file_version, Import3mfProgressFn proFn = nullptr, LoadStrategy strategy = LoadStrategy::Default, BBLProject *project = nullptr);
extern bool load_bbs_3mf(const char* path, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, Model* model, PlateDataPtrs* plate_data_list, std::vector<Preset*>* project_presets,
bool* is_bbl_3mf, Semver* file_version, Import3mfProgressFn proFn = nullptr, LoadStrategy strategy = LoadStrategy::Default, BBLProject *project = nullptr, int plate_id = 0);
extern std::string bbs_3mf_get_thumbnail(const char * path);

View file

@ -0,0 +1,341 @@
#include "../libslic3r.h"
#include "../Model.hpp"
#include "../TriangleMesh.hpp"
#include "svg.hpp"
#include "nanosvg/nanosvg.h"
#include <string>
#include <boost/log/trivial.hpp>
#include "BRepBuilderAPI_MakeWire.hxx"
#include "BRepBuilderAPI_MakeEdge.hxx"
#include "BRepBuilderAPI_MakeFace.hxx"
#include "BRepPrimAPI_MakePrism.hxx"
#include "BRepBuilderAPI_Transform.hxx"
#include "BRepMesh_IncrementalMesh.hxx"
#include "TopoDS_Face.hxx"
#include "TopExp_Explorer.hxx"
#include "TopoDS.hxx"
#include "BRepExtrema_SelfIntersection.hxx"
namespace Slic3r {
const double STEP_TRANS_CHORD_ERROR = 0.005;
const double STEP_TRANS_ANGLE_RES = 1;
struct Element_Info
{
std::string name;
unsigned int color;
TopoDS_Shape shape;
};
bool is_same_points(gp_Pnt pt1, gp_Pnt pt2) {
return abs(pt1.X() - pt2.X()) < 0.001
&& abs(pt1.Y() - pt2.Y()) < 0.001
&& abs(pt1.Z() - pt2.Z()) < 0.001;
}
struct Point_2D
{
Point_2D(float in_x, float in_y) : x(in_x), y(in_y) {}
float x;
float y;
};
void interp_v2_v2v2(float r[2], const float a[2], const float b[2], const float t)
{
const float s = 1.0f - t;
r[0] = s * a[0] + t * b[0];
r[1] = s * a[1] + t * b[1];
}
void interp_v2_v2v2v2v2_cubic(float p[2], const float v1[2], const float v2[2], const float v3[2], const float v4[2], const float u)
{
float q0[2], q1[2], q2[2], r0[2], r1[2];
interp_v2_v2v2(q0, v1, v2, u);
interp_v2_v2v2(q1, v2, v3, u);
interp_v2_v2v2(q2, v3, v4, u);
interp_v2_v2v2(r0, q0, q1, u);
interp_v2_v2v2(r1, q1, q2, u);
interp_v2_v2v2(p, r0, r1, u);
}
bool is_two_lines_interaction(gp_Pnt pL1, gp_Pnt pL2, gp_Pnt pR1, gp_Pnt pR2) {
Vec3d point1(pL1.X(), pL1.Y(), 0);
Vec3d point2(pL2.X(), pL2.Y(), 0);
Vec3d point3(pR1.X(), pR1.Y(), 0);
Vec3d point4(pR2.X(), pR2.Y(), 0);
Vec3d line1 = point2 - point1;
Vec3d line2 = point4 - point3;
Vec3d line_pos1 = point1 - point3;
Vec3d line_pos2 = point2 - point3;
Vec3d line_pos3 = point3 - point1;
Vec3d line_pos4 = point4 - point1;
Vec3d cross_1 = line2.cross(line_pos1);
Vec3d cross_2 = line2.cross(line_pos2);
Vec3d cross_3 = line1.cross(line_pos3);
Vec3d cross_4 = line1.cross(line_pos4);
return (cross_1.dot(cross_2) < 0) && (cross_3.dot(cross_4) < 0);
}
bool is_profile_self_interaction(std::vector<std::pair<gp_Pnt, gp_Pnt>> profile_line_points)
{
for (int i = 0; i < profile_line_points.size(); ++i) {
for (int j = i + 2; j < profile_line_points.size(); ++j)
if (is_two_lines_interaction(profile_line_points[i].first, profile_line_points[i].second, profile_line_points[j].first, profile_line_points[j].second))
return true;
}
return false;
}
double get_profile_area(std::vector<std::pair<gp_Pnt, gp_Pnt>> profile_line_points)
{
double min_x = 0;
for (auto line_points : profile_line_points) {
if (line_points.first.X() < min_x) min_x = line_points.first.X();
}
double area = 0;
for (auto line_points : profile_line_points) {
bool flag = true;
if (line_points.second.Y() < line_points.first.Y()) flag = false;
area += (line_points.second.X() + line_points.first.X() - 2 * min_x) * (line_points.second.Y() - line_points.first.Y()) / 2;
}
return abs(area);
}
bool get_svg_profile(const char *path, std::vector<Element_Info> &element_infos, std::string& message)
{
NSVGimage *svg_data = nullptr;
svg_data = nsvgParseFromFile(path, "mm", 96.0f);
if (svg_data == nullptr) {
message = "import svg failed: could not open svg.";
return false;
}
if (svg_data->shapes == nullptr) {
message = "import svg failed: could not parse imported svg data.";
return false;
}
int name_index = 1;
for (NSVGshape *shape = svg_data->shapes; shape; shape = shape->next) {
char * id = shape->id;
int interpolation_precision = 10; // Number of interpolation points
float step = 1.0f / float(interpolation_precision - 1);
// get the path point
std::vector<std::vector<std::vector<Point_2D>>> all_path_points; // paths<profiles<curves<points>>>
for (NSVGpath *path = shape->paths; path; path = path->next) {
std::vector<std::vector<Point_2D>> profile_points;
int index = 0;
for (int i = 0; i < path->npts - 1; i += 3) {
float * p = &path->pts[i * 2];
float a = 0.0f;
std::vector<Point_2D> curve_points; // points on a curve
for (int v = 0; v < interpolation_precision; v++) {
float pt[2];
// get interpolation points of Bezier curve
interp_v2_v2v2v2v2_cubic(pt, &p[0], &p[2], &p[4], &p[6], a);
Point_2D point(pt[0], -pt[1]);
curve_points.push_back(point);
a += step;
}
profile_points.push_back(curve_points);
// keep the adjacent curves end-to-end
if (profile_points.size() > 1) {
profile_points[index - 1].back() = profile_points[index].front();
}
index++;
}
if (!profile_points.empty())
all_path_points.push_back(profile_points);
}
// remove duplicate points and ensure the profile is closed
std::vector<std::vector<std::pair<gp_Pnt, gp_Pnt>>> path_line_points;
for (auto profile_points : all_path_points) {
std::vector<std::pair<gp_Pnt, gp_Pnt>> profile_line_points;
for (int i = 0; i < profile_points.size(); ++i) {
for (int j = 0; j + 1 < profile_points[i].size(); j++) {
gp_Pnt pt1(profile_points[i][j].x, profile_points[i][j].y, 0);
gp_Pnt pt2(profile_points[i][j + 1].x, profile_points[i][j + 1].y, 0);
if (is_same_points(pt1, pt2))
continue;
profile_line_points.push_back({pt1, pt2});
}
}
// keep the start and end points of profile connected
profile_line_points.back().second = profile_line_points[0].first;
if (is_profile_self_interaction(profile_line_points))
BOOST_LOG_TRIVIAL(warning) << "the profile is self interaction.";
path_line_points.push_back(profile_line_points);
}
// generate all profile curves
std::vector<TopoDS_Wire> wires;
int index = 0;
double max_area = 0;
for (int i = 0; i < path_line_points.size(); ++i) {
BRepBuilderAPI_MakeWire wire_build;
for (auto point_item : path_line_points[i]) {
TopoDS_Edge edge_build = BRepBuilderAPI_MakeEdge(point_item.first, point_item.second);
wire_build.Add(edge_build);
}
TopoDS_Wire wire = wire_build.Wire();
double profile_area = get_profile_area(path_line_points[i]);
if (profile_area > max_area) {
max_area = profile_area;
index = i;
}
wires.emplace_back(wire);
}
gp_Vec dir(0, 0, 10);
BRepBuilderAPI_MakeFace face_make(wires[index]);
for (int i = 0; i < wires.size(); ++i) {
if (index == i)
continue;
face_make.Add(wires[i]);
}
TopoDS_Face face = face_make.Face();
TopoDS_Shape element_shape = BRepPrimAPI_MakePrism(face, dir, false, false).Shape();
Element_Info element_info;
element_info.name = "part_" + std::to_string(name_index);
element_info.color = shape->fill.color;
element_info.shape = element_shape;
element_infos.push_back(element_info);
name_index++;
}
nsvgDelete(svg_data);
return true;
}
bool load_svg(const char *path, Model *model, std::string &message)
{
std::vector<Element_Info> namedSolids;
if (!get_svg_profile(path, namedSolids, message))
return false;
std::vector<stl_file> stl;
stl.resize(namedSolids.size());
// todo: zhimin, Can be accelerated in parallel with tbb
for (size_t i = 0 ; i < namedSolids.size(); i++) {
BRepMesh_IncrementalMesh mesh(namedSolids[i].shape, STEP_TRANS_CHORD_ERROR, false, STEP_TRANS_ANGLE_RES, true);
// BBS: calculate total number of the nodes and triangles
int aNbNodes = 0;
int aNbTriangles = 0;
for (TopExp_Explorer anExpSF(namedSolids[i].shape, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(anExpSF.Current()), aLoc);
if (!aTriangulation.IsNull()) {
aNbNodes += aTriangulation->NbNodes();
aNbTriangles += aTriangulation->NbTriangles();
}
}
if (aNbTriangles == 0 || aNbNodes == 0)
// BBS: No triangulation on the shape.
continue;
stl[i].stats.type = inmemory;
stl[i].stats.number_of_facets = (uint32_t) aNbTriangles;
stl[i].stats.original_num_facets = stl[i].stats.number_of_facets;
stl_allocate(&stl[i]);
std::vector<Vec3f> points;
points.reserve(aNbNodes);
// BBS: count faces missing triangulation
Standard_Integer aNbFacesNoTri = 0;
// BBS: fill temporary triangulation
Standard_Integer aNodeOffset = 0;
Standard_Integer aTriangleOffet = 0;
for (TopExp_Explorer anExpSF(namedSolids[i].shape, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
const TopoDS_Shape &aFace = anExpSF.Current();
TopLoc_Location aLoc;
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(aFace), aLoc);
if (aTriangulation.IsNull()) {
++aNbFacesNoTri;
continue;
}
// BBS: copy nodes
gp_Trsf aTrsf = aLoc.Transformation();
for (Standard_Integer aNodeIter = 1; aNodeIter <= aTriangulation->NbNodes(); ++aNodeIter) {
gp_Pnt aPnt = aTriangulation->Node(aNodeIter);
aPnt.Transform(aTrsf);
points.emplace_back(std::move(Vec3f(aPnt.X(), aPnt.Y(), aPnt.Z())));
}
// BBS: copy triangles
const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation();
Standard_Integer anId[3];
for (Standard_Integer aTriIter = 1; aTriIter <= aTriangulation->NbTriangles(); ++aTriIter) {
Poly_Triangle aTri = aTriangulation->Triangle(aTriIter);
aTri.Get(anId[0], anId[1], anId[2]);
if (anOrientation == TopAbs_REVERSED) std::swap(anId[1], anId[2]);
// BBS: save triangles facets
stl_facet facet;
facet.vertex[0] = points[anId[0] + aNodeOffset - 1].cast<float>();
facet.vertex[1] = points[anId[1] + aNodeOffset - 1].cast<float>();
facet.vertex[2] = points[anId[2] + aNodeOffset - 1].cast<float>();
facet.extra[0] = 0;
facet.extra[1] = 0;
stl_normal normal;
stl_calculate_normal(normal, &facet);
stl_normalize_vector(normal);
facet.normal = normal;
stl[i].facet_start[aTriangleOffet + aTriIter - 1] = facet;
}
aNodeOffset += aTriangulation->NbNodes();
aTriangleOffet += aTriangulation->NbTriangles();
}
}
ModelObject *new_object = model->add_object();
// new_object->name ?
new_object->input_file = path;
auto stage_unit3 = stl.size() / LOAD_STEP_STAGE_UNIT_NUM + 1;
for (size_t i = 0; i < stl.size(); i++) {
// BBS: maybe mesh is empty from step file. Don't add
if (stl[i].stats.number_of_facets > 0) {
TriangleMesh triangle_mesh;
triangle_mesh.from_stl(stl[i]);
ModelVolume *new_volume = new_object->add_volume(std::move(triangle_mesh));
new_volume->name = namedSolids[i].name;
new_volume->source.input_file = path;
new_volume->source.object_idx = (int) model->objects.size() - 1;
new_volume->source.volume_idx = (int) new_object->volumes.size() - 1;
}
}
return true;
}
} // namespace Slic3r

View file

@ -0,0 +1,7 @@
#pragma once
namespace Slic3r {
class Model;
extern bool load_svg(const char *path, Model *model, std::string &message);
}; // namespace Slic3r

View file

@ -682,8 +682,7 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
// first layer may result in skirt/brim in the air and maybe other issues.
if (layers_to_print.size() == 1u) {
if (!has_extrusions)
throw Slic3r::SlicingError(_(L("One object has empty initial layer and can't be printed. Please Cut the bottom or enable supports.")) + "\n" +
_(L("Object")) + ": " + object.model_object()->name);
throw Slic3r::SlicingError(_(L("One object has empty initial layer and can't be printed. Please Cut the bottom or enable supports.")), object.id().id);
}
// In case there are extrusions on this layer, check there is a layer to lay it on.
@ -1379,10 +1378,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
file.write(full_config);
// SoftFever: write compatiple image
std::vector<int> temps_per_bed;
int first_layer_bed_temperature = 0;
get_bed_temperature(0, true, temps_per_bed,
first_layer_bed_temperature);
int first_layer_bed_temperature = get_bed_temperature(0, true, print.config().curr_bed_type);
file.write_format("; first_layer_bed_temperature = %d\n",
first_layer_bed_temperature);
file.write_format(
@ -1501,12 +1497,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
// Emit machine envelope limits for the Marlin firmware.
this->print_machine_envelope(file, print);
//BBS: emit printing accelerate if has non-zero value
if (m_config.default_acceleration.value > 0) {
float acceleration = m_config.default_acceleration.value;
file.write(m_writer.set_acceleration((unsigned int)floor(acceleration + 0.5)));
}
// Disable fan.
if (m_config.auxiliary_fan.value && print.config().close_fan_the_first_x_layers.get_at(initial_extruder_id)) {
file.write(m_writer.set_fan(0));
@ -1874,9 +1864,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
file.write(full_config);
// SoftFever: write compatiple info
std::vector<int> temps_per_bed;
int first_layer_bed_temperature = 0;
get_bed_temperature(0, true, temps_per_bed, first_layer_bed_temperature);
int first_layer_bed_temperature = get_bed_temperature(0, true, print.config().curr_bed_type);
file.write_format("; first_layer_bed_temperature = %d\n",
first_layer_bed_temperature);
file.write_format(
@ -1885,7 +1873,9 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
file.write_format(
"; first_layer_height = %.3f\n",
print.config().initial_layer_print_height.value);
file.write_format("; variable_layer_height = %d\n", m_config.adaptive_layer_height ? 1 : 0);
//SF TODO
// file.write_format("; variable_layer_height = %d\n", print.ad.adaptive_layer_height ? 1 : 0);
file.write("; CONFIG_BLOCK_END\n\n");
@ -2135,17 +2125,11 @@ void GCode::print_machine_envelope(GCodeOutputStream &file, Print &print)
}
// BBS
void GCode::get_bed_temperature(const int extruder_id, const bool is_first_layer, std::vector<int>& temps_per_bed, int& default_temp) const
int GCode::get_bed_temperature(const int extruder_id, const bool is_first_layer, const BedType bed_type) const
{
temps_per_bed.resize((int)BedType::btCount, 0);
for (int bed_type = 0; bed_type < BedType::btCount; bed_type++) {
std::string bed_temp_key = is_first_layer ? get_bed_temp_1st_layer_key((BedType)bed_type) : get_bed_temp_key((BedType)bed_type);
const ConfigOptionInts* bed_temp_opt = m_config.option<ConfigOptionInts>(bed_temp_key);
temps_per_bed[bed_type] = bed_temp_opt->get_at(extruder_id);
if (bed_type == m_config.curr_bed_type)
default_temp = temps_per_bed[bed_type];
}
std::string bed_temp_key = is_first_layer ? get_bed_temp_1st_layer_key(bed_type) : get_bed_temp_key(bed_type);
const ConfigOptionInts* bed_temp_opt = m_config.option<ConfigOptionInts>(bed_temp_key);
return bed_temp_opt->get_at(extruder_id);
}
// Write 1st layer bed temperatures into the G-code.
@ -2157,8 +2141,7 @@ void GCode::_print_first_layer_bed_temperature(GCodeOutputStream &file, Print &p
// Initial bed temperature based on the first extruder.
// BBS
std::vector<int> temps_per_bed;
int default_temp = 0;
get_bed_temperature(first_printing_extruder_id, true, temps_per_bed, default_temp);
int bed_temp = get_bed_temperature(first_printing_extruder_id, true, print.config().curr_bed_type);
// Is the bed temperature set by the provided custom G-code?
int temp_by_gcode = -1;
@ -2171,7 +2154,7 @@ void GCode::_print_first_layer_bed_temperature(GCodeOutputStream &file, Print &p
// Always call m_writer.set_bed_temperature() so it will set the internal "current" state of the bed temp as if
// the custom start G-code emited these.
std::string set_temp_gcode = m_writer.set_bed_temperature(temps_per_bed, default_temp, wait);
std::string set_temp_gcode = m_writer.set_bed_temperature(bed_temp, wait);
if (! temp_set_by_gcode)
file.write(set_temp_gcode);
}
@ -2639,10 +2622,8 @@ GCode::LayerResult GCode::process_layer(
}
// BBS
std::vector<int> temps_per_bed;
int default_temp = 0;
get_bed_temperature(first_extruder_id, false, temps_per_bed, default_temp);
gcode += m_writer.set_bed_temperature(temps_per_bed, default_temp);
int bed_temp = get_bed_temperature(first_extruder_id, false, print.config().curr_bed_type);
gcode += m_writer.set_bed_temperature(bed_temp);
// Mark the temperature transition from 1st to 2nd layer to be finished.
m_second_layer_things_done = true;
}
@ -3297,11 +3278,8 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
// get a copy; don't modify the orientation of the original loop object otherwise
// next copies (if any) would not detect the correct orientation
//BBS: extrude contour of wall ccw, hole of wall cw, except spiral mode
bool was_clockwise = loop.is_clockwise();
if (m_config.spiral_mode || !is_perimeter(loop.role()))
loop.make_counter_clockwise();
bool current_clockwise = loop.is_clockwise();
// extrude all loops ccw
bool was_clockwise = loop.make_counter_clockwise();
// find the point of the loop that is closest to the current extruder position
// or randomize if requested
@ -3371,7 +3349,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
//FIXME improve the algorithm in case the loop is split into segments with a low number of points (see the Point b query).
Point a = paths.front().polyline.points[1]; // second point
Point b = *(paths.back().polyline.points.end()-3); // second to last point
if (was_clockwise != current_clockwise) {
if (was_clockwise) {
// swap points
Point c = a; a = b; b = c;
}
@ -3379,7 +3357,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
double angle = paths.front().first_point().ccw_angle(a, b) / 3;
// turn left if contour, turn right if hole
if (was_clockwise != current_clockwise) angle *= -1;
if (was_clockwise) angle *= -1;
// create the destination point along the first segment and rotate it
// we make sure we don't exceed the segment length because we don't know
@ -3391,7 +3369,12 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
double l2 = v.squaredNorm();
// Shift by no more than a nozzle diameter.
//FIXME Hiding the seams will not work nicely for very densely discretized contours!
Point pt = ((nd * nd >= l2) ? p2 : (p1 + v * (nd / sqrt(l2)))).cast<coord_t>();
//BBS. shorten the travel distant before the wipe path
double threshold = 0.2;
Point pt = (p1 + v * threshold).cast<coord_t>();
if (nd * nd < l2)
pt = (p1 + threshold * v * (nd / sqrt(l2))).cast<coord_t>();
//Point pt = ((nd * nd >= l2) ? (p1+v*0.4): (p1 + 0.2 * v * (nd / sqrt(l2)))).cast<coord_t>();
pt.rotate(angle, paths.front().polyline.points.front());
// generate the travel move
gcode += m_writer.travel_to_xy(this->point_to_gcode(pt), "move inwards before travel");
@ -3654,8 +3637,6 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
acceleration = m_config.first_layer_acceleration_over_raft.value;
} else if (m_config.bridge_acceleration.value > 0 && is_bridge(path.role())) {
acceleration = m_config.bridge_acceleration.value;
} else if (m_config.perimeter_acceleration.value > 0 && is_perimeter(path.role())) {
acceleration = m_config.perimeter_acceleration.value;
#endif
} else if (m_config.outer_wall_acceleration.value > 0 && is_external_perimeter(path.role())) {
acceleration = m_config.outer_wall_acceleration.value;
@ -3807,11 +3788,21 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
std::string comment;
if (m_enable_cooling_markers) {
if (EXTRUDER_CONFIG(enable_overhang_bridge_fan) &&
(path.get_overhang_degree() > EXTRUDER_CONFIG(overhang_fan_threshold) || is_bridge(path.role())))
gcode += ";_OVERHANG_FAN_START\n";
else
if (EXTRUDER_CONFIG(enable_overhang_bridge_fan)) {
//BBS: Overhang_threshold_none means Overhang_threshold_1_4 and forcing cooling for all external perimeter
int overhang_threshold = EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none ?
Overhang_threshold_none : EXTRUDER_CONFIG(overhang_fan_threshold) - 1;
if ((EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none && path.role() == erExternalPerimeter) ||
path.get_overhang_degree() > overhang_threshold ||
is_bridge(path.role()))
gcode += ";_OVERHANG_FAN_START\n";
else
comment = ";_EXTRUDE_SET_SPEED";
}
else {
comment = ";_EXTRUDE_SET_SPEED";
}
if (path.role() == erExternalPerimeter)
comment += ";_EXTERNAL_PERIMETER";
}
@ -3875,10 +3866,22 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
}
}
}
if (m_enable_cooling_markers)
gcode += (EXTRUDER_CONFIG(enable_overhang_bridge_fan) &&
(is_bridge(path.role()) || path.get_overhang_degree() > EXTRUDER_CONFIG(overhang_fan_threshold))) ?
";_OVERHANG_FAN_END\n" : ";_EXTRUDE_END\n";
if (m_enable_cooling_markers) {
if (EXTRUDER_CONFIG(enable_overhang_bridge_fan)) {
//BBS: Overhang_threshold_none means Overhang_threshold_1_4 and forcing cooling for all external perimeter
int overhang_threshold = EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none ?
Overhang_threshold_none : EXTRUDER_CONFIG(overhang_fan_threshold) - 1;
if ((EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none && path.role() == erExternalPerimeter) ||
path.get_overhang_degree() > overhang_threshold ||
is_bridge(path.role()))
gcode += ";_OVERHANG_FAN_END\n";
else
gcode += ";_EXTRUDE_END\n";
}
else {
gcode += ";_EXTRUDE_END\n";
}
}
this->set_last_pos(path.last_point());
return gcode;
@ -4122,13 +4125,13 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
std::vector<float> flush_matrix(cast<float>(m_config.flush_volumes_matrix.values));
const unsigned int number_of_extruders = (unsigned int)(sqrt(flush_matrix.size()) + EPSILON);
assert(m_writer.extruder()->id() < number_of_extruders);
assert(new_retract_length < number_of_extruders);
int previous_extruder_id = m_writer.extruder()->id();
old_retract_length = m_config.retraction_length.get_at(previous_extruder_id);
old_retract_length_toolchange = m_config.retract_length_toolchange.get_at(previous_extruder_id);
old_filament_temp = this->on_first_layer()? m_config.nozzle_temperature_initial_layer.get_at(previous_extruder_id) : m_config.nozzle_temperature.get_at(previous_extruder_id);
wipe_volume = flush_matrix[previous_extruder_id * number_of_extruders + extruder_id];
wipe_volume *= m_config.flush_multiplier;
old_filament_e_feedrate = (int)(60.0 * m_config.filament_max_volumetric_speed.get_at(previous_extruder_id) / filament_area);
old_filament_e_feedrate = old_filament_e_feedrate == 0 ? 100 : old_filament_e_feedrate;
}

View file

@ -492,7 +492,7 @@ private:
static bool gcode_label_objects;
// BBS
void get_bed_temperature(const int extruder_id, const bool is_first_layer, std::vector<int>& temps_per_bed, int& default_temp) const;
int get_bed_temperature(const int extruder_id, const bool is_first_layer, const BedType bed_type) const;
std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
void print_machine_envelope(GCodeOutputStream &file, Print &print);

View file

@ -722,7 +722,7 @@ std::string CoolingBuffer::apply_layer_cooldown(
new_gcode.reserve(gcode.size() * 2);
bool overhang_fan_control= false;
int overhang_fan_speed = 0;
auto change_extruder_set_fan = [ this, layer_id, layer_time, &new_gcode, &overhang_fan_control, &overhang_fan_speed]() {
auto change_extruder_set_fan = [ this, layer_id, layer_time, &new_gcode, &overhang_fan_control, &overhang_fan_speed](bool immediately_apply) {
#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_current_extruder)
int fan_min_speed = EXTRUDER_CONFIG(fan_min_speed);
int fan_speed_new = EXTRUDER_CONFIG(reduce_fan_stop_start_freq) ? fan_min_speed : 0;
@ -771,18 +771,20 @@ std::string CoolingBuffer::apply_layer_cooldown(
m_fan_speed = fan_speed_new;
//BBS
m_current_fan_speed = fan_speed_new;
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_fan_speed);
if (immediately_apply)
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_fan_speed);
}
//BBS
if (additional_fan_speed_new != m_additional_fan_speed && m_config.auxiliary_fan.value) {
m_additional_fan_speed = additional_fan_speed_new;
new_gcode += GCodeWriter::set_additional_fan(m_additional_fan_speed);
if (immediately_apply)
new_gcode += GCodeWriter::set_additional_fan(m_additional_fan_speed);
}
};
const char *pos = gcode.c_str();
int current_feedrate = 0;
change_extruder_set_fan();
change_extruder_set_fan(true);
for (const CoolingLine *line : lines) {
const char *line_start = gcode.c_str() + line->line_start;
const char *line_end = gcode.c_str() + line->line_end;
@ -792,7 +794,7 @@ std::string CoolingBuffer::apply_layer_cooldown(
unsigned int new_extruder = (unsigned int)atoi(line_start + m_toolchange_prefix.size());
if (new_extruder != m_current_extruder) {
m_current_extruder = new_extruder;
change_extruder_set_fan();
change_extruder_set_fan(false); //BBS: will force to resume fan speed when filament change is finished
}
new_gcode.append(line_start, line_end - line_start);
} else if (line->type & CoolingLine::TYPE_OVERHANG_FAN_START) {

View file

@ -36,6 +36,7 @@ static const float DEFAULT_TRAVEL_ACCELERATION = 1250.0f;
static const size_t MIN_EXTRUDERS_COUNT = 5;
static const float DEFAULT_FILAMENT_DIAMETER = 1.75f;
static const int DEFAULT_FILAMENT_HRC = 0;
static const float DEFAULT_FILAMENT_DENSITY = 1.245f;
static const int DEFAULT_FILAMENT_VITRIFICATION_TEMPERATURE = 0;
static const Slic3r::Vec3f DEFAULT_EXTRUDER_OFFSET = Slic3r::Vec3f::Zero();
@ -472,7 +473,16 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename, st
PrintEstimatedStatistics::ETimeMode mode = static_cast<PrintEstimatedStatistics::ETimeMode>(i);
if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) {
char buf[128];
sprintf(buf, "; estimated printing time: %s\n", get_time_dhms(machine.time).c_str());
if(!s_IsBBLPrinter)
sprintf(buf, "; estimated printing time: %s\n", get_time_dhms(machine.time).c_str());
else {
//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());
}
ret += buf;
}
}
@ -794,6 +804,7 @@ void GCodeProcessorResult::reset() {
extruders_count = 0;
extruder_colors = std::vector<std::string>();
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
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>();
warnings.clear();
@ -899,14 +910,17 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_extruder_offsets.resize(extruders_count);
m_extruder_colors.resize(extruders_count);
m_result.filament_diameters.resize(extruders_count);
m_result.required_nozzle_HRC.resize(extruders_count);
m_result.filament_densities.resize(extruders_count);
m_result.filament_vitrification_temperature.resize(extruders_count);
m_extruder_temps.resize(extruders_count);
m_result.nozzle_hrc = static_cast<int>(config.nozzle_hrc.getInt());
for (size_t i = 0; i < extruders_count; ++ i) {
m_extruder_offsets[i] = to_3d(config.extruder_offset.get_at(i).cast<float>().eval(), 0.f);
m_extruder_colors[i] = static_cast<unsigned char>(i);
m_result.filament_diameters[i] = static_cast<float>(config.filament_diameter.get_at(i));
m_result.required_nozzle_HRC[i] = static_cast<int>(config.required_nozzle_HRC.get_at(i));
m_result.filament_densities[i] = static_cast<float>(config.filament_density.get_at(i));
m_result.filament_vitrification_temperature[i] = static_cast<float>(config.temperature_vitrification.get_at(i));
}
@ -958,6 +972,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
if (nozzle_volume != nullptr)
m_nozzle_volume = nozzle_volume->value;
const ConfigOptionInt *nozzle_HRC = config.option<ConfigOptionInt>("nozzle_hrc");
if (nozzle_HRC != nullptr) m_result.nozzle_hrc = nozzle_HRC->value;
const ConfigOptionEnum<GCodeFlavor>* gcode_flavor = config.option<ConfigOptionEnum<GCodeFlavor>>("gcode_flavor");
if (gcode_flavor != nullptr)
m_flavor = gcode_flavor->value;
@ -1001,6 +1018,18 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
}
}
const ConfigOptionInts *filament_HRC = config.option<ConfigOptionInts>("required_nozzle_HRC");
if (filament_HRC != nullptr) {
m_result.required_nozzle_HRC.clear();
m_result.required_nozzle_HRC.resize(filament_HRC->values.size());
for (size_t i = 0; i < filament_HRC->values.size(); ++i) { m_result.required_nozzle_HRC[i] = static_cast<float>(filament_HRC->values[i]); }
}
if (m_result.required_nozzle_HRC.size() < m_result.extruders_count) {
for (size_t i = m_result.required_nozzle_HRC.size(); i < m_result.extruders_count; ++i) { m_result.required_nozzle_HRC.emplace_back(DEFAULT_FILAMENT_HRC);
}
}
const ConfigOptionFloats* filament_densities = config.option<ConfigOptionFloats>("filament_density");
if (filament_densities != nullptr) {
m_result.filament_densities.clear();
@ -2720,10 +2749,11 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
get_retract_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)) :
get_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i)));
//BBS
for (unsigned char a = X; a <= E; ++a) {
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (acceleration * std::abs(delta_pos[a]) * inv_distance > axis_max_acceleration)
acceleration = axis_max_acceleration;
acceleration = axis_max_acceleration / (std::abs(delta_pos[a]) * inv_distance);
}
block.acceleration = acceleration;
@ -2752,23 +2782,30 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
float v_factor = 1.0f;
bool limited = false;
//BBS: currently jerk in x,y,z axis are combined to one value and be limited together in MC side
//So we only need to handle Z axis
for (unsigned char a = X; a <= E; ++a) {
// Limit an axis. We have to differentiate coasting from the reversal of an axis movement, or a full stop.
//BBS
float jerk = 0;
if (a == X) {
Vec3f exit_v = prev.feedrate * (prev.exit_direction);
exit_v(2, 0) = 0;
if (prev_speed_larger)
exit_v *= smaller_speed_factor;
Vec3f entry_v = block.feedrate_profile.cruise * (curr.enter_direction);
Vec3f jerk_v = entry_v - exit_v;
jerk = jerk_v.norm();
} else if (a == Y || a == Z) {
jerk_v = Vec3f(abs(jerk_v.x()), abs(jerk_v.y()), abs(jerk_v.z()));
Vec3f max_xyz_jerk_v = get_xyz_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i));
for (size_t i = 0; i < 3; i++)
{
if (jerk_v[i] > max_xyz_jerk_v[i]) {
v_factor *= max_xyz_jerk_v[i] / jerk_v[i];
jerk_v *= v_factor;
limited = true;
}
}
}
else if (a == Y || a == Z) {
continue;
} else {
}
else {
float v_exit = prev.axis_feedrate[a];
float v_entry = curr.axis_feedrate[a];
@ -2781,7 +2818,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
}
// Calculate the jerk depending on whether the axis is coasting in the same direction or reversing a direction.
jerk =
float jerk =
(v_exit > v_entry) ?
(((v_entry > 0.0f) || (v_exit < 0.0f)) ?
// coasting
@ -2794,12 +2831,13 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
(v_entry - v_exit) :
// axis reversal
std::max(-v_exit, v_entry));
}
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (jerk > axis_max_jerk) {
v_factor *= axis_max_jerk / jerk;
limited = true;
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (jerk > axis_max_jerk) {
v_factor *= axis_max_jerk / jerk;
limited = true;
}
}
}
@ -2866,7 +2904,8 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
}
else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
m_seams_detector.activate(true);
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id]);
Vec3f plate_offset = {(float) m_x_offset, (float) m_y_offset, 0.0f};
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset);
}
// store move
@ -3147,22 +3186,30 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
float v_factor = 1.0f;
bool limited = false;
//BBS: currently jerk in x,y,z axis are combined to one value and be limited together in MC side
//So we only need to handle Z axis
for (unsigned char a = X; a <= E; ++a) {
//BBS: Limit an axis. We have to differentiate coasting from the reversal of an axis movement, or a full stop.
float jerk = 0;
if (a == X) {
Vec3f exit_v = prev.feedrate * (prev.exit_direction);
exit_v(2, 0) = 0;
if (prev_speed_larger)
exit_v *= smaller_speed_factor;
Vec3f entry_v = block.feedrate_profile.cruise * (curr.enter_direction);
Vec3f jerk_v = entry_v - exit_v;
jerk = jerk_v.norm();
} else if (a == Y || a == Z) {
jerk_v = Vec3f(abs(jerk_v.x()), abs(jerk_v.y()), abs(jerk_v.z()));
Vec3f max_xyz_jerk_v = get_xyz_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i));
for (size_t i = 0; i < 3; i++)
{
if (jerk_v[i] > max_xyz_jerk_v[i]) {
v_factor *= max_xyz_jerk_v[i] / jerk_v[i];
jerk_v *= v_factor;
limited = true;
}
}
}
else if (a == Y || a == Z) {
continue;
} else {
}
else {
float v_exit = prev.axis_feedrate[a];
float v_entry = curr.axis_feedrate[a];
@ -3175,7 +3222,7 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
}
//BBS: Calculate the jerk depending on whether the axis is coasting in the same direction or reversing a direction.
jerk =
float jerk =
(v_exit > v_entry) ?
(((v_entry > 0.0f) || (v_exit < 0.0f)) ?
//BBS: coasting
@ -3187,12 +3234,13 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
(v_entry - v_exit) :
//BBS: axis reversal
std::max(-v_exit, v_entry));
}
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (jerk > axis_max_jerk) {
v_factor *= axis_max_jerk / jerk;
limited = true;
float axis_max_jerk = get_axis_max_jerk(static_cast<PrintEstimatedStatistics::ETimeMode>(i), static_cast<Axis>(a));
if (jerk > axis_max_jerk) {
v_factor *= axis_max_jerk / jerk;
limited = true;
}
}
}
@ -3229,18 +3277,20 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
}
//BBS: seam detector
Vec3f plate_offset = {(float) m_x_offset, (float) m_y_offset, 0.0f};
if (m_seams_detector.is_active()) {
//BBS: check for seam starting vertex
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter && !m_seams_detector.has_first_vertex())
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id]);
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter && !m_seams_detector.has_first_vertex()) {
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset);
}
//BBS: check for seam ending vertex and store the resulting move
else if ((type != EMoveType::Extrude || (m_extrusion_role != erExternalPerimeter && m_extrusion_role != erOverhangPerimeter)) && m_seams_detector.has_first_vertex()) {
auto set_end_position = [this](const Vec3f& pos) {
m_end_position[X] = pos.x(); m_end_position[Y] = pos.y(); m_end_position[Z] = pos.z();
};
const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]);
const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id];
const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset;
const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex();
//BBS: the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later
@ -3255,7 +3305,7 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
}
else if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter) {
m_seams_detector.activate(true);
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id]);
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id] - plate_offset);
}
//BBS: store move
store_move_vertex(type, m_move_path_type);
@ -3821,7 +3871,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type)
m_interpolation_points[i] =
Vec3f(m_interpolation_points[i].x() + m_x_offset,
m_interpolation_points[i].y() + m_y_offset,
m_processing_start_custom_gcode ? m_zero_layer_height : m_interpolation_points[i].z()) +
m_processing_start_custom_gcode ? m_first_layer_height : m_interpolation_points[i].z()) +
m_extruder_offsets[m_extruder_id];
}
@ -3832,7 +3882,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type)
m_extruder_id,
m_cp_color.current,
//BBS: add plate's offset to the rendering vertices
Vec3f(m_end_position[X] + m_x_offset, m_end_position[Y] + m_y_offset, m_processing_start_custom_gcode ? m_zero_layer_height : m_end_position[Z]) + m_extruder_offsets[m_extruder_id],
Vec3f(m_end_position[X] + m_x_offset, m_end_position[Y] + m_y_offset, m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z]) + m_extruder_offsets[m_extruder_id],
static_cast<float>(m_end_position[E] - m_start_position[E]),
m_feedrate,
m_width,
@ -3917,6 +3967,13 @@ float GCodeProcessor::get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode
}
}
Vec3f GCodeProcessor::get_xyz_max_jerk(PrintEstimatedStatistics::ETimeMode mode) const
{
return Vec3f(get_option_value(m_time_processor.machine_limits.machine_max_jerk_x, static_cast<size_t>(mode)),
get_option_value(m_time_processor.machine_limits.machine_max_jerk_y, static_cast<size_t>(mode)),
get_option_value(m_time_processor.machine_limits.machine_max_jerk_z, static_cast<size_t>(mode)));
}
float GCodeProcessor::get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const
{
size_t id = static_cast<size_t>(mode);
@ -4074,7 +4131,27 @@ void GCodeProcessor::update_slice_warnings()
}
if (!warning.params.empty()) {
warning.msg = BED_TEMP_TOO_HIGH_THAN_FILAMENT;
warning.msg = BED_TEMP_TOO_HIGH_THAN_FILAMENT;
warning.error_code = "1000C001";
m_result.warnings.push_back(warning);
}
//bbs:HRC checker
warning.params.clear();
warning.level=1;
if (m_result.nozzle_hrc!=0) {
for (size_t i = 0; i < used_extruders.size(); i++) {
int HRC=0;
if (used_extruders[i] < m_result.required_nozzle_HRC.size())
HRC = m_result.required_nozzle_HRC[used_extruders[i]];
if (HRC != 0 && (m_result.nozzle_hrc<HRC))
warning.params.push_back(std::to_string(used_extruders[i]));
}
}
if (!warning.params.empty()) {
warning.msg = NOZZLE_HRC_CHECKER;
warning.error_code = "1000C002";
m_result.warnings.push_back(warning);
}

View file

@ -17,7 +17,8 @@
namespace Slic3r {
// slice warnings enum strings
#define BED_TEMP_TOO_HIGH_THAN_FILAMENT "bed_temperature_too_high_than_filament"
#define NOZZLE_HRC_CHECKER "the_actual_nozzle_hrc_smaller_than_the_required_nozzle_hrc"
#define BED_TEMP_TOO_HIGH_THAN_FILAMENT "bed_temperature_too_high_than_filament"
enum class EMoveType : unsigned char
{
@ -86,6 +87,7 @@ namespace Slic3r {
struct GCodeProcessorResult
{
struct SettingsIds
{
std::string print;
@ -134,6 +136,7 @@ namespace Slic3r {
struct SliceWarning {
int level; // 0: normal tips, 1: warning; 2: error
std::string msg; // enum string
std::string error_code; // error code for studio
std::vector<std::string> params; // extra msg info
};
@ -152,12 +155,14 @@ namespace Slic3r {
size_t extruders_count;
std::vector<std::string> extruder_colors;
std::vector<float> filament_diameters;
std::vector<int> required_nozzle_HRC;
std::vector<float> filament_densities;
std::vector<int> filament_vitrification_temperature;
PrintEstimatedStatistics print_statistics;
std::vector<CustomGCode::Item> custom_gcode_per_print_z;
//BBS
std::vector<SliceWarning> warnings;
int nozzle_hrc;
#if ENABLE_GCODE_VIEWER_STATISTICS
int64_t time{ 0 };
@ -578,7 +583,6 @@ namespace Slic3r {
std::vector<Vec3f> m_extruder_offsets;
GCodeFlavor m_flavor;
float m_nozzle_volume;
AxisCoords m_start_position; // mm
AxisCoords m_end_position; // mm
AxisCoords m_origin; // mm
@ -829,6 +833,7 @@ namespace Slic3r {
float get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
Vec3f get_xyz_max_jerk(PrintEstimatedStatistics::ETimeMode mode) const;
float get_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
void set_retract_acceleration(PrintEstimatedStatistics::ETimeMode mode, float value);
float get_acceleration(PrintEstimatedStatistics::ETimeMode mode) const;
@ -838,7 +843,6 @@ namespace Slic3r {
float get_filament_load_time(size_t extruder_id);
float get_filament_unload_time(size_t extruder_id);
int get_filament_vitrification_temperature(size_t extrude_id);
void process_custom_gcode_time(CustomGCode::Type code);
void process_filaments(CustomGCode::Type code);

View file

@ -12,6 +12,7 @@ void ThumbnailData::set(unsigned int w, unsigned int h)
width = w;
height = h;
// defaults to white texture
pixels.clear();
pixels = std::vector<unsigned char>(width * height * 4, 255);
}
}

View file

@ -76,6 +76,7 @@ struct PlateBBoxData
bool is_seq_print = false;
int first_extruder = 0;
float nozzle_diameter = 0.4;
std::string bed_type;
// version 1: use view type ColorPrint (filament color)
// version 2: use view type FilamentId (filament id)
int version = 2;
@ -88,6 +89,7 @@ struct PlateBBoxData
j["first_extruder"] = first_extruder;
j["nozzle_diameter"] = nozzle_diameter;
j["version"] = version;
j["bed_type"] = bed_type;
for (const auto& bbox : bbox_objs) {
nlohmann::json j_bbox;
bbox.to_json(j_bbox);
@ -102,6 +104,7 @@ struct PlateBBoxData
j.at("first_extruder").get_to(first_extruder);
j.at("nozzle_diameter").get_to(nozzle_diameter);
j.at("version").get_to(version);
j.at("bed_type").get_to(bed_type);
for (auto& bbox_j : j.at("bbox_objects")) {
BBoxData bbox_data;
bbox_data.from_json(bbox_j);

View file

@ -244,6 +244,45 @@ public:
return (*this);
}
WipeTowerWriter &rectangle_fill_box(const WipeTower* wipe_tower, const Vec2f &ld, float width, float height, const float f = 0.f)
{
bool need_change_flow = wipe_tower->need_thick_bridge_flow(ld.y());
Vec2f corners[4];
corners[0] = ld;
corners[1] = ld + Vec2f(width, 0.f);
corners[2] = ld + Vec2f(width, height);
corners[3] = ld + Vec2f(0.f, height);
int index_of_closest = 0;
if (x() - ld.x() > ld.x() + width - x()) // closer to the right
index_of_closest = 1;
if (y() - ld.y() > ld.y() + height - y()) // closer to the top
index_of_closest = (index_of_closest == 0 ? 3 : 2);
travel(corners[index_of_closest].x(), y()); // travel to the closest corner
travel(x(), corners[index_of_closest].y());
int i = index_of_closest;
bool flow_changed = false;
do {
++i;
if (i == 4) i = 0;
if (need_change_flow) {
if (i == 1) {
// using bridge flow in bridge area, and add notes for gcode-check when flow changed
set_extrusion_flow(wipe_tower->extrusion_flow(0.2));
append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) + std::to_string(0.2) + "\n");
flow_changed = true;
} else if (i == 2 && flow_changed) {
set_extrusion_flow(wipe_tower->get_extrusion_flow());
append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) + std::to_string(m_layer_height) + "\n");
}
}
extrude(corners[i], f);
} while (i != index_of_closest);
return (*this);
}
WipeTowerWriter& rectangle(const WipeTower::box_coordinates& box, const float f = 0.f)
{
rectangle(Vec2f(box.ld.x(), box.ld.y()),
@ -664,7 +703,7 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
return std::vector<ToolChangeResult>();
}
WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_perimeter)
WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_perimeter, bool first_toolchange_to_nonsoluble)
{
size_t old_tool = m_current_tool;
@ -733,6 +772,12 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_per
writer.rectangle(wt_box);
writer.travel(initial_position);
}
if (first_toolchange_to_nonsoluble) {
writer.travel(Vec2f(0, 0));
writer.travel(initial_position);
}
toolchange_Wipe(writer, cleaning_box, wipe_length); // Wipe the newly loaded filament until the end of the assigned wipe area.
++ m_num_tool_changes;
} else
@ -974,6 +1019,12 @@ void WipeTower::toolchange_Wipe(
// Increase flow on first layer, slow down print.
writer.set_extrusion_flow(m_extrusion_flow * (is_first_layer() ? 1.15f : 1.f))
.append("; CP TOOLCHANGE WIPE\n");
// BBS: add the note for gcode-check, when the flow changed, the width should follow the change
if (is_first_layer()) {
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Width) + std::to_string(1.15 * m_perimeter_width) + "\n");
}
const float& xl = cleaning_box.ld.x();
const float& xr = cleaning_box.rd.x();
@ -1005,6 +1056,7 @@ void WipeTower::toolchange_Wipe(
writer.travel(xl, writer.y() + dy);
#endif
bool need_change_flow = false;
// now the wiping itself:
for (int i = 0; true; ++i) {
if (i!=0) {
@ -1014,11 +1066,24 @@ void WipeTower::toolchange_Wipe(
else wipe_speed = std::min(target_speed, wipe_speed + 50.f);
}
// BBS: check the bridging area and use the bridge flow
if (need_change_flow || need_thick_bridge_flow(writer.y())) {
writer.set_extrusion_flow(extrusion_flow(0.2));
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) + std::to_string(0.2) + "\n");
need_change_flow = true;
}
if (m_left_to_right)
writer.extrude(xr + 0.25f * m_perimeter_width, writer.y(), wipe_speed);
else
writer.extrude(xl - 0.25f * m_perimeter_width, writer.y(), wipe_speed);
// BBS: recover the flow in non-bridging area
if (need_change_flow) {
writer.set_extrusion_flow(m_extrusion_flow);
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) + std::to_string(m_layer_height) + "\n");
}
if (writer.y() - float(EPSILON) > cleaning_box.lu.y())
break; // in case next line would not fit
@ -1044,6 +1109,10 @@ void WipeTower::toolchange_Wipe(
m_left_to_right = !m_left_to_right;
writer.set_extrusion_flow(m_extrusion_flow); // Reset the extrusion flow.
// BBS: add the note for gcode-check when the flow changed
if (is_first_layer()) {
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Width) + std::to_string(m_perimeter_width) + "\n");
}
}
@ -1099,7 +1168,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer(bool extrude_perimeter, bool
// inner perimeter of the sparse section, if there is space for it:
if (fill_box.ru.y() - fill_box.rd.y() > m_perimeter_width - WT_EPSILON)
writer.rectangle(fill_box.ld, fill_box.rd.x() - fill_box.ld.x(), fill_box.ru.y() - fill_box.rd.y(), feedrate);
writer.rectangle_fill_box(this, fill_box.ld, fill_box.rd.x() - fill_box.ld.x(), fill_box.ru.y() - fill_box.rd.y(), feedrate);
// we are in one of the corners, travel to ld along the perimeter:
if (writer.x() > fill_box.ld.x() + EPSILON) writer.travel(fill_box.ld.x(), writer.y());
@ -1241,6 +1310,10 @@ void WipeTower::plan_toolchange(float z_par, float layer_height_par, unsigned in
float depth = 0.f;
float width = m_wipe_tower_width - 2 * m_perimeter_width;
// BBS: if the wipe tower width is too small, the depth will be infinity
if (width <= EPSILON)
return;
// BBS: remove old filament ramming and first line
#if 0
float length_to_extrude = volume_to_length(0.25f * std::accumulate(m_filpar[old_tool].ramming_speed.begin(), m_filpar[old_tool].ramming_speed.end(), 0.f),
@ -1518,7 +1591,11 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
finish_layer_tcr = finish_layer(false, layer.extruder_fill);
}
else {
layer_result.emplace_back(tool_change(layer.tool_changes[i].new_tool));
if (idx == -1 && i == 0) {
layer_result.emplace_back(tool_change(layer.tool_changes[i].new_tool, false, true));
} else {
layer_result.emplace_back(tool_change(layer.tool_changes[i].new_tool));
}
}
}
@ -1585,4 +1662,30 @@ WipeTower::ToolChangeResult WipeTower::only_generate_out_wall()
return construct_tcr(writer, false, old_tool, true, 0.f);
}
bool WipeTower::get_floating_area(float &start_pos_y, float &end_pos_y) const {
if (m_layer_info == m_plan.begin() || (m_layer_info - 1) == m_plan.begin())
return false;
float last_layer_fill_box_y = (m_layer_info - 1)->toolchanges_depth() + m_perimeter_width;
float last_layer_wipe_depth = (m_layer_info - 1)->depth;
if (last_layer_wipe_depth - last_layer_fill_box_y <= 2 * m_perimeter_width)
return false;
start_pos_y = last_layer_fill_box_y + m_perimeter_width;
end_pos_y = last_layer_wipe_depth - m_perimeter_width;
return true;
}
bool WipeTower::need_thick_bridge_flow(float pos_y) const {
if (m_extrusion_flow >= extrusion_flow(0.2))
return false;
float y_min = 0., y_max = 0.;
if (get_floating_area(y_min, y_max)) {
return pos_y > y_min && pos_y < y_max;
}
return false;
}
} // namespace Slic3r

View file

@ -217,12 +217,23 @@ public:
// Returns gcode for a toolchange and a final print head position.
// On the first layer, extrude a brim around the future wipe tower first.
// BBS
ToolChangeResult tool_change(size_t new_tool, bool extrude_perimeter = false);
ToolChangeResult tool_change(size_t new_tool, bool extrude_perimeter = false, bool first_toolchange_to_nonsoluble = false);
// Fill the unfilled space with a sparse infill.
// Call this method only if layer_finished() is false.
ToolChangeResult finish_layer(bool extruder_perimeter = true, bool extruder_fill = true);
// Calculates extrusion flow needed to produce required line width for given layer height
float extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
{
if (layer_height < 0) return m_extrusion_flow;
return layer_height * (m_perimeter_width - layer_height * (1.f - float(M_PI) / 4.f)) / filament_area();
}
bool get_floating_area(float& start_pos_y, float& end_pos_y) const;
bool need_thick_bridge_flow(float pos_y) const;
float get_extrusion_flow() const { return m_extrusion_flow; }
// Is the current layer finished?
bool layer_finished() const {
return m_current_layer_finished;
@ -336,14 +347,6 @@ private:
bool is_first_layer() const { return size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx; }
// Calculates extrusion flow needed to produce required line width for given layer height
float extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
{
if ( layer_height < 0 )
return m_extrusion_flow;
return layer_height * ( m_perimeter_width - layer_height * (1.f-float(M_PI)/4.f)) / filament_area();
}
// Calculates length of extrusion line to extrude given volume
float volume_to_length(float volume, float line_width, float layer_height) const {
return std::max(0.f, volume / (layer_height * (line_width - layer_height * (1.f - float(M_PI) / 4.f))));

View file

@ -125,13 +125,12 @@ std::string GCodeWriter::set_temperature(unsigned int temperature, bool wait, in
}
// BBS
std::string GCodeWriter::set_bed_temperature(std::vector<int> temps_per_bed, int default_temp, bool wait)
std::string GCodeWriter::set_bed_temperature(int temperature, bool wait)
{
if (temps_per_bed == m_last_bed_temperature && (! wait || m_last_bed_temperature_reached))
if (temperature == m_last_bed_temperature && (! wait || m_last_bed_temperature_reached))
return std::string();
bool target_temp_changed = (temps_per_bed != m_last_bed_temperature);
m_last_bed_temperature = temps_per_bed;
m_last_bed_temperature = temperature;
m_last_bed_temperature_reached = wait;
std::string code, comment;
@ -146,7 +145,7 @@ std::string GCodeWriter::set_bed_temperature(std::vector<int> temps_per_bed, int
comment = "set bed temperature";
}
gcode << code << " S" << default_temp << " ; " << comment << "\n";
gcode << code << " S" << temperature << " ; " << comment << "\n";
return gcode.str();
}

View file

@ -43,8 +43,7 @@ public:
std::string preamble();
std::string postamble() const;
std::string set_temperature(unsigned int temperature, bool wait = false, int tool = -1) const;
// BBS
std::string set_bed_temperature(std::vector<int> temps_per_bed, int default_temp, bool wait = false);
std::string set_bed_temperature(int temperature, bool wait = false);
std::string set_acceleration(unsigned int acceleration);
std::string set_jerk_xy(unsigned int jerk);
std::string set_pressure_advance(double pa) const;
@ -114,8 +113,7 @@ private:
//BBS
unsigned int m_last_additional_fan_speed;
// BBS
std::vector<int> m_last_bed_temperature;
int m_last_bed_temperature;
bool m_last_bed_temperature_reached;
double m_lifted;

View file

@ -65,7 +65,7 @@ public:
// ordered collection of extrusion paths to fill surfaces
// (this collection contains only ExtrusionEntityCollection objects)
ExtrusionEntityCollection fills;
Flow flow(FlowRole role) const;
Flow flow(FlowRole role, double layer_height) const;
Flow bridging_flow(FlowRole role, bool thick_bridge = false) const;
@ -110,7 +110,7 @@ private:
const PrintRegion *m_region;
};
class Layer
class Layer
{
public:
// Sequential index of this layer in PrintObject::m_layers, offsetted by the number of raft layers.
@ -132,7 +132,7 @@ public:
mutable ExPolygons cantilevers;
mutable std::map<const ExPolygon*, float> sharp_tails_height;
// Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry
// Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry
// (with possibly differing extruder ID and slicing parameters) and merged.
// For the first layer, if the Elephant foot compensation is applied, this lslice is uncompensated, therefore
// it includes the Elephant foot effect, thus it corresponds to the shape of the printed 1st layer.
@ -149,7 +149,7 @@ public:
LayerRegion* add_region(const PrintRegion *print_region);
const LayerRegionPtrs& regions() const { return m_regions; }
// Test whether whether there are any slices assigned to this layer.
bool empty() const;
bool empty() const;
void make_slices();
// Backup and restore raw sliced regions if needed.
//FIXME Review whether not to simplify the code by keeping the raw_slices all the time.
@ -209,7 +209,7 @@ private:
LayerRegionPtrs m_regions;
};
class SupportLayer : public Layer
class SupportLayer : public Layer
{
public:
// Polygons covered by the supports: base, interface and contact areas.
@ -248,14 +248,22 @@ public:
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 {
enum AreaType {
BaseType=0,
RoofType=1,
FloorType=2,
Roof1stLayer=3
};
std::vector<std::pair<ExPolygon *, int>> area_groups;
struct AreaGroup
{
ExPolygon *area;
int type;
int dist_to_top;
AreaGroup(ExPolygon *a, int t, int d) : area(a), type(t), dist_to_top(d) {}
};
std::vector<AreaGroup> area_groups;
enum OverhangType {
Detected=0,

View file

@ -105,8 +105,11 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
g.ext_perimeter_flow = this->flow(frExternalPerimeter);
g.overhang_flow = this->bridging_flow(frPerimeter, object_config.thick_bridges);
g.solid_infill_flow = this->flow(frSolidInfill);
g.process();
if (this->layer()->object()->config().wall_generator.value == PerimeterGeneratorType::Arachne && !spiral_mode)
g.process_arachne();
else
g.process_classic();
}
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
@ -225,7 +228,9 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
break;
}
// Grown by 3mm.
Polygons polys = offset(bridges[i].expolygon, bridge_margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS);
//BBS: eliminate too narrow area to avoid generating bridge on top layer when wall loop is 1
//Polygons polys = offset(bridges[i].expolygon, bridge_margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS);
Polygons polys = offset2({ bridges[i].expolygon }, -scale_(nozzle_diameter * 0.1), bridge_margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS);
if (idx_island == -1) {
BOOST_LOG_TRIVIAL(trace) << "Bridge did not fall into the source region!";
} else {

View file

@ -14,6 +14,7 @@
#include "Format/OBJ.hpp"
#include "Format/STL.hpp"
#include "Format/STEP.hpp"
#include "Format/svg.hpp"
// BBS
#include "FaceDetector.hpp"
@ -137,7 +138,7 @@ Model::~Model()
// Loading model from a file, it may be a simple geometry file as STL or OBJ, however it may be a project file as well.
Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions,
LoadStrategy options, PlateDataPtrs* plate_data, std::vector<Preset*>* project_presets, bool *is_xxx, Semver* file_version, Import3mfProgressFn proFn,
ImportstlProgressFn stlFn, ImportStepProgressFn stepFn, StepIsUtf8Fn stepIsUtf8Fn, BBLProject* project)
ImportstlProgressFn stlFn, ImportStepProgressFn stepFn, StepIsUtf8Fn stepIsUtf8Fn, BBLProject* project, int plate_id)
{
Model model;
@ -159,6 +160,7 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
file_version = &temp_version;
bool result = 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);
@ -166,6 +168,8 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
result = load_stl(input_file.c_str(), &model, nullptr, stlFn);
else if (boost::algorithm::iends_with(input_file, ".obj"))
result = load_obj(input_file.c_str(), &model);
else if (boost::algorithm::iends_with(input_file, ".svg"))
result = load_svg(input_file.c_str(), &model, message);
//BBS: remove the old .amf.xml files
//else if (boost::algorithm::iends_with(input_file, ".amf") || boost::algorithm::iends_with(input_file, ".amf.xml"))
else if (boost::algorithm::iends_with(input_file, ".amf"))
@ -176,12 +180,16 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
// BBS: backup & restore
//FIXME options & LoadStrategy::CheckVersion ?
//BBS: is_xxx is used for is_bbs_3mf when load 3mf
result = load_bbs_3mf(input_file.c_str(), config, config_substitutions, &model, plate_data, project_presets, is_xxx, file_version, proFn, options, project);
result = load_bbs_3mf(input_file.c_str(), config, config_substitutions, &model, plate_data, project_presets, is_xxx, file_version, proFn, options, project, plate_id);
else
throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .amf(.xml) extension.");
if (! result)
throw Slic3r::RuntimeError("Loading of a model file failed.");
if (!result) {
if (message.empty())
throw Slic3r::RuntimeError("Loading of a model file failed.");
else
throw Slic3r::RuntimeError(message);
}
if (model.objects.empty())
throw Slic3r::RuntimeError("The supplied file couldn't be read because it's empty");
@ -203,7 +211,8 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
//BBS: add part plate related logic
// BBS: backup & restore
// Loading model from a file (3MF or AMF), not from a simple geometry file (STL or OBJ).
Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, En3mfType& out_file_type, LoadStrategy options, PlateDataPtrs* plate_data, std::vector<Preset*>* project_presets, Semver* file_version, Import3mfProgressFn proFn, BBLProject *project)
Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, En3mfType& out_file_type, LoadStrategy options,
PlateDataPtrs* plate_data, std::vector<Preset*>* project_presets, Semver* file_version, Import3mfProgressFn proFn, BBLProject *project)
{
assert(config != nullptr);
assert(config_substitutions != nullptr);
@ -2911,13 +2920,14 @@ double getTemperatureFromExtruder(const ModelVolumePtrs objectVolumes) {
double ModelInstance::get_auto_brim_width() const
{
return 0.;
double adhcoeff = getadhesionCoeff(object->volumes);
double DeltaT = getTemperatureFromExtruder(object->volumes);
// get auto brim width (Note even if the global brim_type=btOuterBrim, we can still go into this branch)
return get_auto_brim_width(DeltaT, adhcoeff);
}
void ModelInstance::get_arrange_polygon(void* ap) const
void ModelInstance::get_arrange_polygon(void *ap, const Slic3r::DynamicPrintConfig &config_global) const
{
// static const double SIMPLIFY_TOLERANCE_MM = 0.1;
@ -2956,6 +2966,16 @@ void ModelInstance::get_arrange_polygon(void* ap) const
ret.extrude_ids = volume->get_extruders();
if (ret.extrude_ids.empty()) //the default extruder
ret.extrude_ids.push_back(1);
// get per-object support extruders
auto op = object->get_config_value<ConfigOptionBool>(config_global, "enable_support");
bool is_support_enabled = op && op->getBool();
if (is_support_enabled) {
auto op1 = object->get_config_value<ConfigOptionInt>(config_global, "support_filament");
auto op2 = object->get_config_value<ConfigOptionInt>(config_global, "support_interface_filament");
if (op1) ret.extrude_ids.push_back(op1->getInt());
if (op2) ret.extrude_ids.push_back(op2->getInt());
}
}
indexed_triangle_set FacetsAnnotation::get_facets(const ModelVolume& mv, EnforcerBlockerType type) const

View file

@ -1050,7 +1050,7 @@ public:
// BBS
void set_offset_to_assembly(const Vec3d& offset) { m_offset_to_assembly = offset; }
Vec3d get_offset_to_assembly() { return m_offset_to_assembly; }
Vec3d get_offset_to_assembly() const { return m_offset_to_assembly; }
const Vec3d& get_offset() const { return m_transformation.get_offset(); }
double get_offset(Axis axis) const { return m_transformation.get_offset(axis); }
@ -1111,7 +1111,7 @@ public:
// Getting the input polygon for arrange
// We use void* as input type to avoid including Arrange.hpp in Model.hpp.
void get_arrange_polygon(void* arrange_polygon) const;
void get_arrange_polygon(void *arrange_polygon, const Slic3r::DynamicPrintConfig &config = Slic3r::DynamicPrintConfig()) const;
// Apply the arrange result on the ModelInstance
void apply_arrange_result(const Vec2d& offs, double rotation)
@ -1213,6 +1213,7 @@ struct GlobalSpeedMap
double supportSpeed;
double smallPerimeterSpeed;
double maxSpeed;
Polygon bed_poly;
};
/* info in ModelDesignInfo can not changed after initialization */
@ -1234,12 +1235,15 @@ public:
std::string copyright; // utf8 format
std::string model_name; // utf8 format
std::map<std::string, std::string> metadata_items; // other meta data items
void load(ModelInfo &info) {
this->cover_file = info.cover_file;
this->license = info.license;
this->description = info.description;
this->copyright = info.copyright;
this->model_name = info.model_name;
this->metadata_items = info.metadata_items;
}
};
@ -1300,11 +1304,12 @@ public:
DynamicPrintConfig* config = nullptr, ConfigSubstitutionContext* config_substitutions = nullptr,
LoadStrategy options = LoadStrategy::AddDefaultInstances, PlateDataPtrs* plate_data = nullptr,
std::vector<Preset*>* project_presets = nullptr, bool* is_xxx = nullptr, Semver* file_version = nullptr, Import3mfProgressFn proFn = nullptr,
ImportstlProgressFn stlFn = nullptr, ImportStepProgressFn stepFn = nullptr, StepIsUtf8Fn stepIsUtf8Fn = nullptr, BBLProject* project = nullptr);
ImportstlProgressFn stlFn = nullptr, ImportStepProgressFn stepFn = nullptr, StepIsUtf8Fn stepIsUtf8Fn = nullptr, BBLProject* project = nullptr, int plate_id = 0);
// BBS
static double findMaxSpeed(const ModelObject* object);
static double getThermalLength(const ModelVolume* modelVolumePtr);
static double getThermalLength(const std::vector<ModelVolume*> modelVolumePtrs);
static Polygon getBedPolygon() { return Model::printSpeedMap.bed_poly; }
// BBS: backup
static Model read_from_archive(

View file

@ -88,9 +88,9 @@ void duplicate_objects(Model &model, size_t copies_num)
// Set up arrange polygon for a ModelInstance and Wipe tower
template<class T>
arrangement::ArrangePolygon get_arrange_poly(T obj)
arrangement::ArrangePolygon get_arrange_poly(T obj, const Slic3r::DynamicPrintConfig& config)
{
ArrangePolygon ap = obj.get_arrange_polygon();
ArrangePolygon ap = obj.get_arrange_polygon(config);
//BBS: always set bed_idx to 0 to use original transforms with no bed_idx
//if this object is not arranged, it can keep the original transforms
//ap.bed_idx = ap.translation.x() / bed_stride_x(plater);
@ -110,14 +110,14 @@ arrangement::ArrangePolygon get_arrange_poly(T obj)
}
template<>
arrangement::ArrangePolygon get_arrange_poly(ModelInstance* inst)
arrangement::ArrangePolygon get_arrange_poly(ModelInstance* inst, const Slic3r::DynamicPrintConfig& config)
{
return get_arrange_poly(PtrWrapper{ inst });
return get_arrange_poly(PtrWrapper{ inst },config);
}
ArrangePolygon get_instance_arrange_poly(ModelInstance* instance, const Slic3r::DynamicPrintConfig& config)
{
ArrangePolygon ap = get_arrange_poly(PtrWrapper{ instance });
ArrangePolygon ap = get_arrange_poly(PtrWrapper{ instance }, config);
//BBS: add temperature information
if (config.has("curr_bed_type")) {
@ -127,24 +127,25 @@ ArrangePolygon get_instance_arrange_poly(ModelInstance* instance, const Slic3r::
const ConfigOptionInts* bed_opt = config.option<ConfigOptionInts>(get_bed_temp_key(curr_bed_type));
if (bed_opt != nullptr)
ap.bed_temp = bed_opt->get_at(ap.extrude_ids.back()-1);
ap.bed_temp = bed_opt->get_at(ap.extrude_ids.front()-1);
const ConfigOptionInts* bed_opt_1st_layer = config.option<ConfigOptionInts>(get_bed_temp_1st_layer_key(curr_bed_type));
if (bed_opt_1st_layer != nullptr)
ap.first_bed_temp = bed_opt_1st_layer->get_at(ap.extrude_ids.back()-1);
ap.first_bed_temp = bed_opt_1st_layer->get_at(ap.extrude_ids.front()-1);
}
if (config.has("nozzle_temperature")) //get the print temperature
ap.print_temp = config.opt_int("nozzle_temperature", ap.extrude_ids.back() - 1);
ap.print_temp = config.opt_int("nozzle_temperature", ap.extrude_ids.front() - 1);
if (config.has("nozzle_temperature_initial_layer")) //get the nozzle_temperature_initial_layer
ap.first_print_temp = config.opt_int("nozzle_temperature_initial_layer", ap.extrude_ids.back() - 1);
ap.first_print_temp = config.opt_int("nozzle_temperature_initial_layer", ap.extrude_ids.front() - 1);
if (config.has("temperature_vitrification")) {
ap.vitrify_temp = config.opt_int("temperature_vitrification", ap.extrude_ids.back() - 1);
ap.vitrify_temp = config.opt_int("temperature_vitrification", ap.extrude_ids.front() - 1);
}
// get brim width
auto obj = instance->get_object();
#if 0
ap.brim_width = instance->get_auto_brim_width();
auto brim_type_ptr = obj->get_config_value<ConfigOptionEnum<BrimType>>(config, "brim_type");
if (brim_type_ptr) {
@ -154,7 +155,9 @@ ArrangePolygon get_instance_arrange_poly(ModelInstance* instance, const Slic3r::
else if (brim_type == btNoBrim)
ap.brim_width = 0;
}
#else
ap.brim_width = 0;
#endif
ap.height = obj->bounding_box().size().z();
ap.name = obj->name;

View file

@ -71,10 +71,10 @@ template<class T> struct PtrWrapper
explicit PtrWrapper(T* p) : ptr{ p } {}
arrangement::ArrangePolygon get_arrange_polygon() const
arrangement::ArrangePolygon get_arrange_polygon(const Slic3r::DynamicPrintConfig &config = Slic3r::DynamicPrintConfig()) const
{
arrangement::ArrangePolygon ap;
ptr->get_arrange_polygon(&ap);
ptr->get_arrange_polygon(&ap, config);
return ap;
}
@ -86,12 +86,12 @@ template<class T> struct PtrWrapper
};
template<class T>
arrangement::ArrangePolygon get_arrange_poly(T obj);
arrangement::ArrangePolygon get_arrange_poly(T obj, const DynamicPrintConfig &config = DynamicPrintConfig());
template<>
arrangement::ArrangePolygon get_arrange_poly(ModelInstance* inst);
arrangement::ArrangePolygon get_arrange_poly(ModelInstance* inst, const DynamicPrintConfig& config);
ArrangePolygon get_instance_arrange_poly(ModelInstance* instance, const Slic3r::DynamicPrintConfig& config);
ArrangePolygon get_instance_arrange_poly(ModelInstance* instance, const DynamicPrintConfig& config);
}
#endif // MODELARRANGE_HPP

View file

@ -1100,8 +1100,12 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
return graph;
}
static inline Polygon to_polygon(const std::vector<Linef> &lines)
static inline Polygon to_polygon(const std::vector<std::pair<size_t, Linef>> &id_to_lines)
{
std::vector<Linef> lines;
for (auto id_to_line : id_to_lines)
lines.emplace_back(id_to_line.second);
Polygon poly_out;
poly_out.points.reserve(lines.size());
for (const Linef &line : lines)
@ -1109,6 +1113,112 @@ static inline Polygon to_polygon(const std::vector<Linef> &lines)
return poly_out;
}
static std::vector<std::vector<const MMU_Graph::Arc *>> get_all_next_arcs(
const MMU_Graph &graph,
std::vector<bool> &used_arcs,
const Linef &process_line,
const MMU_Graph::Arc &original_arc,
const int color)
{
std::vector<std::vector<const MMU_Graph::Arc *>> all_next_arcs;
for (const size_t &arc_idx : graph.nodes[original_arc.to_idx].arc_idxs) {
std::vector<const MMU_Graph::Arc *> next_continue_arc;
const MMU_Graph::Arc & arc = graph.arcs[arc_idx];
if (graph.nodes[arc.to_idx].point == process_line.a || used_arcs[arc_idx])
continue;
if (original_arc.type == MMU_Graph::ARC_TYPE::BORDER && original_arc.color != color)
continue;
if (arc.type == MMU_Graph::ARC_TYPE::BORDER && arc.color != color)
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);
}
}
return all_next_arcs;
}
// two points that are very close are considered as one point
// std::vector contain the close points
static std::vector<const MMU_Graph::Arc *> get_next_arc(
const MMU_Graph &graph,
std::vector<bool> &used_arcs,
const Linef &process_line,
const MMU_Graph::Arc &original_arc,
const int color)
{
std::vector<const MMU_Graph::Arc *> res;
std::vector<std::vector<const MMU_Graph::Arc *>> all_next_arcs = get_all_next_arcs(graph, used_arcs, process_line, original_arc, color);
if (all_next_arcs.empty()) {
res.emplace_back(&original_arc);
return res;
}
std::vector<std::pair<std::vector<const MMU_Graph::Arc *>, double>> sorted_arcs;
for (auto next_arc : all_next_arcs) {
if (next_arc.empty())
continue;
Vec2d process_line_vec_n = (process_line.a - process_line.b).normalized();
Vec2d neighbour_line_vec_n = (graph.nodes[next_arc.back()->to_idx].point - graph.nodes[next_arc.back()->from_idx].point).normalized();
double angle = ::acos(std::clamp(neighbour_line_vec_n.dot(process_line_vec_n), -1.0, 1.0));
if (Slic3r::cross2(neighbour_line_vec_n, process_line_vec_n) < 0.0)
angle = 2.0 * (double) PI - angle;
sorted_arcs.emplace_back(next_arc, angle);
}
std::sort(sorted_arcs.begin(), sorted_arcs.end(),
[](std::pair<std::vector<const MMU_Graph::Arc *>, double> &l, std::pair<std::vector<const MMU_Graph::Arc *>, double> &r) -> bool {
return l.second < r.second;
});
// Try to return left most edge witch is unused
for (auto &sorted_arc : sorted_arcs) {
if (size_t arc_idx = sorted_arc.first.back() - &graph.arcs.front(); !used_arcs[arc_idx])
return sorted_arc.first;
}
if (sorted_arcs.empty()) {
res.emplace_back(&original_arc);
return res;
}
return sorted_arcs.front().first;
}
static bool is_profile_self_interaction(Polygon poly)
{
auto lines = poly.lines();
Point intersection;
for (int i = 0; i < lines.size(); ++i) {
for (int j = i + 2; j < std::min(lines.size(), lines.size() + i - 1); ++j) {
if (lines[i].intersection(lines[j], &intersection))
return true;
}
}
return false;
}
// Returns list of polygons and assigned colors.
// It iterates through all nodes on the border between two different colors, and from this point,
// start selection always left most edges for every node to construct CCW polygons.
@ -1116,43 +1226,7 @@ static inline Polygon to_polygon(const std::vector<Linef> &lines)
static std::vector<ExPolygons> extract_colored_segments(const MMU_Graph &graph, const size_t num_extruders)
{
std::vector<bool> used_arcs(graph.arcs.size(), false);
// When there is no next arc, then is returned original_arc or edge with is marked as used
auto get_next = [&graph, &used_arcs](const Linef &process_line, const MMU_Graph::Arc &original_arc, const int color) -> const MMU_Graph::Arc & {
std::vector<std::pair<const MMU_Graph::Arc *, double>> sorted_arcs;
for (const size_t &arc_idx : graph.nodes[original_arc.to_idx].arc_idxs) {
const MMU_Graph::Arc &arc = graph.arcs[arc_idx];
if (graph.nodes[arc.to_idx].point == process_line.a || used_arcs[arc_idx])
continue;
// BBS
if (original_arc.type == MMU_Graph::ARC_TYPE::BORDER && original_arc.color != color)
continue;
assert(original_arc.to_idx == arc.from_idx);
Vec2d process_line_vec_n = (process_line.a - process_line.b).normalized();
Vec2d neighbour_line_vec_n = (graph.nodes[arc.to_idx].point - graph.nodes[arc.from_idx].point).normalized();
double angle = ::acos(std::clamp(neighbour_line_vec_n.dot(process_line_vec_n), -1.0, 1.0));
if (Slic3r::cross2(neighbour_line_vec_n, process_line_vec_n) < 0.0)
angle = 2.0 * (double) PI - angle;
sorted_arcs.emplace_back(&arc, angle);
}
std::sort(sorted_arcs.begin(), sorted_arcs.end(),
[](std::pair<const MMU_Graph::Arc *, double> &l, std::pair<const MMU_Graph::Arc *, double> &r) -> bool { return l.second < r.second; });
// Try to return left most edge witch is unused
for (auto &sorted_arc : sorted_arcs)
if (size_t arc_idx = sorted_arc.first - &graph.arcs.front(); !used_arcs[arc_idx])
return *sorted_arc.first;
if (sorted_arcs.empty())
return original_arc;
return *(sorted_arcs.front().first);
};
auto all_arc_used = [&used_arcs](const MMU_Graph::Node &node) -> bool {
return std::all_of(node.arc_idxs.cbegin(), node.arc_idxs.cend(), [&used_arcs](const size_t &arc_idx) -> bool { return used_arcs[arc_idx]; });
};
@ -1166,29 +1240,58 @@ static std::vector<ExPolygons> extract_colored_segments(const MMU_Graph &graph,
if (arc.type == MMU_Graph::ARC_TYPE::NON_BORDER || used_arcs[arc_idx])
continue;
Linef process_line(node.point, graph.nodes[arc.to_idx].point);
Linef process_line(graph.nodes[arc.from_idx].point, graph.nodes[arc.to_idx].point);
used_arcs[arc_idx] = true;
std::vector<Linef> face_lines;
face_lines.emplace_back(process_line);
std::vector<std::pair<size_t, Linef>> arc_id_to_face_lines;
arc_id_to_face_lines.emplace_back(std::make_pair(arc_idx, process_line));
Vec2d start_p = process_line.a;
Linef p_vec = process_line;
const MMU_Graph::Arc *p_arc = &arc;
bool flag = false;
do {
const MMU_Graph::Arc& next = get_next(p_vec, *p_arc, arc.color);
size_t next_arc_idx = &next - &graph.arcs.front();
face_lines.emplace_back(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point);
if (used_arcs[next_arc_idx])
std::vector<const MMU_Graph::Arc *> nexts = get_next_arc(graph, used_arcs, p_vec, *p_arc, arc.color);
for (auto next : nexts) {
size_t next_arc_idx = next - &graph.arcs.front();
if (used_arcs[next_arc_idx]) {
flag = true;
break;
}
}
if (flag)
break;
used_arcs[next_arc_idx] = true;
p_vec = Linef(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point);
p_arc = &next;
for (auto next : nexts) {
size_t next_arc_idx = next - &graph.arcs.front();
arc_id_to_face_lines.emplace_back(std::make_pair(next_arc_idx, Linef(graph.nodes[next->from_idx].point, graph.nodes[next->to_idx].point)));
used_arcs[next_arc_idx] = true;
}
p_vec = Linef(graph.nodes[nexts.back()->from_idx].point, graph.nodes[nexts.back()->to_idx].point);
p_arc = nexts.back();
} while (graph.nodes[p_arc->to_idx].point != start_p || !all_arc_used(graph.nodes[p_arc->to_idx]));
if (Polygon poly = to_polygon(face_lines); poly.is_counter_clockwise() && poly.is_valid())
if (Polygon poly = to_polygon(arc_id_to_face_lines); poly.is_counter_clockwise() && poly.is_valid()) {
expolygons_segments[arc.color].emplace_back(std::move(poly));
} else{
while (arc_id_to_face_lines.size() > 1)
{
auto id_to_line = arc_id_to_face_lines.back();
used_arcs[id_to_line.first] = false;
arc_id_to_face_lines.pop_back();
Linef add_line(arc_id_to_face_lines.back().second.b, arc_id_to_face_lines.front().second.a);
arc_id_to_face_lines.emplace_back(std::make_pair(-1, add_line));
Polygon poly = to_polygon(arc_id_to_face_lines);
if (!is_profile_self_interaction(poly) && poly.is_counter_clockwise() && poly.is_valid()) {
expolygons_segments[arc.color].emplace_back(std::move(poly));
break;
}
arc_id_to_face_lines.pop_back();
}
}
}
}
return expolygons_segments;

View file

@ -57,16 +57,19 @@ namespace orientation {
// management and spatial index structures for acceleration.
class AutoOrienter {
public:
int face_count_hull;
OrientMesh *orient_mesh = NULL;
TriangleMesh* mesh;
TriangleMesh mesh_convex_hull;
Eigen::MatrixXf normals, normals_hull;
Eigen::MatrixXf normals, normals_quantize, normals_hull, normals_hull_quantize;
Eigen::VectorXf areas, areas_hull;
Eigen::VectorXf is_apperance; // whether a facet is outer apperance
Eigen::MatrixXf z_projected;
Eigen::VectorXf z_max, z_max_hull; // max of projected z
Eigen::VectorXf z_median; // median of projected z
Eigen::VectorXf z_mean; // mean of projected z
std::vector<Vec3f> face_normals;
std::vector<Vec3f> face_normals_hull;
OrientParams params;
@ -111,9 +114,9 @@ public:
{
orientations = { { 0,0,-1 } }; // original orientation
area_cumulation(normals, areas);
area_cumulation_accurate(face_normals, normals_quantize, areas, 10);
area_cumulation(normals_hull, areas_hull, 10);
area_cumulation_accurate(face_normals_hull, normals_hull_quantize, areas_hull, 10);
add_supplements();
@ -152,8 +155,23 @@ public:
if (progressind)
progressind(80);
//To avoid flipping, we need to verify if there are orientations with same unprintability.
Vec3f n1 = {0, 0, 1};
auto best_orientation = results_vector[0].first;
for (int i = 1; i< results_vector.size()-1; i++) {
if (abs(results_vector[i].second.unprintability - results_vector[0].second.unprintability) < EPSILON && abs(results_vector[0].first.dot(n1)-1) > EPSILON) {
if (abs(results_vector[i].first.dot(n1)-1) < EPSILON*EPSILON) {
best_orientation = n1;
break;
}
}
else {
break;
}
}
BOOST_LOG_TRIVIAL(info) << std::fixed << std::setprecision(6) << "best:" << best_orientation.transpose() << ", costs:" << results_vector[0].second.field_values();
std::cout << std::fixed << std::setprecision(6) << "best:" << best_orientation.transpose() << ", costs:" << results_vector[0].second.field_values() << std::endl;
@ -166,17 +184,16 @@ public:
{
int face_count = mesh->facets_count();
auto its = mesh->its;
auto face_normals = its_face_normals(its);
face_normals = its_face_normals(its);
areas = Eigen::VectorXf::Zero(face_count);
is_apperance = Eigen::VectorXf::Zero(face_count);
normals = Eigen::MatrixXf::Zero(face_count, 3);
normals_quantize = Eigen::MatrixXf::Zero(face_count, 3);
for (size_t i = 0; i < face_count; i++)
{
float area = its.facet_area(i);
if (params.NEGL_FACE_SIZE > 0 && area < params.NEGL_FACE_SIZE)
continue;
normals.row(i) = quantize_vec3f(face_normals[i]);
normals.row(i) = face_normals[i];
normals_quantize.row(i) = quantize_vec3f(face_normals[i]);
areas(i) = area;
is_apperance(i) = (its.get_property(i).type == EnumFaceTypes::eExteriorAppearance);
count_apperance += (is_apperance(i)==1);
@ -193,15 +210,17 @@ public:
int face_count = mesh_convex_hull.facets_count();
auto its = mesh_convex_hull.its;
auto face_normals = its_face_normals(its);
face_count_hull = mesh_convex_hull.facets_count();
face_normals_hull = its_face_normals(its);
areas_hull = Eigen::VectorXf::Zero(face_count);
normals_hull = Eigen::MatrixXf::Zero(face_count, 3);
normals_hull = Eigen::MatrixXf::Zero(face_count_hull, 3);
normals_hull_quantize = Eigen::MatrixXf::Zero(face_count_hull, 3);
for (size_t i = 0; i < face_count; i++)
{
float area = its.facet_area(i);
if (params.NEGL_FACE_SIZE > 0 && area < params.NEGL_FACE_SIZE)
continue;
normals_hull.row(i) = quantize_vec3f(face_normals[i]);
//We cannot use quantized vector here, the accumulated error will result in bad orientations.
normals_hull.row(i) = face_normals_hull[i];
normals_hull_quantize.row(i) = quantize_vec3f(face_normals_hull[i]);
areas_hull(i) = area;
}
}
@ -227,10 +246,41 @@ public:
for (size_t i = 0; i < num_directions; i++)
{
orientations.push_back(align_counts[i].first);
//orientations.push_back(its_face_normals(mesh->its)[i]);
BOOST_LOG_TRIVIAL(debug) << align_counts[i].first.transpose() << ", area: " << align_counts[i].second;
}
}
//This function is to make sure to return the accurate normal rather than quantized normal
void area_cumulation_accurate( std::vector<Vec3f>& normals_, const Eigen::MatrixXf& quantize_normals_, const Eigen::VectorXf& areas_, int num_directions = 10)
{
std::unordered_map<stl_normal, std::pair<std::vector<float>, Vec3f>, VecHash> alignments_;
Vec3f n1 = { 0, 0, 0 };
std::vector<float> current_areas = {0, 0};
// init to 0
for (size_t i = 0; i < areas_.size(); i++) {
alignments_.insert(std::pair(quantize_normals_.row(i), std::pair(current_areas, n1)));
}
// cumulate areas
for (size_t i = 0; i < areas_.size(); i++)
{
alignments_[quantize_normals_.row(i)].first[1] += areas_(i);
if (areas_(i) > alignments_[quantize_normals_.row(i)].first[0]){
alignments_[quantize_normals_.row(i)].second = normals_[i];
alignments_[quantize_normals_.row(i)].first[0] = areas_(i);
}
}
typedef std::pair<stl_normal, std::pair<std::vector<float>, Vec3f>> PAIR;
std::vector<PAIR> align_counts(alignments_.begin(), alignments_.end());
sort(align_counts.begin(), align_counts.end(), [](const PAIR& p1, const PAIR& p2) {return p1.second.first[1] > p2.second.first[1]; });
num_directions = std::min((size_t)num_directions, align_counts.size());
for (size_t i = 0; i < num_directions; i++)
{
orientations.push_back(align_counts[i].second.second);
BOOST_LOG_TRIVIAL(debug) << align_counts[i].second.second.transpose() << ", area: " << align_counts[i].second.first[1];
}
}
void add_supplements()
{
std::vector<Vec3f> vecs = { {0, 0, -1} ,{0.70710678, 0, -0.70710678},{0, 0.70710678, -0.70710678},
@ -246,7 +296,7 @@ public:
/// remove duplicate orientations
/// </summary>
/// <param name="tol">tolerance. default 0.01 =sin(0.57\degree)</param>
void remove_duplicates(float tol=0.01)
void remove_duplicates(double tol=0.0000001)
{
for (auto it = orientations.begin()+1; it < orientations.end(); )
{
@ -332,8 +382,14 @@ public:
float total_min_z = z_projected.minCoeff();
// filter bottom area
auto bottom_condition = z_max.array() < total_min_z + this->params.FIRST_LAY_H;
costs.bottom = bottom_condition.select(areas, 0).sum();
auto bottom_condition = z_max.array() < total_min_z + this->params.FIRST_LAY_H - EPSILON;
auto bottom_condition_hull = z_max_hull.array() < total_min_z + this->params.FIRST_LAY_H - EPSILON;
auto bottom_condition_2nd = z_max.array() < total_min_z + this->params.FIRST_LAY_H/2.f - EPSILON;
//The first layer is sliced on half of the first layer height.
//The bottom area is measured by accumulating first layer area with the facets area below first layer height.
//By combining these two factors, we can avoid the wrong orientation of large planar faces while not influence the
//orientations of complex objects with small bottom areas.
costs.bottom = bottom_condition.select(areas, 0).sum()*0.5 + bottom_condition_2nd.select(areas, 0).sum();
// filter overhang
Eigen::VectorXf normal_projection(normals.rows(), 1);// = this->normals.dot(orientation);
@ -342,7 +398,7 @@ public:
normal_projection(i) = normals.row(i).dot(orientation);
}
auto areas_appearance = areas.cwiseProduct((is_apperance * params.APPERANCE_FACE_SUPP + Eigen::VectorXf::Ones(is_apperance.rows(), is_apperance.cols())));
auto overhang_areas = ((normal_projection.array() < params.ASCENT) * (!bottom_condition)).select(areas_appearance, 0);
auto overhang_areas = ((normal_projection.array() < params.ASCENT) * (!bottom_condition_2nd)).select(areas_appearance, 0);
Eigen::MatrixXf inner = normal_projection.array() - params.ASCENT;
inner = inner.cwiseMin(0).cwiseAbs();
if (min_volume)
@ -378,7 +434,7 @@ public:
}
// bottom of convex hull
costs.bottom_hull = (z_max_hull.array()< total_min_z + this->params.FIRST_LAY_H).select(areas_hull, 0).sum();
costs.bottom_hull = (bottom_condition_hull).select(areas_hull, 0).sum();
// low angle faces
auto normal_projection_abs = normal_projection.cwiseAbs();

View file

@ -53,7 +53,7 @@ struct OrientParamsArea {
float TAR_E = 0.0115f;
float FIRST_LAY_H = 0.2f;//0.0475;
float VECTOR_TOL = -0.00083f;
float NEGL_FACE_SIZE = 0.1f;
float NEGL_FACE_SIZE = 0.01f;
float ASCENT = -0.5f;
float PLAFOND_ADV = 0.0599f;
float CONTOUR_AMOUNT = 0.0182427f;
@ -61,14 +61,14 @@ struct OrientParamsArea {
float height_offset = 2.3728f;
float height_log = 0.041375f;
float height_log_k = 1.9325457f;
float LAF_MAX = 0.9997f; // cos(1.4\degree) for low angle face
float LAF_MIN = 0.9703f; // cos(14\degree)
float TAR_LAF = 0.01f;
float LAF_MAX = 0.999f; // cos(1.4\degree) for low angle face 0.9997f
float LAF_MIN = 0.97f; // cos(14\degree) 0.9703f
float TAR_LAF = 0.001f; //0.01f
float TAR_PROJ_AREA = 0.1f;
float BOTTOM_MIN = 0.1f; // min bottom area. If lower than it the object may be unstable
float BOTTOM_MAX = 400; // max bottom area. If get to it the object is stable enough (further increase bottom area won't do more help)
float BOTTOM_MAX = 2000; // max bottom area. If get to it the object is stable enough (further increase bottom area won't do more help)
float height_to_bottom_hull_ratio_MIN = 1;
float BOTTOM_HULL_MAX = 600;// max bottom hull area
float BOTTOM_HULL_MAX = 2000;// max bottom hull area
float APPERANCE_FACE_SUPP=3; // penalty of generating supports on appearance face
float overhang_angle = 60.f;
@ -109,14 +109,14 @@ struct OrientParams {
float height_offset = 2.7417608343142073f;
float height_log = 0.06442030687034085f;
float height_log_k = 0.3933594673063997f;
float LAF_MAX = 0.9997f; // cos(1.4\degree) for low angle face
float LAF_MIN= 0.9703f; // cos(14\degree)
float TAR_LAF= 0.1f;
float LAF_MAX = 0.999f; // cos(1.4\degree) for low angle face //0.9997f;
float LAF_MIN= 0.9703f; // cos(14\degree) 0.9703f;
float TAR_LAF = 0.01f; //0.1f
float TAR_PROJ_AREA = 0.1f;
float BOTTOM_MIN = 0.1f; // min bottom area. If lower than it the objects may be unstable
float BOTTOM_MAX = 400;
float BOTTOM_MAX = 2000; //400
float height_to_bottom_hull_ratio_MIN = 1;
float BOTTOM_HULL_MAX = 600;// max bottom hull area to clip
float BOTTOM_HULL_MAX = 2000;// max bottom hull area to clip //600
float APPERANCE_FACE_SUPP=3; // penalty of generating supports on appearance face
float overhang_angle = 60.f;

View file

@ -4,6 +4,8 @@
#include "ShortestPath.hpp"
#include "VariableWidth.hpp"
#include "CurveAnalyzer.hpp"
#include "Clipper2Utils.hpp"
#include "Arachne/WallToolPaths.hpp"
#include <cmath>
#include <cassert>
@ -78,6 +80,51 @@ static void fuzzy_polygon(Polygon &poly, double fuzzy_skin_thickness, double fuz
poly.points = std::move(out);
}
// Thanks Cura developers for this function.
static void fuzzy_extrusion_line(Arachne::ExtrusionLine& ext_lines, double fuzzy_skin_thickness, double fuzzy_skin_point_dist)
{
const double min_dist_between_points = fuzzy_skin_point_dist * 3. / 4.; // hardcoded: the point distance may vary between 3/4 and 5/4 the supplied value
const double range_random_point_dist = fuzzy_skin_point_dist / 2.;
double dist_left_over = double(rand()) * (min_dist_between_points / 2) / double(RAND_MAX); // the distance to be traversed on the line before making the first new point
auto* p0 = &ext_lines.front();
std::vector<Arachne::ExtrusionJunction> out;
out.reserve(ext_lines.size());
for (auto& p1 : ext_lines) {
if (p0->p == p1.p) { // Connect endpoints.
out.emplace_back(p1.p, p1.w, p1.perimeter_index);
continue;
}
// 'a' is the (next) new point between p0 and p1
Vec2d p0p1 = (p1.p - p0->p).cast<double>();
double p0p1_size = p0p1.norm();
// so that p0p1_size - dist_last_point evaulates to dist_left_over - p0p1_size
double dist_last_point = dist_left_over + p0p1_size * 2.;
for (double p0pa_dist = dist_left_over; p0pa_dist < p0p1_size; p0pa_dist += min_dist_between_points + double(rand()) * range_random_point_dist / double(RAND_MAX)) {
double r = double(rand()) * (fuzzy_skin_thickness * 2.) / double(RAND_MAX) - fuzzy_skin_thickness;
out.emplace_back(p0->p + (p0p1 * (p0pa_dist / p0p1_size) + perp(p0p1).cast<double>().normalized() * r).cast<coord_t>(), p1.w, p1.perimeter_index);
dist_last_point = p0pa_dist;
}
dist_left_over = p0p1_size - dist_last_point;
p0 = &p1;
}
while (out.size() < 3) {
size_t point_idx = ext_lines.size() - 2;
out.emplace_back(ext_lines[point_idx].p, ext_lines[point_idx].w, ext_lines[point_idx].perimeter_index);
if (point_idx == 0)
break;
--point_idx;
}
if (ext_lines.back().p == ext_lines.front().p) // Connect endpoints.
out.front().p = out.back().p;
if (out.size() >= 3)
ext_lines.junctions = std::move(out);
}
using PerimeterGeneratorLoops = std::vector<PerimeterGeneratorLoop>;
static void lowpass_filter_by_paths_overhang_degree(ExtrusionPaths& paths) {
@ -230,7 +277,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
Polylines inside_polines = (it == lower_polygons_series->begin()) ?
intersection_pl({ polygon }, it->second) :
intersection_pl(remain_polines, it->second);
intersection_pl_2(remain_polines, it->second);
extrusion_paths_append(
paths,
std::move(inside_polines),
@ -243,7 +290,7 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
remain_polines = (it == lower_polygons_series->begin()) ?
diff_pl({ polygon }, it->second) :
diff_pl(remain_polines, it->second);
diff_pl_2(remain_polines, it->second);
if (remain_polines.size() == 0)
break;
@ -355,7 +402,229 @@ static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perime
return out;
}
void PerimeterGenerator::process()
static ClipperLib_Z::Paths clip_extrusion(const ClipperLib_Z::Path& subject, const ClipperLib_Z::Paths& clip, ClipperLib_Z::ClipType clipType)
{
ClipperLib_Z::Clipper clipper;
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint& e1bot, const ClipperLib_Z::IntPoint& e1top, const ClipperLib_Z::IntPoint& e2bot,
const ClipperLib_Z::IntPoint& e2top, ClipperLib_Z::IntPoint& pt) {
ClipperLib_Z::IntPoint start = e1bot;
ClipperLib_Z::IntPoint end = e1top;
if (start.z() <= 0 && end.z() <= 0) {
start = e2bot;
end = e2top;
}
assert(start.z() > 0 && end.z() > 0);
// Interpolate extrusion line width.
double length_sqr = (end - start).cast<double>().squaredNorm();
double dist_sqr = (pt - start).cast<double>().squaredNorm();
double t = std::sqrt(dist_sqr / length_sqr);
pt.z() = start.z() + coord_t((end.z() - start.z()) * t);
});
clipper.AddPath(subject, ClipperLib_Z::ptSubject, false);
clipper.AddPaths(clip, ClipperLib_Z::ptClip, true);
ClipperLib_Z::PolyTree clipped_polytree;
ClipperLib_Z::Paths clipped_paths;
clipper.Execute(clipType, clipped_polytree, ClipperLib_Z::pftNonZero, ClipperLib_Z::pftNonZero);
ClipperLib_Z::PolyTreeToPaths(clipped_polytree, clipped_paths);
// Clipped path could contain vertices from the clip with a Z coordinate equal to zero.
// For those vertices, we must assign value based on the subject.
// This happens only in sporadic cases.
for (ClipperLib_Z::Path& path : clipped_paths)
for (ClipperLib_Z::IntPoint& c_pt : path)
if (c_pt.z() == 0) {
// Now we must find the corresponding line on with this point is located and compute line width (Z coordinate).
if (subject.size() <= 2)
continue;
const Point pt(c_pt.x(), c_pt.y());
Point projected_pt_min;
auto it_min = subject.begin();
auto dist_sqr_min = std::numeric_limits<double>::max();
Point prev(subject.front().x(), subject.front().y());
for (auto it = std::next(subject.begin()); it != subject.end(); ++it) {
Point curr(it->x(), it->y());
Point projected_pt = pt.projection_onto(Line(prev, curr));
if (double dist_sqr = (projected_pt - pt).cast<double>().squaredNorm(); dist_sqr < dist_sqr_min) {
dist_sqr_min = dist_sqr;
projected_pt_min = projected_pt;
it_min = std::prev(it);
}
prev = curr;
}
assert(dist_sqr_min <= SCALED_EPSILON);
assert(std::next(it_min) != subject.end());
const Point pt_a(it_min->x(), it_min->y());
const Point pt_b(std::next(it_min)->x(), std::next(it_min)->y());
const double line_len = (pt_b - pt_a).cast<double>().norm();
const double dist = (projected_pt_min - pt_a).cast<double>().norm();
c_pt.z() = coord_t(double(it_min->z()) + (dist / line_len) * double(std::next(it_min)->z() - it_min->z()));
}
assert([&clipped_paths = std::as_const(clipped_paths)]() -> bool {
for (const ClipperLib_Z::Path& path : clipped_paths)
for (const ClipperLib_Z::IntPoint& pt : path)
if (pt.z() <= 0)
return false;
return true;
}());
return clipped_paths;
}
struct PerimeterGeneratorArachneExtrusion
{
Arachne::ExtrusionLine* extrusion = nullptr;
// Indicates if closed ExtrusionLine is a contour or a hole. Used it only when ExtrusionLine is a closed loop.
bool is_contour = false;
// Should this extrusion be fuzzyfied on path generation?
bool fuzzify = false;
};
static ExtrusionEntityCollection traverse_extrusions(const PerimeterGenerator& perimeter_generator, std::vector<PerimeterGeneratorArachneExtrusion>& pg_extrusions)
{
ExtrusionEntityCollection extrusion_coll;
for (PerimeterGeneratorArachneExtrusion& pg_extrusion : pg_extrusions) {
Arachne::ExtrusionLine* extrusion = pg_extrusion.extrusion;
if (extrusion->empty())
continue;
const bool is_external = extrusion->inset_idx == 0;
ExtrusionRole role = is_external ? erExternalPerimeter : erPerimeter;
if (pg_extrusion.fuzzify)
fuzzy_extrusion_line(*extrusion, scaled<float>(perimeter_generator.config->fuzzy_skin_thickness.value), scaled<float>(perimeter_generator.config->fuzzy_skin_point_distance.value));
ExtrusionPaths paths;
// detect overhanging/bridging perimeters
if (perimeter_generator.config->detect_overhang_wall && perimeter_generator.layer_id > perimeter_generator.object_config->raft_layers
&& !((perimeter_generator.object_config->enable_support || perimeter_generator.object_config->enforce_support_layers > 0) &&
perimeter_generator.object_config->support_top_z_distance.value == 0)) {
ClipperLib_Z::Path extrusion_path;
extrusion_path.reserve(extrusion->size());
for (const Arachne::ExtrusionJunction& ej : extrusion->junctions)
extrusion_path.emplace_back(ej.p.x(), ej.p.y(), ej.w);
ClipperLib_Z::Paths lower_slices_paths;
lower_slices_paths.reserve(perimeter_generator.lower_slices_polygons().size());
for (const Polygon& poly : perimeter_generator.lower_slices_polygons()) {
lower_slices_paths.emplace_back();
ClipperLib_Z::Path& out = lower_slices_paths.back();
out.reserve(poly.points.size());
for (const Point& pt : poly.points)
out.emplace_back(pt.x(), pt.y(), 0);
}
// get non-overhang paths by intersecting this loop with the grown lower slices
extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctIntersection), role,
is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
// get overhang paths by checking what parts of this loop fall
// outside the grown lower slices (thus where the distance between
// the loop centerline and original lower slices is >= half nozzle diameter
extrusion_paths_append(paths, clip_extrusion(extrusion_path, lower_slices_paths, ClipperLib_Z::ctDifference), erOverhangPerimeter,
perimeter_generator.overhang_flow);
// Reapply the nearest point search for starting point.
// We allow polyline reversal because Clipper may have randomly reversed polylines during clipping.
// Arachne sometimes creates extrusion with zero-length (just two same endpoints);
if (!paths.empty()) {
Point start_point = paths.front().first_point();
if (!extrusion->is_closed) {
// Especially for open extrusion, we need to select a starting point that is at the start
// or the end of the extrusions to make one continuous line. Also, we prefer a non-overhang
// starting point.
struct PointInfo
{
size_t occurrence = 0;
bool is_overhang = false;
};
std::unordered_map<Point, PointInfo, PointHash> point_occurrence;
for (const ExtrusionPath& path : paths) {
++point_occurrence[path.polyline.first_point()].occurrence;
++point_occurrence[path.polyline.last_point()].occurrence;
if (path.role() == erOverhangPerimeter) {
point_occurrence[path.polyline.first_point()].is_overhang = true;
point_occurrence[path.polyline.last_point()].is_overhang = true;
}
}
// Prefer non-overhang point as a starting point.
for (const std::pair<Point, PointInfo> pt : point_occurrence)
if (pt.second.occurrence == 1) {
start_point = pt.first;
if (!pt.second.is_overhang) {
start_point = pt.first;
break;
}
}
}
chain_and_reorder_extrusion_paths(paths, &start_point);
}
}
else {
extrusion_paths_append(paths, *extrusion, role, is_external ? perimeter_generator.ext_perimeter_flow : perimeter_generator.perimeter_flow);
}
// Append paths to collection.
if (!paths.empty()) {
if (extrusion->is_closed) {
ExtrusionLoop extrusion_loop(std::move(paths));
// Restore the orientation of the extrusion loop.
if (pg_extrusion.is_contour)
extrusion_loop.make_counter_clockwise();
else
extrusion_loop.make_clockwise();
for (auto it = std::next(extrusion_loop.paths.begin()); it != extrusion_loop.paths.end(); ++it) {
assert(it->polyline.points.size() >= 2);
assert(std::prev(it)->polyline.last_point() == it->polyline.first_point());
}
assert(extrusion_loop.paths.front().first_point() == extrusion_loop.paths.back().last_point());
extrusion_coll.append(std::move(extrusion_loop));
}
else {
// Because we are processing one ExtrusionLine all ExtrusionPaths should form one connected path.
// But there is possibility that due to numerical issue there is poss
assert([&paths = std::as_const(paths)]() -> bool {
for (auto it = std::next(paths.begin()); it != paths.end(); ++it)
if (std::prev(it)->polyline.last_point() != it->polyline.first_point())
return false;
return true;
}());
ExtrusionMultiPath multi_path;
multi_path.paths.emplace_back(std::move(paths.front()));
for (auto it_path = std::next(paths.begin()); it_path != paths.end(); ++it_path) {
if (multi_path.paths.back().last_point() != it_path->first_point()) {
extrusion_coll.append(ExtrusionMultiPath(std::move(multi_path)));
multi_path = ExtrusionMultiPath();
}
multi_path.paths.emplace_back(std::move(*it_path));
}
extrusion_coll.append(ExtrusionMultiPath(std::move(multi_path)));
}
}
}
return extrusion_coll;
}
void PerimeterGenerator::process_classic()
{
// other perimeters
m_mm3_per_mm = this->perimeter_flow.mm3_per_mm();
@ -562,7 +831,7 @@ void PerimeterGenerator::process()
//BBS: refer to superslicer
//store surface for top infill if only_one_wall_top
if (i == 0 && config->only_one_wall_top && this->upper_slices != NULL) {
if (i == 0 && i!=loop_number && config->only_one_wall_top && this->upper_slices != NULL) {
//split the polygons with top/not_top
//get the offset from solid surface anchor
coord_t offset_top_surface = scale_(1.5 * (config->wall_loops.value == 0 ? 0. : unscaled(double(ext_perimeter_width + perimeter_spacing * int(int(config->wall_loops.value) - int(1))))));
@ -577,9 +846,19 @@ void PerimeterGenerator::process()
//set the clip to a virtual "second perimeter"
fill_clip = offset_ex(last, -double(ext_perimeter_spacing));
// get the real top surface
ExPolygons top_polygons = diff_ex(last, grown_upper_slices, ApplySafetyOffset::Yes);
ExPolygons grown_lower_slices;
ExPolygons bridge_checker;
// BBS: check whether surface be bridge or not
if (this->lower_slices != NULL) {
grown_lower_slices =*this->lower_slices;
double bridge_offset = std::max(double(ext_perimeter_spacing), (double(perimeter_width)));
bridge_checker = offset_ex(diff_ex(last, grown_lower_slices, ApplySafetyOffset::Yes), 1.5 * bridge_offset);
}
ExPolygons delete_bridge = diff_ex(last, bridge_checker, ApplySafetyOffset::Yes);
ExPolygons top_polygons = diff_ex(delete_bridge, grown_upper_slices, ApplySafetyOffset::Yes);
//get the not-top surface, from the "real top" but enlarged by external_infill_margin (and the min_width_top_surface we removed a bit before)
ExPolygons temp_gap = diff_ex(top_polygons, fill_clip);
ExPolygons inner_polygons = diff_ex(last,
offset_ex(top_polygons, offset_top_surface + min_width_top_surface - double(ext_perimeter_spacing / 2)),
ApplySafetyOffset::Yes);
@ -591,6 +870,8 @@ void PerimeterGenerator::process()
double infill_spacing_unscaled = this->config->sparse_infill_line_width.value;
fill_clip = offset_ex(last, double(ext_perimeter_spacing / 2) - scale_(infill_spacing_unscaled / 2));
last = intersection_ex(inner_polygons, last);
if (has_gap_fill)
last = union_ex(last,temp_gap);
//{
// std::stringstream stri;
// stri << this->layer->id() << "_1_"<< i <<"_only_one_peri"<< ".svg";
@ -818,6 +1099,261 @@ void PerimeterGenerator::process()
} // for each island
}
// Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper
// "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling"
void PerimeterGenerator::process_arachne()
{
// other perimeters
m_mm3_per_mm = this->perimeter_flow.mm3_per_mm();
coord_t perimeter_spacing = this->perimeter_flow.scaled_spacing();
// external perimeters
m_ext_mm3_per_mm = this->ext_perimeter_flow.mm3_per_mm();
coord_t ext_perimeter_width = this->ext_perimeter_flow.scaled_width();
coord_t ext_perimeter_spacing = this->ext_perimeter_flow.scaled_spacing();
coord_t ext_perimeter_spacing2 = scaled<coord_t>(0.5f * (this->ext_perimeter_flow.spacing() + this->perimeter_flow.spacing()));
// overhang perimeters
m_mm3_per_mm_overhang = this->overhang_flow.mm3_per_mm();
// solid infill
coord_t solid_infill_spacing = this->solid_infill_flow.scaled_spacing();
// prepare grown lower layer slices for overhang detection
if (this->lower_slices != nullptr && this->config->detect_overhang_wall) {
// We consider overhang any part where the entire nozzle diameter is not supported by the
// lower layer, so we take lower slices and offset them by half the nozzle diameter used
// in the current layer
double nozzle_diameter = this->print_config->nozzle_diameter.get_at(this->config->wall_filament - 1);
m_lower_slices_polygons = offset(*this->lower_slices, float(scale_(+nozzle_diameter / 2)));
}
// we need to process each island separately because we might have different
// extra perimeters for each one
for (const Surface& surface : this->slices->surfaces) {
// detect how many perimeters must be generated for this island
int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops
ExPolygons last = offset_ex(surface.expolygon.simplify_p(m_scaled_resolution), -float(ext_perimeter_width / 2. - ext_perimeter_spacing / 2.));
Polygons last_p = to_polygons(last);
double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end());
Arachne::WallToolPathsParams input_params;
{
if (const auto& min_feature_size_opt = object_config->min_feature_size)
input_params.min_feature_size = min_feature_size_opt.value * 0.01 * min_nozzle_diameter;
if (const auto& min_bead_width_opt = object_config->min_bead_width)
input_params.min_bead_width = min_bead_width_opt.value * 0.01 * min_nozzle_diameter;
if (const auto& wall_transition_filter_deviation_opt = object_config->wall_transition_filter_deviation)
input_params.wall_transition_filter_deviation = wall_transition_filter_deviation_opt.value * 0.01 * min_nozzle_diameter;
if (const auto& wall_transition_length_opt = object_config->wall_transition_length)
input_params.wall_transition_length = wall_transition_length_opt.value * 0.01 * min_nozzle_diameter;
input_params.wall_transition_angle = this->object_config->wall_transition_angle.value;
input_params.wall_distribution_count = this->object_config->wall_distribution_count.value;
}
Arachne::WallToolPaths wallToolPaths(last_p, ext_perimeter_spacing, perimeter_spacing, coord_t(loop_number + 1), 0, layer_height, input_params);
std::vector<Arachne::VariableWidthLines> perimeters = wallToolPaths.getToolPaths();
loop_number = int(perimeters.size()) - 1;
#ifdef ARACHNE_DEBUG
{
static int iRun = 0;
export_perimeters_to_svg(debug_out_path("arachne-perimeters-%d-%d.svg", layer_id, iRun++), to_polygons(last), perimeters, union_ex(wallToolPaths.getInnerContour()));
}
#endif
// All closed ExtrusionLine should have the same the first and the last point.
// But in rare cases, Arachne produce ExtrusionLine marked as closed but without
// equal the first and the last point.
assert([&perimeters = std::as_const(perimeters)]() -> bool {
for (const Arachne::VariableWidthLines& perimeter : perimeters)
for (const Arachne::ExtrusionLine& el : perimeter)
if (el.is_closed && el.junctions.front().p != el.junctions.back().p)
return false;
return true;
}());
int start_perimeter = int(perimeters.size()) - 1;
int end_perimeter = -1;
int direction = -1;
bool is_outer_wall_first =
this->print_config->wall_infill_order == WallInfillOrder::OuterInnerInfill ||
this->print_config->wall_infill_order == WallInfillOrder::InfillOuterInner;
if (is_outer_wall_first) {
start_perimeter = 0;
end_perimeter = int(perimeters.size());
direction = 1;
}
std::vector<Arachne::ExtrusionLine*> all_extrusions;
for (int perimeter_idx = start_perimeter; perimeter_idx != end_perimeter; perimeter_idx += direction) {
if (perimeters[perimeter_idx].empty())
continue;
for (Arachne::ExtrusionLine& wall : perimeters[perimeter_idx])
all_extrusions.emplace_back(&wall);
}
// Find topological order with constraints from extrusions_constrains.
std::vector<size_t> blocked(all_extrusions.size(), 0); // Value indicating how many extrusions it is blocking (preceding extrusions) an extrusion.
std::vector<std::vector<size_t>> blocking(all_extrusions.size()); // Each extrusion contains a vector of extrusions that are blocked by this extrusion.
std::unordered_map<const Arachne::ExtrusionLine*, size_t> map_extrusion_to_idx;
for (size_t idx = 0; idx < all_extrusions.size(); idx++)
map_extrusion_to_idx.emplace(all_extrusions[idx], idx);
auto extrusions_constrains = Arachne::WallToolPaths::getRegionOrder(all_extrusions, is_outer_wall_first);
for (auto [before, after] : extrusions_constrains) {
auto after_it = map_extrusion_to_idx.find(after);
++blocked[after_it->second];
blocking[map_extrusion_to_idx.find(before)->second].emplace_back(after_it->second);
}
std::vector<bool> processed(all_extrusions.size(), false); // Indicate that the extrusion was already processed.
Point current_position = all_extrusions.empty() ? Point::Zero() : all_extrusions.front()->junctions.front().p; // Some starting position.
std::vector<PerimeterGeneratorArachneExtrusion> ordered_extrusions; // To store our result in. At the end we'll std::swap.
ordered_extrusions.reserve(all_extrusions.size());
while (ordered_extrusions.size() < all_extrusions.size()) {
size_t best_candidate = 0;
double best_distance_sqr = std::numeric_limits<double>::max();
bool is_best_closed = false;
std::vector<size_t> available_candidates;
for (size_t candidate = 0; candidate < all_extrusions.size(); ++candidate) {
if (processed[candidate] || blocked[candidate])
continue; // Not a valid candidate.
available_candidates.push_back(candidate);
}
std::sort(available_candidates.begin(), available_candidates.end(), [&all_extrusions](const size_t a_idx, const size_t b_idx) -> bool {
return all_extrusions[a_idx]->is_closed < all_extrusions[b_idx]->is_closed;
});
for (const size_t candidate_path_idx : available_candidates) {
auto& path = all_extrusions[candidate_path_idx];
if (path->junctions.empty()) { // No vertices in the path. Can't find the start position then or really plan it in. Put that at the end.
if (best_distance_sqr == std::numeric_limits<double>::max()) {
best_candidate = candidate_path_idx;
is_best_closed = path->is_closed;
}
continue;
}
const Point candidate_position = path->junctions.front().p;
double distance_sqr = (current_position - candidate_position).cast<double>().norm();
if (distance_sqr < best_distance_sqr) { // Closer than the best candidate so far.
if (path->is_closed || (!path->is_closed && best_distance_sqr != std::numeric_limits<double>::max()) || (!path->is_closed && !is_best_closed)) {
best_candidate = candidate_path_idx;
best_distance_sqr = distance_sqr;
is_best_closed = path->is_closed;
}
}
}
auto& best_path = all_extrusions[best_candidate];
ordered_extrusions.push_back({ best_path, best_path->is_contour(), false });
processed[best_candidate] = true;
for (size_t unlocked_idx : blocking[best_candidate])
blocked[unlocked_idx]--;
if (!best_path->junctions.empty()) { //If all paths were empty, the best path is still empty. We don't upate the current position then.
if (best_path->is_closed)
current_position = best_path->junctions[0].p; //We end where we started.
else
current_position = best_path->junctions.back().p; //Pick the other end from where we started.
}
}
if (this->layer_id > 0 && this->config->fuzzy_skin != FuzzySkinType::None) {
std::vector<PerimeterGeneratorArachneExtrusion*> closed_loop_extrusions;
for (PerimeterGeneratorArachneExtrusion& extrusion : ordered_extrusions)
if (extrusion.extrusion->inset_idx == 0) {
if (extrusion.extrusion->is_closed && this->config->fuzzy_skin == FuzzySkinType::External) {
closed_loop_extrusions.emplace_back(&extrusion);
}
else {
extrusion.fuzzify = true;
}
}
if (this->config->fuzzy_skin == FuzzySkinType::External) {
ClipperLib_Z::Paths loops_paths;
loops_paths.reserve(closed_loop_extrusions.size());
for (const auto& cl_extrusion : closed_loop_extrusions) {
assert(cl_extrusion->extrusion->junctions.front() == cl_extrusion->extrusion->junctions.back());
size_t loop_idx = &cl_extrusion - &closed_loop_extrusions.front();
ClipperLib_Z::Path loop_path;
loop_path.reserve(cl_extrusion->extrusion->junctions.size() - 1);
for (auto junction_it = cl_extrusion->extrusion->junctions.begin(); junction_it != std::prev(cl_extrusion->extrusion->junctions.end()); ++junction_it)
loop_path.emplace_back(junction_it->p.x(), junction_it->p.y(), loop_idx);
loops_paths.emplace_back(loop_path);
}
ClipperLib_Z::Clipper clipper;
clipper.AddPaths(loops_paths, ClipperLib_Z::ptSubject, true);
ClipperLib_Z::PolyTree loops_polytree;
clipper.Execute(ClipperLib_Z::ctUnion, loops_polytree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
for (const ClipperLib_Z::PolyNode* child_node : loops_polytree.Childs) {
// The whole contour must have the same index.
coord_t polygon_idx = child_node->Contour.front().z();
bool has_same_idx = std::all_of(child_node->Contour.begin(), child_node->Contour.end(),
[&polygon_idx](const ClipperLib_Z::IntPoint& point) -> bool { return polygon_idx == point.z(); });
if (has_same_idx)
closed_loop_extrusions[polygon_idx]->fuzzify = true;
}
}
}
if (ExtrusionEntityCollection extrusion_coll = traverse_extrusions(*this, ordered_extrusions); !extrusion_coll.empty())
this->loops->append(extrusion_coll);
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
if (offset_ex(infill_contour, -float(spacing / 2.)).empty())
infill_contour.clear(); // Infill region is too small, so let's filter it out.
// create one more offset to be used as boundary for fill
// we offset by half the perimeter spacing (to get to the actual infill boundary)
// and then we offset back and forth by half the infill spacing to only consider the
// non-collapsing regions
coord_t inset =
(loop_number < 0) ? 0 :
(loop_number == 0) ?
// one loop
ext_perimeter_spacing :
// two or more loops?
perimeter_spacing;
inset = coord_t(scale_(this->config->infill_wall_overlap.get_abs_value(unscale<double>(inset))));
Polygons pp;
for (ExPolygon& ex : infill_contour)
ex.simplify_p(m_scaled_resolution, &pp);
// collapse too narrow infill areas
const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
// append infill areas to fill_surfaces
this->fill_surfaces->append(
offset2_ex(
union_ex(pp),
float(-min_perimeter_infill_spacing / 2.),
float(inset + min_perimeter_infill_spacing / 2.)),
stInternal);
// BBS: get the no-overlap infill expolygons
{
append(*this->fill_no_overlap, offset2_ex(
union_ex(pp),
float(-min_perimeter_infill_spacing / 2.),
float(+min_perimeter_infill_spacing / 2.)));
}
}
}
bool PerimeterGeneratorLoop::is_internal_contour() const
{
// An internal contour is a contour containing no other contours

View file

@ -66,13 +66,15 @@ public:
m_ext_mm3_per_mm(-1), m_mm3_per_mm(-1), m_mm3_per_mm_overhang(-1), m_ext_mm3_per_mm_smaller_width(-1)
{}
void process();
void process_classic();
void process_arachne();
double ext_mm3_per_mm() const { return m_ext_mm3_per_mm; }
double mm3_per_mm() const { return m_mm3_per_mm; }
double mm3_per_mm_overhang() const { return m_mm3_per_mm_overhang; }
//BBS
double smaller_width_ext_mm3_per_mm() const { return m_ext_mm3_per_mm_smaller_width; }
Polygons lower_slices_polygons() const { return m_lower_slices_polygons; }
private:
std::map<int, Polygons> generate_lower_polygons_series(float width);
@ -85,6 +87,7 @@ private:
double m_mm3_per_mm_overhang;
//BBS
double m_ext_mm3_per_mm_smaller_width;
Polygons m_lower_slices_polygons;
};
}

View file

@ -8,8 +8,7 @@
#include <string>
#include <sstream>
#include <unordered_map>
#include <Eigen/Geometry>
#include <Eigen/Geometry>
#include "LocalesUtils.hpp"

View file

@ -521,10 +521,10 @@ void Preset::save(DynamicPrintConfig* parent_config)
ConfigOption *opt_dst = temp_config.option(option, true);
opt_dst->set(opt_src);
}
temp_config.save_to_json(this->file, this->name, from_str, this->version.to_string());
temp_config.save_to_json(this->file, this->name, from_str, this->version.to_string(), this->custom_defined);
}
else
this->config.save_to_json(this->file, this->name, from_str, this->version.to_string());
this->config.save_to_json(this->file, this->name, from_str, this->version.to_string(), this->custom_defined);
fs::path idx_file(this->file);
idx_file.replace_extension(".info");
@ -662,6 +662,28 @@ std::string Preset::get_printer_type(PresetBundle *preset_bundle)
return "";
}
std::string Preset::get_current_printer_type(PresetBundle *preset_bundle)
{
if (preset_bundle) {
auto config = &(this->config);
std::string vendor_name;
for (auto vendor_profile : preset_bundle->vendors) {
for (auto vendor_model : vendor_profile.second.models)
if (vendor_model.name == config->opt_string("printer_model")) {
vendor_name = vendor_profile.first;
return vendor_model.model_id;
}
}
}
return "";
}
bool Preset::is_custom_defined()
{
if (custom_defined == "1")
return true;
return false;
}
bool Preset::is_bbl_vendor_preset(PresetBundle *preset_bundle)
{
@ -684,11 +706,11 @@ bool Preset::is_bbl_vendor_preset(PresetBundle *preset_bundle)
}
static std::vector<std::string> s_Preset_print_options {
"layer_height", "initial_layer_print_height", "wall_loops", "slice_closing_radius", "spiral_mode",
"layer_height", "initial_layer_print_height", "wall_loops", "slice_closing_radius", "spiral_mode", "slicing_mode",
"top_shell_layers", "top_shell_thickness", "bottom_shell_layers", "bottom_shell_thickness",
"reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall",
"ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall",
"seam_position", "wall_infill_order", "sparse_infill_density", "sparse_infill_pattern", "top_surface_pattern", "bottom_surface_pattern",
"infill_direction",
"infill_direction", "bridge_angle",
"minimum_sparse_infill_area", "reduce_infill_retraction",
"ironing_type", "ironing_flow", "ironing_speed", "ironing_spacing",
"max_travel_detour_distance",
@ -703,13 +725,13 @@ static std::vector<std::string> s_Preset_print_options {
"default_jerk", "outer_wall_jerk", "inner_wall_jerk", "top_surface_jerk", "initial_layer_jerk","travel_jerk",
"brim_width", "brim_object_gap", "brim_type", "enable_support", "support_type", "support_threshold_angle", "enforce_support_layers",
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
"support_base_pattern", "support_base_pattern_spacing", "support_style",
"support_base_pattern", "support_base_pattern_spacing", "support_expansion", "support_style",
// BBS
//"independent_support_layer_height",
"support_angle", "support_interface_top_layers", "support_interface_bottom_layers",
"support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern",
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "max_bridge_length", "print_sequence",
"filename_format", "wall_filament",
"filename_format", "wall_filament", "support_bottom_z_distance",
"sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament",
"ooze_prevention", "standby_temperature_delta", "interface_shells", "line_width", "initial_layer_line_width",
"inner_wall_line_width", "outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width",
@ -719,20 +741,21 @@ static std::vector<std::string> s_Preset_print_options {
"wipe_tower_no_sparse_layers", "compatible_printers", "compatible_printers_condition", "inherits",
"flush_into_infill", "flush_into_objects", "flush_into_support",
// BBS
"tree_support_branch_angle", "tree_support_with_infill", "tree_support_wall_count", "tree_support_branch_distance",
"tree_support_branch_angle", "tree_support_wall_count", "tree_support_branch_distance",
"tree_support_branch_diameter",
"detect_narrow_internal_solid_infill",
"gcode_add_line_number", "enable_arc_fitting", "infill_combination", "adaptive_layer_height",
"gcode_add_line_number", "enable_arc_fitting", "infill_combination", /*"adaptive_layer_height",*/
"support_bottom_interface_spacing", "enable_overhang_speed", "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed",
"initial_layer_infill_speed", "only_one_wall_top", "only_one_wall_first_layer",
"timelapse_type",
"initial_layer_infill_speed", "only_one_wall_top",
"timelapse_type", "internal_bridge_support_thickness",
"wall_generator", "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
"wall_distribution_count", "min_feature_size", "min_bead_width",
//SoftFever
"top_solid_infill_flow_ratio","bottom_solid_infill_flow_ratio"
"top_solid_infill_flow_ratio","bottom_solid_infill_flow_ratio","only_one_wall_first_layer"
};
static std::vector<std::string> s_Preset_filament_options {
/*"filament_colour", */"filament_diameter", "filament_type", "filament_soluble", "filament_is_support", "filament_max_volumetric_speed",
/*"filament_colour", */ "default_filament_colour","filament_diameter", "filament_type", "filament_soluble", "filament_is_support", "filament_max_volumetric_speed",
"filament_flow_ratio", "enable_pressure_advance", "pressure_advance", "filament_density", "filament_cost", "filament_minimal_purge_on_wipe_tower",
"chamber_temperature", "nozzle_temperature", "nozzle_temperature_initial_layer",
// BBS
@ -762,14 +785,14 @@ static std::vector<std::string> s_Preset_machine_limits_options {
static std::vector<std::string> s_Preset_printer_options {
"printer_technology",
"printable_area", "bed_exclude_area", "gcode_flavor","z_lift_type",
"printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor", "z_lift_type",
"single_extruder_multi_material", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "layer_change_gcode", "change_filament_gcode",
"printer_model", "printer_variant", "printable_height", "extruder_clearance_radius", "extruder_clearance_max_radius","extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod",
"default_print_profile", "inherits",
"silent_mode",
// BBS
"scan_first_layer", "machine_load_filament_time", "machine_unload_filament_time", "machine_pause_gcode", "template_custom_gcode",
"nozzle_type", "nozzle_diameter", "auxiliary_fan", "nozzle_volume",
"nozzle_type", "nozzle_hrc","nozzle_diameter", "auxiliary_fan", "nozzle_volume","upward_compatible_machine",
//SoftFever
"host_type", "print_host", "printhost_apikey",
"printhost_cafile","printhost_port","printhost_authorization_type",
@ -848,7 +871,7 @@ static std::vector<std::string> s_Preset_sla_material_options {
static std::vector<std::string> s_Preset_sla_printer_options {
"printer_technology",
"printable_area", "printable_height",
"printable_area","bed_custom_texture", "bed_custom_model", "printable_height",
"display_width", "display_height", "display_pixels_x", "display_pixels_y",
"display_mirror_x", "display_mirror_y",
"display_orientation",
@ -1024,14 +1047,20 @@ void PresetCollection::load_presets(
}
preset.version = *version;
if (key_values.find(BBL_JSON_KEY_IS_CUSTOM) != key_values.end())
preset.custom_defined = key_values[BBL_JSON_KEY_IS_CUSTOM];
//BBS: use inherit config as the base
Preset* inherit_preset = nullptr;
ConfigOption* inherits_config = config.option(BBL_JSON_KEY_INHERITS);
// check inherits_config
if (inherits_config) {
ConfigOptionString * option_str = dynamic_cast<ConfigOptionString *> (inherits_config);
std::string inherits_value = option_str->value;
inherit_preset = this->find_preset(inherits_value, false, true);
} else {
;
}
const Preset& default_preset = this->default_preset_for(config);
if (inherit_preset) {
@ -1039,19 +1068,22 @@ void PresetCollection::load_presets(
preset.filament_id = inherit_preset->filament_id;
}
else {
if (!preset.is_custom_defined()) {
BOOST_LOG_TRIVIAL(error) << boost::format("can not find parent for config %1%!")%preset.file;
continue;
}
//should not happen
BOOST_LOG_TRIVIAL(error) << boost::format("can not find parent for config %1%!")%preset.file;
//BOOST_LOG_TRIVIAL(error) << boost::format("can not find parent for config %1%!")%preset.file;
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
//preset.config = default_preset.config;
continue;
preset.config = default_preset.config;
}
preset.config.apply(std::move(config));
Preset::normalize(preset.config);
// Report configuration fields, which are misplaced into a wrong group.
std::string incorrect_keys = Preset::remove_invalid_keys(preset.config, default_preset.config);
if (! incorrect_keys.empty())
if (!incorrect_keys.empty())
BOOST_LOG_TRIVIAL(error) << "Error in a preset file: The preset \"" <<
preset.file << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
preset.file << "\" contains the following incorrect keys: " << incorrect_keys << ", which were removed";
preset.loaded = true;
//BBS: add some workaround for previous incorrect settings
if ((!preset.setting_id.empty())&&(preset.setting_id == preset.base_id))
@ -1359,25 +1391,29 @@ void PresetCollection::save_user_presets(const std::string& dir_path, const std:
if (!preset->is_user()) continue;
preset->file = path_from_name(preset->name);
//BBS: only save difference for user preset
std::string inherits = Preset::inherits(preset->config);
if (inherits.empty()) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" can not find inherits for %1% , should not happen")%preset->name;
// BBS add sync info
preset->sync_info = "delete";
need_to_delete_list.push_back(preset->setting_id);
delete_name_list.push_back(preset->name);
continue;
}
Preset* parent_preset = this->find_preset(inherits, false, true);
if (!parent_preset) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" can not find parent preset for %1% , inherits %2%")%preset->name %inherits;
continue;
}
if (preset->is_custom_defined()) {
preset->save(nullptr);
} else {
//BBS: only save difference for user preset
std::string inherits = Preset::inherits(preset->config);
if (inherits.empty()) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" can not find inherits for %1% , should not happen")%preset->name;
// BBS add sync info
preset->sync_info = "delete";
need_to_delete_list.push_back(preset->setting_id);
delete_name_list.push_back(preset->name);
continue;
}
Preset* parent_preset = this->find_preset(inherits, false, true);
if (!parent_preset) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" can not find parent preset for %1% , inherits %2%")%preset->name %inherits;
continue;
}
if (preset->base_id.empty())
preset->base_id = parent_preset->setting_id;
preset->save(&(parent_preset->config));
if (preset->base_id.empty())
preset->base_id = parent_preset->setting_id;
preset->save(&(parent_preset->config));
}
}
for (auto delete_name: delete_name_list)
@ -1603,11 +1639,11 @@ bool PresetCollection::validate_printers(const std::string &name, DynamicPrintCo
// Load a preset from an already parsed config file, insert it into the sorted sequence of presets
// and select it, losing previous modifications.
Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select)
Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select, Semver file_version, bool is_custom_defined)
{
DynamicPrintConfig cfg(this->default_preset().config);
cfg.apply_only(config, cfg.keys(), true);
return this->load_preset(path, name, std::move(cfg), select);
return this->load_preset(path, name, std::move(cfg), select, file_version, is_custom_defined);
}
static bool profile_print_params_same(const DynamicPrintConfig &cfg_old, const DynamicPrintConfig &cfg_new)
@ -1874,7 +1910,7 @@ std::pair<Preset*, bool> PresetCollection::load_external_preset(
return std::make_pair(&preset, false);
}
Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select)
Preset& PresetCollection::load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select, Semver file_version, bool is_custom_defined)
{
lock();
auto it = this->find_preset_internal(name);
@ -1889,6 +1925,10 @@ Preset& PresetCollection::load_preset(const std::string &path, const std::string
preset.config = std::move(config);
preset.loaded = true;
preset.is_dirty = false;
preset.custom_defined = is_custom_defined ? "1": "0";
//BBS
if (file_version.valid())
preset.version = file_version;
if (select)
this->select_preset_by_name(name, true);
unlock();

View file

@ -29,6 +29,7 @@
//BBS: add json support
#define BBL_JSON_KEY_VERSION "version"
#define BBL_JSON_KEY_IS_CUSTOM "is_custom_defined"
#define BBL_JSON_KEY_URL "url"
#define BBL_JSON_KEY_NAME "name"
#define BBL_JSON_KEY_DESCRIPTION "description"
@ -229,6 +230,7 @@ public:
std::string user_id; // preset user_id
std::string base_id; // base id of preset
std::string sync_info; // enum: "delete", "create", "update", ""
std::string custom_defined; // enum: "1", "0", ""
long long updated_time{0}; //last updated time
std::map<std::string, std::string> key_values;
@ -296,10 +298,14 @@ public:
// special for upport G and Support W
std::string get_filament_type(std::string &display_filament_type);
std::string get_printer_type(PresetBundle *preset_bundle);
std::string get_printer_type(PresetBundle *preset_bundle); // get edited preset type
std::string get_current_printer_type(PresetBundle *preset_bundle); // get current preset type
bool is_custom_defined();
bool is_bbl_vendor_preset(PresetBundle *preset_bundle);
static const std::vector<std::string>& print_options();
static const std::vector<std::string>& filament_options();
// Printer options contain the nozzle options.
@ -434,8 +440,8 @@ public:
// Load a preset from an already parsed config file, insert it into the sorted sequence of presets
// and select it, losing previous modifications.
Preset& load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select = true);
Preset& load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select = true);
Preset& load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select = true, Semver file_version = Semver(), bool is_custom_defined = false);
Preset& load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select = true, Semver file_version = Semver(), bool is_custom_defined = false);
// Returns a loaded preset, returns true if an existing preset was selected AND modified from config.
// In that case the successive filament loaded for a multi material printer should not be modified, but

View file

@ -13,7 +13,7 @@
#include <boost/filesystem.hpp>
#include <boost/algorithm/clamp.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/nowide/cenv.hpp>
#include <boost/nowide/cstdio.hpp>
#include <boost/nowide/fstream.hpp>
@ -1165,7 +1165,12 @@ void PresetBundle::load_installed_filaments(AppConfig &config)
if (!add_default_materials)
continue;
for (auto default_filament: printer.vendor->models[0].default_materials)
const VendorProfile::PrinterModel *printer_model = PresetUtils::system_printer_model(printer);
if (!printer_model) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": can not find printer_model for printer %1%")%printer.name;
continue;
}
for (auto default_filament: printer_model->default_materials)
{
Preset* filament = filaments.find_preset(default_filament, false, true);
if (filament && filament->is_system)
@ -1256,8 +1261,6 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
sla_prints.select_preset_by_name_strict(initial_sla_print_profile_name);
sla_materials.select_preset_by_name_strict(initial_sla_material_profile_name);
// BBS: filament_presets are now considered as project config instead of app config
#if 0
// Load the names of the other filament profiles selected for a multi-material printer.
// Load it even if the current printer technology is SLA.
// The possibly excessive filament names will be later removed with this->update_multi_material_filament_presets()
@ -1270,7 +1273,22 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
break;
this->filament_presets.emplace_back(remove_ini_suffix(config.get("presets", name)));
}
#endif
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;
}
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("|"));
auto flush_volumes_matrix = matrix | boost::adaptors::transformed(boost::lexical_cast<double, std::string>);
project_config.option<ConfigOptionFloats>("flush_volumes_matrix")->values = std::vector<double>(flush_volumes_matrix.begin(), flush_volumes_matrix.end());
}
if (config.has("presets", "flush_volumes_vector")) {
boost::algorithm::split(matrix, config.get("presets", "flush_volumes_vector"), boost::algorithm::is_any_of("|"));
auto flush_volumes_vector = matrix | boost::adaptors::transformed(boost::lexical_cast<double, std::string>);
project_config.option<ConfigOptionFloats>("flush_volumes_vector")->values = std::vector<double>(flush_volumes_vector.begin(), flush_volumes_vector.end());
}
// Update visibility of presets based on their compatibility with the active printer.
// Always try to select a compatible print and filament preset to the current printer preset,
@ -1298,6 +1316,15 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
}
}
std::string first_visible_filament_name;
for (auto & fp : filament_presets) {
if (auto it = filaments.find_preset_internal(fp); it == filaments.end() || !it->is_visible || !it->is_compatible) {
if (first_visible_filament_name.empty())
first_visible_filament_name = filaments.first_compatible().name;
fp = first_visible_filament_name;
}
}
// Parse the initial physical printer name.
std::string initial_physical_printer_name = remove_ini_suffix(config.get("presets", "physical_printer"));
@ -1322,6 +1349,17 @@ void PresetBundle::export_selections(AppConfig &config)
sprintf(name, "filament_%u", i);
config.set("presets", name, filament_presets[i]);
}
CNumericLocalesSetter locales_setter;
std::string filament_colors = boost::algorithm::join(project_config.option<ConfigOptionStrings>("filament_colour")->values, ",");
config.set("presets", "filament_colors", filament_colors);
std::string flush_volumes_matrix = boost::algorithm::join(project_config.option<ConfigOptionFloats>("flush_volumes_matrix")->values |
boost::adaptors::transformed(static_cast<std::string (*)(double)>(std::to_string)),
"|");
config.set("presets", "flush_volumes_matrix", flush_volumes_matrix);
std::string flush_volumes_vector = boost::algorithm::join(project_config.option<ConfigOptionFloats>("flush_volumes_vector")->values |
boost::adaptors::transformed(static_cast<std::string (*)(double)>(std::to_string)),
"|");
config.set("presets", "flush_volumes_vector", flush_volumes_vector);
config.set("presets", PRESET_PRINTER_NAME, printers.get_selected_preset_name());
// BBS
@ -1357,7 +1395,7 @@ void PresetBundle::set_num_filaments(unsigned int n, std::string new_color)
update_multi_material_filament_presets();
}
unsigned int PresetBundle::sync_ams_list()
unsigned int PresetBundle::sync_ams_list(unsigned int &unknowns)
{
std::vector<std::string> filament_presets;
std::vector<std::string> filament_colors;
@ -1367,7 +1405,16 @@ unsigned int PresetBundle::sync_ams_list()
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;
continue;
auto filament_type = "Generic " + ams.opt_string("filament_type", 0u);
iter = std::find_if(filaments.begin(), filaments.end(), [&filament_type](auto &f) { return f.is_compatible && f.is_system
&& boost::algorithm::starts_with(f.name, filament_type);
});
if (iter == filaments.end())
iter = std::find_if(filaments.begin(), filaments.end(), [&filament_type](auto &f) { return f.is_compatible && f.is_system; });
if (iter == filaments.end())
continue;
++unknowns;
filament_id = iter->filament_id;
}
filament_presets.push_back(iter->name);
filament_colors.push_back(filament_color);
@ -1449,10 +1496,15 @@ DynamicPrintConfig PresetBundle::full_fff_config() const
std::vector<std::string> compatible_prints_condition;
std::vector<std::string> inherits;
std::vector<std::string> filament_ids;
std::vector<std::string> print_compatible_printers;
//BBS: add logic for settings check between different system presets
std::vector<std::string> different_settings;
std::string different_print_settings, different_printer_settings;
compatible_printers_condition.emplace_back(this->prints.get_edited_preset().compatible_printers_condition());
const ConfigOptionStrings* compatible_printers = (const_cast<PresetBundle*>(this))->prints.get_edited_preset().config.option<ConfigOptionStrings>("compatible_printers", false);
if (compatible_printers)
print_compatible_printers = compatible_printers->values;
//BBS: add logic for settings check between different system presets
std::string print_inherits = this->prints.get_edited_preset().inherits();
inherits .emplace_back(print_inherits);
@ -1627,6 +1679,7 @@ DynamicPrintConfig PresetBundle::full_fff_config() const
add_if_some_non_empty(std::move(inherits), "inherits_group");
//BBS: add logic for settings check between different system presets
add_if_some_non_empty(std::move(different_settings), "different_settings_to_system");
add_if_some_non_empty(std::move(print_compatible_printers), "print_compatible_printers");
out.option<ConfigOptionEnumGeneric>("printer_technology", true)->value = ptFFF;
return out;
@ -1756,20 +1809,19 @@ ConfigSubstitutions PresetBundle::load_config_file(const std::string &path, Forw
// Load a config file from a boost property_tree. This is a private method called from load_config_file.
// is_external == false on if called from ConfigWizard
void PresetBundle::load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config, Semver file_version)
void PresetBundle::load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config, Semver file_version, bool selected, bool is_custom_defined)
{
PrinterTechnology printer_technology = Preset::printer_technology(config);
// The "compatible_printers" field should not have been exported into a config.ini or a G-code anyway,
// but some of the alpha versions of Slic3r did.
{
auto clear_compatible_printers = [](DynamicPrintConfig& config){
ConfigOption *opt_compatible = config.optptr("compatible_printers");
if (opt_compatible != nullptr) {
assert(opt_compatible->type() == coStrings);
if (opt_compatible->type() == coStrings)
static_cast<ConfigOptionStrings*>(opt_compatible)->values.clear();
}
}
};
clear_compatible_printers(config);
#if 0
size_t num_extruders = (printer_technology == ptFFF) ?
@ -1791,6 +1843,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
std::vector<std::string> compatible_prints_condition_values = std::move(config.option<ConfigOptionStrings>("compatible_process_expression_group", true)->values);
std::vector<std::string> inherits_values = std::move(config.option<ConfigOptionStrings>("inherits_group", true)->values);
std::vector<std::string> filament_ids = std::move(config.option<ConfigOptionStrings>("filament_ids", true)->values);
std::vector<std::string> print_compatible_printers = std::move(config.option<ConfigOptionStrings>("print_compatible_printers", true)->values);
//BBS: add different settings check logic
bool has_different_settings_to_system = config.option("different_settings_to_system")?true:false;
std::vector<std::string> different_values = std::move(config.option<ConfigOptionStrings>("different_settings_to_system", true)->values);
@ -1826,7 +1879,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
[&config, &inherits, &inherits_values,
&compatible_printers_condition, &compatible_printers_condition_values,
&compatible_prints_condition, &compatible_prints_condition_values,
is_external, &name, &name_or_path, file_version]
is_external, &name, &name_or_path, file_version, selected, is_custom_defined]
(PresetCollection &presets, size_t idx, const std::string &key, const std::set<std::string> &different_keys, std::string filament_id) {
// Split the "compatible_printers_condition" and "inherits" values one by one from a single vector to the print & printer profiles.
inherits = inherits_values[idx];
@ -1838,7 +1891,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
if (is_external)
presets.load_external_preset(name_or_path, name, config.opt_string(key, true), config, different_keys, PresetCollection::LoadAndSelect::Always, file_version, filament_id);
else
presets.load_preset(presets.path_from_name(name), name, config).save(nullptr);
presets.load_preset(presets.path_from_name(name), name, config, selected, file_version, is_custom_defined).save(nullptr);
};
switch (Preset::printer_technology(config)) {
@ -1855,8 +1908,16 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
//}
//else
print_different_keys_set.insert(ignore_settings_list.begin(), ignore_settings_list.end());
if (!print_compatible_printers.empty()) {
ConfigOptionStrings* compatible_printers = config.option<ConfigOptionStrings>("compatible_printers", true);
compatible_printers->values = print_compatible_printers;
}
load_preset(this->prints, 0, "print_settings_id", print_different_keys_set, std::string());
//clear compatible printers
clear_compatible_printers(config);
std::vector<std::string> printer_different_keys_vector;
std::string printer_different_settings = different_values[num_filaments + 1];
Slic3r::unescape_strings_cstyle(printer_different_settings, printer_different_keys_vector);
@ -1899,7 +1960,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
loaded = this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config, filament_different_keys_set, PresetCollection::LoadAndSelect::Always, file_version, filament_id).first;
else {
// called from Config Wizard.
loaded= &this->filaments.load_preset(this->filaments.path_from_name(name), name, config);
loaded= &this->filaments.load_preset(this->filaments.path_from_name(name), name, config, true, file_version, is_custom_defined);
loaded->save(nullptr);
}
this->filament_presets.clear();
@ -3424,8 +3485,10 @@ std::vector<std::string> PresetBundle::export_current_configs(const std::string
// an optional "(modified)" suffix will be removed from the filament name.
void PresetBundle::set_filament_preset(size_t idx, const std::string &name)
{
if (idx >= filament_presets.size())
filament_presets.resize(idx + 1, filaments.default_preset().name);
if (idx >= filament_presets.size()) {
BOOST_LOG_TRIVIAL(warning) << boost::format("Warning: set_filament_preset out of range %1% - %2%") % idx % filament_presets.size();
return;
}
filament_presets[idx] = Preset::remove_suffix_modified(name);
}

View file

@ -81,7 +81,7 @@ public:
// BBS
void set_num_filaments(unsigned int n, std::string new_col = "");
unsigned int sync_ams_list();
unsigned int sync_ams_list(unsigned int & unknowns);
//BBS: check whether this is the only edited filament
bool is_the_only_edited_filament(unsigned int filament_index);
@ -126,8 +126,8 @@ public:
// Load user configuration and store it into the user profiles.
// This method is called by the configuration wizard.
void load_config_from_wizard(const std::string &name, DynamicPrintConfig config)
{ this->load_config_file_config(name, false, std::move(config)); }
void load_config_from_wizard(const std::string &name, DynamicPrintConfig config, Semver file_version, bool is_custom_defined = false)
{ this->load_config_file_config(name, false, std::move(config), file_version, true, is_custom_defined); }
// Load configuration that comes from a model file containing configuration, such as 3MF et al.
// This method is called by the Plater.
@ -225,7 +225,7 @@ private:
// Load print, filament & printer presets from a config. If it is an external config, then the name is extracted from the external path.
// and the external config is just referenced, not stored into user profile directory.
// If it is not an external config, then the config will be stored into the user profile directory.
void load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config, Semver file_version = Semver());
void load_config_file_config(const std::string &name_or_path, bool is_external, DynamicPrintConfig &&config, Semver file_version = Semver(), bool selected = false, bool is_custom_defined = false);
/*ConfigSubstitutions load_config_file_config_bundle(
const std::string &path, const boost::property_tree::ptree &tree, ForwardCompatibilitySubstitutionRule compatibility_rule);*/

File diff suppressed because it is too large Load diff

View file

@ -515,7 +515,6 @@ private:
// This was a per-object setting and now we default enable it.
static bool clip_multipart_objects;
static bool infill_only_where_needed;
static bool ensure_vertical_shell_thickness;
};
struct WipeTowerData
@ -629,10 +628,13 @@ public:
ApplyStatus apply(const Model &model, DynamicPrintConfig config) override;
void process() override;
void process(bool use_cache = false) override;
// Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
// If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).
std::string export_gcode(const std::string& path_template, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
//return 0 means successful
int export_cached_data(const std::string& dir_path, bool with_space=false);
int load_cached_data(const std::string& directory);
// methods for handling state
bool is_step_done(PrintStep step) const { return Inherited::is_step_done(step); }
@ -721,6 +723,8 @@ public:
void export_gcode_from_previous_file(const std::string& file, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
//BBS: add modify_count logic
int get_modified_count() const {return m_modified_count;}
//BBS: add status for whether support used
bool is_support_used() const {return m_support_used;}
//BBS
static StringObjectException sequential_print_clearance_valid(const Print &print, Polygons *polygons = nullptr, std::vector<std::pair<Polygon, float>>* height_polygons = nullptr);
@ -777,6 +781,7 @@ private:
// Estimated print time, filament consumed.
PrintStatistics m_print_statistics;
bool m_support_used {false};
//BBS: plate's origin
Vec3d m_origin;
@ -793,6 +798,7 @@ public:
static float min_skirt_length;
};
} /* slic3r_Print_hpp_ */
#endif

View file

@ -97,7 +97,7 @@ static inline void layer_height_ranges_copy_configs(t_layer_config_ranges &lr_ds
}
}
static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs)
static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &rhs)
{
typedef Transform3d::Scalar T;
const T *lv = lhs.data();
@ -111,7 +111,7 @@ static inline bool transform3d_lower(const Transform3d &lhs, const Transform3d &
return false;
}
static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &rhs)
static inline bool transform3d_equal(const Transform3d &lhs, const Transform3d &rhs)
{
typedef Transform3d::Scalar T;
const T *lv = lhs.data();
@ -434,7 +434,7 @@ struct PrintObjectStatus {
New
};
PrintObjectStatus(PrintObject *print_object, Status status = Unknown) :
PrintObjectStatus(PrintObject *print_object, Status status = Unknown) :
id(print_object->model_object()->id()),
print_object(print_object),
trafo(print_object->trafo()),
@ -445,7 +445,7 @@ struct PrintObjectStatus {
ObjectID id;
// Pointer to the old PrintObject
PrintObject *print_object;
// Trafo generated with model_object->world_matrix(true)
// Trafo generated with model_object->world_matrix(true)
Transform3d trafo;
Status status;
@ -464,7 +464,7 @@ public:
}
struct iterator_range : std::pair<const_iterator, const_iterator>
{
{
using std::pair<const_iterator, const_iterator>::pair;
iterator_range(const std::pair<const_iterator, const_iterator> in) : std::pair<const_iterator, const_iterator>(in) {}
@ -549,7 +549,7 @@ static PrintObjectRegions::BoundingBox transformed_its_bbox2d(const indexed_tria
}
static void transformed_its_bboxes_in_z_ranges(
const indexed_triangle_set &its,
const indexed_triangle_set &its,
const Transform3f &m,
const std::vector<t_layer_height_range> &z_ranges,
std::vector<std::pair<PrintObjectRegions::BoundingBox, bool>> &bboxes,
@ -732,7 +732,7 @@ bool verify_update_print_object_regions(
assert(next_region_id == int(layer_range.volume_regions.size()) ||
layer_range.volume_regions[next_region_id].model_volume != region.model_volume ||
layer_range.volume_regions[next_region_id].parent <= parent_region_id);
if (next_region_id < int(layer_range.volume_regions.size()) &&
if (next_region_id < int(layer_range.volume_regions.size()) &&
layer_range.volume_regions[next_region_id].model_volume == region.model_volume &&
layer_range.volume_regions[next_region_id].parent == parent_region_id) {
// A parent region is already overridden.
@ -767,7 +767,7 @@ bool verify_update_print_object_regions(
}
}
// Verify and / or update PrintRegions produced by color painting.
// Verify and / or update PrintRegions produced by color painting.
for (const PrintObjectRegions::LayerRangeRegions &layer_range : print_object_regions.layer_ranges)
for (const PrintObjectRegions::PaintedRegion &region : layer_range.painted_regions) {
const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[region.parent];
@ -819,7 +819,7 @@ void update_volume_bboxes(
std::vector<PrintObjectRegions::LayerRangeRegions> &layer_ranges,
std::vector<ObjectID> &cached_volume_ids,
ModelVolumePtrs model_volumes,
const Transform3d &object_trafo,
const Transform3d &object_trafo,
const float offset)
{
// output will be sorted by the order of model_volumes sorted by their ObjectIDs.
@ -968,7 +968,7 @@ static PrintObjectRegions* generate_print_object_regions(
if (parent_volume.is_model_part() || parent_volume.is_modifier())
if (PrintObjectRegions::BoundingBox parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id); parent_bbox.intersects(*bbox)) {
// Only create new region for a modifier, which actually modifies config of it's parent.
if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, volume, num_extruders);
if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, volume, num_extruders);
config != parent_region.region->config()) {
added = true;
layer_range.volume_regions.push_back({ &volume, parent_region_id, get_create_region(std::move(config)), bbox });
@ -1021,7 +1021,21 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
new_full_config.option("printer_settings_id", true);
// BBS
int used_filaments = this->extruders().size();
new_full_config.normalize_fdm(used_filaments);
//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);
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++)
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", i=%1%, key=%2%")%i %changed_keys[i];
}
}
const ConfigOption* enable_support_option = new_full_config.option("enable_support");
if (enable_support_option && enable_support_option->getBool())
m_support_used = true;
else
m_support_used = false;
// Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
DynamicPrintConfig filament_overrides;
@ -1075,13 +1089,14 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
m_default_object_config.apply_only(new_full_config, object_diff, true);
// Handle changes to regions config defaults
m_default_region_config.apply_only(new_full_config, region_diff, true);
m_full_print_config = std::move(new_full_config);
//m_full_print_config = std::move(new_full_config);
m_full_print_config = new_full_config;
if (num_extruders != m_config.filament_diameter.size()) {
num_extruders = m_config.filament_diameter.size();
num_extruders_changed = true;
}
}
ModelObjectStatusDB model_object_status_db;
// 1) Synchronize model objects.
@ -1219,7 +1234,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
if (solid_or_modifier_differ || model_origin_translation_differ || layer_height_ranges_differ ||
! model_object.layer_height_profile.timestamp_matches(model_object_new.layer_height_profile)) {
// The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects.
model_object_status.print_object_regions_status =
model_object_status.print_object_regions_status =
model_object_status.print_object_regions == nullptr || model_origin_translation_differ || layer_height_ranges_differ ?
// Drop print_objects_regions.
ModelObjectStatus::PrintObjectRegionsStatus::Invalid :
@ -1279,7 +1294,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
model_object.name = model_object_new.name;
model_object.input_file = model_object_new.input_file;
// Only refresh ModelInstances if there is any change.
if (model_object.instances.size() != model_object_new.instances.size() ||
if (model_object.instances.size() != model_object_new.instances.size() ||
! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(), [](auto l, auto r){ return l->id() == r->id(); })) {
// G-code generator accesses model_object.instances to generate sequential print ordering matching the Plater object list.
update_apply_status(this->invalidate_step(psGCodeExport));
@ -1289,8 +1304,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
model_object.instances.emplace_back(new ModelInstance(*model_instance));
model_object.instances.back()->set_model_object(&model_object);
}
} else if (! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(),
[](auto l, auto r){ return l->print_volume_state == r->print_volume_state && l->printable == r->printable &&
} else if (! std::equal(model_object.instances.begin(), model_object.instances.end(), model_object_new.instances.begin(),
[](auto l, auto r){ return l->print_volume_state == r->print_volume_state && l->printable == r->printable &&
l->get_transformation().get_matrix().isApprox(r->get_transformation().get_matrix()); })) {
// If some of the instances changed, the bounding box of the updated ModelObject is likely no more valid.
// This is safe as the ModelObject's bounding box is only accessed from this function, which is called from the main thread only.
@ -1391,12 +1406,50 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// BBS
for (PrintObject* object : m_objects) {
auto ept_iter = std::find(print_diff.begin(), print_diff.end(), "enable_prime_tower");
if (object->config().adaptive_layer_height && ept_iter != print_diff.end()) {
if (/*object->config().adaptive_layer_height &&*/ ept_iter != print_diff.end()) {
update_apply_status(object->invalidate_step(posSlice));
}
}
}
//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);
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++)
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(", i=%1%, key=%2%")%i %new_changed_keys[i];
}
update_apply_status(false);
// The following call may stop the background processing.
update_apply_status(this->invalidate_state_by_config_options(new_full_config, new_changed_keys));
update_apply_status(this->invalidate_step(psGCodeExport));
if (full_config_diff.empty()) {
//BBS: previous empty
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: full_config_diff previous empty, need to apply now.")%__LINE__;
m_placeholder_parser.clear_config();
// Set the profile aliases for the PrintBase::output_filename()
m_placeholder_parser.set("print_preset", new_full_config.option("print_settings_id")->clone());
m_placeholder_parser.set("filament_preset", new_full_config.option("filament_settings_id")->clone());
m_placeholder_parser.set("printer_preset", new_full_config.option("printer_settings_id")->clone());
//m_placeholder_parser.apply_config(filament_overrides);
}
// It is also safe to change m_config now after this->invalidate_state_by_config_options() call.
m_config.apply_only(new_full_config, new_changed_keys, true);
// Handle changes to object config defaults
m_default_object_config.apply_only(new_full_config, new_changed_keys, true);
// Handle changes to regions config defaults
m_default_region_config.apply_only(new_full_config, new_changed_keys, true);
m_full_print_config = std::move(new_full_config);
}
// All regions now have distinct settings.
// Check whether applying the new region config defaults we would get different regions,
// update regions or create regions from scratch.
@ -1502,7 +1555,10 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
// If it is not valid, then it is ensured that PrintObject.m_slicing_params is not in use
// (posSlicing and posSupportMaterial was invalidated).
for (PrintObject *object : m_objects)
{
object->update_slicing_parameters();
m_support_used |= object->config().enable_support;
}
#ifdef _DEBUG
check_model_ids_equal(m_model, model);
@ -1511,7 +1567,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
//BBS: add timestamp logic
if (apply_status != APPLY_STATUS_UNCHANGED)
m_modified_count++;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: finished, this %2%, m_modified_count %3%, apply_status %4%, ")%__LINE__ %this %m_modified_count %apply_status;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" %1%: finished, this %2%, m_modified_count %3%, apply_status %4%, m_support_used %5%")%__LINE__ %this %m_modified_count %apply_status %m_support_used;
return static_cast<ApplyStatus>(apply_status);
}

View file

@ -16,12 +16,20 @@
namespace Slic3r {
enum StringExceptionType {
STRING_EXCEPT_NOT_DEFINED = 0,
STRING_EXCEPT_FILAMENT_NOT_MATCH_BED_TYPE = 1,
STRING_EXCEPT_COUNT
};
// BBS: error with object
struct StringObjectException
{
std::string string;
ObjectBase const *object = nullptr;
std::string opt_key;
StringExceptionType type; // warning type for tips
std::vector<std::string> params; // warning params for tips
};
class CanceledException : public std::exception
@ -411,7 +419,9 @@ public:
// After calling the apply() function, call set_task() to limit the task to be processed by process().
virtual void set_task(const TaskParams &params) {}
// Perform the calculation. This is the only method that is to be called at a worker thread.
virtual void process() = 0;
virtual void process(bool use_cache = false) = 0;
virtual int export_cached_data(const std::string& dir_path, bool with_space=false) { return 0;}
virtual int load_cached_data(const std::string& directory) { return 0;}
// Clean up after process() finished, either with success, error or if canceled.
// The adjustments on the Print / PrintObject data due to set_task() are to be reverted here.
virtual void finalize() {}

View file

@ -141,9 +141,7 @@ static t_config_enum_values s_keys_map_InfillPattern {
{ "archimedeanchords", ipArchimedeanChords },
{ "octagramspiral", ipOctagramSpiral },
{ "supportcubic", ipSupportCubic },
#if HAS_LIGHTNING_INFILL
{ "lightning", ipLightning }
#endif // HAS_LIGHTNING_INFILL
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InfillPattern)
@ -183,9 +181,9 @@ static t_config_enum_values s_keys_map_SupportMaterialPattern {
{ "rectilinear", smpRectilinear },
{ "rectilinear-grid", smpRectilinearGrid },
{ "honeycomb", smpHoneycomb },
#if HAS_LIGHTNING_INFILL
{ "lightning", smpLightning },
#endif
{ "default", smpDefault},
{ "none", smpNone},
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportMaterialPattern)
@ -206,8 +204,8 @@ static t_config_enum_values s_keys_map_SupportType{
{ "normal(auto)", stNormalAuto },
{ "tree(auto)", stTreeAuto },
{ "hybrid(auto)", stHybridAuto },
{ "normal", stNormal },
{ "tree", stTree }
{ "normal(manual)", stNormal },
{ "tree(manual)", stTree }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SupportType)
@ -247,11 +245,10 @@ static const t_config_enum_values s_keys_map_BrimType = {
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BrimType)
// using 0,1,2 to compatible with old files
// using 0,1 to compatible with old files
static const t_config_enum_values s_keys_map_TimelapseType = {
{"0", tlNone},
{"1", tlSmooth},
{"2", tlTraditional}
{"0", tlTraditional},
{"1", tlSmooth}
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(TimelapseType)
@ -270,19 +267,22 @@ static const t_config_enum_values s_keys_map_ForwardCompatibilitySubstitutionRul
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(ForwardCompatibilitySubstitutionRule)
static const t_config_enum_values s_keys_map_OverhangFanThreshold = {
{ "0%", Overhang_threshold_none },
{ "5%", Overhang_threshold_1_4 },
{ "25%", Overhang_threshold_2_4 },
{ "50%", Overhang_threshold_3_4 },
{ "75%", Overhang_threshold_4_4 },
{ "95%", Overhang_threshold_bridge }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(OverhangFanThreshold)
// BBS
static const t_config_enum_values s_keys_map_BedType = {
{ "Default Plate", btDefault },
{ "Cool Plate", btPC },
{ "Engineering Plate", btEP },
{ "High Temp Plate", btPEI },
{ "Textured PEI Plate", btPTE }
{ "Textured PEI Plate", btPTE }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BedType)
@ -294,6 +294,12 @@ static t_config_enum_values s_keys_map_NozzleType {
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(NozzleType)
static t_config_enum_values s_keys_map_PerimeterGeneratorType{
{ "classic", int(PerimeterGeneratorType::Classic) },
{ "arachne", int(PerimeterGeneratorType::Arachne) }
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(PerimeterGeneratorType)
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)
@ -340,6 +346,16 @@ void PrintConfigDef::init_common_params()
def->gui_type = ConfigOptionDef::GUIType::one_string;
def->set_default_value(new ConfigOptionPoints{ Vec2d(0, 0) });
def = this->add("bed_custom_texture", coString);
def->label = L("Bed custom texture");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
def = this->add("bed_custom_model", coString);
def->label = L("Bed custom model");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
def = this->add("elefant_foot_compensation", coFloat);
def->label = L("Elephant foot compensation");
def->category = L("Quality");
@ -412,14 +428,14 @@ void PrintConfigDef::init_common_params()
def = this->add("printhost_user", coString);
def->label = L("User");
// def->tooltip = L("");
// def->tooltip = "";
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionString(""));
def = this->add("printhost_password", coString);
def->label = L("Password");
// def->tooltip = L("");
// def->tooltip = "";
def->mode = comAdvanced;
def->cli = ConfigOptionDef::nocli;
def->set_default_value(new ConfigOptionString(""));
@ -441,7 +457,7 @@ void PrintConfigDef::init_common_params()
def = this->add("printhost_authorization_type", coEnum);
def->label = L("Authorization Type");
// def->tooltip = L("");
// def->tooltip = "";
def->enum_keys_map = &ConfigOptionEnum<AuthorizationType>::get_enum_values();
def->enum_values.push_back("key");
def->enum_values.push_back("user");
@ -565,7 +581,7 @@ void PrintConfigDef::init_fff_params()
def->max = 120;
def->set_default_value(new ConfigOptionInts{45});
def = this->add("curr_bed_type", coEnums);
def = this->add("curr_bed_type", coEnum);
def->label = L("Bed type");
def->tooltip = L("Bed types supported by the printer");
def->mode = comSimple;
@ -629,15 +645,18 @@ void PrintConfigDef::init_fff_params()
def = this->add("overhang_fan_threshold", coEnums);
def->label = L("Cooling overhang threshold");
def->tooltip = L("Force cooling fan to be specific speed when overhang degree of printed part exceeds this value. "
"Expressed as percentage which indicides how much width of the line without support from lower layer");
def->sidetext = L("");
def->enum_keys_map = &s_keys_map_OverhangFanThreshold;
"Expressed as percentage which indicides how much width of the line without support from lower layer. "
"0% means forcing cooling for all outer wall no matter how much overhang degree");
def->sidetext = "";
def->enum_keys_map = &ConfigOptionEnum<OverhangFanThreshold>::get_enum_values();
def->mode = comAdvanced;
def->enum_values.emplace_back("0%");
def->enum_values.emplace_back("5%");
def->enum_values.emplace_back("25%");
def->enum_values.emplace_back("50%");
def->enum_values.emplace_back("75%");
def->enum_values.emplace_back("95%");
def->enum_labels.emplace_back("0%");
def->enum_labels.emplace_back("10%");
def->enum_labels.emplace_back("25%");
def->enum_labels.emplace_back("50%");
@ -645,6 +664,17 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.emplace_back("95%");
def->set_default_value(new ConfigOptionEnumsGeneric{ (int)Overhang_threshold_bridge });
def = this->add("bridge_angle", coFloat);
def->label = L("Bridge direction");
def->category = L("Strength");
def->tooltip = L("Bridging angle override. If left to zero, the bridging angle will be calculated "
"automatically. Otherwise the provided angle will be used for external bridges. "
"Use 180°for zero angle.");
def->sidetext = L("°");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.));
def = this->add("bridge_flow", coFloat);
def->label = L("Bridge flow");
def->category = L("Quality");
@ -791,6 +821,13 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionStrings());
def->cli = ConfigOptionDef::nocli;
//BBS.
def = this->add("upward_compatible_machine", coStrings);
def->label = L("upward compatible machine");
def->mode = comDevelop;
def->set_default_value(new ConfigOptionStrings());
def->cli = ConfigOptionDef::nocli;
def = this->add("compatible_printers_condition", coString);
def->label = L("Compatible machine condition");
//def->tooltip = L("A boolean expression using the configuration values of an active printer profile. "
@ -829,6 +866,10 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionStrings());
def->cli = ConfigOptionDef::nocli;
def = this->add("print_compatible_printers", coStrings);
def->set_default_value(new ConfigOptionStrings());
def->cli = ConfigOptionDef::nocli;
def = this->add("print_sequence", coEnum);
def->label = L("Print sequence");
def->tooltip = L("Print sequence, layer by layer or object by object");
@ -887,7 +928,7 @@ void PrintConfigDef::init_fff_params()
def = this->add("thick_bridges", coBool);
def->label = L("Thick bridges");
def->category = L("Layers and Perimeters");
def->category = L("Quality");
def->tooltip = L("If enabled, bridges are more reliable, can bridge longer distances, but may look worse. "
"If disabled, bridges look better but are reliable just for shorter bridged distances.");
def->mode = comAdvanced;
@ -920,6 +961,26 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionStrings { " " });
def = this->add("ensure_vertical_shell_thickness", coBool);
def->label = L("Ensure vertical shell thickness");
def->category = L("Strength");
def->tooltip = L("Add solid infill near sloping surfaces to guarantee the vertical shell thickness "
"(top+bottom solid layers)");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(true));
def = this->add("internal_bridge_support_thickness", coFloat);
def->label = L("Internal bridge support thickness");
def->category = L("Strength");
def->tooltip = L("If enabled, Studio will generate support loops under the contours of internal bridges."
"These support loops could prevent internal bridges from extruding over the air and improve the top surface quality, especially when the sparse infill density is low."
"This value determines the thickness of the support loops. 0 means disable this feature");
def->sidetext = L("mm");
def->min = 0;
def->max = 2;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0));
auto def_top_fill_pattern = def = this->add("top_surface_pattern", coEnum);
def->label = L("Top surface pattern");
def->category = L("Strength");
@ -931,16 +992,16 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("monotonicline");
def->enum_values.push_back("alignedrectilinear");
def->enum_values.push_back("hilbertcurve");
//def->enum_values.push_back("archimedeanchords");
//def->enum_values.push_back("octagramspiral");
def->enum_values.push_back("archimedeanchords");
def->enum_values.push_back("octagramspiral");
def->enum_labels.push_back(L("Concentric"));
def->enum_labels.push_back(L("Rectilinear"));
def->enum_labels.push_back(L("Monotonic"));
def->enum_labels.push_back(L("Monotonic line"));
def->enum_labels.push_back(L("Aligned Rectilinear"));
def->enum_labels.push_back(L("Hilbert Curve"));
//def->enum_labels.push_back(L("Archimedean Chords"));
//def->enum_labels.push_back(L("Octagram Spiral"));
def->enum_labels.push_back(L("Archimedean Chords"));
def->enum_labels.push_back(L("Octagram Spiral"));
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipRectilinear));
def = this->add("bottom_surface_pattern", coEnum);
@ -1126,6 +1187,13 @@ void PrintConfigDef::init_fff_params()
def->mode = comSimple;
def->set_default_value(new ConfigOptionFloats{ 60.0f });
def = this->add("default_filament_colour", coStrings);
def->label = L("Default color");
def->tooltip = L("Default filament color");
def->gui_type = ConfigOptionDef::GUIType::color;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionStrings{""});
def = this->add("filament_colour", coStrings);
def->label = L("Color");
def->tooltip = L("Only used as a visual help on UI");
@ -1133,15 +1201,25 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionStrings{ "#00AE42" });
//bbs
def = this->add("required_nozzle_HRC", coInts);
def->label = L("Required nozzle HRC");
def->tooltip = L("Minimum HRC of nozzle required to print the filament. Zero means no checking of nozzle's HRC.");
def->min = 0;
def->max = 500;
def->mode = comDevelop;
def->set_default_value(new ConfigOptionInts{0});
def = this->add("filament_max_volumetric_speed", coFloats);
def->label = L("Max volumetric speed");
def->tooltip = L("This setting stands for how much volume of filament can be melted and extruded per second. "
"Printing speed is limited by max volumetric speed, in case of too high and unreasonable speed setting. "
"Zero means no limit");
"Can't be zero");
def->sidetext = L("mm³/s");
def->min = 0;
def->max = 50;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloats { 0. });
def->set_default_value(new ConfigOptionFloats { 2. });
def = this->add("filament_minimal_purge_on_wipe_tower", coFloats);
def->label = L("Minimal purge on wipe tower");
@ -1289,14 +1367,12 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("honeycomb");
def->enum_values.push_back("adaptivecubic");
def->enum_values.push_back("alignedrectilinear");
//def->enum_values.push_back("3dhoneycomb");
//def->enum_values.push_back("hilbertcurve");
//def->enum_values.push_back("archimedeanchords");
//def->enum_values.push_back("octagramspiral");
//def->enum_values.push_back("supportcubic");
#if HAS_LIGHTNING_INFILL
def->enum_values.push_back("3dhoneycomb");
def->enum_values.push_back("hilbertcurve");
def->enum_values.push_back("archimedeanchords");
def->enum_values.push_back("octagramspiral");
def->enum_values.push_back("supportcubic");
def->enum_values.push_back("lightning");
#endif // HAS_LIGHTNING_INFILL
def->enum_labels.push_back(L("Concentric"));
def->enum_labels.push_back(L("Rectilinear"));
def->enum_labels.push_back(L("Grid"));
@ -1308,14 +1384,12 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back(L("Honeycomb"));
def->enum_labels.push_back(L("Adaptive Cubic"));
def->enum_labels.push_back(L("Aligned Rectilinear"));
//def->enum_labels.push_back(L("3D Honeycomb"));
//def->enum_labels.push_back(L("Hilbert Curve"));
//def->enum_labels.push_back(L("Archimedean Chords"));
//def->enum_labels.push_back(L("Octagram Spiral"));
//def->enum_labels.push_back(L("Support Cubic"));
#if HAS_LIGHTNING_INFILL
def->enum_labels.push_back(L("3D Honeycomb"));
def->enum_labels.push_back(L("Hilbert Curve"));
def->enum_labels.push_back(L("Archimedean Chords"));
def->enum_labels.push_back(L("Octagram Spiral"));
def->enum_labels.push_back(L("Support Cubic"));
def->enum_labels.push_back(L("Lightning"));
#endif // HAS_LIGHTNING_INFILL
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipCubic));
def = this->add("outer_wall_acceleration", coFloat);
@ -1350,6 +1424,14 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(500));
def = this->add("outer_wall_acceleration", coFloat);
def->label = L("Outer wall");
def->tooltip = L("Acceleration of outer wall. Using a lower value can improve quality");
def->sidetext = L("mm/s²");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(500));
def = this->add("initial_layer_acceleration", coFloat);
def->label = L("Initial layer");
def->tooltip = L("Acceleration of initial layer. Using a lower value can improve build plate adhensive");
@ -1423,13 +1505,13 @@ void PrintConfigDef::init_fff_params()
def->min = 0;
def->set_default_value(new ConfigOptionFloat(0.2));
def = this->add("adaptive_layer_height", coBool);
def->label = L("Adaptive layer height");
def->category = L("Quality");
def->tooltip = L("Enabling this option means the height of every layer except the first will be automatically calculated "
"during slicing according to the slope of the models surface.\n"
"Note that this option only takes effect if no prime tower is generated in current plate.");
def->set_default_value(new ConfigOptionBool(0));
//def = this->add("adaptive_layer_height", coBool);
//def->label = L("Adaptive layer height");
//def->category = L("Quality");
//def->tooltip = L("Enabling this option means the height of every layer except the first will be automatically calculated "
// "during slicing according to the slope of the models surface.\n"
// "Note that this option only takes effect if no prime tower is generated in current plate.");
//def->set_default_value(new ConfigOptionBool(0));
def = this->add("initial_layer_speed", coFloat);
def->label = L("Initial layer");
@ -1488,7 +1570,7 @@ void PrintConfigDef::init_fff_params()
def->tooltip = L("The width within which to jitter. It's adversed to be below outer wall line width");
def->sidetext = L("mm");
def->min = 0;
def->mode = comAdvanced;
def->mode = comSimple;
def->set_default_value(new ConfigOptionFloat(0.3));
def = this->add("fuzzy_skin_point_distance", coFloat);
@ -1496,7 +1578,7 @@ void PrintConfigDef::init_fff_params()
def->category = L("Others");
def->tooltip = L("The average diatance between the random points introducded on each line segment");
def->sidetext = L("mm");
def->mode = comAdvanced;
def->mode = comSimple;
def->set_default_value(new ConfigOptionFloat(0.8));
def = this->add("filter_out_gap_fill", coFloat);
@ -1558,6 +1640,15 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<NozzleType>(ntUndefine));
def = this->add("nozzle_hrc", coInt);
def->label = L("Nozzle HRC");
def->tooltip = L("The nozzle's hardness. Zero means no checking for nozzle's hardness during slicing.");
def->sidetext = L("HRC");
def->min = 0;
def->max = 500;
def->mode = comDevelop;
def->set_default_value(new ConfigOptionInt{0});
def = this->add("auxiliary_fan", coBool);
def->label = L("Auxiliary part cooling fan");
def->tooltip = L("Enable this option if machine has auxiliary part cooling fan");
@ -2358,12 +2449,10 @@ void PrintConfigDef::init_fff_params()
def->enum_keys_map = &ConfigOptionEnum<TimelapseType>::get_enum_values();
def->enum_values.emplace_back("0");
def->enum_values.emplace_back("1");
def->enum_values.emplace_back("2");
def->enum_labels.emplace_back(L("None"));
def->enum_labels.emplace_back(L("Smooth"));
def->enum_labels.emplace_back(L("Traditional"));
def->enum_labels.emplace_back(L("Smooth"));
def->mode = comSimple;
def->set_default_value(new ConfigOptionEnum<TimelapseType>(tlNone));
def->set_default_value(new ConfigOptionEnum<TimelapseType>(tlTraditional));
def = this->add("standby_temperature_delta", coInt);
def->label = L("Temperature variation");
@ -2418,6 +2507,20 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.049));
def = this->add("slicing_mode", coEnum);
def->label = L("Slicing Mode");
def->category = L("Other");
def->tooltip = L("Use \"Even-odd\" for 3DLabPrint airplane models. Use \"Close holes\" to close all holes in the model.");
def->enum_keys_map = &ConfigOptionEnum<SlicingMode>::get_enum_values();
def->enum_values.push_back("regular");
def->enum_values.push_back("even_odd");
def->enum_values.push_back("close_holes");
def->enum_labels.push_back(L("Regular"));
def->enum_labels.push_back(L("Even-odd"));
def->enum_labels.push_back(L("Close holes"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<SlicingMode>(SlicingMode::Regular));
def = this->add("enable_support", coBool);
//BBS: remove material behind support
def->label = L("Enable support");
@ -2434,13 +2537,13 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("normal(auto)");
def->enum_values.push_back("tree(auto)");
def->enum_values.push_back("hybrid(auto)");
def->enum_values.push_back("normal");
def->enum_values.push_back("tree");
def->enum_values.push_back("normal(manual)");
def->enum_values.push_back("tree(manual)");
def->enum_labels.push_back(L("normal(auto)"));
def->enum_labels.push_back(L("tree(auto)"));
def->enum_labels.push_back(L("hybrid(auto)"));
def->enum_labels.push_back(L("normal"));
def->enum_labels.push_back(L("tree"));
def->enum_labels.push_back(L("normal(manual)"));
def->enum_labels.push_back(L("tree(manual)"));
def->mode = comSimple;
def->set_default_value(new ConfigOptionEnum<SupportType>(stNormalAuto));
@ -2500,6 +2603,15 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.2));
// BBS:MusangKing
def = this->add("support_bottom_z_distance", coFloat);
def->label = L("Bottom Z distance");
def->category = L("Support");
def->tooltip = L("The z gap between the bottom support interface and object");
def->sidetext = L("mm");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.2));
def = this->add("enforce_support_layers", coInt);
//def->label = L("Enforce support for the first");
def->category = L("Support");
@ -2515,9 +2627,10 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionInt(0));
def = this->add("support_filament", coInt);
def->label = L("Support");
def->gui_type = ConfigOptionDef::GUIType::i_enum_open;
def->label = L("Support");
def->category = L("Support");
def->tooltip = L("Filament to print support and skirt. 0 means no specific filament for support and current filament is used");
def->tooltip = L("Filament to print support and raft. \"Default\" means no specific filament for support and current filament is used");
def->min = 0;
def->mode = comSimple;
def->set_default_value(new ConfigOptionInt(1));
@ -2539,11 +2652,12 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionBool(false));
def = this->add("support_interface_filament", coInt);
def->label = L("Support interface");
def->gui_type = ConfigOptionDef::GUIType::i_enum_open;
def->label = L("Support interface");
def->category = L("Support");
def->tooltip = L("Filament to print support interface. 0 means no specific filament for support interface and current filament is used");
def->tooltip = L("Filament to print support interface. \"Default\" means no specific filament for support interface and current filament is used");
def->min = 0;
//BBS
// BBS
def->mode = comSimple;
def->set_default_value(new ConfigOptionInt(1));
@ -2614,18 +2728,18 @@ void PrintConfigDef::init_fff_params()
def->category = L("Support");
def->tooltip = L("Line pattern of support");
def->enum_keys_map = &ConfigOptionEnum<SupportMaterialPattern>::get_enum_values();
def->enum_values.push_back("default");
def->enum_values.push_back("rectilinear");
def->enum_values.push_back("rectilinear-grid");
def->enum_values.push_back("honeycomb");
#if HAS_LIGHTNING_INFILL
def->enum_values.push_back("lightning");
#endif
def->enum_values.push_back("none");
def->enum_labels.push_back(L("Default"));
def->enum_labels.push_back(L("Rectilinear"));
def->enum_labels.push_back(L("Rectilinear grid"));
def->enum_labels.push_back(L("Honeycomb"));
#if HAS_LIGHTNING_INFILL
def->enum_labels.push_back(L("Lightning"));
#endif
def->enum_labels.push_back(L("None"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<SupportMaterialPattern>(smpRectilinear));
@ -2654,6 +2768,14 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(2.5));
def = this->add("support_expansion", coFloat);
def->label = L("Normal Support expansion");
def->category = L("Support");
def->tooltip = L("Expand (+) or shrink (-) the horizontal span of normal support");
def->sidetext = L("mm");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("support_speed", coFloat);
def->label = L("Support");
def->category = L("Speed");
@ -2760,19 +2882,19 @@ void PrintConfigDef::init_fff_params()
def = this->add("nozzle_temperature_range_low", coInts);
def->label = L("Min");
//def->tooltip = L("");
//def->tooltip = "";
def->sidetext = L("°C");
def->min = 0;
def->max = max_temp;
def->set_default_value(new ConfigOptionInts { 0 });
def->set_default_value(new ConfigOptionInts { 190 });
def = this->add("nozzle_temperature_range_high", coInts);
def->label = L("Max");
//def->tooltip = L("");
//def->tooltip = "";
def->sidetext = L("°C");
def->min = 0;
def->max = max_temp;
def->set_default_value(new ConfigOptionInts { 0 });
def->set_default_value(new ConfigOptionInts { 240 });
def = this->add("bed_temperature_difference", coInts);
def->label = L("Bed temperature difference");
@ -2914,10 +3036,8 @@ void PrintConfigDef::init_fff_params()
def = this->add("flush_multiplier", coFloat);
def->label = L("Flush multiplier");
def->tooltip = L("");
def->sidetext = L("");
def->mode = comAdvanced;
def->min = 0;
def->tooltip = L("The actual flushing volumes is equal to the flush multiplier multiplied by the flushing volumes in the table.");
def->sidetext = "";
def->set_default_value(new ConfigOptionFloat(1.0));
// BBS
@ -2925,7 +3045,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Prime volume");
def->tooltip = L("The volume of material to prime extruder on tower.");
def->sidetext = L("mm³");
def->min = 0;
def->min = 1.0;
def->mode = comSimple;
def->set_default_value(new ConfigOptionFloat(45.));
@ -2949,6 +3069,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Width");
def->tooltip = L("Width of prime tower");
def->sidetext = L("mm");
def->min = 2.0;
def->mode = comSimple;
def->set_default_value(new ConfigOptionFloat(35.));
@ -2972,21 +3093,24 @@ void PrintConfigDef::init_fff_params()
def->label = L("Flush into objects' infill");
def->tooltip = L("Purging after filament change will be done inside objects' infills. "
"This may lower the amount of waste and decrease the print time. "
"If the walls are printed with transparent filament, the mixed color infill will be seen outside");
"If the walls are printed with transparent filament, the mixed color infill will be seen outside. "
"It will not take effect, unless the prime tower is enabled.");
def->set_default_value(new ConfigOptionBool(false));
def = this->add("flush_into_support", coBool);
def->category = L("Flush options");
def->label = L("Flush into objects' support");
def->tooltip = L("Purging after filament change will be done inside objects' support. "
"This may lower the amount of waste and decrease the print time");
"This may lower the amount of waste and decrease the print time. "
"It will not take effect, unless the prime tower is enabled.");
def->set_default_value(new ConfigOptionBool(true));
def = this->add("flush_into_objects", coBool);
def->category = L("Flush options");
def->label = L("Flush into this object");
def->tooltip = L("This object will be used to purge the nozzle after a filament change to save filament and decrease the print time. "
"Colours of the objects will be mixed as a result");
"Colours of the objects will be mixed as a result. "
"It will not take effect, unless the prime tower is enabled.");
def->set_default_value(new ConfigOptionBool(false));
//BBS
@ -3024,6 +3148,92 @@ void PrintConfigDef::init_fff_params()
def->gui_type = ConfigOptionDef::GUIType::one_string;
def->set_default_value(new ConfigOptionPoints{Vec2d(300, 300)});
def = this->add("wall_generator", coEnum);
def->label = L("Wall generator");
def->category = L("Quality");
def->tooltip = L("Classic wall generator produces walls with constant extrusion width and for "
"very thin areas is used gap-fill. "
"Arachne engine produces walls with variable extrusion width");
def->enum_keys_map = &ConfigOptionEnum<PerimeterGeneratorType>::get_enum_values();
def->enum_values.push_back("classic");
def->enum_values.push_back("arachne");
def->enum_labels.push_back(L("Classic"));
def->enum_labels.push_back(L("Arachne"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<PerimeterGeneratorType>(PerimeterGeneratorType::Classic));
def = this->add("wall_transition_length", coPercent);
def->label = L("Wall transition length");
def->category = L("Quality");
def->tooltip = L("When transitioning between different numbers of walls as the part becomes "
"thinner, a certain amount of space is allotted to split or join the wall segments. "
"It's expressed as a percentage over nozzle diameter");
def->sidetext = L("%");
def->mode = comAdvanced;
def->min = 0;
def->set_default_value(new ConfigOptionPercent(100));
def = this->add("wall_transition_filter_deviation", coPercent);
def->label = L("Wall transitioning filter margin");
def->category = L("Quality");
def->tooltip = L("Prevent transitioning back and forth between one extra wall and one less. This "
"margin extends the range of extrusion widths which follow to [Minimum wall width "
"- margin, 2 * Minimum wall width + margin]. Increasing this margin "
"reduces the number of transitions, which reduces the number of extrusion "
"starts/stops and travel time. However, large extrusion width variation can lead to "
"under- or overextrusion problems. "
"It's expressed as a percentage over nozzle diameter");
def->sidetext = L("%");
def->mode = comAdvanced;
def->min = 0;
def->set_default_value(new ConfigOptionPercent(25));
def = this->add("wall_transition_angle", coFloat);
def->label = L("Wall transitioning threshold angle");
def->category = L("Quality");
def->tooltip = L("When to create transitions between even and odd numbers of walls. A wedge shape with"
" an angle greater than this setting will not have transitions and no walls will be "
"printed in the center to fill the remaining space. Reducing this setting reduces "
"the number and length of these center walls, but may leave gaps or overextrude");
def->sidetext = L("°");
def->mode = comAdvanced;
def->min = 1.;
def->max = 59.;
def->set_default_value(new ConfigOptionFloat(10.));
def = this->add("wall_distribution_count", coInt);
def->label = L("Wall distribution count");
def->category = L("Quality");
def->tooltip = L("The number of walls, counted from the center, over which the variation needs to be "
"spread. Lower values mean that the outer walls don't change in width");
def->mode = comAdvanced;
def->min = 1;
def->set_default_value(new ConfigOptionInt(1));
def = this->add("min_feature_size", coPercent);
def->label = L("Minimum feature size");
def->category = L("Quality");
def->tooltip = L("Minimum thickness of thin features. Model features that are thinner than this value will "
"not be printed, while features thicker than the Minimum feature size will be widened to "
"the Minimum wall width. "
"It's expressed as a percentage over nozzle diameter");
def->sidetext = L("%");
def->mode = comAdvanced;
def->min = 0;
def->set_default_value(new ConfigOptionPercent(25));
def = this->add("min_bead_width", coPercent);
def->label = L("Minimum wall width");
def->category = L("Quality");
def->tooltip = L("Width of the wall that will replace thin features (according to the Minimum feature size) "
"of the model. If the Minimum wall width is thinner than the thickness of the feature,"
" the wall will become as thick as the feature itself. "
"It's expressed as a percentage over nozzle diameter");
def->sidetext = L("%");
def->mode = comAdvanced;
def->min = 0;
def->set_default_value(new ConfigOptionPercent(85));
// Declare retract values for filament profile, overriding the printer's extruder profile.
for (const char *opt_key : {
// floats
@ -3097,7 +3307,7 @@ void PrintConfigDef::init_filament_option_keys()
"filament_diameter", "min_layer_height", "max_layer_height",
"retraction_length", "z_hop", "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",*/
"retract_when_changing_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "filament_colour",
"default_filament_profile"
};
@ -3797,6 +4007,14 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
opt_key = "slow_down_for_layer_cooling";
} else if (opt_key == "timelapse_no_toolhead") {
opt_key = "timelapse_type";
} else if (opt_key == "timelapse_type" && value == "2") {
// old file "0" is None, "2" is Traditional
// new file "0" is Traditional, erase "2"
value = "0";
} else if (opt_key == "support_type" && value == "normal") {
value = "normal(manual)";
} else if (opt_key == "support_type" && value == "tree") {
value = "tree(manual)";
} else if (opt_key == "different_settings_to_system") {
std::string copy_value = value;
copy_value.erase(std::remove(copy_value.begin(), copy_value.end(), '\"'), copy_value.end()); // remove '"' in string
@ -3820,12 +4038,12 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
#endif /* HAS_PRESSURE_EQUALIZER */
// BBS
, "support_sharp_tails","remove_small_overhangs", "support_with_sheath",
"tree_support_branch_diameter_angle", "tree_support_collision_resolution",
"tree_support_branch_diameter_angle", "tree_support_collision_resolution", "tree_support_with_infill",
"max_volumetric_speed", "max_print_speed",
"support_bottom_z_distance", "support_closing_radius", "slicing_mode",
"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"
"can_switch_nozzle_type", "can_add_auxiliary_fan", "extra_flush_volume", "spaghetti_detector", "adaptive_layer_height"
};
if (ignore.find(opt_key) != ignore.end()) {
@ -3931,7 +4149,7 @@ void DynamicPrintConfig::normalize_fdm(int used_filaments)
ConfigOptionBool* ept_opt = this->option<ConfigOptionBool>("enable_prime_tower");
if (used_filaments > 0 && ept_opt != nullptr) {
ConfigOptionBool* islh_opt = this->option<ConfigOptionBool>("independent_support_layer_height", true);
ConfigOptionBool* alh_opt = this->option<ConfigOptionBool>("adaptive_layer_height");
//ConfigOptionBool* alh_opt = this->option<ConfigOptionBool>("adaptive_layer_height");
ConfigOptionEnum<PrintSequence>* ps_opt = this->option<ConfigOptionEnum<PrintSequence>>("print_sequence");
ConfigOptionEnum<TimelapseType>* timelapse_opt = this->option<ConfigOptionEnum<TimelapseType>>("timelapse_type");
@ -3943,8 +4161,8 @@ void DynamicPrintConfig::normalize_fdm(int used_filaments)
if (ept_opt->value) {
if (islh_opt)
islh_opt->value = false;
if (alh_opt)
alh_opt->value = false;
//if (alh_opt)
// alh_opt->value = false;
}
else {
if (islh_opt)
@ -3953,6 +4171,102 @@ void DynamicPrintConfig::normalize_fdm(int used_filaments)
}
}
//BBS:divide normalize_fdm to 2 steps and call them one by one in Print::Apply
void DynamicPrintConfig::normalize_fdm_1()
{
if (this->has("extruder")) {
int extruder = this->option("extruder")->getInt();
this->erase("extruder");
if (extruder != 0) {
if (!this->has("sparse_infill_filament"))
this->option("sparse_infill_filament", true)->setInt(extruder);
if (!this->has("wall_filament"))
this->option("wall_filament", true)->setInt(extruder);
// Don't propagate the current extruder to support.
// For non-soluble supports, the default "0" extruder means to use the active extruder,
// for soluble supports one certainly does not want to set the extruder to non-soluble.
// if (!this->has("support_filament"))
// this->option("support_filament", true)->setInt(extruder);
// if (!this->has("support_interface_filament"))
// this->option("support_interface_filament", true)->setInt(extruder);
}
}
if (!this->has("solid_infill_filament") && this->has("sparse_infill_filament"))
this->option("solid_infill_filament", true)->setInt(this->option("sparse_infill_filament")->getInt());
if (this->has("spiral_mode") && this->opt<ConfigOptionBool>("spiral_mode", true)->value) {
{
// this should be actually done only on the spiral layers instead of all
auto* opt = this->opt<ConfigOptionBools>("retract_when_changing_layer", true);
opt->values.assign(opt->values.size(), false); // set all values to false
// Disable retract on layer change also for filament overrides.
auto* opt_n = this->opt<ConfigOptionBoolsNullable>("filament_retract_when_changing_layer", true);
opt_n->values.assign(opt_n->values.size(), false); // Set all values to false.
}
{
this->opt<ConfigOptionInt>("wall_loops", true)->value = 1;
this->opt<ConfigOptionInt>("top_shell_layers", true)->value = 0;
this->opt<ConfigOptionPercent>("sparse_infill_density", true)->value = 0;
}
}
if (auto *opt_gcode_resolution = this->opt<ConfigOptionFloat>("resolution", false); opt_gcode_resolution)
// Resolution will be above 1um.
opt_gcode_resolution->value = std::max(opt_gcode_resolution->value, 0.001);
return;
}
t_config_option_keys DynamicPrintConfig::normalize_fdm_2(int used_filaments)
{
t_config_option_keys changed_keys;
ConfigOptionBool* ept_opt = this->option<ConfigOptionBool>("enable_prime_tower");
if (used_filaments > 0 && ept_opt != nullptr) {
ConfigOptionBool* islh_opt = this->option<ConfigOptionBool>("independent_support_layer_height", true);
//ConfigOptionBool* alh_opt = this->option<ConfigOptionBool>("adaptive_layer_height");
ConfigOptionEnum<PrintSequence>* ps_opt = this->option<ConfigOptionEnum<PrintSequence>>("print_sequence");
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 (ept_opt->value) {
ept_opt->value = false;
changed_keys.push_back("enable_prime_tower");
}
//ept_opt->value = false;
}
if (ept_opt->value) {
if (islh_opt) {
if (islh_opt->value) {
islh_opt->value = false;
changed_keys.push_back("independent_support_layer_height");
}
//islh_opt->value = false;
}
//if (alh_opt) {
// if (alh_opt->value) {
// alh_opt->value = false;
// changed_keys.push_back("adaptive_layer_height");
// }
// //alh_opt->value = false;
//}
}
else {
if (islh_opt) {
if (!islh_opt->value) {
islh_opt->value = true;
changed_keys.push_back("independent_support_layer_height");
}
//islh_opt->value = true;
}
}
}
return changed_keys;
}
void handle_legacy_sla(DynamicPrintConfig &config)
{
for (std::string corr : {"relative_correction", "material_correction"}) {
@ -4084,6 +4398,16 @@ std::string DynamicPrintConfig::get_filament_type(std::string &displayed_filamen
return "PLA";
}
bool DynamicPrintConfig::is_custom_defined()
{
auto* is_custom_defined = dynamic_cast<const ConfigOptionStrings*>(this->option("is_custom_defined"));
if (!is_custom_defined || is_custom_defined->empty())
return false;
if (is_custom_defined->get_at(0) == "1")
return true;
return false;
}
//FIXME localize this function.
std::string validate(const FullPrintConfig &cfg)
{
@ -4166,7 +4490,7 @@ std::string validate(const FullPrintConfig &cfg)
// config before exporting, leaving this check in would mean that config would be rejected before export
// (although both the UI and the backend handle it).
// --default-acceleration
//if ((cfg.perimeter_acceleration != 0. || cfg.infill_acceleration != 0. || cfg.bridge_acceleration != 0. || cfg.initial_layer_acceleration != 0.) &&
//if ((cfg.outer_wall_acceleration != 0. || cfg.infill_acceleration != 0. || cfg.bridge_acceleration != 0. || cfg.initial_layer_acceleration != 0.) &&
// cfg.default_acceleration == 0.)
// return "Invalid zero value for --default-acceleration when using other acceleration settings";
@ -4299,6 +4623,18 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def->cli_params = "filename.3mf";
def->set_default_value(new ConfigOptionString("output.3mf"));
def = this->add("export_slicedata", coString);
def->label = L("Export slicing data");
def->tooltip = L("Export slicing data to a folder.");
def->cli_params = "slicing_data_directory";
def->set_default_value(new ConfigOptionString("cached_data"));
def = this->add("load_slicedata", coStrings);
def->label = L("Load slicing data");
def->tooltip = L("Load cached slicing data from directory");
def->cli_params = "slicing_data_directory";
def->set_default_value(new ConfigOptionString("cached_data"));
/*def = this->add("export_amf", coBool);
def->label = L("Export AMF");
def->tooltip = L("Export the model(s) as AMF.");
@ -4335,6 +4671,12 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def->cli = "help|h";
def->set_default_value(new ConfigOptionBool(false));
def = this->add("uptodate", coBool);
def->label = L("UpToDate");
def->tooltip = L("Update the configs values of 3mf to latest.");
def->cli = "uptodate";
def->set_default_value(new ConfigOptionBool(false));
/*def = this->add("help_fff", coBool);
def->label = L("Help (FFF options)");
def->tooltip = L("Show the full list of print/G-code configuration options.");
@ -4355,6 +4697,12 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def->tooltip = L("Export settings to a file.");
def->cli_params = "settings.json";
def->set_default_value(new ConfigOptionString("output.json"));
def = this->add("pipe", coString);
def->label = L("Send progress to pipe");
def->tooltip = L("Send progress to pipe.");
def->cli_params = "pipename";
def->set_default_value(new ConfigOptionString("cli_pipe"));
}
//BBS: remove unused command currently
@ -4433,10 +4781,10 @@ CLITransformConfigDef::CLITransformConfigDef()
//def->cli = "orient|o";
def->set_default_value(new ConfigOptionBool(false));
def = this->add("repair", coBool);
/*def = this->add("repair", coBool);
def->label = L("Repair");
def->tooltip = L("Repair the model's meshes if it is non-manifold mesh");
def->set_default_value(new ConfigOptionBool(false));
def->set_default_value(new ConfigOptionBool(false));*/
/*def = this->add("rotate", coFloat);
def->label = L("Rotate");

View file

@ -50,14 +50,10 @@ enum AuthorizationType {
atKeyPassword, atUserPassword
};
#define HAS_LIGHTNING_INFILL 1
enum InfillPattern : int {
ipConcentric, ipRectilinear, ipGrid, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip3DHoneycomb,
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricInternal,
#if HAS_LIGHTNING_INFILL
ipLightning,
#endif // HAS_LIGHTNING_INFILL
ipLightning,
ipCount,
};
@ -96,10 +92,10 @@ enum class SlicingMode
};
enum SupportMaterialPattern {
smpDefault,
smpRectilinear, smpRectilinearGrid, smpHoneycomb,
#if HAS_LIGHTNING_INFILL
smpLightning,
#endif // HAS_LIGHTNING_INFILL
smpNone,
};
enum SupportMaterialStyle {
@ -153,18 +149,27 @@ enum BrimType {
};
enum TimelapseType {
tlNone,
tlSmooth,
tlTraditional
tlTraditional,
tlSmooth
};
enum DraftShield {
dsDisabled, dsLimited, dsEnabled
};
enum class PerimeterGeneratorType
{
// Classic perimeter generator using Clipper offsets with constant extrusion width.
Classic,
// Perimeter generator with variable extrusion width based on the paper
// "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling" ported from Cura.
Arachne
};
// BBS
enum OverhangFanThreshold {
Overhang_threshold_1_4 = 0,
Overhang_threshold_none = 0,
Overhang_threshold_1_4,
Overhang_threshold_2_4,
Overhang_threshold_3_4,
Overhang_threshold_4_4,
@ -173,7 +178,8 @@ enum OverhangFanThreshold {
// BBS
enum BedType {
btPC = 0,
btDefault = 0,
btPC,
btEP,
btPEI,
btPTE,
@ -198,13 +204,14 @@ static std::string bed_type_to_gcode_string(const BedType type)
type_str = "cool_plate";
break;
case btEP:
type_str = "engineering_plate";
type_str = "eng_plate";
break;
case btPEI:
type_str = "high_temp_plate";
type_str = "hot_plate";
break;
case btPTE:
type_str = "frosted_plate";
type_str = "textured_plate";
break;
default:
type_str = "unknown";
break;
@ -273,6 +280,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ForwardCompatibilitySubstitutionRule)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PrintHostType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(AuthorizationType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType)
#undef CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS
@ -346,6 +354,9 @@ public:
const ConfigDef* def() const override { return &print_config_def; }
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);
void set_num_extruders(unsigned int num_extruders);
@ -364,6 +375,8 @@ public:
//BBS special case Support G/ Support W
std::string get_filament_type(std::string &displayed_filament_type, int id = 0);
bool is_custom_defined();
};
void handle_legacy_sla(DynamicPrintConfig &config);
@ -610,6 +623,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionInt, raft_layers))
((ConfigOptionEnum<SeamPosition>, seam_position))
((ConfigOptionFloat, slice_closing_radius))
((ConfigOptionEnum<SlicingMode>, slicing_mode))
((ConfigOptionBool, enable_support))
// Automatic supports (generated based on support_threshold_angle).
((ConfigOptionEnum<SupportType>, support_type))
@ -618,6 +632,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, support_on_build_plate_only))
((ConfigOptionBool, support_critical_regions_only))
((ConfigOptionFloat, support_top_z_distance))
((ConfigOptionFloat, support_bottom_z_distance))
((ConfigOptionInt, enforce_support_layers))
((ConfigOptionInt, support_filament))
((ConfigOptionFloat, support_line_width))
@ -632,10 +647,11 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionEnum<SupportMaterialInterfacePattern>, support_interface_pattern))
// Spacing between support material lines (the hatching distance).
((ConfigOptionFloat, support_base_pattern_spacing))
((ConfigOptionFloat, support_expansion))
((ConfigOptionFloat, support_speed))
((ConfigOptionEnum<SupportMaterialStyle>, support_style))
// BBS
((ConfigOptionBool, independent_support_layer_height))
//((ConfigOptionBool, independent_support_layer_height))
((ConfigOptionBool, thick_bridges))
// Overhang angle threshold.
((ConfigOptionInt, support_threshold_angle))
@ -651,10 +667,17 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, tree_support_branch_diameter))
((ConfigOptionFloat, tree_support_branch_angle))
((ConfigOptionInt, tree_support_wall_count))
((ConfigOptionBool, tree_support_with_infill))
((ConfigOptionBool, detect_narrow_internal_solid_infill))
((ConfigOptionBool, adaptive_layer_height))
// ((ConfigOptionBool, adaptive_layer_height))
((ConfigOptionFloat, support_bottom_interface_spacing))
((ConfigOptionFloat, internal_bridge_support_thickness))
((ConfigOptionEnum<PerimeterGeneratorType>, wall_generator))
((ConfigOptionPercent, wall_transition_length))
((ConfigOptionPercent, wall_transition_filter_deviation))
((ConfigOptionFloat, wall_transition_angle))
((ConfigOptionInt, wall_distribution_count))
((ConfigOptionPercent, min_feature_size))
((ConfigOptionPercent, min_bead_width))
)
// This object is mapped to Perl as Slic3r::Config::PrintRegion.
@ -663,9 +686,10 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionInt, bottom_shell_layers))
((ConfigOptionFloat, bottom_shell_thickness))
((ConfigOptionFloat, bridge_angle))
((ConfigOptionFloat, bridge_flow))
((ConfigOptionFloat, bridge_speed))
((ConfigOptionFloat, bridge_angle))
((ConfigOptionBool, ensure_vertical_shell_thickness))
((ConfigOptionEnum<InfillPattern>, top_surface_pattern))
((ConfigOptionFloat, top_solid_infill_flow_ratio))
((ConfigOptionFloat, bottom_solid_infill_flow_ratio))
@ -769,8 +793,10 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBools, filament_soluble))
((ConfigOptionBools, filament_is_support))
((ConfigOptionFloats, filament_cost))
((ConfigOptionStrings, default_filament_colour))
((ConfigOptionInts, temperature_vitrification)) //BBS
((ConfigOptionFloats, filament_max_volumetric_speed))
((ConfigOptionInts, required_nozzle_HRC))
((ConfigOptionFloat, machine_load_filament_time))
((ConfigOptionFloat, machine_unload_filament_time))
((ConfigOptionFloats, filament_minimal_purge_on_wipe_tower))
@ -805,13 +831,14 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionString, template_custom_gcode))
//BBS
((ConfigOptionEnum<NozzleType>, nozzle_type))
((ConfigOptionInt, nozzle_hrc))
((ConfigOptionBool, auxiliary_fan))
)
// This object is mapped to Perl as Slic3r::Config::Print.
PRINT_CONFIG_CLASS_DERIVED_DEFINE(
PrintConfig,
PrintConfig,
(MachineEnvelopeConfig, GCodeConfig),
//BBS
@ -918,6 +945,8 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionFloat, nozzle_volume))
((ConfigOptionEnum<TimelapseType>, timelapse_type))
((ConfigOptionPoints, thumbnails))
// BBS: move from PrintObjectConfig
((ConfigOptionBool, independent_support_layer_height))
)
// This object is mapped to Perl as Slic3r::Config::Full.

View file

@ -546,7 +546,7 @@ FillLightning::GeneratorPtr PrintObject::prepare_lightning_infill_data()
break;
}
return has_lightning_infill ? FillLightning::build_generator(std::as_const(*this)) : FillLightning::GeneratorPtr();
return has_lightning_infill ? FillLightning::build_generator(std::as_const(*this), [this]() -> void { this->throw_if_canceled(); }) : FillLightning::GeneratorPtr();
}
void PrintObject::clear_layers()
@ -665,6 +665,14 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "top_surface_speed") {
// Brim is printed below supports, support invalidates brim and skirt.
steps.emplace_back(posSupportMaterial);
if (opt_key == "brim_type") {
const auto* old_brim_type = old_config.option<ConfigOptionEnum<BrimType>>(opt_key);
const auto* new_brim_type = new_config.option<ConfigOptionEnum<BrimType>>(opt_key);
//BBS: When switch to manual brim, the object must have brim, then re-generate perimeter
//to make the wall order of first layer to be outer-first
if (old_brim_type->value == btOuterOnly || new_brim_type->value == btOuterOnly)
steps.emplace_back(posPerimeters);
}
} else if (
opt_key == "wall_loops"
|| opt_key == "only_one_wall_top"
@ -695,15 +703,15 @@ bool PrintObject::invalidate_state_by_config_options(
steps.emplace_back(posPerimeters);
} else if (
opt_key == "layer_height"
//BBS
|| opt_key == "adaptive_layer_height"
|| opt_key == "raft_layers"
|| opt_key == "raft_contact_distance"
|| opt_key == "slice_closing_radius") {
|| opt_key == "slice_closing_radius"
|| opt_key == "slicing_mode") {
steps.emplace_back(posSlice);
} else if (
opt_key == "elefant_foot_compensation"
|| opt_key == "support_top_z_distance"
|| opt_key == "support_bottom_z_distance"
|| opt_key == "xy_hole_compensation"
|| opt_key == "xy_contour_compensation") {
steps.emplace_back(posSlice);
@ -735,7 +743,8 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "support_style"
|| opt_key == "support_object_xy_distance"
|| opt_key == "support_base_pattern_spacing"
|| opt_key == "independent_support_layer_height" // BBS
|| opt_key == "support_expansion"
//|| opt_key == "independent_support_layer_height" // BBS
|| opt_key == "support_threshold_angle"
|| opt_key == "raft_expansion"
|| opt_key == "raft_first_layer_density"
@ -746,7 +755,6 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "tree_support_branch_distance"
|| opt_key == "tree_support_branch_diameter"
|| opt_key == "tree_support_branch_angle"
|| opt_key == "tree_support_with_infill"
|| opt_key == "tree_support_wall_count") {
steps.emplace_back(posSupportMaterial);
} else if (opt_key == "bottom_shell_layers") {
@ -767,7 +775,10 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "solid_infill_filament"
|| opt_key == "sparse_infill_line_width"
|| opt_key == "infill_direction"
|| opt_key == "bridge_angle") {
|| opt_key == "ensure_vertical_shell_thickness"
|| opt_key == "bridge_angle"
//BBS
|| opt_key == "internal_bridge_support_thickness") {
steps.emplace_back(posPrepareInfill);
} else if (
opt_key == "top_surface_pattern"
@ -779,7 +790,6 @@ bool PrintObject::invalidate_state_by_config_options(
steps.emplace_back(posInfill);
} else if (opt_key == "sparse_infill_pattern") {
steps.emplace_back(posInfill);
#if HAS_LIGHTNING_INFILL
const auto *old_fill_pattern = old_config.option<ConfigOptionEnum<InfillPattern>>(opt_key);
const auto *new_fill_pattern = new_config.option<ConfigOptionEnum<InfillPattern>>(opt_key);
assert(old_fill_pattern && new_fill_pattern);
@ -787,7 +797,6 @@ bool PrintObject::invalidate_state_by_config_options(
// the Lightning infill to another infill or vice versa.
if (PrintObject::infill_only_where_needed && (new_fill_pattern->value == ipLightning || old_fill_pattern->value == ipLightning))
steps.emplace_back(posPrepareInfill);
#endif
} else if (opt_key == "sparse_infill_density") {
// One likely wants to reslice only when switching between zero infill to simulate boolean difference (subtracting volumes),
// normal infill and 100% (solid) infill.
@ -823,6 +832,15 @@ bool PrintObject::invalidate_state_by_config_options(
steps.emplace_back(posInfill);
steps.emplace_back(posSupportMaterial);
}
} else if (
opt_key == "wall_generator"
|| opt_key == "wall_transition_length"
|| opt_key == "wall_transition_filter_deviation"
|| opt_key == "wall_transition_angle"
|| opt_key == "wall_distribution_count"
|| opt_key == "min_feature_size"
|| opt_key == "min_bead_width") {
steps.emplace_back(posSlice);
} else if (
opt_key == "seam_position"
|| opt_key == "support_speed"
@ -1223,9 +1241,7 @@ void PrintObject::discover_vertical_shells()
bool has_extra_layers = false;
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
const PrintRegionConfig &config = this->printing_region(region_id).config();
//BBS
//if (config.ensure_vertical_shell_thickness.value && has_extra_layers_fn(config)) {
if (PrintObject::ensure_vertical_shell_thickness && has_extra_layers_fn(config)) {
if (config.ensure_vertical_shell_thickness.value && has_extra_layers_fn(config)) {
has_extra_layers = true;
break;
}
@ -1305,9 +1321,7 @@ void PrintObject::discover_vertical_shells()
PROFILE_BLOCK(discover_vertical_shells_region);
const PrintRegion &region = this->printing_region(region_id);
//BBS
//if (! region.config().ensure_vertical_shell_thickness.value)
if (! PrintObject::ensure_vertical_shell_thickness)
if (! region.config().ensure_vertical_shell_thickness.value)
// This region will be handled by discover_horizontal_shells().
continue;
if (! has_extra_layers_fn(region.config()))
@ -1614,6 +1628,7 @@ void PrintObject::bridge_over_infill()
Layer *layer = *layer_it;
LayerRegion *layerm = layer->m_regions[region_id];
const PrintObjectConfig& object_config = layer->object()->config();
//BBS: enable thick bridge for internal bridge only
Flow bridge_flow = layerm->bridging_flow(frSolidInfill, true);
@ -1645,6 +1660,10 @@ void PrintObject::bridge_over_infill()
to_bridge_pp = intersection(to_bridge_pp, lower_internal);
}
// BBS: expand to make avoid gap between bridge and inner wall
to_bridge_pp = expand(to_bridge_pp, bridge_flow.scaled_width());
to_bridge_pp = intersection(to_bridge_pp, internal_solid);
// there's no point in bridging too thin/short regions
//FIXME Vojtech: The offset2 function is not a geometric offset,
// therefore it may create 1) gaps, and 2) sharp corners, which are outside the original contour.
@ -1677,8 +1696,43 @@ void PrintObject::bridge_over_infill()
(layerm->fill_surfaces.surfaces.end() - 1)->bridge_angle = ibd.angle;
}
}
for (ExPolygon &ex : not_to_bridge)
layerm->fill_surfaces.surfaces.push_back(Surface(stInternalSolid, ex));
//BBS: modify stInternal to be stInternalWithLoop to give better support to internal bridge
if (!to_bridge.empty()){
float internal_loop_thickness = object_config.internal_bridge_support_thickness.value;
double bottom_z = layer->print_z - layer->height - internal_loop_thickness + EPSILON;
//BBS: lighting infill doesn't support this feature. Don't need to add loop when infill density is high than 50%
if (region.config().sparse_infill_pattern != InfillPattern::ipLightning && region.config().sparse_infill_density.value < 50)
for (int i = int(layer_it - m_layers.begin()) - 1; i >= 0; --i) {
const Layer* lower_layer = m_layers[i];
if (lower_layer->print_z < bottom_z) break;
for (LayerRegion* lower_layerm : lower_layer->m_regions) {
Polygons lower_internal;
lower_layerm->fill_surfaces.filter_by_type(stInternal, &lower_internal);
ExPolygons internal_with_loop = intersection_ex(lower_internal, to_bridge);
ExPolygons internal = diff_ex(lower_internal, to_bridge);
if (internal_with_loop.empty()) {
//BBS: don't need to do anything
}
else if (internal.empty()) {
lower_layerm->fill_surfaces.change_to_new_type(stInternal, stInternalWithLoop);
}
else {
lower_layerm->fill_surfaces.remove_type(stInternal);
for (ExPolygon& ex : internal_with_loop)
lower_layerm->fill_surfaces.surfaces.push_back(Surface(stInternalWithLoop, ex));
for (ExPolygon& ex : internal)
lower_layerm->fill_surfaces.surfaces.push_back(Surface(stInternal, ex));
}
}
}
}
/*
# exclude infill from the layers below if needed
# see discussion at https://github.com/alexrj/Slic3r/issues/240
@ -1895,8 +1949,7 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
{
bool updated = false;
//BBS:annotate these part and will do adaptive layer height below
/*if (layer_height_profile.empty()) {
if (layer_height_profile.empty()) {
// use the constructor because the assignement is crashing on ASAN OsX
layer_height_profile = std::vector<coordf_t>(model_object.layer_height_profile.get());
// layer_height_profile = model_object.layer_height_profile;
@ -1911,22 +1964,11 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_parameters.object_print_z_max + slicing_parameters.object_print_z_min) > 1e-3))
layer_height_profile.clear();
if (layer_height_profile.empty()) {
if (layer_height_profile.empty() || layer_height_profile[1] != slicing_parameters.first_object_layer_height) {
//layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges);
updated = true;
}*/
//BBS
if (slicing_parameters.adaptive_layer_height) {
layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object, 0.5);
HeightProfileSmoothingParams smoothing_params(5, true);
layer_height_profile = smooth_height_profile(layer_height_profile, slicing_parameters, smoothing_params);
}
else {
layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges);
}
updated = true;
return updated;
}
@ -2048,9 +2090,7 @@ void PrintObject::discover_horizontal_shells()
#endif
// If ensure_vertical_shell_thickness, then the rest has already been performed by discover_vertical_shells().
//BBS
//if (region_config.ensure_vertical_shell_thickness.value)
if (PrintObject::ensure_vertical_shell_thickness)
if (region_config.ensure_vertical_shell_thickness.value)
continue;
coordf_t print_z = layer->print_z;
@ -2772,6 +2812,10 @@ 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 = true;
for (const ModelVolume* mv : this->model_object()->volumes)
if (mv->is_model_part()) {
const indexed_triangle_set custom_facets = seam
@ -2785,7 +2829,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, [](){});
slice_mesh_slabs(custom_facets, zs_from_layers(this->layers()), this->trafo_centered() * mv->get_matrix(), nullptr, &projected, [](){}, config);
// Merge these projections with the output, layer by layer.
assert(! projected.empty());
assert(out.empty() || out.size() == projected.size());

View file

@ -18,7 +18,6 @@ namespace Slic3r {
bool PrintObject::clip_multipart_objects = true;
bool PrintObject::infill_only_where_needed = false;
bool PrintObject::ensure_vertical_shell_thickness = true;
LayerPtrs new_layers(
PrintObject *print_object,
@ -142,13 +141,11 @@ static std::vector<VolumeSlices> slice_volumes_inner(
//BBS: 0.0025mm is safe enough to simplify the data to speed slicing up for high-resolution model.
//Also has on influence on arc fitting which has default resolution 0.0125mm.
params_base.resolution = 0.0025;
//BBS: remove slice mode, always regular
//switch (print_object_config.slicing_mode.value) {
//case SlicingMode::Regular: params_base.mode = MeshSlicingParams::SlicingMode::Regular; break;
//case SlicingMode::EvenOdd: params_base.mode = MeshSlicingParams::SlicingMode::EvenOdd; break;
//case SlicingMode::CloseHoles: params_base.mode = MeshSlicingParams::SlicingMode::Positive; break;
//}
params_base.mode = MeshSlicingParams::SlicingMode::Regular;
switch (print_object_config.slicing_mode.value) {
case SlicingMode::Regular: params_base.mode = MeshSlicingParams::SlicingMode::Regular; break;
case SlicingMode::EvenOdd: params_base.mode = MeshSlicingParams::SlicingMode::EvenOdd; break;
case SlicingMode::CloseHoles: params_base.mode = MeshSlicingParams::SlicingMode::Positive; break;
}
params_base.mode_below = params_base.mode;
@ -584,7 +581,7 @@ void PrintObject::slice()
//BBS: send warning message to slicing callback
if (!warning.empty()) {
BOOST_LOG_TRIVIAL(info) << warning;
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning+L(" Object:")+this->m_model_object->name, PrintStateBase::SlicingReplaceInitEmptyLayers);
this->active_step_add_warning(PrintStateBase::WarningLevel::CRITICAL, warning, PrintStateBase::SlicingReplaceInitEmptyLayers);
}
// Update bounding boxes, back up raw slices of complex models.
tbb::parallel_for(
@ -938,8 +935,7 @@ void PrintObject::slice_volumes()
//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.") +
// "\n" + (L("Object")) + ": " + this->model_object()->name);
// "compensation cannot be combined with multi-material painting."));
BOOST_LOG_TRIVIAL(info) << "xy compensation will not work for object " << this->model_object()->name << " for multi filament.";
}

View file

@ -37,7 +37,7 @@ bool is_zero_elevation(const SLAPrintObjectConfig &c)
sla::SupportTreeConfig make_support_cfg(const SLAPrintObjectConfig& c)
{
sla::SupportTreeConfig scfg;
scfg.enabled = c.supports_enable.getBool();
scfg.head_front_radius_mm = 0.5*c.support_head_front_diameter.getFloat();
double pillar_r = 0.5 * c.support_pillar_diameter.getFloat();
@ -66,18 +66,18 @@ sla::SupportTreeConfig make_support_cfg(const SLAPrintObjectConfig& c)
scfg.pillar_base_safety_distance_mm =
c.support_base_safety_distance.getFloat() < EPSILON ?
scfg.safety_distance_mm : c.support_base_safety_distance.getFloat();
scfg.max_bridges_on_pillar = unsigned(c.support_max_bridges_on_pillar.getInt());
return scfg;
}
sla::PadConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c)
{
sla::PadConfig::EmbedObject ret;
ret.enabled = is_zero_elevation(c);
if(ret.enabled) {
ret.everywhere = c.pad_around_object_everywhere.getBool();
ret.object_gap_mm = c.pad_object_gap.getFloat();
@ -86,24 +86,24 @@ sla::PadConfig::EmbedObject builtin_pad_cfg(const SLAPrintObjectConfig& c)
ret.stick_penetration_mm = c.pad_object_connector_penetration
.getFloat();
}
return ret;
}
sla::PadConfig make_pad_cfg(const SLAPrintObjectConfig& c)
{
sla::PadConfig pcfg;
pcfg.wall_thickness_mm = c.pad_wall_thickness.getFloat();
pcfg.wall_slope = c.pad_wall_slope.getFloat() * PI / 180.0;
pcfg.max_merge_dist_mm = c.pad_max_merge_distance.getFloat();
pcfg.wall_height_mm = c.pad_wall_height.getFloat();
pcfg.brim_size_mm = c.pad_brim_size.getFloat();
// set builtin pad implicitly ON
pcfg.embed_object = builtin_pad_cfg(c);
return pcfg;
}
@ -174,8 +174,8 @@ static std::vector<SLAPrintObject::Instance> sla_instances(const ModelObject &mo
return instances;
}
std::vector<ObjectID> SLAPrint::print_object_ids() const
{
std::vector<ObjectID> SLAPrint::print_object_ids() const
{
std::vector<ObjectID> out;
// Reserve one more for the caller to append the ID of the Print itself.
out.reserve(m_objects.size() + 1);
@ -238,7 +238,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con
m_material_config.apply_only(config, material_diff, true);
// Handle changes to object config defaults
m_default_object_config.apply_only(config, object_diff, true);
if (m_printer) m_printer->apply(m_printer_config);
struct ModelObjectStatus {
@ -429,7 +429,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con
model_object.sla_support_points = model_object_new.sla_support_points;
}
model_object.sla_points_status = model_object_new.sla_points_status;
// Invalidate hollowing if drain holes have changed
if (model_object.sla_drain_holes != model_object_new.sla_drain_holes)
{
@ -629,15 +629,15 @@ StringObjectException SLAPrint::validate(StringObjectException *exception, Polyg
sla::SupportTreeConfig cfg = make_support_cfg(po->config());
double elv = cfg.object_elevation_mm;
sla::PadConfig padcfg = make_pad_cfg(po->config());
sla::PadConfig::EmbedObject &builtinpad = padcfg.embed_object;
if(supports_en && !builtinpad.enabled && elv < cfg.head_fullwidth())
return {L(
"Elevation is too low for object. Use the \"Pad around "
"object\" feature to print the object without elevation."), po};
if(supports_en && builtinpad.enabled &&
cfg.pillar_base_safety_distance_mm < builtinpad.object_gap_mm) {
return {L(
@ -646,7 +646,7 @@ StringObjectException SLAPrint::validate(StringObjectException *exception, Polyg
"distance' has to be greater than the 'Pad object gap' "
"parameter to avoid this."), po};
}
std::string pval = padcfg.validate();
if (!pval.empty()) return {pval, po};
}
@ -686,7 +686,7 @@ bool SLAPrint::invalidate_step(SLAPrintStep step)
return invalidated;
}
void SLAPrint::process()
void SLAPrint::process(bool use_cache)
{
if (m_objects.empty())
return;
@ -695,7 +695,7 @@ void SLAPrint::process()
// Assumption: at this point the print objects should be populated only with
// the model objects we have to process and the instances are also filtered
Steps printsteps(this);
// We want to first process all objects...
@ -709,7 +709,7 @@ void SLAPrint::process()
};
SLAPrintStep print_steps[] = { slapsMergeSlicesAndEval, slapsRasterize };
double st = Steps::min_objstatus;
BOOST_LOG_TRIVIAL(info) << "Start slicing process.";
@ -749,7 +749,7 @@ void SLAPrint::process()
throw_if_canceled();
po->set_done(step);
}
incr = printsteps.progressrange(step);
}
}
@ -760,7 +760,7 @@ void SLAPrint::process()
// this would disable the rasterization step
// std::fill(m_stepmask.begin(), m_stepmask.end(), false);
st = Steps::max_objstatus;
for(SLAPrintStep currentstep : print_steps) {
throw_if_canceled();
@ -774,7 +774,7 @@ void SLAPrint::process()
throw_if_canceled();
set_done(currentstep);
}
st += printsteps.progressrange(currentstep);
}
@ -1132,7 +1132,7 @@ const TriangleMesh& SLAPrintObject::support_mesh() const
{
if(m_config.supports_enable.getBool() && m_supportdata)
return m_supportdata->tree_mesh;
return EMPTY_MESH;
}
@ -1149,7 +1149,7 @@ const indexed_triangle_set &SLAPrintObject::hollowed_interior_mesh() const
if (m_hollowing_data && m_hollowing_data->interior &&
m_config.hollowing_enable.getBool())
return sla::get_mesh(*m_hollowing_data->interior);
return EMPTY_TRIANGLE_SET;
}
@ -1174,7 +1174,7 @@ sla::SupportPoints SLAPrintObject::transformed_support_points() const
for (sla::SupportPoint& suppt : spts) {
suppt.pos = tr * suppt.pos;
}
return spts;
}

View file

@ -77,7 +77,7 @@ public:
// Get a pad mesh centered around origin in XY, and with zero rotation around Z applied.
// Support mesh is only valid if this->is_step_done(slaposPad) is true.
const TriangleMesh& pad_mesh() const;
// Ready after this->is_step_done(slaposDrillHoles) is true
const indexed_triangle_set &hollowed_interior_mesh() const;
@ -201,7 +201,7 @@ private:
{
return level<T>(r1) < level<T>(r2);
});
if(it == cont.end()) return it;
T diff = std::abs(level<T>(*it) - lvl);
@ -307,18 +307,18 @@ private:
// Caching the transformed (m_trafo) raw mesh of the object
mutable CachedObject<TriangleMesh> m_transformed_rmesh;
class SupportData : public sla::SupportableMesh
{
public:
sla::SupportTree::UPtr support_tree_ptr; // the supports
std::vector<ExPolygons> support_slices; // sliced supports
TriangleMesh tree_mesh, pad_mesh, full_mesh;
inline SupportData(const TriangleMesh &t)
: sla::SupportableMesh{t.its, {}, {}}
{}
sla::SupportTree::UPtr &create_support_tree(const sla::JobController &ctl)
{
support_tree_ptr = sla::SupportTree::create(*this, ctl);
@ -335,9 +335,9 @@ private:
pad_mesh = TriangleMesh{support_tree_ptr->retrieve_mesh(sla::MeshType::Pad)};
}
};
std::unique_ptr<SupportData> m_supportdata;
class HollowingData
{
public:
@ -346,7 +346,7 @@ private:
mutable TriangleMesh hollow_mesh_with_holes; // caching the complete hollowed mesh
mutable TriangleMesh hollow_mesh_with_holes_trimmed;
};
std::unique_ptr<HollowingData> m_hollowing_data;
};
@ -390,15 +390,15 @@ struct SLAPrintStatistics
class SLAArchive {
protected:
std::vector<sla::EncodedRaster> m_layers;
virtual std::unique_ptr<sla::RasterBase> create_raster() const = 0;
virtual sla::RasterEncoder get_encoder() const = 0;
public:
virtual ~SLAArchive() = default;
virtual void apply(const SLAPrinterConfig &cfg) = 0;
// Fn have to be thread safe: void(sla::RasterBase& raster, size_t lyrid);
template<class Fn, class CancelFn, class EP = ExecutionTBB>
void draw_layers(
@ -434,9 +434,9 @@ class SLAPrint : public PrintBaseWithState<SLAPrintStep, slapsCount>
{
private: // Prevents erroneous use by other classes.
typedef PrintBaseWithState<SLAPrintStep, slapsCount> Inherited;
class Steps; // See SLAPrintSteps.cpp
public:
SLAPrint(): m_stepmask(slapsCount, true) {}
@ -451,7 +451,7 @@ public:
std::vector<ObjectID> print_object_ids() const override;
ApplyStatus apply(const Model &model, DynamicPrintConfig config) override;
void set_task(const TaskParams &params) override;
void process() override;
void process(bool use_cache = false) override;
void finalize() override;
// Returns true if an object step is done on all objects and there's at least one object.
bool is_step_done(SLAPrintObjectStep step) const;
@ -501,11 +501,11 @@ public:
{
m_transformed_slices = std::forward<Container>(c);
}
friend class SLAPrint::Steps;
public:
explicit PrintLayer(coord_t lvl) : m_level(lvl) {}
// for being sorted in their container (see m_printer_input)
@ -527,11 +527,11 @@ public:
// The aggregated and leveled print records from various objects.
// TODO: use this structure for the preview in the future.
const std::vector<PrintLayer>& print_layers() const { return m_printer_input; }
void set_printer(SLAArchive *archiver);
private:
// Implement same logic as in SLAPrintObject
bool invalidate_step(SLAPrintStep st);
@ -548,24 +548,24 @@ private:
// Ready-made data for rasterization.
std::vector<PrintLayer> m_printer_input;
// The archive object which collects the raster images after slicing
SLAArchive *m_printer = nullptr;
// Estimated print time, material consumed.
SLAPrintStatistics m_print_statistics;
class StatusReporter
{
double m_st = 0;
public:
void operator()(SLAPrint & p,
double st,
const std::string &msg,
unsigned flags = SlicingStatus::DEFAULT,
const std::string &logmsg = "");
double status() const { return m_st; }
} m_report_status;

View file

@ -30,6 +30,17 @@
namespace Slic3r {
static std::map<std::string, std::string> g_occt_fonts_maps; //map<font_name, font_path>
static const std::vector<Standard_CString> fonts_suffix{ "Bold", "Medium", "Heavy", "Italic", "Oblique", "Inclined", "Light", "Thin",
"Semibold", "ExtraBold", "ExtraBold", "Semilight", "SemiLight", "ExtraLight", "Extralight", "Ultralight",
"Condensed", "Ultra", "Extra", "Expanded", "Extended", "1", "2", "3", "4", "5", "6", "7", "8", "9", "Al Tarikh"};
std::map<std::string, std::string> get_occt_fonts_maps()
{
return g_occt_fonts_maps;
}
std::vector<std::string> init_occt_fonts()
{
std::vector<std::string> stdFontNames;
@ -41,9 +52,46 @@ std::vector<std::string> init_occt_fonts()
aFontMgr->GetAvailableFontsNames(availFontNames);
stdFontNames.reserve(availFontNames.Size());
for (auto afn : availFontNames)
stdFontNames.push_back(afn->ToCString());
g_occt_fonts_maps.clear();
BOOST_LOG_TRIVIAL(info) << "init_occt_fonts start";
#ifdef __APPLE__
//from resource
stdFontNames.push_back("HarmonyOS Sans SC");
g_occt_fonts_maps.insert(std::make_pair("HarmoneyOS Sans SC", Slic3r::resources_dir() + "/fonts/" + "HarmonyOS_Sans_SC_Regular.ttf"));
#endif
for (auto afn : availFontNames) {
#ifdef __APPLE__
if(afn->String().StartsWith("."))
continue;
#endif
if(afn->Search("Emoji") != -1 || afn->Search("emoji") != -1)
continue;
bool repeat = false;
for (size_t i = 0; i < fonts_suffix.size(); i++) {
if (afn->SearchFromEnd(fonts_suffix[i]) != -1) {
repeat = true;
break;
}
}
if (repeat)
continue;
Handle(Font_SystemFont) sys_font = aFontMgr->GetFont(afn->ToCString());
TCollection_AsciiString font_path = sys_font->FontPath(Font_FontAspect::Font_FontAspect_Regular);
if (!font_path.IsEmpty() && font_path.SearchFromEnd(".") != -1) {
auto file_type = font_path.SubString(font_path.SearchFromEnd(".") + 1, font_path.Length());
file_type.LowerCase();
if (file_type == "ttf" || file_type == "otf" || file_type == "ttc") {
g_occt_fonts_maps.insert(std::make_pair(afn->ToCString(), decode_path(font_path.ToCString())));
}
}
}
BOOST_LOG_TRIVIAL(info) << "init_occt_fonts end";
// in order
for (auto occt_font : g_occt_fonts_maps) {
stdFontNames.push_back(occt_font.first);
}
return stdFontNames;
}

View file

@ -7,6 +7,8 @@ class TriangleMesh;
extern std::vector<std::string> init_occt_fonts();
extern void load_text_shape(const char* text, const char* font, const float text_height, const float thickness, bool is_bold, bool is_italic, TriangleMesh& text_mesh);
std::map<std::string, std::string> get_occt_fonts_maps();
}; // namespace Slic3r
#endif // slic3r_Text_Shape_hpp_

View file

@ -24,6 +24,19 @@ void chain_and_reorder_extrusion_paths(std::vect
Polylines chain_polylines(Polylines &&src, const Point *start_near = nullptr);
inline Polylines chain_polylines(const Polylines& src, const Point* start_near = nullptr) { Polylines tmp(src); return chain_polylines(std::move(tmp), start_near); }
template<typename T> inline void reorder_by_shortest_traverse(std::vector<T> &polylines_out)
{
Points start_point;
start_point.reserve(polylines_out.size());
for (const T contour : polylines_out) start_point.push_back(contour.points.front());
std::vector<Points::size_type> order = chain_points(start_point);
std::vector<T> Temp = polylines_out;
polylines_out.erase(polylines_out.begin(), polylines_out.end());
for (size_t i:order) polylines_out.emplace_back(std::move(Temp[i]));
}
std::vector<ClipperLib::PolyNode*> chain_clipper_polynodes(const Points &points, const std::vector<ClipperLib::PolyNode*> &items);

View file

@ -84,8 +84,6 @@ SlicingParameters SlicingParameters::create_from_config(
params.object_print_z_max = object_height;
params.base_raft_layers = object_config.raft_layers.value;
params.soluble_interface = soluble_interface;
//BBS
params.adaptive_layer_height = print_config.enable_prime_tower ? false : object_config.adaptive_layer_height;
// Miniumum/maximum of the minimum layer height over all extruders.
params.min_layer_height = MIN_LAYER_HEIGHT;
@ -115,13 +113,12 @@ SlicingParameters SlicingParameters::create_from_config(
if (! soluble_interface) {
params.gap_raft_object = object_config.raft_contact_distance.value;
//BBS
//params.gap_object_support = object_config.support_bottom_z_distance.value;
params.gap_object_support = object_config.support_top_z_distance.value;
params.gap_object_support = object_config.support_bottom_z_distance.value;
params.gap_support_object = object_config.support_top_z_distance.value;
if (params.gap_object_support <= 0)
params.gap_object_support = params.gap_support_object;
if (!object_config.independent_support_layer_height) {
if (!print_config.independent_support_layer_height) {
params.gap_raft_object = std::round(params.gap_raft_object / object_config.layer_height + EPSILON) * object_config.layer_height;
params.gap_object_support = std::round(params.gap_object_support / object_config.layer_height + EPSILON) * object_config.layer_height;
params.gap_support_object = std::round(params.gap_support_object / object_config.layer_height + EPSILON) * object_config.layer_height;
@ -400,31 +397,32 @@ std::vector<double> smooth_height_profile(const std::vector<double>& profile, co
};
//BBS: avoid the layer height change to be too steep
auto has_steep_height_change = [&slicing_params](const std::vector<double>& profile, const double height_step) {
//BBS: skip first layer
size_t skip_count = slicing_params.first_object_layer_height_fixed() ? 4 : 0;
size_t size = profile.size();
//BBS: not enough data to smmoth, return false directly
if ((int)size - (int)skip_count < 6)
return false;
//auto has_steep_height_change = [&slicing_params](const std::vector<double>& profile, const double height_step) {
// //BBS: skip first layer
// size_t skip_count = slicing_params.first_object_layer_height_fixed() ? 4 : 0;
// size_t size = profile.size();
// //BBS: not enough data to smmoth, return false directly
// if ((int)size - (int)skip_count < 6)
// return false;
//BBS: Don't need to check the difference between top layer and the last 2th layer
for (size_t i = skip_count; i < size - 6; i += 2) {
if (abs(profile[i + 1] - profile[i + 3]) > height_step)
return true;
}
return false;
};
// //BBS: Don't need to check the difference between top layer and the last 2th layer
// for (size_t i = skip_count; i < size - 6; i += 2) {
// if (abs(profile[i + 1] - profile[i + 3]) > height_step)
// return true;
// }
// return false;
//};
int count = 0;
std::vector<double> ret = profile;
bool has_steep_change = has_steep_height_change(ret, LAYER_HEIGHT_CHANGE_STEP);
while (has_steep_change && count < 6) {
ret = gauss_blur(ret, smoothing_params);
has_steep_change = has_steep_height_change(ret, LAYER_HEIGHT_CHANGE_STEP);
count++;
}
return ret;
//int count = 0;
//std::vector<double> ret = profile;
//bool has_steep_change = has_steep_height_change(ret, LAYER_HEIGHT_CHANGE_STEP);
//while (has_steep_change && count < 6) {
// ret = gauss_blur(ret, smoothing_params);
// has_steep_change = has_steep_height_change(ret, LAYER_HEIGHT_CHANGE_STEP);
// count++;
//}
//return ret;
return gauss_blur(profile, smoothing_params);
}
void adjust_layer_height_profile(

View file

@ -96,8 +96,6 @@ struct SlicingParameters
// In case of a soluble interface, object_print_z_min == raft_contact_top_z, otherwise there is a gap between the raft and the 1st object layer.
coordf_t object_print_z_min { 0 };
coordf_t object_print_z_max { 0 };
//BBS
bool adaptive_layer_height{ false };
};
static_assert(IsTriviallyCopyable<SlicingParameters>::value, "SlicingParameters class is not POD (and it should be - see constructor).");

View file

@ -1512,11 +1512,22 @@ static inline Polygons detect_overhangs(
0.;
const coordf_t max_bridge_length = scale_(object_config.max_bridge_length.value);
const bool bridge_no_support = object_config.bridge_no_support.value;
const coordf_t xy_expansion = scale_(object_config.support_expansion.value);
if (layer_id == 0)
if (layer_id == 0)
{
// Don't fill in the holes. The user may apply a higher raft_expansion if one wants a better 1st layer adhesion.
overhang_polygons = to_polygons(layer.lslices);
for (auto& slice : layer.lslices) {
auto bbox_size = get_extents(slice).size();
if (g_config_support_sharp_tails &&
!(bbox_size.x() > length_thresh_well_supported && bbox_size.y() > length_thresh_well_supported))
{
layer.sharp_tails.push_back(slice);
layer.sharp_tails_height.insert({ &slice, layer.height });
}
}
}
else if (! layer.regions().empty())
{
@ -1567,9 +1578,9 @@ static inline Polygons detect_overhangs(
// Offset the support regions back to a full overhang, restrict them to the full overhang.
// This is done to increase size of the supporting columns below, as they are calculated by
// propagating these contact surfaces downwards.
diff_polygons = diff(
intersection(expand(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons),
lower_layer_polygons);
diff_polygons =
expand(diff(intersection(expand(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons), lower_layer_polygons),
xy_expansion, SUPPORT_SURFACES_OFFSET_PARAMETERS);
}
//FIXME add user defined filtering here based on minimal area or minimum radius or whatever.
@ -1608,12 +1619,12 @@ static inline Polygons detect_overhangs(
supported_area += temp.area();
bbox.merge(get_extents(temp));
}
#if 0
if (supported_area > area_thresh_well_supported) {
is_sharp_tail = false;
break;
}
#endif
if (bbox.size().x() > length_thresh_well_supported && bbox.size().y() > length_thresh_well_supported) {
is_sharp_tail = false;
break;
@ -1839,7 +1850,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
bottom_z = (layer_id == 1) ? slicing_params.object_print_z_min : layer.lower_layer->lower_layer->print_z;
} else {
print_z = layer.bottom_z() - slicing_params.gap_support_object;
height = object_config.independent_support_layer_height ? 0. : object_config.layer_height;
height = print_config.independent_support_layer_height ? 0. : object_config.layer_height;
bottom_z = print_z - height;
// Ignore this contact area if it's too low.
// Don't want to print a layer below the first layer height as it may not stick well.
@ -1870,7 +1881,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
bridging_height += region->region().bridging_height_avg(print_config);
bridging_height /= coordf_t(layer.regions().size());
// BBS: align bridging height
if (!object_config.independent_support_layer_height)
if (!print_config.independent_support_layer_height)
bridging_height = std::ceil(bridging_height / object_config.layer_height - EPSILON) * object_config.layer_height;
coordf_t bridging_print_z = layer.print_z - bridging_height - slicing_params.gap_support_object;
if (bridging_print_z >= min_print_z) {
@ -1890,7 +1901,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
} else {
// BBS: if independent_support_layer_height is not enabled, the support layer_height should be the same as layer height.
// Note that for this case, adaptive layer height must be disabled.
bridging_layer->height = object_config.independent_support_layer_height ? 0. : object_config.layer_height;
bridging_layer->height = print_config.independent_support_layer_height ? 0. : object_config.layer_height;
// Don't know the height yet.
bridging_layer->bottom_z = bridging_print_z - bridging_layer->height;
}
@ -2423,7 +2434,7 @@ static inline PrintObjectSupportMaterial::MyLayer* detect_bottom_contacts(
// top shapes so this can be done here
//FIXME calculate layer height based on the actual thickness of the layer:
// If the layer is extruded with no bridging flow, support just the normal extrusions.
layer_new.height = slicing_params.soluble_interface || !object.config().independent_support_layer_height ?
layer_new.height = slicing_params.soluble_interface || !object.print()->config().independent_support_layer_height ?
// Align the interface layer with the object's layer height.
layer.upper_layer->height :
// Place a bridge flow interface layer or the normal flow interface layer over the top surface.
@ -4517,9 +4528,13 @@ void PrintObjectSupportMaterial::generate_toolpaths(
(m_object_config->support_interface_bottom_layers == 0 && &layer_ex == &bottom_contact_layer);
//FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore
// the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b)
auto interface_flow = layer_ex.layer->bridging ?
Flow::bridging_flow(layer_ex.layer->height, m_support_params.support_material_bottom_interface_flow.nozzle_diameter()) :
(interface_as_base ? &m_support_params.support_material_flow : &m_support_params.support_material_interface_flow)->with_height(float(layer_ex.layer->height));
Flow interface_flow;
if (layer_ex.layer->bridging)
interface_flow = Flow::bridging_flow(layer_ex.layer->height, m_support_params.support_material_bottom_interface_flow.nozzle_diameter());
else if (layer_ex.layer->bottom_z < EPSILON) {
interface_flow = m_support_params.first_layer_flow;
}else
interface_flow = (interface_as_base ? &m_support_params.support_material_flow : &m_support_params.support_material_interface_flow)->with_height(float(layer_ex.layer->height));
filler_interface->angle = interface_as_base ?
// If zero interface layers are configured, use the same angle as for the base layers.
angles[support_layer_id % angles.size()] :

View file

@ -136,6 +136,7 @@ public:
float base_angle;
float interface_angle;
coordf_t interface_spacing;
coordf_t support_expansion;
coordf_t interface_density;
coordf_t support_spacing;
coordf_t support_density;
@ -161,7 +162,7 @@ public:
bool has_support() const { return m_object_config->enable_support.value || m_object_config->enforce_support_layers; }
bool build_plate_only() const { return this->has_support() && m_object_config->support_on_build_plate_only.value; }
// BBS
bool synchronize_layers() const { return /*m_slicing_params.soluble_interface && */!m_object_config->independent_support_layer_height.value; }
bool synchronize_layers() const { return /*m_slicing_params.soluble_interface && */!m_print_config->independent_support_layer_height.value; }
bool has_contact_loops() const { return m_object_config->support_interface_loop_pattern.value; }
// Generate support material for the object.

View file

@ -6,7 +6,7 @@
namespace Slic3r {
enum SurfaceType {
enum SurfaceType {
// Top horizontal surface, visible from the top.
stTop,
// Bottom horizontal surface, visible from the bottom, printed with a normal extrusion flow.
@ -15,6 +15,8 @@ enum SurfaceType {
stBottomBridge,
// Normal sparse infill.
stInternal,
// Normal sparse infill.
stInternalWithLoop,
// Full infill, supporting the top surfaces and/or defining the verticall wall thickness.
stInternalSolid,
// 1st layer of dense infill over sparse infill, printed with a bridging extrusion flow.
@ -37,10 +39,14 @@ public:
unsigned short thickness_layers; // in layers
double bridge_angle; // in radians, ccw, 0 = East, only 0+ (negative means undefined)
unsigned short extra_perimeters;
Surface(SurfaceType _surface_type = stInternal)
: surface_type(_surface_type),
thickness(-1), thickness_layers(1), bridge_angle(-1), extra_perimeters(0)
{};
Surface(const Slic3r::Surface &rhs)
: surface_type(rhs.surface_type), expolygon(rhs.expolygon),
thickness(rhs.thickness), thickness_layers(rhs.thickness_layers),
thickness(rhs.thickness), thickness_layers(rhs.thickness_layers),
bridge_angle(rhs.bridge_angle), extra_perimeters(rhs.extra_perimeters)
{};
@ -50,12 +56,12 @@ public:
{};
Surface(const Surface &other, const ExPolygon &_expolygon)
: surface_type(other.surface_type), expolygon(_expolygon),
thickness(other.thickness), thickness_layers(other.thickness_layers),
thickness(other.thickness), thickness_layers(other.thickness_layers),
bridge_angle(other.bridge_angle), extra_perimeters(other.extra_perimeters)
{};
Surface(Surface &&rhs)
: surface_type(rhs.surface_type), expolygon(std::move(rhs.expolygon)),
thickness(rhs.thickness), thickness_layers(rhs.thickness_layers),
thickness(rhs.thickness), thickness_layers(rhs.thickness_layers),
bridge_angle(rhs.bridge_angle), extra_perimeters(rhs.extra_perimeters)
{};
Surface(SurfaceType _surface_type, const ExPolygon &&_expolygon)
@ -64,7 +70,7 @@ public:
{};
Surface(const Surface &other, const ExPolygon &&_expolygon)
: surface_type(other.surface_type), expolygon(std::move(_expolygon)),
thickness(other.thickness), thickness_layers(other.thickness_layers),
thickness(other.thickness), thickness_layers(other.thickness_layers),
bridge_angle(other.bridge_angle), extra_perimeters(other.extra_perimeters)
{};
@ -192,8 +198,8 @@ inline size_t number_polygons(const SurfacesPtr &surfaces)
}
// Append a vector of Surfaces at the end of another vector of polygons.
inline void polygons_append(Polygons &dst, const Surfaces &src)
{
inline void polygons_append(Polygons &dst, const Surfaces &src)
{
dst.reserve(dst.size() + number_polygons(src));
for (Surfaces::const_iterator it = src.begin(); it != src.end(); ++ it) {
dst.emplace_back(it->expolygon.contour);
@ -201,8 +207,8 @@ inline void polygons_append(Polygons &dst, const Surfaces &src)
}
}
inline void polygons_append(Polygons &dst, Surfaces &&src)
{
inline void polygons_append(Polygons &dst, Surfaces &&src)
{
dst.reserve(dst.size() + number_polygons(src));
for (Surfaces::iterator it = src.begin(); it != src.end(); ++ it) {
dst.emplace_back(std::move(it->expolygon.contour));
@ -212,8 +218,8 @@ inline void polygons_append(Polygons &dst, Surfaces &&src)
}
// Append a vector of Surfaces at the end of another vector of polygons.
inline void polygons_append(Polygons &dst, const SurfacesPtr &src)
{
inline void polygons_append(Polygons &dst, const SurfacesPtr &src)
{
dst.reserve(dst.size() + number_polygons(src));
for (SurfacesPtr::const_iterator it = src.begin(); it != src.end(); ++ it) {
dst.emplace_back((*it)->expolygon.contour);
@ -221,8 +227,8 @@ inline void polygons_append(Polygons &dst, const SurfacesPtr &src)
}
}
inline void polygons_append(Polygons &dst, SurfacesPtr &&src)
{
inline void polygons_append(Polygons &dst, SurfacesPtr &&src)
{
dst.reserve(dst.size() + number_polygons(src));
for (SurfacesPtr::const_iterator it = src.begin(); it != src.end(); ++ it) {
dst.emplace_back(std::move((*it)->expolygon.contour));
@ -232,41 +238,41 @@ inline void polygons_append(Polygons &dst, SurfacesPtr &&src)
}
// Append a vector of Surfaces at the end of another vector of polygons.
inline void surfaces_append(Surfaces &dst, const ExPolygons &src, SurfaceType surfaceType)
{
inline void surfaces_append(Surfaces &dst, const ExPolygons &src, SurfaceType surfaceType)
{
dst.reserve(dst.size() + src.size());
for (const ExPolygon &expoly : src)
dst.emplace_back(Surface(surfaceType, expoly));
}
inline void surfaces_append(Surfaces &dst, const ExPolygons &src, const Surface &surfaceTempl)
{
inline void surfaces_append(Surfaces &dst, const ExPolygons &src, const Surface &surfaceTempl)
{
dst.reserve(dst.size() + number_polygons(src));
for (const ExPolygon &expoly : src)
dst.emplace_back(Surface(surfaceTempl, expoly));
}
inline void surfaces_append(Surfaces &dst, const Surfaces &src)
{
inline void surfaces_append(Surfaces &dst, const Surfaces &src)
{
dst.insert(dst.end(), src.begin(), src.end());
}
inline void surfaces_append(Surfaces &dst, ExPolygons &&src, SurfaceType surfaceType)
{
inline void surfaces_append(Surfaces &dst, ExPolygons &&src, SurfaceType surfaceType)
{
dst.reserve(dst.size() + src.size());
for (ExPolygon &expoly : src)
dst.emplace_back(Surface(surfaceType, std::move(expoly)));
src.clear();
}
inline void surfaces_append(Surfaces &dst, ExPolygons &&src, const Surface &surfaceTempl)
{
inline void surfaces_append(Surfaces &dst, ExPolygons &&src, const Surface &surfaceTempl)
{
dst.reserve(dst.size() + number_polygons(src));
for (ExPolygons::const_iterator it = src.begin(); it != src.end(); ++ it)
dst.emplace_back(Surface(surfaceTempl, std::move(*it)));
src.clear();
}
inline void surfaces_append(Surfaces &dst, Surfaces &&src)
{
inline void surfaces_append(Surfaces &dst, Surfaces &&src)
{
if (dst.empty()) {
dst = std::move(src);
} else {
@ -281,7 +287,7 @@ extern BoundingBox get_extents(const SurfacesPtr &surfaces);
inline bool surfaces_could_merge(const Surface &s1, const Surface &s2)
{
return
return
s1.surface_type == s2.surface_type &&
s1.thickness == s2.thickness &&
s1.thickness_layers == s2.thickness_layers &&

View file

@ -37,6 +37,12 @@ public:
for (Surface &surface : this->surfaces)
surface.surface_type = type;
}
//BBS
void change_to_new_type(SurfaceType old_type, SurfaceType new_type) {
for (Surface& surface : this->surfaces)
if (surface.surface_type == old_type)
surface.surface_type = new_type;
}
void clear() { surfaces.clear(); }
bool empty() const { return surfaces.empty(); }

View file

@ -29,7 +29,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
{
@ -687,12 +687,11 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p
SupportMaterialPattern support_pattern = m_object_config->support_base_pattern;
m_support_params.base_fill_pattern =
#if HAS_LIGHTNING_INFILL
support_pattern == smpLightning ? ipLightning :
#endif
support_pattern == smpHoneycomb ? ipHoneycomb :
m_support_params.support_density > 0.95 || m_support_params.with_sheath ? ipRectilinear :
ipSupportBase;
m_support_params.interface_fill_pattern = (m_support_params.interface_density > 0.95 ? ipRectilinear : ipSupportBase);
m_support_params.contact_fill_pattern = (m_object_config->support_interface_pattern == smipAuto && m_slicing_params.soluble_interface) ||
m_object_config->support_interface_pattern == smipConcentric ?
@ -708,7 +707,7 @@ void TreeSupport::detect_object_overhangs()
if (m_object->tree_support_layer_count() >= m_object->layer_count())
return;
// Create Tree Support Layers
// Clear and create Tree Support Layers
m_object->clear_tree_support_layers();
m_object->clear_tree_support_preview_cache();
@ -721,11 +720,11 @@ void TreeSupport::detect_object_overhangs()
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);
const bool bridge_no_support = max_bridge_length > 0;// config.bridge_no_support.value;
const bool bridge_no_support = max_bridge_length > 0;
const bool support_critical_regions_only = config.support_critical_regions_only.value;
const int enforce_support_layers = config.enforce_support_layers.value;
const double area_thresh_well_supported = SQ(scale_(6)); // min: 6x6=36mm^2
const double length_thresh_well_supported = scale_(6); // min: 6mm
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;
// 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;
@ -822,20 +821,18 @@ void TreeSupport::detect_object_overhangs()
region2clusterInd.emplace(&region, regionClusters.size() - 1);
}
};
has_sharp_tail = false;
// main part of sharptail detections
if (std::set<SupportType>{stTreeAuto, stHybridAuto, stTree}.count(stype))// == stTreeAuto || stype == stHybridAuto || stype == stTree)
{
double threshold_rad = (config.support_threshold_angle.value < EPSILON ? 30 : config.support_threshold_angle.value+1) * M_PI / 180.;
ExPolygons regions_well_supported; // regions on buildplate or well supported
std::map<ExPolygon, int, ExPolygonComp> region_layers_below; // regions and the number of layers below
ExPolygons lower_overhang_dilated; // for small overhang
for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++)
{
ExPolygons regions_well_supported;
std::map<ExPolygon, int, ExPolygonComp> region_layers_below;
ExPolygons lower_overhang_dilated;
for (size_t layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++){
if (m_object->print()->canceled())
break;
if (!is_auto && layer_nr > enforce_support_layers)
continue;
@ -846,9 +843,13 @@ void TreeSupport::detect_object_overhangs()
if (layer->lower_layer == nullptr) {
for (auto& slice : layer->lslices) {
auto bbox_size = get_extents(slice).size();
if (slice.area() > area_thresh_well_supported
|| (bbox_size.x()>length_thresh_well_supported && bbox_size.y()>length_thresh_well_supported))
if (/*slice.area() > area_thresh_well_supported || */
(bbox_size.x()>length_thresh_well_supported && bbox_size.y()>length_thresh_well_supported))
regions_well_supported.emplace_back(slice);
else if(g_config_support_sharp_tails){
layer->sharp_tails.push_back(slice);
layer->sharp_tails_height.insert({ &slice, layer->height });
}
}
continue;
}
@ -873,7 +874,7 @@ void TreeSupport::detect_object_overhangs()
// normal overhang
ExPolygons lower_layer_offseted = offset_ex(lower_polys, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS);
ExPolygons overhang_areas = std::move(diff_ex(curr_polys, lower_layer_offseted));
// overhang_areas = std::move(offset2_ex(overhang_areas, -0.1 * extrusion_width_scaled, 0.1 * extrusion_width_scaled));
overhang_areas.erase(std::remove_if(overhang_areas.begin(), overhang_areas.end(),
[extrusion_width_scaled](ExPolygon &area) { return offset_ex(area, -0.1 * extrusion_width_scaled).empty(); }),
overhang_areas.end());
@ -903,7 +904,6 @@ void TreeSupport::detect_object_overhangs()
overhangs_sharp_tail = diff_ex(overhangs_sharp_tail, overhang_areas);
}
if (!overhangs_sharp_tail.empty()) {
has_sharp_tail = true;
append(layer->sharp_tails, overhangs_sharp_tail);
overhang_areas = union_ex(overhang_areas, overhangs_sharp_tail);
}
@ -935,12 +935,13 @@ void TreeSupport::detect_object_overhangs()
// 2.2 If sharp tail below, check whether it support this region enough.
float supported_area = area(supported_by_lower);
BoundingBox bbox = get_extents(supported_by_lower);
#if 0
// judge by area isn't reliable, failure cases include 45 degree rotated cube
if (supported_area > area_thresh_well_supported) {
is_sharp_tail = false;
break;
}
#endif
if (bbox.size().x() > length_thresh_well_supported && bbox.size().y() > length_thresh_well_supported) {
is_sharp_tail = false;
break;
@ -953,7 +954,6 @@ void TreeSupport::detect_object_overhangs()
break;
}
}
if (accum_height >= sharp_tail_max_support_height) {
is_sharp_tail = false;
break;
@ -961,7 +961,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);
ExPolygons new_overhang_expolys = diff_ex({expoly}, lower_layer_sharptails);
if (!offset_ex(new_overhang_expolys, -5.0 * extrusion_width_scaled).empty()) {
is_sharp_tail = false;
break;
@ -972,7 +972,6 @@ void TreeSupport::detect_object_overhangs()
} while (0);
if (is_sharp_tail) {
has_sharp_tail = true;
ExPolygons overhang = diff_ex({expoly}, lower_layer->lslices);
layer->sharp_tails.push_back(expoly);
layer->sharp_tails_height.insert({ &expoly, accum_height });
@ -1065,7 +1064,7 @@ void TreeSupport::detect_object_overhangs()
}
dist_max = std::max(dist_max, dist_pt);
}
if (dist_max > scale_(5)) { // this cluster is cantilever, add all expolygons to sharp tail
if (dist_max > scale_(3)) { // this cluster is cantilever if the farmost point is larger than 3mm away from base
for (auto it = cluster.layer_overhangs.begin(); it != cluster.layer_overhangs.end(); it++) {
int layer_nr = it->first;
auto p_overhang = it->second;
@ -1133,10 +1132,10 @@ void TreeSupport::detect_object_overhangs()
// if (erode1.empty() && !inter_with_others.empty())
// blockers[layer_nr].push_back(p_overhang->contour);
}
}
}
has_overhangs = false;
for (int layer_nr = 0; layer_nr < m_object->layer_count(); layer_nr++) {
if (m_object->print()->canceled())
break;
@ -1144,7 +1143,12 @@ void TreeSupport::detect_object_overhangs()
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
if (support_critical_regions_only) {
auto layer = m_object->get_layer(layer_nr);
ts_layer->overhang_areas = layer->sharp_tails;
auto lower_layer = layer->lower_layer;
if (lower_layer == nullptr)
ts_layer->overhang_areas = layer->sharp_tails;
else
ts_layer->overhang_areas = diff_ex(layer->sharp_tails, lower_layer->lslices);
append(ts_layer->overhang_areas, layer->cantilevers);
}
@ -1156,17 +1160,20 @@ void TreeSupport::detect_object_overhangs()
for (auto &area : ts_layer->overhang_areas) {
ts_layer->overhang_types.emplace(&area, TreeSupportLayer::Detected);
}
// enforcers
if (layer_nr < enforcers.size()) {
Polygons& enforcer = enforcers[layer_nr];
// coconut: enforcer can't do offset2_ex, otherwise faces with angle near 90 degrees can't have enforcers, which
// is not good. For example: tails of animals needs extra support except the lowest tip.
//enforcer = std::move(offset2_ex(enforcer, -0.1 * extrusion_width_scaled, 0.1 * extrusion_width_scaled));
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);
}
}
if (!ts_layer->overhang_areas.empty()) has_overhangs = true;
}
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
@ -1296,7 +1303,7 @@ static void make_perimeter_and_inner_brim(ExtrusionEntitiesPtr &dst, const Print
float(flow.mm3_per_mm()), float(flow.width()), float(flow.height()));
}
static void make_perimeter_and_infill(ExtrusionEntitiesPtr& dst, const Print& print, const ExPolygon& support_area, size_t wall_count, const Flow& flow, ExtrusionRole role, Fill* filler_support, double support_density)
static void make_perimeter_and_infill(ExtrusionEntitiesPtr& dst, const Print& print, const ExPolygon& support_area, size_t wall_count, const Flow& flow, ExtrusionRole role, Fill* filler_support, double support_density, bool infill_first=true)
{
Polygons loops;
ExPolygons support_area_new = offset_ex(support_area, -0.5f * float(flow.scaled_spacing()), jtSquare);
@ -1357,20 +1364,29 @@ static void make_perimeter_and_infill(ExtrusionEntitiesPtr& dst, const Print& pr
expoly_list.erase(first_iter);
}
extrusion_entities_append_loops(dst, std::move(loops), role,
float(flow.mm3_per_mm()), float(flow.width()), float(flow.height()));
if (infill_first)
extrusion_entities_append_loops(dst, std::move(loops), role,
float(flow.mm3_per_mm()), float(flow.width()), float(flow.height()));
else { // loops first
ExtrusionEntitiesPtr loops_entities;
extrusion_entities_append_loops(loops_entities, std::move(loops), role,
float(flow.mm3_per_mm()), float(flow.width()), float(flow.height()));
loops_entities.insert(loops_entities.end(), dst.begin(), dst.end());
dst = std::move(loops_entities);
}
}
if (infill_first) {
// sort regions to reduce travel
Points ordering_points;
for (const auto& area : dst)
ordering_points.push_back(area->first_point());
std::vector<Points::size_type> order = chain_points(ordering_points);
ExtrusionEntitiesPtr new_dst;
new_dst.reserve(ordering_points.size());
for (size_t i : order)
new_dst.emplace_back(dst[i]);
dst = new_dst;
}
// sort regions to reduce travel
Points ordering_points;
for (const auto& area : dst)
ordering_points.push_back(area->first_point());
std::vector<Points::size_type> order = chain_points(ordering_points);
ExtrusionEntitiesPtr new_dst;
new_dst.reserve(ordering_points.size());
for (size_t i : order)
new_dst.emplace_back(dst[i]);
dst = new_dst;
}
void TreeSupport::generate_toolpaths()
@ -1379,10 +1395,10 @@ void TreeSupport::generate_toolpaths()
const PrintObjectConfig &object_config = m_object->config();
coordf_t support_extrusion_width = object_config.support_line_width.value > 0 ? object_config.support_line_width : object_config.line_width;
coordf_t nozzle_diameter = print_config.nozzle_diameter.get_at(object_config.support_filament - 1);
coordf_t layer_height = object_config.layer_height.value;
const size_t wall_count = object_config.tree_support_wall_count.value;
const bool with_infill = object_config.tree_support_with_infill.value;
const bool contact_loops = object_config.support_interface_loop_pattern.value;
const bool with_infill = object_config.support_base_pattern != smpNone && object_config.support_base_pattern != smpDefault;
auto m_support_material_flow = support_material_flow(m_object, float(m_slicing_params.layer_height));
// coconut: use same intensity settings as SupportMaterial.cpp
@ -1502,20 +1518,20 @@ void TreeSupport::generate_toolpaths()
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_id);
Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter);
ts_layer->support_fills.no_sort = false;
for (auto& area_group : ts_layer->area_groups) {
ExPolygon& poly = *area_group.first;
ExPolygon& poly = *area_group.area;
ExPolygons polys;
FillParams fill_params;
if (area_group.second != TreeSupportLayer::BaseType) {
if (area_group.type != TreeSupportLayer::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, *m_object->print(), poly, wall_count, flow,
area_group.second == TreeSupportLayer::RoofType);
area_group.type == TreeSupportLayer::RoofType);
polys = std::move(offset_ex(poly, -flow.scaled_spacing()));
} else if (area_group.second == TreeSupportLayer::Roof1stLayer) {
} else if (area_group.type == TreeSupportLayer::Roof1stLayer) {
polys = std::move(offset_ex(poly, 0.5*support_flow.scaled_width()));
}
else {
@ -1524,73 +1540,61 @@ void TreeSupport::generate_toolpaths()
fill_params.density = interface_density;
fill_params.dont_adjust = true;
}
if (area_group.second == TreeSupportLayer::Roof1stLayer) {
if (area_group.type == TreeSupportLayer::Roof1stLayer) {
// roof_1st_layer
fill_params.density = interface_density;
// Note: spacing means the separation between two lines as if they are tightly extruded
filler_Roof1stLayer->spacing = m_support_material_interface_flow.spacing();
fill_expolygons_generate_paths(ts_layer->support_fills.entities, std::move(polys), filler_Roof1stLayer.get(), fill_params, erSupportMaterial,
m_support_material_interface_flow);
} else if (area_group.second == TreeSupportLayer::FloorType) {
// generate a perimeter first to support interface better
ExtrusionEntityCollection* temp_support_fills = new ExtrusionEntityCollection();
make_perimeter_and_infill(temp_support_fills->entities, *m_object->print(), poly, 1, m_support_material_interface_flow, erSupportMaterial,
filler_Roof1stLayer.get(), interface_density, false);
temp_support_fills->no_sort = true; // make sure loops are first
if (!temp_support_fills->entities.empty())
ts_layer->support_fills.entities.push_back(temp_support_fills);
else
delete temp_support_fills;
} else if (area_group.type == TreeSupportLayer::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.second == TreeSupportLayer::RoofType) {
} else if (area_group.type == TreeSupportLayer::RoofType) {
// roof_areas
fill_params.density = interface_density;
fill_params.density = interface_density;
filler_interface->spacing = m_support_material_interface_flow.spacing();
/*if (contact_loops) {
make_perimeter_and_inner_brim(ts_layer->support_fills.entities, *m_object->print(), poly,
std::numeric_limits<size_t>::max(), m_support_material_interface_flow, true);
}
else*/ {
fill_expolygons_generate_paths(ts_layer->support_fills.entities, std::move(polys),
filler_interface.get(), fill_params, erSupportMaterialInterface, m_support_material_interface_flow);
}
fill_expolygons_generate_paths(ts_layer->support_fills.entities, std::move(polys), filler_interface.get(), fill_params, erSupportMaterialInterface,
m_support_material_interface_flow);
}
else {
// base_areas
filler_support->spacing = m_support_material_flow.spacing();
ExtrusionRole role;
Flow flow = (layer_id == 0 && m_raft_layers == 0) ? m_object->print()->brim_flow() :
(m_support_params.base_fill_pattern == ipRectilinear && (layer_id % num_layers_to_change_infill_direction == 0) ? support_transition_flow(m_object) : support_flow);
if (with_infill && layer_id > 0 && m_support_params.base_fill_pattern != ipLightning) {
if (m_support_params.base_fill_pattern == ipRectilinear) {
role = erSupportMaterial;// layer_id% num_layers_to_change_infill_direction == 0 ? erSupportTransition : erSupportMaterial;
filler_support->angle = Geometry::deg2rad(object_config.support_angle.value);// obj_is_vertical* M_PI_2;// (obj_is_vertical + int(layer_id / num_layers_to_change_infill_direction))* M_PI_2;
}
else {
role = erSupportMaterial;
filler_support->angle = Geometry::deg2rad(object_config.support_angle.value);// obj_is_vertical * M_PI_2 + (float)layer_id / num_layers_to_change_infill_direction * M_PI_4;
}
// only wall at the top of tree branch
if (offset(poly, -branch_radius_scaled*1.5).empty())
{
if (area_group.dist_to_top < 10 / layer_height) {
// extra 2 walls for the top tips
make_perimeter_and_inner_brim(ts_layer->support_fills.entities, *m_object->print(), poly, wall_count + 2, flow, false);
} else {
if (with_infill && layer_id > 0 && m_support_params.base_fill_pattern != ipLightning) {
filler_support->angle = Geometry::deg2rad(object_config.support_angle.value);
// allow infill-only mode if support is thick enough
if (offset(poly, -scale_(support_spacing * 1.5)).empty() == false) {
make_perimeter_and_infill(ts_layer->support_fills.entities, *m_object->print(), poly, wall_count, flow, erSupportMaterial,
filler_support.get(), support_density);
} else { // otherwise must draw 1 wall
make_perimeter_and_infill(ts_layer->support_fills.entities, *m_object->print(), poly, std::max(size_t(1), wall_count), flow,
erSupportMaterial, filler_support.get(), support_density);
}
} else {
make_perimeter_and_inner_brim(ts_layer->support_fills.entities, *m_object->print(), poly,
wall_count, flow, false);
layer_id > 0 ? wall_count : std::numeric_limits<size_t>::max(), flow, false);
}
// allow infill-only mode if support is thick enough
else if (offset(poly, -scale_(support_spacing * 1.5)).empty() == false)
{
make_perimeter_and_infill(ts_layer->support_fills.entities, *m_object->print(), poly, wall_count, flow, role, filler_support.get(), support_density);
}
else { // otherwise must draw 1 wall
//if (m_support_params.base_fill_pattern == ipRectilinear)
make_perimeter_and_infill(ts_layer->support_fills.entities, *m_object->print(), poly, 1, flow, role, filler_support.get(), support_density);
//else
// make_perimeter_and_inner_brim(ts_layer->support_fills.entities, *m_object->print(), poly, 1, flow, false);
}
}
else
{
make_perimeter_and_inner_brim(ts_layer->support_fills.entities, *m_object->print(), poly,
layer_id > 0 ? wall_count : std::numeric_limits<size_t>::max(), flow, false);
}
}
}
if (with_infill && m_support_params.base_fill_pattern == ipLightning)
if (m_support_params.base_fill_pattern == ipLightning)
{
double print_z = ts_layer->print_z;
if (printZ_to_lightninglayer.find(print_z) == printZ_to_lightninglayer.end())
@ -1603,11 +1607,7 @@ void TreeSupport::generate_toolpaths()
// strengthen lightnings while it may make support harder. decide to enable it or not. if yes, proper values for params are remained to be tested
auto& lightning_layer = generator->getTreesForLayer(printZ_to_lightninglayer[print_z]);
Flow flow = (layer_id == 0 && m_raft_layers == 0) ?
m_object->print()->brim_flow() :
(m_support_params.base_fill_pattern == ipRectilinear && (layer_id % num_layers_to_change_infill_direction == 0) ?
support_transition_flow(m_object) :
support_flow);
Flow flow = (layer_id == 0 && m_raft_layers == 0) ? m_object->print()->brim_flow() :support_flow;
ExPolygons areas = offset_ex(ts_layer->base_areas, -flow.scaled_spacing());
for (auto& area : areas)
@ -1636,24 +1636,21 @@ void TreeSupport::generate_toolpaths()
float(flow.mm3_per_mm()), float(flow.width()), float(flow.height()));
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
std::string prefix = "./SVG/";
std::string suffix = ".svg";
std::string name = prefix + "trees_polyline" + "_" + std::to_string(ts_layer->print_z) /*+ "_" + std::to_string(rand_num)*/ + suffix;
std::string name = "./SVG/trees_polyline_" + std::to_string(ts_layer->print_z) /*+ "_" + std::to_string(rand_num)*/ + ".svg";
BoundingBox bbox = get_extents(ts_layer->base_areas);
SVG svg(name, bbox);
svg.draw(ts_layer->base_areas, "blue");
svg.draw(generator->Overhangs()[printZ_to_lightninglayer[print_z]], "red");
for (auto& line : opt_polylines)
{
svg.draw(line, "yellow");
if (svg.is_opened()) {
svg.draw(ts_layer->base_areas, "blue");
svg.draw(generator->Overhangs()[printZ_to_lightninglayer[print_z]], "red");
for (auto &line : opt_polylines) svg.draw(line, "yellow");
}
#endif
}
}
// sort extrusions to reduce travel, also make sure walls go before infills
chain_and_reorder_extrusion_entities(ts_layer->support_fills.entities);
if(ts_layer->support_fills.no_sort==false)
chain_and_reorder_extrusion_entities(ts_layer->support_fills.entities);
}
}
);
@ -1871,7 +1868,7 @@ void TreeSupport::generate_support_areas()
if (!tree_support_enable)
return;
std::vector<std::vector<Node*>> contact_nodes(m_object->layers().size()); //Generate empty layers to store the points in.
std::vector<std::vector<Node*>> contact_nodes(m_object->layers().size());
profiler.stage_start(STAGE_total);
@ -1881,6 +1878,8 @@ void TreeSupport::generate_support_areas()
detect_object_overhangs();
profiler.stage_finish(STAGE_DETECT_OVERHANGS);
if (!has_overhangs) return;
// Generate contact points of tree support
profiler.stage_start(STAGE_GENERATE_CONTACT_NODES);
m_object->print()->set_status(56, _L("Support: generate contact points"));
@ -1963,8 +1962,9 @@ ExPolygons avoid_object_remove_extra_small_parts(ExPolygons &expolys, const ExPo
void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_nodes)
{
const PrintObjectConfig &config = m_object->config();
bool has_brim = m_object->print()->has_brim();
bool has_infill = config.tree_support_with_infill.value;
const Print* print = m_object->print();
bool has_brim = print->has_brim();
bool has_infill = config.support_base_pattern.value != smpNone && config.support_base_pattern != smpDefault;
int bottom_gap_layers = round(m_slicing_params.gap_object_support / m_slicing_params.layer_height);
const coordf_t branch_radius = config.tree_support_branch_diameter.value / 2;
const coordf_t branch_radius_scaled = scale_(branch_radius);
@ -1988,7 +1988,6 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
// Performance optimization. Only generate lslices for brim and skirt.
size_t brim_skirt_layers = has_brim ? 1 : 0;
const Print* print = m_object->print();
const PrintConfig& print_config = print->config();
for (const PrintObject* object : print->objects())
{
@ -2005,7 +2004,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
const coordf_t line_width = config.support_line_width;
const coordf_t line_width_scaled = scale_(line_width);
const bool with_lightning_infill = config.tree_support_with_infill.value && config.support_base_pattern.value == smpLightning;
const bool with_lightning_infill = m_support_params.base_fill_pattern == ipLightning;
coordf_t support_extrusion_width = config.support_line_width.value > 0 ? config.support_line_width : config.line_width;
const size_t wall_count = config.tree_support_wall_count.value;
@ -2013,6 +2012,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
auto m_support_material_flow = support_material_flow(m_object, float(m_slicing_params.layer_height));
coordf_t support_spacing = object_config.support_base_pattern_spacing.value + m_support_material_flow.spacing();
coordf_t support_density = std::min(1., m_support_material_flow.spacing() / support_spacing);
BOOST_LOG_TRIVIAL(info) << "draw_circles for object: " << m_object->model_object()->name;
// coconut: previously std::unordered_map in m_collision_cache is not multi-thread safe which may cause programs stuck, here we change to tbb::concurrent_unordered_map
tbb::parallel_for(
@ -2042,6 +2042,10 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
ExPolygons& roof_areas = ts_layer->roof_areas;
ExPolygons& roof_1st_layer = ts_layer->roof_1st_layer;
ExPolygons& floor_areas = ts_layer->floor_areas;
ExPolygons& roof_gap_areas = ts_layer->roof_gap_areas;
int max_layers_above_base = 0;
int max_layers_above_roof = 0;
int max_layers_above_roof1 = 0;
BOOST_LOG_TRIVIAL(debug) << "circles at layer " << layer_nr << " contact nodes size=" << contact_nodes[layer_nr].size();
//Draw the support areas and add the roofs appropriately to the support roof instead of normal areas.
@ -2053,14 +2057,24 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
const Node& node = *p_node;
ExPolygon area;
if (node.type == ePolygon) {
area = offset_ex({ *node.overhang }, scale_(m_ts_data->m_xy_distance))[0];
// 直接从overhang多边形生成如果<E5A682>?
// 1) 是混合支撑里的普通部分,
// 2) 启用了顶部接触层<E8A7A6>?
// 3) 是顶部空<E983A8>?
if (node.type == ePolygon || (top_interface_layers>0 &&node.support_roof_layers_below > 0) || node.distance_to_top<0) {
if (node.overhang->contour.size() > 100 || node.overhang->holes.size()>1)
area = *node.overhang;
else {
auto tmp = offset_ex({ *node.overhang }, scale_(m_ts_data->m_xy_distance));
if(!tmp.empty()) // 对于有缺陷的模型overhang膨胀以后可能是空的
area = tmp[0];
}
}
else {
Polygon circle;
size_t layers_to_top = node.distance_to_top;
double scale;
if (top_interface_layers>0) { // if has infill, branch circles should be larger
if (top_interface_layers>0) { // if has interface, branch circles should be larger
scale = static_cast<double>(layers_to_top + 1) / tip_layers;
scale = layers_to_top < tip_layers ? (0.5 + scale / 2) : (1 + static_cast<double>(layers_to_top - tip_layers) * diameter_angle_scale_factor);
} else {
@ -2080,17 +2094,22 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
area = ExPolygon(circle);
}
if (node.support_roof_layers_below == 1)
if (node.distance_to_top < 0)
roof_gap_areas.emplace_back(area);
else if (node.support_roof_layers_below == 1)
{
roof_1st_layer.emplace_back(area);
max_layers_above_roof1 = std::max(max_layers_above_roof1, node.distance_to_top);
}
else if (node.support_roof_layers_below > 0)
{
roof_areas.emplace_back(area);
max_layers_above_roof = std::max(max_layers_above_roof, node.distance_to_top);
}
else
{
base_areas.emplace_back(area);
max_layers_above_base = std::max(max_layers_above_base, node.distance_to_top);
}
if (layer_nr < brim_skirt_layers)
@ -2129,6 +2148,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
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));
if (SQUARE_SUPPORT) {
// simplify support contours
@ -2142,9 +2162,12 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
{
if (layer_nr >= bottom_interface_layers + bottom_gap_layers)
{
const Layer* below_layer = m_object->get_layer(layer_nr - bottom_interface_layers);
ExPolygons bottom_interface = std::move(intersection_ex(base_areas, below_layer->lslices));
floor_areas.insert(floor_areas.end(), bottom_interface.begin(), bottom_interface.end());
for (size_t i = 0; i <= bottom_gap_layers; i++)
{
const Layer* below_layer = m_object->get_layer(layer_nr - bottom_interface_layers - i);
ExPolygons bottom_interface = std::move(intersection_ex(base_areas, below_layer->lslices));
floor_areas.insert(floor_areas.end(), bottom_interface.begin(), bottom_interface.end());
}
}
if (floor_areas.empty() == false) {
floor_areas = std::move(diff_ex(floor_areas, avoid_region_interface));
@ -2159,15 +2182,14 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
floor_areas = std::move(diff_ex(floor_areas, bottom_gap));
}
}
auto &area_groups = ts_layer->area_groups;
for (auto &area : ts_layer->base_areas) area_groups.emplace_back(&area, TreeSupportLayer::BaseType);
for (auto &area : ts_layer->roof_areas) area_groups.emplace_back(&area, TreeSupportLayer::RoofType);
for (auto &area : ts_layer->floor_areas) area_groups.emplace_back(&area, TreeSupportLayer::FloorType);
for (auto &area : ts_layer->roof_1st_layer) area_groups.emplace_back(&area, TreeSupportLayer::Roof1stLayer);
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_group : area_groups) {
auto expoly = area_group.first;
auto& expoly = area_group.area;
expoly->holes.erase(std::remove_if(expoly->holes.begin(), expoly->holes.end(),
[](auto &hole) {
auto bbox_size = get_extents(hole).size();
@ -2242,7 +2264,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
#endif
}
generator = std::make_unique<FillLightning::Generator>(m_object, contours, overhangs, support_density);
generator = std::make_unique<FillLightning::Generator>(m_object, contours, overhangs, []() {}, support_density);
}
else if (!has_infill) {
@ -2294,13 +2316,13 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
auto& area_groups_lower = m_object->get_tree_support_layer(layer_nr_lower + m_raft_layers)->area_groups;
for (const auto& area_group : ts_layer->area_groups) {
if (area_group.second != TreeSupportLayer::BaseType) continue;
const auto area = area_group.first;
if (area_group.type != TreeSupportLayer::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.second != TreeSupportLayer::BaseType) continue;
auto& base_area_lower = *area_group_lower.first;
if (area_group.type != TreeSupportLayer::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
if (base_area_lower.contour.contains(hole.points.front()) && !intersects_contour(hole, base_area_lower, pt_on_poly, pt_on_expoly, pt_far_on_poly)) {
@ -2391,8 +2413,9 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
//Use Minimum Spanning Tree to connect the points on each layer and move them while dropping them down.
const coordf_t layer_height = config.layer_height.value;
const double angle = config.tree_support_branch_angle.value * M_PI / 180.;
const int wall_count = std::max(1, config.tree_support_wall_count.value);
const double tan_angle = tan(angle);
const coordf_t max_move_distance = (angle < M_PI / 2) ? (coordf_t)(tan_angle * layer_height) : std::numeric_limits<coordf_t>::max();
const coordf_t max_move_distance = (angle < M_PI / 2) ? (coordf_t)(tan_angle * layer_height)*wall_count : std::numeric_limits<coordf_t>::max();
const double max_move_distance2 = max_move_distance * max_move_distance;
const coordf_t branch_radius = config.tree_support_branch_diameter.value / 2;
const size_t tip_layers = branch_radius / layer_height; //The number of layers to be shrinking the circle to create a tip. This produces a 45 degree angle.
@ -2410,7 +2433,7 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
//m_object->print()->set_status(59, "Support: preparing avoidance regions ");
// get all the possible radiis
std::vector<std::set<coordf_t> > all_layer_radius(m_highest_overhang_layer+1);
std::vector<std::set<size_t> > all_layer_node_dist(m_highest_overhang_layer+1);
std::vector<std::set<int> > all_layer_node_dist(m_highest_overhang_layer+1);
for (size_t layer_nr = m_highest_overhang_layer; layer_nr > 0; layer_nr--)
{
auto& layer_contact_nodes = contact_nodes[layer_nr];
@ -2486,6 +2509,13 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
{
const Node& node = *p_node;
if (node.distance_to_top < 0) {
// virtual node 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,
m_object->get_layer(layer_nr - 1)->print_z, m_object->get_layer(layer_nr - 1)->height);
contact_nodes[layer_nr - 1].emplace_back(next_node);
continue;
}
if (support_on_buildplate_only && !node.to_buildplate) //Can't rest on model and unable to reach the build plate. Then we must drop the node and leave parts unsupported.
{
unsupported_branch_leaves.push_front({ layer_nr, p_node });
@ -2664,13 +2694,18 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
// 1. do not merge neighbors under 5mm
// 2. Only merge node with single neighbor in distance between [max_move_distance, 10mm/layer_height]
float dist2_to_first_neighbor = neighbours.empty() ? 0 : vsize2_with_unscale(neighbours[0] - node.position);
float max_dist_to_move = 10.0*tan_angle; // don't move if moving down by 10mm and they still can't merge
if (ts_layer->print_z > DO_NOT_MOVER_UNDER_MM &&
(neighbours.size() > 1 || (neighbours.size() == 1 && dist2_to_first_neighbor >= max_move_distance2 && dist2_to_first_neighbor < SQ(10/layer_height)*max_move_distance2))) //Only nodes that aren't about to collapse.
(neighbours.size() > 1 || (neighbours.size() == 1 && dist2_to_first_neighbor >= max_move_distance2))) //Only nodes that aren't about to collapse.
{
//Move towards the average position of all neighbours.
Point sum_direction(0, 0);
for (const Point& neighbour : neighbours)
{
// do not move to neighbor that's too far away
float dist2_to_neighbor = vsize2_with_unscale(neighbour - node.position);
if (dist2_to_neighbor > SQ(max_dist_to_move)) continue;
Point direction = neighbour - node.position;
Node *neighbour_node = nodes_per_part[group_index][neighbour];
coordf_t branch_bottom_radius = calc_branch_radius(branch_radius, node.distance_to_top + layer_nr, tip_layers, diameter_angle_scale_factor);
@ -2707,7 +2742,6 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
#endif
auto avoid_layer = m_ts_data->get_avoidance(branch_radius_node, layer_nr - 1);
#if 1
Point to_outside = projection_onto_ex(avoid_layer, node.position);
Point movement = to_outside - node.position;
double movelength2 = vsize2_with_unscale(movement);
@ -2726,14 +2760,12 @@ void TreeSupport::drop_nodes(std::vector<std::vector<Node*>>& contact_nodes)
if (movement.dot(move_to_neighbor_center) >= 0)
movement = movement + move_to_neighbor_center;
// Cant do this. Otherwise we'll get a lot of supports in-the-air (nodes terminated too early)
//else
// movement = move_to_neighbor_center; // otherwise move to neighbor center first
else
movement = move_to_neighbor_center; // otherwise move to neighbor center first
if (vsize2_with_unscale(movement) > max_move_distance2)
movement = normal(movement, scale_(max_move_distance));
#else
Point movement = move_to_neighbor_center;
#endif
next_layer_vertex += movement;
@ -2815,8 +2847,9 @@ void TreeSupport::adjust_layer_heights(std::vector<std::vector<Node*>>& contact_
if (contact_nodes.empty())
return;
const PrintConfig& print_config = m_object->print()->config();
const PrintObjectConfig& config = m_object->config();
if (!config.independent_support_layer_height) {
if (!print_config.independent_support_layer_height) {
for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) {
std::vector<Node*>& curr_layer_nodes = contact_nodes[layer_nr];
for (Node* node : curr_layer_nodes) {
@ -2936,7 +2969,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
if (!m_slicing_params.soluble_interface && m_object_config->thick_bridges) {
z_distance_top += m_object->layers()[0]->regions()[0]->region().bridging_height_avg(m_object->print()->config()) - layer_height;
}
const size_t z_distance_top_layers = round_up_divide(scale_(z_distance_top), scale_(layer_height)) + 1; //Support must always be 1 layer below overhang.
const int z_distance_top_layers = round_up_divide(scale_(z_distance_top), scale_(layer_height)) + 1; //Support must always be 1 layer below overhang.
const size_t support_roof_layers = config.support_interface_top_layers.value + 1; // BBS: add a normal support layer below interface
coordf_t thresh_angle = config.support_threshold_angle.value < EPSILON ? 30.f : config.support_threshold_angle.value;
@ -2949,11 +2982,11 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
m_highest_overhang_layer = 0;
int nonempty_layers = 0;
std::vector<Slic3r::Vec3f> all_nodes;
for (size_t layer_nr = 1; layer_nr < m_object->layers().size() - z_distance_top_layers; layer_nr++)
for (size_t layer_nr = 1; layer_nr < m_object->layers().size(); layer_nr++)
{
if (m_object->print()->canceled())
break;
auto ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers + z_distance_top_layers);
auto ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
const ExPolygons &overhang = ts_layer->overhang_areas;
auto & curr_nodes = contact_nodes[layer_nr];
if (overhang.empty())
@ -2970,7 +3003,7 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
Point candidate = overhang_bounds.center();
if (!overhang_part.contains(candidate))
move_inside_expoly(overhang_part, candidate);
Node *contact_node = new Node(candidate, 0, (layer_nr + z_distance_top_layers) % 2, support_roof_layers, true, Node::NO_PARENT, print_z, height);
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, height);
contact_node->type = ePolygon;
contact_node->overhang = &overhang_part;
curr_nodes.emplace_back(contact_node);
@ -2996,9 +3029,9 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
// collision radius has to be 0 or the supports are too few at curved slopes
//if (!is_inside_ex(m_ts_data->get_collision(0, layer_nr), candidate))
{
constexpr size_t distance_to_top = 0;
constexpr bool to_buildplate = true;
Node* contact_node = new Node(candidate, distance_to_top, (layer_nr + z_distance_top_layers) % 2, support_roof_layers, to_buildplate, Node::NO_PARENT,print_z,height);
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,print_z,height);
contact_node->overhang = &overhang_part;
curr_nodes.emplace_back(contact_node);
added = true;
}
@ -3018,9 +3051,9 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
for (Point candidate : candidates) {
if (!overhang_part.contains(candidate))
move_inside_expoly(overhang_part, candidate);
constexpr size_t distance_to_top = 0;
constexpr bool to_buildplate = true;
Node * contact_node = new Node(candidate, distance_to_top, layer_nr % 2, support_roof_layers, to_buildplate, Node::NO_PARENT, print_z, height);
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, print_z, height);
contact_node->overhang = &overhang_part;
curr_nodes.emplace_back(contact_node);
}
}
@ -3032,7 +3065,8 @@ void TreeSupport::generate_contact_points(std::vector<std::vector<TreeSupport::N
auto v1 = (pt - points[(i - 1 + points.size()) % points.size()]).normalized();
auto v2 = (pt - points[(i + 1) % points.size()]).normalized();
if (v1.dot(v2) > -0.7) {
Node *contact_node = new Node(pt, 0, layer_nr % 2, support_roof_layers, true, Node::NO_PARENT, print_z, height);
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, height);
contact_node->overhang = &overhang_part;
curr_nodes.emplace_back(contact_node);
}
}

View file

@ -223,7 +223,7 @@ public:
, height(0.0)
{}
Node(const Point position, const size_t 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 bool skin_direction, const int support_roof_layers_below, const bool to_buildplate, Node* parent,
coordf_t print_z_, coordf_t height_)
: distance_to_top(distance_to_top)
, position(position)
@ -252,8 +252,9 @@ public:
/*!
* \brief The number of layers to go to the top of this branch.
* Negative value means it's a virtual node between support and overhang, which doesn't need to be extruded.
*/
size_t distance_to_top;
int distance_to_top;
/*!
* \brief The position of this node on the layer.
@ -350,7 +351,7 @@ public:
int avg_node_per_layer = 0;
float nodes_angle = 0;
bool has_sharp_tail;
bool has_overhangs = false;
std::unique_ptr<FillLightning::Generator> generator;
std::unordered_map<double, size_t> printZ_to_lightninglayer;

View file

@ -399,7 +399,7 @@ struct SlabLines {
std::vector<IntersectionLines> at_slice;
// Projections of triangle set boundary lines into layer below (for projection from the top)
// or into layer above (for projection from the bottom).
// In both cases the intersection liens are CCW oriented.
// In both cases the intersection lines are CCW oriented.
std::vector<IntersectionLines> between_slices;
};
@ -753,7 +753,9 @@ inline std::pair<SlabLines, SlabLines> slice_slabs_make_lines(
const std::vector<float> &zs,
bool top,
bool bottom,
const ThrowOnCancel throw_on_cancel_fn)
const ThrowOnCancel throw_on_cancel_fn,
// BBS: solve conflicts (see declaration) and most elegant way I can get
SlabSlicingConfig config)
{
std::pair<SlabLines, SlabLines> out;
SlabLines &lines_top = out.first;
@ -772,7 +774,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]
[&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]
(const tbb::blocked_range<int> &range) {
for (int face_idx = range.begin(); face_idx < range.end(); ++ face_idx) {
if ((face_idx & 0x0ffff) == 0)
@ -790,7 +792,8 @@ 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);
}
if (bottom && (fo == FaceOrientation::Down || fo == FaceOrientation::Degenerate)) {
// BBS: add vertical faces option
if (bottom && (fo == FaceOrientation::Down || (config.isVertical && fo == FaceOrientation::Vertical) || 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)
@ -1895,7 +1898,8 @@ void slice_mesh_slabs(
const Transform3d &trafo,
std::vector<Polygons> *out_top,
std::vector<Polygons> *out_bottom,
std::function<void()> throw_on_cancel)
std::function<void()> throw_on_cancel,
SlabSlicingConfig config)
{
BOOST_LOG_TRIVIAL(debug) << "slice_mesh_slabs to polygons";
@ -1974,7 +1978,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);
out_top != nullptr, out_bottom != nullptr, throw_on_cancel, config);
throw_on_cancel();

View file

@ -46,6 +46,19 @@ 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().
@ -107,7 +120,9 @@ void slice_mesh_slabs(
const Transform3d &trafo,
std::vector<Polygons> *out_top,
std::vector<Polygons> *out_bottom,
std::function<void()> throw_on_cancel);
std::function<void()> throw_on_cancel,
// BBS: MusangKing
SlabSlicingConfig config = SlabSlicingConfig());
// Project mesh upwards pointing surfaces / downwards pointing surfaces into 2D polygons.
void project_mesh(

View file

@ -13,6 +13,42 @@
#include "libslic3r.h"
//define CLI errors
#define CLI_SUCCESS 0
#define CLI_ENVIRONMENT_ERROR -1
#define CLI_INVALID_PARAMS -2
#define CLI_FILE_NOTFOUND -3
#define CLI_FILELIST_INVALID_ORDER -4
#define CLI_CONFIG_FILE_ERROR -5
#define CLI_DATA_FILE_ERROR -6
#define CLI_INVALID_PRINTER_TECH -7
#define CLI_UNSUPPORTED_OPERATION -8
#define CLI_COPY_OBJECTS_ERROR -9
#define CLI_SCALE_TO_FIT_ERROR -10
#define CLI_EXPORT_STL_ERROR -11
#define CLI_EXPORT_OBJ_ERROR -12
#define CLI_EXPORT_3MF_ERROR -13
#define CLI_OUT_OF_MEMORY -14
#define CLI_3MF_NOT_SUPPORT_MACHINE_CHANGE -15
#define CLI_3MF_NEW_MACHINE_NOT_SUPPORTED -16
#define CLI_PROCESS_NOT_COMPATIBLE -17
#define CLI_NO_SUITABLE_OBJECTS -50
#define CLI_VALIDATE_ERROR -51
#define CLI_OBJECTS_PARTLY_INSIDE -52
#define CLI_EXPORT_CACHE_DIRECTORY_CREATE_FAILED -53
#define CLI_EXPORT_CACHE_WRITE_FAILED -54
#define CLI_IMPORT_CACHE_NOT_FOUND -55
#define CLI_IMPORT_CACHE_DATA_CAN_NOT_USE -56
#define CLI_IMPORT_CACHE_LOAD_FAILED -57
#define CLI_SLICING_ERROR -100
namespace boost { namespace filesystem { class directory_entry; }}
namespace Slic3r {
@ -161,11 +197,11 @@ namespace PerlUtils {
std::string string_printf(const char *format, ...);
// Standard "generated by Slic3r version xxx timestamp xxx" header string,
// Standard "generated by Slic3r version xxx timestamp xxx" header string,
// to be placed at the top of Slic3r generated files.
std::string header_slic3r_generated();
// Standard "generated by PrusaGCodeViewer version xxx timestamp xxx" header string,
// Standard "generated by PrusaGCodeViewer version xxx timestamp xxx" header string,
// to be placed at the top of Slic3r generated files.
std::string header_gcodeviewer_generated();
@ -247,38 +283,38 @@ inline INDEX_TYPE next_idx_modulo(INDEX_TYPE idx, const INDEX_TYPE count)
}
template<typename CONTAINER_TYPE>
inline typename CONTAINER_TYPE::size_type prev_idx_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container)
{
inline typename CONTAINER_TYPE::size_type prev_idx_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container)
{
return prev_idx_modulo(idx, container.size());
}
template<typename CONTAINER_TYPE>
inline typename CONTAINER_TYPE::size_type next_idx_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container)
{
{
return next_idx_modulo(idx, container.size());
}
template<typename CONTAINER_TYPE>
inline const typename CONTAINER_TYPE::value_type& prev_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container)
{
{
return container[prev_idx_modulo(idx, container.size())];
}
template<typename CONTAINER_TYPE>
inline typename CONTAINER_TYPE::value_type& prev_value_modulo(typename CONTAINER_TYPE::size_type idx, CONTAINER_TYPE &container)
{
inline typename CONTAINER_TYPE::value_type& prev_value_modulo(typename CONTAINER_TYPE::size_type idx, CONTAINER_TYPE &container)
{
return container[prev_idx_modulo(idx, container.size())];
}
template<typename CONTAINER_TYPE>
inline const typename CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container)
{
{
return container[next_idx_modulo(idx, container.size())];
}
template<typename CONTAINER_TYPE>
inline typename CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER_TYPE::size_type idx, CONTAINER_TYPE &container)
{
{
return container[next_idx_modulo(idx, container.size())];
}
@ -300,7 +336,7 @@ template<typename T> struct IsTriviallyCopyable : public std::is_trivially_copya
struct FilePtr {
FilePtr(FILE *f) : f(f) {}
~FilePtr() { this->close(); }
void close() {
void close() {
if (this->f) {
::fclose(this->f);
this->f = nullptr;
@ -347,16 +383,19 @@ inline std::string short_time(const std::string &time)
int hours = 0;
int minutes = 0;
int seconds = 0;
float f_seconds = 0.0;
if (time.find('d') != std::string::npos)
::sscanf(time.c_str(), "%dd %dh %dm %ds", &days, &hours, &minutes, &seconds);
else if (time.find('h') != std::string::npos)
::sscanf(time.c_str(), "%dh %dm %ds", &hours, &minutes, &seconds);
else if (time.find('m') != std::string::npos)
::sscanf(time.c_str(), "%dm %ds", &minutes, &seconds);
else if (time.find('s') != std::string::npos)
::sscanf(time.c_str(), "%ds", &seconds);
else if (time.find('s') != std::string::npos) {
::sscanf(time.c_str(), "%fs", &f_seconds);
seconds = int(f_seconds);
}
// Round to full minutes.
if (days + hours + minutes > 0 && seconds >= 30) {
if (days + hours > 0 && seconds >= 30) {
if (++minutes == 60) {
minutes = 0;
if (++hours == 24) {
@ -372,9 +411,13 @@ inline std::string short_time(const std::string &time)
else if (hours > 0)
::sprintf(buffer, "%dh%dm", hours, minutes);
else if (minutes > 0)
::sprintf(buffer, "%dm%ds", minutes, seconds);
else
::sprintf(buffer, "%ds", seconds);
::sprintf(buffer, "%dm%ds", minutes, (int)seconds);
else if (seconds >= 1)
::sprintf(buffer, "%ds", (int)seconds);
else if (f_seconds > 0 && f_seconds < 1)
::sprintf(buffer, "<1s");
else if (seconds == 0)
::sprintf(buffer, "0s");
return buffer;
}
@ -395,8 +438,10 @@ inline std::string get_time_dhms(float time_in_secs)
::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)time_in_secs);
else if (minutes > 0)
::sprintf(buffer, "%dm %ds", minutes, (int)time_in_secs);
else
else if (time_in_secs > 1)
::sprintf(buffer, "%ds", (int)time_in_secs);
else
::sprintf(buffer, "%fs", time_in_secs);
return buffer;
}

View file

@ -75,6 +75,7 @@ static constexpr double BRIDGE_INFILL_MARGIN = 1;
//FIXME Better to use an inline function with an explicit return type.
//inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); }
#define scale_(val) ((val) / SCALING_FACTOR)
#define unscale_(val) ((val) * SCALING_FACTOR)
//BBS: BBS only support relative E and can't been changed by user at the moment. because
//BBS need to support skip object when printing.

View file

@ -818,7 +818,7 @@ CopyFileResult copy_file_inner(const std::string& from, const std::string& to, s
if (ec) {
error_message = ec.message();
BOOST_LOG_TRIVIAL(error) << boost::format("###copy_file from %1% to %2% failed, error: %3% ")
%source.string() %target.string() << error_message;
%source.string() %target.string() % error_message;
return FAIL_COPY_FILE;
}
ec.clear();