Merge branch 'main' into zaa

This commit is contained in:
SoftFever
2026-04-28 16:57:02 +08:00
committed by GitHub
11158 changed files with 1904567 additions and 1867499 deletions
+7 -2
View File
@@ -523,6 +523,11 @@ void AppConfig::set_defaults()
set_bool("installed_networking", false);
}
#ifdef __linux__
if (get("window_buttons_on_left").empty())
set_bool("window_buttons_on_left", false);
#endif
// Remove legacy window positions/sizes
erase("app", "main_frame_maximized");
erase("app", "main_frame_pos");
@@ -941,13 +946,13 @@ void AppConfig::save()
}
boost::nowide::ofstream c;
c.open(path_pid, std::ios::out | std::ios::trunc);
c << std::setw(4) << j << std::endl;
c << j.dump(1, '\t') << std::endl;
#ifdef WIN32
// WIN32 specific: The final "rename_file()" call is not safe in case of an application crash, there is no atomic "rename file" API
// provided by Windows (sic!). Therefore we save a MD5 checksum to be able to verify file corruption. In addition,
// we save the config file into a backup first before moving it to the final destination.
c << appconfig_md5_hash_line(j.dump(4));
c << appconfig_md5_hash_line(j.dump(1, '\t'));
#endif
c.close();
+27 -11
View File
@@ -54,6 +54,12 @@ WallToolPathsParams make_paths_params(const int layer_id, const PrintObjectConfi
input_params.wall_distribution_count = print_object_config.wall_distribution_count.value;
input_params.is_top_or_bottom_layer = false; // Set to default value
if (const auto& wall_maximum_resolution_opt = print_object_config.wall_maximum_resolution)
input_params.wall_maximum_resolution = scaled<coord_t>(wall_maximum_resolution_opt.value);
if (const auto& wall_maximum_deviation_opt = print_object_config.wall_maximum_deviation)
input_params.wall_maximum_deviation = scaled<coord_t>(wall_maximum_deviation_opt.value);
}
return input_params;
@@ -125,7 +131,11 @@ void simplify(Polygon &thiss, const int64_t smallest_line_segment_squared, const
accumulated_area_removed += removed_area_next;
const int64_t length2 = (current - previous).cast<int64_t>().squaredNorm();
if (length2 < scaled<int64_t>(25.)) {
// Orca:
// Checking if the segment's length is smaller than 5 microns (0.005mm).
// The value of `length2` is scaled and squared, so we need to compare it with the squared value of 5 microns
if (length2 < Slic3r::sqr(scaled<coord_t>(0.005))) {
// We're allowed to always delete segments of less than 5 micron.
continue;
}
@@ -143,6 +153,7 @@ void simplify(Polygon &thiss, const int64_t smallest_line_segment_squared, const
//h^2 = (L / b)^2 [square it]
//h^2 = L^2 / b^2 [factor the divisor]
const int64_t height_2 = double(area_removed_so_far) * double(area_removed_so_far) / double(base_length_2);
// Orca: The value of `height_2` is squared, so we need to compare it with the squared value
if ((height_2 <= Slic3r::sqr(scaled<coord_t>(0.005)) //Almost exactly colinear (barring rounding errors).
&& Line::distance_to_infinite(current, previous, next) <= scaled<double>(0.005))) // make sure that height_2 is not small because of cancellation of positive and negative areas
continue;
@@ -473,8 +484,8 @@ const std::vector<VariableWidthLines> &WallToolPaths::generate()
if (this->inset_count < 1)
return toolpaths;
const coord_t smallest_segment = Slic3r::Arachne::meshfix_maximum_resolution();
const coord_t allowed_distance = Slic3r::Arachne::meshfix_maximum_deviation();
const coord_t smallest_segment = m_params.wall_maximum_resolution;
const coord_t allowed_distance = m_params.wall_maximum_deviation;
const coord_t epsilon_offset = (allowed_distance / 2) - 1;
const double transitioning_angle = Geometry::deg2rad(m_params.wall_transition_angle);
const coord_t discretization_step_size = scaled<coord_t>(0.8);
@@ -547,7 +558,7 @@ const std::vector<VariableWidthLines> &WallToolPaths::generate()
separateOutInnerContour();
simplifyToolPaths(toolpaths);
simplifyToolPaths(toolpaths, m_params);
removeEmptyToolPaths(toolpaths);
assert(std::is_sorted(toolpaths.cbegin(), toolpaths.cend(),
@@ -688,16 +699,21 @@ void WallToolPaths::removeSmallLines(std::vector<VariableWidthLines> &toolpaths)
}
}
void WallToolPaths::simplifyToolPaths(std::vector<VariableWidthLines> &toolpaths)
void WallToolPaths::simplifyToolPaths(std::vector<VariableWidthLines>& toolpaths, const WallToolPathsParams& params)
{
for (size_t toolpaths_idx = 0; toolpaths_idx < toolpaths.size(); ++toolpaths_idx)
const int64_t maximum_resolution = params.wall_maximum_resolution;
const int64_t maximum_deviation = params.wall_maximum_deviation;
const int64_t smallest_line_segment_squared = maximum_resolution * maximum_resolution;
const int64_t allowed_error_distance_squared = maximum_deviation * maximum_deviation;
const int64_t maximum_extrusion_area_deviation = Slic3r::Arachne::meshfix_maximum_extrusion_area_deviation(); // unit: μm²
for (VariableWidthLines& lines : toolpaths)
{
const int64_t maximum_resolution = Slic3r::Arachne::meshfix_maximum_resolution();
const int64_t maximum_deviation = Slic3r::Arachne::meshfix_maximum_deviation();
const int64_t maximum_extrusion_area_deviation = Slic3r::Arachne::meshfix_maximum_extrusion_area_deviation(); // unit: μm²
for (auto& line : toolpaths[toolpaths_idx])
for (ExtrusionLine& line : lines)
{
line.simplify(maximum_resolution * maximum_resolution, maximum_deviation * maximum_deviation, maximum_extrusion_area_deviation);
line.simplify(smallest_line_segment_squared, allowed_error_distance_squared, maximum_extrusion_area_deviation);
}
}
}
+7 -3
View File
@@ -15,7 +15,7 @@
namespace Slic3r::Arachne
{
constexpr bool fill_outline_gaps = true;
constexpr bool fill_outline_gaps = true;
inline coord_t meshfix_maximum_resolution() { return scaled<coord_t>(0.5); }
inline coord_t meshfix_maximum_deviation() { return scaled<coord_t>(0.025); }
inline coord_t meshfix_maximum_extrusion_area_deviation() { return scaled<coord_t>(2.); }
@@ -31,6 +31,9 @@ public:
float wall_transition_filter_deviation;
int wall_distribution_count;
bool is_top_or_bottom_layer;
coord_t wall_maximum_resolution = meshfix_maximum_resolution();
coord_t wall_maximum_deviation = meshfix_maximum_deviation();
};
WallToolPathsParams make_paths_params(const int layer_id, const PrintObjectConfig &print_object_config, const PrintConfig &print_config);
@@ -116,10 +119,11 @@ protected:
/*!
* Simplifies the variable-width toolpaths by calling the simplify on every line in the toolpath using the provided
* settings.
* \param settings The settings as provided by the user
* \param toolpaths The toolpaths vector to simplify
* \param params The settings as provided by the user
* \return
*/
static void simplifyToolPaths(std::vector<VariableWidthLines> &toolpaths);
static void simplifyToolPaths(std::vector<VariableWidthLines>& toolpaths, const WallToolPathsParams& params);
private:
const Polygons& outline; //<! A reference to the outline polygon that is the designated area
@@ -106,7 +106,11 @@ void ExtrusionLine::simplify(const int64_t smallest_line_segment_squared, const
accumulated_area_removed += removed_area_next;
const int64_t length2 = (current - previous).cast<int64_t>().squaredNorm();
if (length2 < scaled<coord_t>(0.025))
// Orca:
// Checking if the segment's length is smaller than 5 microns (0.005mm).
// The value of `length2` is scaled and squared, so we need to compare it with the squared value of 5 microns
if (length2 < Slic3r::sqr(scaled<coord_t>(0.005)))
{
// We're allowed to always delete segments of less than 5 micron. The width in this case doesn't matter that much.
continue;
@@ -128,8 +132,9 @@ void ExtrusionLine::simplify(const int64_t smallest_line_segment_squared, const
//h^2 = L^2 / b^2 [factor the divisor]
const auto height_2 = int64_t(double(area_removed_so_far) * double(area_removed_so_far) / double(base_length_2));
const int64_t extrusion_area_error = calculateExtrusionAreaDeviationError(previous, current, next);
if ((height_2 <= scaled<coord_t>(0.001) //Almost exactly colinear (barring rounding errors).
&& Line::distance_to_infinite(current.p, previous.p, next.p) <= scaled<double>(0.001)) // Make sure that height_2 is not small because of cancellation of positive and negative areas
// Orca: The value of `height_2` is squared, so we need to compare it with the squared value
if ((height_2 <= Slic3r::sqr(scaled<coord_t>(0.005)) // Almost exactly colinear (barring rounding errors).
&& Line::distance_to_infinite(current.p, previous.p, next.p) <= scaled<double>(0.005)) // Make sure that height_2 is not small because of cancellation of positive and negative areas
// We shouldn't remove middle junctions of colinear segments if the area changed for the C-P segment is exceeding the maximum allowed
&& extrusion_area_error <= maximum_extrusion_area_deviation)
{
+10 -1
View File
@@ -527,7 +527,16 @@ bool BuildVolume::all_paths_inside(const GCodeProcessorResult& paths, const Boun
build_volume.max.z() = std::numeric_limits<double>::max();
if (ignore_bottom)
build_volume.min.z() = -std::numeric_limits<double>::max();
return build_volume.contains(paths_bbox);
// BBox-only callers may provide no moves. Validate bbox corners regardless of paths_bbox.defined.
if (paths.moves.empty())
return build_volume.contains(paths_bbox.min) && build_volume.contains(paths_bbox.max);
if (paths_bbox.defined && build_volume.contains(paths_bbox))
return true;
// Fallback: validate only relevant extrusion moves.
const BoundingBox3Base<Vec3f> build_volumef(build_volume.min.cast<float>(), build_volume.max.cast<float>());
return std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, build_volumef](const GCodeProcessorResult::MoveVertex &move)
{ return !move_valid(move) || build_volumef.contains(move.position); });
}
case BuildVolume_Type::Circle:
{
+1 -1
View File
@@ -1498,7 +1498,7 @@ void ConfigBase::save_to_json(const std::string &file, const std::string &name,
boost::nowide::ofstream c;
c.open(file, std::ios::out | std::ios::trunc);
c << std::setw(4) << j << std::endl;
c << j.dump(1, '\t') << std::endl;
c.close();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", saved config to %1%\n")%file;
+1
View File
@@ -199,6 +199,7 @@ enum ConfigOptionType {
enum ConfigOptionMode {
comSimple = 0,
comAdvanced,
comExpert,
comDevelop,
};
+15
View File
@@ -2559,6 +2559,21 @@ void priv::create_face_types(FaceTypeMap &map,
bool priv::clip_cut(SurfacePatch &cut, CutMesh clipper)
{
CutMesh& tm = cut.mesh;
auto is_mesh_usable_for_clip = [](const CutMesh &mesh) {
return !mesh.is_empty() &&
mesh.number_of_vertices() >= 3 &&
mesh.number_of_faces() > 0 &&
mesh.is_valid(false);
};
if (!is_mesh_usable_for_clip(tm) || !is_mesh_usable_for_clip(clipper))
return false;
// Hard-stop pathological inputs before entering corefinement internals.
if (CGAL::Polygon_mesh_processing::does_self_intersect(tm) ||
CGAL::Polygon_mesh_processing::does_self_intersect(clipper))
return false;
// create backup for case that there is no intersection
CutMesh backup_copy = tm;
+2 -2
View File
@@ -443,9 +443,9 @@ namespace Emboss
/// Sample slice polygon by bounding boxes centers
/// slice start point has shape_center_x coor
/// </summary>
/// <param name="slice">Polygon and start point[Slic3r scaled milimeters]</param>
/// <param name="slice">Polygon and start point[Slic3r scaled millimeters]</param>
/// <param name="bbs">Bounding boxes of letter on one line[in font scales]</param>
/// <param name="scale">Scale for bbs (after multiply bb is in milimeters)</param>
/// <param name="scale">Scale for bbs (after multiply bb is in millimeters)</param>
/// <returns>Sampled polygon by bounding boxes</returns>
PolygonPoints sample_slice(const TextLine &slice, const BoundingBoxes &bbs, double scale);
+12 -3
View File
@@ -808,8 +808,10 @@ void split_solid_surface(size_t layer_id, const SurfaceFill &fill, ExPolygons &n
return;
}
// Expand the normal infills a little bit to avoid gaps between normal and narrow infills
normal_infill = intersection_ex(offset_ex(normal_fill_areas_ex, scaled_spacing * 0.1), fill.expolygons);
// Expand the normal infills to avoid gaps between normal and narrow infills.
// The inner_area was shrunk by scaled_spacing * 0.5, so we need to expand
// by at least that amount to ensure proper coverage and avoid gaps.
normal_infill = intersection_ex(offset_ex(normal_fill_areas_ex, scaled_spacing * 0.5), fill.expolygons);
narrow_infill = narrow_fill_areas;
#ifdef DEBUG_SURFACE_SPLIT
@@ -1319,7 +1321,14 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.density = f->print_object_config->internal_bridge_density.get_abs_value(1.0);
params.dont_adjust = true;
}
// BBS: make fill
// Orca: Elefant foot compensation for solid layers above bottommost by infill density manipulation.
float elefant_density = f->print_object_config->elefant_foot_layers_density.get_abs_value(1.0);
if (!is_approx(elefant_density, 1.0f) && surface_fill.surface.is_solid_infill()) {
size_t elefant_layers = f->print_object_config->elefant_foot_compensation_layers.value;
if (f->layer_id > 0 && f->layer_id <= elefant_layers)
params.density = elefant_density * (elefant_layers - (f->layer_id - 1)) / elefant_layers;
}
// make fill
f->fill_surface_extrusion(&surface_fill.surface,
params,
m_regions[surface_fill.region_id]->fills.entities);
+4 -3
View File
@@ -3082,6 +3082,7 @@ bool FillRectilinear::fill_surface_trapezoidal(
// Use extended object bounding box for consistent pattern across layers
BoundingBox bb = this->extended_object_bounding_box();
const size_t infill_layer_id = (surface->thickness_layers > 0) ? this->layer_id / surface->thickness_layers : this->layer_id;
switch (Pattern_type) {
case 0: // Grid / Trapezoidal
@@ -3144,8 +3145,8 @@ bool FillRectilinear::fill_surface_trapezoidal(
flip_vertical = !flip_vertical;
}
// transpose points for odd layers
if (layer_id % 2 == 1) {
// transpose points for odd infill layers (taking infill combination into account)
if (infill_layer_id % 2 == 1) {
for (Polyline& pl : polylines) {
for (Point& p : pl.points) {
std::swap(p.x(), p.y());
@@ -3174,7 +3175,7 @@ bool FillRectilinear::fill_surface_trapezoidal(
// Align bounding box to the grid
bb.merge(align_to_grid(bb.center(), Point(period,h)));
const int layer_mod = layer_id % 3;
const size_t layer_mod = infill_layer_id % 3;
const double angle = layer_mod * 2.0 * M_PI / 3.0;
const Point rotation_center = bb.center();
+50 -16
View File
@@ -172,7 +172,10 @@ const std::string BBL_LICENSE_TAG = "License";
const std::string BBL_REGION_TAG = "Region";
const std::string BBL_MODIFICATION_TAG = "ModificationDate";
const std::string BBL_CREATION_DATE_TAG = "CreationDate";
// Orca: BBL current version
const std::string BBL_APPLICATION_TAG = "Application";
// OrcaSlicer version tag
const std::string ORCASLICER_TAG = "OrcaSlicer";
const std::string BBL_MAKERLAB_TAG = "MakerLab";
const std::string BBL_MAKERLAB_VERSION_TAG = "MakerLabVersion";
@@ -313,6 +316,7 @@ static constexpr const char* TRANSFORM_ATTR = "transform";
// BBS
static constexpr const char* OFFSET_ATTR = "offset";
static constexpr const char* PRINTABLE_ATTR = "printable";
static constexpr const char* AUTO_DROP_ATTR = "auto_drop";
static constexpr const char* INSTANCESCOUNT_ATTR = "instances_count";
static constexpr const char* CUSTOM_SUPPORTS_ATTR = "paint_supports";
static constexpr const char* CUSTOM_FUZZY_SKIN_ATTR = "paint_fuzzy_skin";
@@ -1030,6 +1034,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
std::string m_origin_file;
// Semantic version of Orca Slicer, that generated this 3MF.
boost::optional<Semver> m_bambuslicer_generator_version;
// Semantic version from the OrcaSlicer metadata tag (if present).
boost::optional<Semver> m_orca_slicer_version;
unsigned int m_fdm_supports_painting_version = 0;
unsigned int m_seam_painting_version = 0;
unsigned int m_mm_painting_version = 0;
@@ -1101,7 +1107,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
//BBS: add plate data related logic
// add backup & restore logic
bool load_model_from_file(const std::string& filename, Model& model, PlateDataPtrs& plate_data_list, std::vector<Preset*>& project_presets, DynamicPrintConfig& config,
ConfigSubstitutionContext& config_substitutions, LoadStrategy strategy, bool* is_bbl_3mf, Semver& file_version, Import3mfProgressFn proFn = nullptr, BBLProject *project = nullptr, int plate_id = 0);
ConfigSubstitutionContext& config_substitutions, LoadStrategy strategy, bool* is_bbl_3mf, bool* is_orca_3mf, Semver& file_version, Import3mfProgressFn proFn = nullptr, BBLProject *project = nullptr, int plate_id = 0);
bool get_thumbnail(const std::string &filename, std::string &data);
bool load_gcode_3mf_from_stream(std::istream & data, Model& model, PlateDataPtrs& plate_data_list, DynamicPrintConfig& config, Semver& file_version);
unsigned int version() const { return m_version; }
@@ -1204,7 +1210,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
bool _handle_start_text_configuration(const char** attributes, unsigned int num_attributes);
bool _handle_start_shape_configuration(const char **attributes, unsigned int num_attributes);
bool _create_object_instance(std::string const & path, int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter);
bool _create_object_instance(std::string const & path, int object_id, const Transform3d& transform, const bool printable, const bool auto_drop, unsigned int recur_counter);
void _apply_transform(ModelInstance& instance, const Transform3d& transform);
@@ -1309,7 +1315,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
//BBS: add plate data related logic
// add backup & restore logic
bool _BBS_3MF_Importer::load_model_from_file(const std::string& filename, Model& model, PlateDataPtrs& plate_data_list, std::vector<Preset*>& project_presets, DynamicPrintConfig& config,
ConfigSubstitutionContext& config_substitutions, LoadStrategy strategy, bool* is_bbl_3mf, Semver& file_version, Import3mfProgressFn proFn, BBLProject *project, int plate_id)
ConfigSubstitutionContext& config_substitutions, LoadStrategy strategy, bool* is_bbl_3mf, bool* is_orca_3mf, Semver& file_version, Import3mfProgressFn proFn, BBLProject *project, int plate_id)
{
m_version = 0;
m_fdm_supports_painting_version = 0;
@@ -1365,8 +1371,18 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
if (is_bbl_3mf) {
*is_bbl_3mf = m_is_bbl_3mf;
}
if (m_bambuslicer_generator_version)
file_version = *m_bambuslicer_generator_version;
// If the OrcaSlicer tag is present, use it as file_version (ignoring the Bambu Application version).
// Otherwise fall back to the version parsed from the Application tag.
if (m_orca_slicer_version) {
file_version = *m_orca_slicer_version;
if (is_orca_3mf)
*is_orca_3mf = true;
} else {
if (m_bambuslicer_generator_version)
file_version = *m_bambuslicer_generator_version;
if (is_orca_3mf)
*is_orca_3mf = false;
}
// save for restore
if (result && m_load_aux && !m_load_restore) {
save_string_file(model.get_backup_path() + "/origin.txt", filename);
@@ -3766,8 +3782,9 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
std::string path = bbs_get_attribute_value_string(attributes, num_attributes, PPATH_ATTR);
Transform3d transform = bbs_get_transform_from_3mf_specs_string(bbs_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
int printable = bbs_get_attribute_value_bool(attributes, num_attributes, PRINTABLE_ATTR);
int auto_drop = bbs_get_attribute_value_bool(attributes, num_attributes, AUTO_DROP_ATTR);
return !m_load_model || _create_object_instance(path, object_id, transform, printable, 1);
return !m_load_model || _create_object_instance(path, object_id, transform, printable, auto_drop, 1);
}
bool _BBS_3MF_Importer::_handle_end_item()
@@ -3816,6 +3833,12 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
m_is_bbl_3mf = true;
m_bambuslicer_generator_version = Semver::parse(m_curr_characters.substr(11));
}
} else if (m_curr_metadata_name == ORCASLICER_TAG) {
// OrcaSlicer version tag (written from OrcaSlicer 2.3.2 onwards)
m_orca_slicer_version = Semver::parse(m_curr_characters);
if (m_orca_slicer_version) {
m_is_bbl_3mf = true;
}
//TODO: currently use version 0, no need to load&&save this string
/*} else if (m_curr_metadata_name == BBS_FDM_SUPPORTS_PAINTING_VERSION) {
m_fdm_supports_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
@@ -3989,7 +4012,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return true;
}
bool _BBS_3MF_Importer::_create_object_instance(std::string const & path, int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter)
bool _BBS_3MF_Importer::_create_object_instance(std::string const & path, int object_id, const Transform3d& transform, const bool printable, const bool auto_drop, unsigned int recur_counter)
{
static const unsigned int MAX_RECURSIONS = 10;
@@ -4026,6 +4049,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return false;
}
instance->printable = printable;
instance->auto_drop = auto_drop;
m_instances.emplace_back(instance, transform);
@@ -4058,6 +4082,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return false;
}
instance->printable = printable;
instance->auto_drop = auto_drop;
m_instances.emplace_back(instance, transform);
}
@@ -5614,12 +5639,14 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
unsigned int id;
Transform3d transform;
bool printable;
bool auto_drop;
BuildItem(std::string const & path, unsigned int id, const Transform3d& transform, const bool printable)
BuildItem(std::string const & path, unsigned int id, const Transform3d& transform, const bool printable, const bool auto_drop)
: path(path)
, id(id)
, transform(transform)
, printable(printable)
, auto_drop(auto_drop)
{
}
};
@@ -6652,8 +6679,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
// Orca: PRIVACY: do not store creation & modification date in 3mf
metadata_item_map[BBL_CREATION_DATE_TAG] = "";
metadata_item_map[BBL_MODIFICATION_TAG] = "";
//SoftFever: write BambuStudio tag to keep it compatible
metadata_item_map[BBL_APPLICATION_TAG] = (boost::format("%1%-%2%") % "BambuStudio" % SoftFever_VERSION).str();
// Orca: Write the BambuStudio compatibility version string using SLIC3R_VERSION
metadata_item_map[BBL_APPLICATION_TAG] = (boost::format("%1%-%2%") % "BambuStudio" % SLIC3R_VERSION).str();
}
metadata_item_map[BBS_3MF_VERSION] = std::to_string(VERSION_BBS_3MF);
@@ -6678,6 +6705,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
BOOST_LOG_TRIVIAL(info) << "bbs_3mf: save key= " << item.first << ", value = " << item.second;
stream << " <" << METADATA_TAG << " name=\"" << item.first << "\">"
<< xml_escape(item.second) << "</" << METADATA_TAG << ">\n";
if (item.first == BBL_APPLICATION_TAG) {
stream << " <" << METADATA_TAG << " name=\"" << ORCASLICER_TAG << "\">"
<< xml_escape(SoftFever_VERSION) << "</" << METADATA_TAG << ">\n";
}
}
stream << " <" << RESOURCES_TAG << ">\n";
@@ -6784,7 +6815,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
Transform3d t = instance->get_matrix();
// instance_id is just a 1 indexed index in build_items.
//assert(m_skip_static || curr_id == build_items.size() + 1);
build_items.emplace_back("", object_it->second.object_id, t, instance->printable);
build_items.emplace_back("", object_it->second.object_id, t, instance->printable, instance->auto_drop);
count++;
}
@@ -7220,7 +7252,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
stream << "\" " << PPATH_ATTR << "=\"" << xml_escape(item.path);
stream << "\" " << TRANSFORM_ATTR << "=\"";
add_transformation(stream, item.transform);
stream << "\" " << PRINTABLE_ATTR << "=\"" << item.printable << "\"/>\n";
stream << "\" " << PRINTABLE_ATTR << "=\"" << item.printable;
stream << "\" " << AUTO_DROP_ATTR << "=\"" << item.auto_drop << "\"/>\n";
}
stream << " </" << BUILD_TAG << ">\n";
@@ -7535,7 +7568,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
{
const std::string& temp_path = model.get_backup_path();
std::string temp_file = temp_path + std::string("/") + "_temp_1.config";
config.save_to_json(temp_file, std::string("project_settings"), std::string("project"), std::string(SoftFever_VERSION));
config.save_to_json(temp_file, std::string("project_settings"), std::string("project"), std::string(SLIC3R_VERSION));
return _add_file_to_archive(archive, BBS_PROJECT_CONFIG_FILE, temp_file);
}
@@ -7930,7 +7963,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
// save slice header for debug
stream << " <" << SLICE_HEADER_TAG << ">\n";
stream << " <" << SLICE_HEADER_ITEM_TAG << " " << KEY_ATTR << "=\"" << "X-BBL-Client-Type" << "\" " << VALUE_ATTR << "=\"" << "slicer" << "\"/>\n";
stream << " <" << SLICE_HEADER_ITEM_TAG << " " << KEY_ATTR << "=\"" << "X-BBL-Client-Version" << "\" " << VALUE_ATTR << "=\"" << convert_to_full_version(SoftFever_VERSION) << "\"/>\n";
stream << " <" << SLICE_HEADER_ITEM_TAG << " " << KEY_ATTR << "=\"" << "X-BBL-Client-Version" << "\" " << VALUE_ATTR << "=\"" << convert_to_full_version(SLIC3R_VERSION) << "\"/>\n";
stream << " <" << SLICE_HEADER_ITEM_TAG << " " << KEY_ATTR << "=\"" << "OrcaSlicer-Version" << "\" " << VALUE_ATTR << "=\"" << SoftFever_VERSION << "\"/>\n";
stream << " </" << SLICE_HEADER_TAG << ">\n";
for (unsigned int i = 0; i < (unsigned int)plate_data_list.size(); ++i)
@@ -8626,7 +8660,7 @@ private:
//BBS: add plate data list related logic
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, LoadStrategy strategy, BBLProject *project, int plate_id)
bool* is_bbl_3mf, bool* is_orca_3mf, Semver* file_version, Import3mfProgressFn proFn, LoadStrategy strategy, BBLProject *project, int plate_id)
{
if (path == nullptr || config == nullptr || model == nullptr)
return false;
@@ -8634,7 +8668,7 @@ bool load_bbs_3mf(const char* path, DynamicPrintConfig* config, ConfigSubstituti
// All import should use "C" locales for number formatting.
CNumericLocalesSetter locales_setter;
_BBS_3MF_Importer importer;
bool res = importer.load_model_from_file(path, *model, *plate_data_list, *project_presets, *config, *config_substitutions, strategy, is_bbl_3mf, *file_version, proFn, project, plate_id);
bool res = importer.load_model_from_file(path, *model, *plate_data_list, *project_presets, *config, *config_substitutions, strategy, is_bbl_3mf, is_orca_3mf, *file_version, proFn, project, plate_id);
importer.log_errors();
//BBS: remove legacy project logic currently
//handle_legacy_project_loaded(importer.version(), *config);
+1 -1
View File
@@ -248,7 +248,7 @@ struct StoreParams
// 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, int plate_id = 0);
bool* is_bbl_3mf, bool* is_orca_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);
+113 -43
View File
@@ -1949,6 +1949,7 @@ namespace DoExport {
//if (ret.size() < MAX_TAGS_COUNT) check(_(L("Color Change G-code")), config.color_change_gcode.value);
//Orca
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Change extrusion role G-code")), config.change_extrusion_role_gcode.value);
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Process change extrusion role G-code")), config.process_change_extrusion_role_gcode.value);
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Pause G-code")), config.machine_pause_gcode.value);
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Template Custom G-code")), config.template_custom_gcode.value);
if (ret.size() < MAX_TAGS_COUNT) {
@@ -1965,6 +1966,13 @@ namespace DoExport {
break;
}
}
if (ret.size() < MAX_TAGS_COUNT) {
for (const std::string& value : config.filament_change_extrusion_role_gcode.values) {
check(_(L("Filament change extrusion role G-code")), value);
if (ret.size() == MAX_TAGS_COUNT)
break;
}
}
//BBS: no custom_gcode_per_print_z, don't need to check
//if (ret.size() < MAX_TAGS_COUNT) {
// const CustomGCode::Info& custom_gcode_per_print_z = print.model().custom_gcode_per_print_z;
@@ -2479,7 +2487,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
} else
m_enable_extrusion_role_markers = false;
if (!print.config().small_area_infill_flow_compensation_model.empty())
if (m_config.small_area_infill_flow_compensation.value && !m_config.small_area_infill_flow_compensation_model.empty())
m_small_area_infill_flow_compensator = make_unique<SmallAreaInfillFlowCompensator>(print.config());
// Process file_start_gcode - written at the very top of the file, before any header
@@ -3102,15 +3110,16 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
this->_print_first_layer_extruder_temperatures(file, print, machine_start_gcode, initial_extruder_id, true);
}
// Orca: when activate_air_filtration is set on any extruder, find and set the highest during_print_exhaust_fan_speed
bool activate_air_filtration = false;
bool activate_air_filtration_during_print = false;
int during_print_exhaust_fan_speed = 0;
for (const auto &extruder : m_writer.extruders()) {
activate_air_filtration |= m_config.activate_air_filtration.get_at(extruder.id());
if (m_config.activate_air_filtration.get_at(extruder.id()))
if (m_config.activate_air_filtration.get_at(extruder.id()) && m_config.activate_air_filtration_during_print.get_at(extruder.id())) {
activate_air_filtration_during_print = true;
during_print_exhaust_fan_speed = std::max(during_print_exhaust_fan_speed,
m_config.during_print_exhaust_fan_speed.get_at(extruder.id()));
}
}
if (activate_air_filtration)
if (activate_air_filtration_during_print)
file.write(m_writer.set_exhaust_fan(during_print_exhaust_fan_speed, true));
print.throw_if_canceled();
@@ -3410,13 +3419,16 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
if (activate_chamber_temp_control && max_chamber_temp > 0)
file.write(m_writer.set_chamber_temperature(0, false)); //close chamber_temperature
if (activate_air_filtration) {
int complete_print_exhaust_fan_speed = 0;
for (const auto& extruder : m_writer.extruders())
if (m_config.activate_air_filtration.get_at(extruder.id()))
complete_print_exhaust_fan_speed = std::max(complete_print_exhaust_fan_speed, m_config.complete_print_exhaust_fan_speed.get_at(extruder.id()));
file.write(m_writer.set_exhaust_fan(complete_print_exhaust_fan_speed, true));
bool activate_air_filtration_on_completion = false;
int complete_print_exhaust_fan_speed = 0;
for (const auto& extruder : m_writer.extruders()) {
if (m_config.activate_air_filtration.get_at(extruder.id()) && m_config.activate_air_filtration_on_completion.get_at(extruder.id())) {
activate_air_filtration_on_completion = true;
complete_print_exhaust_fan_speed = std::max(complete_print_exhaust_fan_speed, m_config.complete_print_exhaust_fan_speed.get_at(extruder.id()));
}
}
if (activate_air_filtration_on_completion)
file.write(m_writer.set_exhaust_fan(complete_print_exhaust_fan_speed, true));
// adds tags for time estimators
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Last_Line_M73_Placeholder).c_str());
file.write_format("; EXECUTABLE_BLOCK_END\n\n");
@@ -4481,8 +4493,7 @@ LayerResult GCode::process_layer(
break;
}
case CalibMode::Calib_Temp_Tower: {
auto offset = static_cast<unsigned int>(print_z / 10.001) * 5;
gcode += writer().set_temperature(print.calib_params().start - offset);
gcode += writer().set_temperature(this->interpolate_value_across_layers(static_cast<float>(print.calib_params().start), static_cast<float>(print.calib_params().end), 5.0f));
break;
}
case CalibMode::Calib_VFA_Tower: {
@@ -5422,6 +5433,7 @@ void GCode::apply_print_config(const PrintConfig &print_config)
&m_config.time_lapse_gcode,
&m_config.change_filament_gcode,
&m_config.change_extrusion_role_gcode,
&m_config.process_change_extrusion_role_gcode,
&m_config.printing_by_object_gcode,
&m_config.machine_pause_gcode,
&m_config.template_custom_gcode,
@@ -5431,7 +5443,8 @@ void GCode::apply_print_config(const PrintConfig &print_config)
}
for (auto opt : std::initializer_list<ConfigOptionStrings*>{
&m_config.filament_start_gcode,
&m_config.filament_end_gcode
&m_config.filament_end_gcode,
&m_config.filament_change_extrusion_role_gcode
}) {
if (opt->empty())
for (int i = 0; i < opt->size(); ++i)
@@ -6220,9 +6233,12 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
m_need_change_layer_lift_z = false;
// Orca: ensure Z matches planned layer height
if (_last_pos_undefined && !slope_need_z_travel) {
gcode += this->writer().travel_to_z(m_nominal_z, "ensure Z matches planned layer height", true);
if (!slope_need_z_travel && (_last_pos_undefined || m_need_change_layer_lift_z)) {
const std::string z_sync_comment = _last_pos_undefined ?
"ensure Z matches planned layer height" : ""; // no comment for normal layer-Z lift
gcode += this->writer().travel_to_z(m_nominal_z, z_sync_comment, true);
}
m_need_change_layer_lift_z = false;
}
if (path.z_contoured && !path.polyline.lines().empty()) {
@@ -6315,6 +6331,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
_mm3_per_mm *= m_config.bottom_solid_infill_flow_ratio;
} else if (path.role() == erInternalBridgeInfill) {
_mm3_per_mm *= m_config.internal_bridge_flow;
} else if (path.role() == erBrim) {
_mm3_per_mm *= m_config.brim_flow_ratio;
} else if (sloped) {
_mm3_per_mm *= m_config.scarf_joint_flow_ratio;
}
@@ -6397,14 +6415,17 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
if (speed == 0)
speed = filament_max_volumetric_speed / _mm3_per_mm;
if (this->on_first_layer()) {
const auto _layer = layer_id();
if (this->on_first_layer() || object_layer_over_raft()) {
//BBS: for solid infill of first layer, speed can be higher as long as
//wall lines have be attached
if (path.role() != erBottomSurface)
speed = m_config.get_abs_value("initial_layer_speed");
}
else if(m_config.slow_down_layers > 1){
const auto _layer = layer_id();
if (path.role() != erBottomSurface) {
speed = is_perimeter(path.role()) ? m_config.get_abs_value("initial_layer_speed") :
m_config.get_abs_value("initial_layer_infill_speed");
}
} else if (m_config.slow_down_layers > 1 && !m_config.raft_layers > 0) {
if (_layer > 0 && _layer < m_config.slow_down_layers) {
const auto first_layer_speed =
is_perimeter(path.role())
@@ -6414,7 +6435,18 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
speed = std::min(
speed,
Slic3r::lerp(first_layer_speed, speed,
(double)_layer / m_config.slow_down_layers));
(double) (_layer) / m_config.slow_down_layers));
}
}
} else if (m_config.slow_down_layers > 1 && m_config.raft_layers > 0 ) {
if (_layer > m_config.raft_layers && (_layer - m_config.raft_layers) < m_config.slow_down_layers) {
const auto first_layer_speed
= is_perimeter(path.role()) ? m_config.get_abs_value("initial_layer_speed") :
m_config.get_abs_value("initial_layer_infill_speed");
if (first_layer_speed < speed) {
speed = std::min(speed, Slic3r::lerp(first_layer_speed, speed,
(double) (_layer - m_config.raft_layers) / m_config.slow_down_layers));
}
}
}
@@ -6477,7 +6509,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
bool variable_speed = false;
std::vector<ProcessedPoint> new_points {};
if (m_config.enable_overhang_speed && !this->on_first_layer() &&
if (m_config.enable_overhang_speed && !this->on_first_layer() && !object_layer_over_raft() &&
(is_bridge(path.role()) || is_perimeter(path.role()))) {
bool is_external = is_external_perimeter(path.role());
double ref_speed = is_external ? m_config.get_abs_value("outer_wall_speed") : m_config.get_abs_value("inner_wall_speed");
@@ -6565,15 +6597,29 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
// Orca: End of dynamic PA trigger flag segment
//Orca: process custom gcode for extrusion role change
if (path.role() != m_last_extrusion_role && !m_config.change_extrusion_role_gcode.value.empty()) {
if (path.role() != m_last_extrusion_role) {
const auto current_filament_id = m_writer.filament()->id();
const std::string& machine_role_change_gcode = m_config.change_extrusion_role_gcode.value;
const std::string& filament_role_change_gcode = m_config.filament_change_extrusion_role_gcode.get_at(current_filament_id);
const std::string& process_role_change_gcode = m_config.process_change_extrusion_role_gcode.value;
if (!machine_role_change_gcode.empty() || !filament_role_change_gcode.empty() || !process_role_change_gcode.empty()) {
DynamicConfig config;
config.set_key_value("extrusion_role", new ConfigOptionString(extrusion_role_to_string_for_parser(path.role())));
config.set_key_value("last_extrusion_role", new ConfigOptionString(extrusion_role_to_string_for_parser(m_last_extrusion_role)));
config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1));
config.set_key_value("layer_z", new ConfigOptionFloat(m_layer == nullptr ? m_last_height : m_layer->print_z));
gcode += this->placeholder_parser_process("change_extrusion_role_gcode",
m_config.change_extrusion_role_gcode.value, m_writer.filament()->id(), &config)
+ "\n";
const auto append_role_gcode = [this, current_filament_id, &config, &gcode](const std::string& key, const std::string& templ) {
if (templ.empty())
return;
gcode += this->placeholder_parser_process(key, templ, current_filament_id, &config) + "\n";
};
append_role_gcode("change_extrusion_role_gcode", machine_role_change_gcode);
append_role_gcode("filament_change_extrusion_role_gcode", filament_role_change_gcode);
append_role_gcode("process_change_extrusion_role_gcode", process_role_change_gcode);
}
}
// extrude arc or line
@@ -7094,14 +7140,29 @@ std::string GCode::extrusion_role_to_string_for_parser(const ExtrusionRole & rol
}
}
// Calculate the interpolated value for the current layer between start_value and end_value
float GCode::interpolate_value_across_layers(float start_value, float end_value) const {
if (m_layer_index == 1) {
// Calculate the interpolated value for the current layer between start_value and end_value.
// Step will create equal layers steps from first to last value.
// Step = 0 means gradual interpolation finishing at last value.
float GCode::interpolate_value_across_layers(float start_value, float end_value, float step) const
{
if (m_layer_index <= 1) {
return start_value;
} else {
float ratio = (m_layer_index - 2.0f) / (m_layer_count - 3.0f);
ratio = std::max(0.0f, std::min(1.0f, ratio)); // clamp
return start_value + ratio * (end_value - start_value);
}
else {
bool use_steps = step > 0.f;
if (use_steps) {
if (start_value > end_value) {
start_value += step;
} else {
end_value += step;
}
}
float ratio = m_layer_index / (m_layer_count - 1.f);
float value = start_value + ratio * (end_value - start_value);
if (use_steps) {
value = trunc(value / step) * step;
}
return value;
}
}
@@ -7158,16 +7219,25 @@ std::string GCode::travel_to(const Point& point, ExtrusionRole role, std::string
unsigned int acceleration_to_set = 0;
if (this->on_first_layer()) {
if (m_config.default_acceleration.value > 0 && m_config.initial_layer_acceleration.value > 0) {
acceleration_to_set = (unsigned int) floor(m_config.initial_layer_acceleration.value + 0.5);
unsigned int initial_layer_travel_acceleration = m_config.get_abs_value("initial_layer_travel_acceleration");
double initial_layer_travel_jerk = m_config.get_abs_value("initial_layer_travel_jerk");
if (m_config.default_acceleration.value > 0 && initial_layer_travel_acceleration > 0) {
acceleration_to_set = (unsigned int) floor(initial_layer_travel_acceleration + 0.5);
}
if (m_config.default_jerk.value > 0 && m_config.initial_layer_jerk.value > 0) {
jerk_to_set = m_config.initial_layer_jerk.value;
if (m_config.default_jerk.value > 0 && initial_layer_travel_jerk > 0) {
jerk_to_set = initial_layer_travel_jerk;
}
} else {
} else { // ORCA: Handle short-travel acceleration and jerk for outer perimeters (if applicable)
const bool is_short_travel = travel.length() < scale_(EXTRUDER_CONFIG(retraction_minimum_travel));
if (m_config.default_acceleration.value > 0) {
if (role == erExternalPerimeter && travel.length() < scale_(EXTRUDER_CONFIG(retraction_minimum_travel))) {
if (role == erOverhangPerimeter && is_short_travel) {
const double bridge_acceleration = m_config.get_abs_value("bridge_acceleration");
if (bridge_acceleration > 0)
acceleration_to_set = (unsigned int) floor(bridge_acceleration + 0.5);
} else if (role == erExternalPerimeter && is_short_travel) {
if (m_config.outer_wall_acceleration.value > 0)
acceleration_to_set = (unsigned int) floor(m_config.outer_wall_acceleration.value + 0.5);
} else {
@@ -7177,7 +7247,7 @@ std::string GCode::travel_to(const Point& point, ExtrusionRole role, std::string
}
if (m_config.default_jerk.value > 0) {
if (role == erExternalPerimeter && travel.length() < scale_(EXTRUDER_CONFIG(retraction_minimum_travel))) {
if ((role == erExternalPerimeter || role == erOverhangPerimeter) && is_short_travel) {
if (m_config.outer_wall_jerk.value > 0)
jerk_to_set = m_config.outer_wall_jerk.value;
} else {
+4 -2
View File
@@ -239,8 +239,10 @@ public:
bool enable_cooling_markers() const { return m_enable_cooling_markers; }
std::string extrusion_role_to_string_for_parser(const ExtrusionRole &);
// Calculate the interpolated value for the current layer between start_value and end_value
float interpolate_value_across_layers(float start_value, float end_value) const;
// Calculate the interpolated value for the current layer between start_value and end_value.
// Step will create equal layers steps from first to last value.
// Step = 0 means gradual interpolation finishing at last value.
float interpolate_value_across_layers(float start_value, float end_value, float step = 0.0f) const;
// For Perl bindings, to be used exclusively by unit tests.
unsigned int layer_count() const { return m_layer_count; }
+30 -8
View File
@@ -220,7 +220,13 @@ ConflictComputeOpt ConflictChecker::find_inter_of_lines(const LineWithIDs &lines
ConflictResultOpt ConflictChecker::find_inter_of_lines_in_diff_objs(PrintObjectPtrs objs,
std::optional<const FakeWipeTower *> wtdptr) // find the first intersection point of lines in different objects
{
if (objs.size() <= 1 && !wtdptr) { return {}; }
if (objs.empty() && !wtdptr) { return {}; }
// Orca: check if we have enough items to potentially conflict (instances count)
size_t total_instances = 0;
for (auto obj : objs) total_instances += obj->instances().size();
if (total_instances <= 1 && !wtdptr) return {};
LinesBucketQueue conflictQueue;
if (wtdptr.has_value()) { // wipe tower at 0 by default
@@ -238,8 +244,19 @@ ConflictResultOpt ConflictChecker::find_inter_of_lines_in_diff_objs(PrintObjectP
}
for (PrintObject *obj : objs) {
auto layers = getAllLayersExtrusionPathsFromObject(obj);
conflictQueue.emplace_back_bucket(std::move(layers.perimeters), obj, obj->instances().front().shift);
conflictQueue.emplace_back_bucket(std::move(layers.support), obj, obj->instances().front().shift);
// Orca: check for collisions between all instances
const auto& instances = obj->instances();
for (size_t inst_idx = 0; inst_idx < instances.size(); ++inst_idx) {
const PrintInstance& inst = instances[inst_idx];
const bool is_last_instance = inst_idx + 1 == instances.size();
if (is_last_instance) {
conflictQueue.emplace_back_bucket(std::move(layers.perimeters), &inst, inst.shift);
conflictQueue.emplace_back_bucket(std::move(layers.support), &inst, inst.shift);
} else {
conflictQueue.emplace_back_bucket(ExtrusionLayers(layers.perimeters), &inst, inst.shift);
conflictQueue.emplace_back_bucket(ExtrusionLayers(layers.support), &inst, inst.shift);
}
}
}
std::vector<LineWithIDs> layersLines;
@@ -272,13 +289,18 @@ ConflictResultOpt ConflictChecker::find_inter_of_lines_in_diff_objs(PrintObjectP
const FakeWipeTower *wtdp = wtdptr.value();
if (ptr1 == wtdp || ptr2 == wtdp) {
if (ptr2 == wtdp) { std::swap(ptr1, ptr2); }
const PrintObject *obj2 = reinterpret_cast<const PrintObject *>(ptr2);
return std::make_optional<ConflictResult>("WipeTower", obj2->model_object()->name, conflictPrintZ, nullptr, ptr2);
// ptr1 is now wipe tower, ptr2 is PrintInstance*
const PrintInstance *inst2 = reinterpret_cast<const PrintInstance *>(ptr2);
const PrintObject *obj2 = inst2->print_object;
return std::make_optional<ConflictResult>("WipeTower", obj2->model_object()->name, conflictPrintZ, nullptr, inst2);
}
}
const PrintObject *obj1 = reinterpret_cast<const PrintObject *>(ptr1);
const PrintObject *obj2 = reinterpret_cast<const PrintObject *>(ptr2);
return std::make_optional<ConflictResult>(obj1->model_object()->name, obj2->model_object()->name, conflictPrintZ, ptr1, ptr2);
const PrintInstance *inst1 = reinterpret_cast<const PrintInstance *>(ptr1);
const PrintInstance *inst2 = reinterpret_cast<const PrintInstance *>(ptr2);
const PrintObject *obj1 = inst1->print_object;
const PrintObject *obj2 = inst2->print_object;
return std::make_optional<ConflictResult>(obj1->model_object()->name, obj2->model_object()->name, conflictPrintZ, inst1, inst2);
} else
return {};
}
+5 -1
View File
@@ -33,7 +33,11 @@ const std::string& FanMover::process_gcode(const std::string& gcode, bool flush)
if (flush) {
while (!m_buffer.empty()) {
m_process_output += m_buffer.front().raw + "\n";
BufferData &front = m_buffer.front();
m_process_output += front.raw + "\n";
// Orca: Keep the emitted fan state in sync when flushing buffered fan commands.
if (front.fan_speed >= 0)
m_front_buffer_fan_speed = front.fan_speed;
remove_from_buffer(m_buffer.begin());
}
}
+48 -3
View File
@@ -5480,6 +5480,37 @@ void GCodeProcessor::process_filament_change(int id)
void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type, bool internal_only)
{
int filament_id = get_filament_id();
const auto normal_mode = PrintEstimatedStatistics::ETimeMode::Normal;
const size_t normal_mode_id = static_cast<size_t>(normal_mode);
const float delta_x = std::abs(m_end_position[X] - m_start_position[X]);
const float delta_y = std::abs(m_end_position[Y] - m_start_position[Y]);
const float delta_z = std::abs(m_end_position[Z] - m_start_position[Z]);
const float delta_e = std::abs(m_end_position[E] - m_start_position[E]);
const bool has_x = delta_x > 0.0f;
const bool has_y = delta_y > 0.0f;
const bool has_z = delta_z > 0.0f;
const bool has_e = delta_e > 0.0f;
const float move_acceleration =
(type == EMoveType::Travel) ? get_travel_acceleration(normal_mode) :
((type == EMoveType::Retract || type == EMoveType::Unretract) ? get_retract_acceleration(normal_mode) :
get_acceleration(normal_mode));
const float junction_deviation = get_option_value(m_time_processor.machine_limits.machine_max_junction_deviation, normal_mode_id);
const bool use_jd_jerk = (m_flavor == gcfMarlinFirmware && junction_deviation > 0.0f);
const auto axis_jerk_for_preview = [this, normal_mode, use_jd_jerk, move_acceleration](Axis axis) {
return use_jd_jerk ? get_axis_max_jerk_with_jd(normal_mode, axis, move_acceleration) : get_axis_max_jerk(normal_mode, axis);
};
const float jerk_x = axis_jerk_for_preview(X);
const float jerk_y = axis_jerk_for_preview(Y);
const float jerk_z = axis_jerk_for_preview(Z);
const float jerk_e = axis_jerk_for_preview(E);
const float move_jerk =
(has_e && !has_x && !has_y && !has_z) ? jerk_e :
(has_z && !has_x && !has_y) ? jerk_z :
(has_x && has_y) ? std::min(jerk_x, jerk_y) :
has_x ? jerk_x :
has_y ? jerk_y :
has_z ? jerk_z :
std::min(jerk_x, jerk_y);
m_last_line_id = (type == EMoveType::Color_change || type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) ?
m_line_id + 1 :
((type == EMoveType::Seam) ? m_last_line_id : m_line_id);
@@ -5503,6 +5534,10 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type,
m_extruder_temps[filament_id],
// ORCA: Add Pressure Advance visualization support
m_pressure_advance,
// ORCA: Add Acceleration visualization support
move_acceleration,
// ORCA: Add Jerk visualization support
move_jerk,
{ 0.0f, 0.0f }, // time
static_cast<float>(m_layer_id), //layer_duration: set later
std::max<unsigned int>(1, m_layer_id) - 1,
@@ -5578,7 +5613,7 @@ float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedStatistics::ETimeM
}
}
float GCodeProcessor::get_axis_max_jerk_with_jd(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
float GCodeProcessor::get_axis_max_jerk_with_jd(PrintEstimatedStatistics::ETimeMode mode, Axis axis, float acceleration) const
{
if (axis != X && axis != Y && axis != Z && axis != E)
return 0.0f;
@@ -5589,12 +5624,22 @@ float GCodeProcessor::get_axis_max_jerk_with_jd(PrintEstimatedStatistics::ETimeM
return 0.0f;
const float axis_max_acc = get_axis_max_acceleration(mode, axis);
const float generic_acc = get_acceleration(mode);
const float effective_acc = axis_max_acc > 0.0f ? axis_max_acc : generic_acc;
float effective_acc = acceleration;
if (effective_acc <= 0.0f)
effective_acc = get_acceleration(mode);
if (axis_max_acc > 0.0f)
effective_acc = effective_acc > 0.0f ? std::min(effective_acc, axis_max_acc) : axis_max_acc;
if (effective_acc <= 0.0f)
return 0.0f;
return std::sqrt(jd * effective_acc * 2.5f);
}
float GCodeProcessor::get_axis_max_jerk_with_jd(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
{
return get_axis_max_jerk_with_jd(mode, axis, get_acceleration(mode));
}
float GCodeProcessor::get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
{
const size_t id = static_cast<size_t>(mode);
+5
View File
@@ -186,6 +186,10 @@ class Print;
float temperature{ 0.0f }; // Celsius degrees
// ORCA: Add Pressure Advance visualization support
float pressure_advance{ 0.0f };
// ORCA: Add Acceleration visualization support
float acceleration{ 0.0f }; // mm/s^2
// ORCA: Add Jerk visualization support
float jerk{ 0.0f }; // mm/s
std::array<float, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> time{ 0.0f, 0.0f }; // s
float layer_duration{ 0.0f }; // s
unsigned int layer_id{ 0 };
@@ -1074,6 +1078,7 @@ class Print;
// per-nozzle machine limits (filament_map_2 / get_config_idx_for_filament).
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_with_jd(PrintEstimatedStatistics::ETimeMode mode, Axis axis, float acceleration) const;
float get_axis_max_jerk_with_jd(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;
+15 -1
View File
@@ -30,7 +30,21 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config)
m_single_extruder_multi_material = print_config.single_extruder_multi_material.value;
bool use_mach_limits = print_config.gcode_flavor.value == gcfMarlinLegacy || print_config.gcode_flavor.value == gcfMarlinFirmware ||
print_config.gcode_flavor.value == gcfKlipper || print_config.gcode_flavor.value == gcfRepRapFirmware;
m_max_acceleration = std::lrint(use_mach_limits ? print_config.machine_max_acceleration_extruding.values.front() : 0);
if (use_mach_limits) {
// For Klipper, SET_VELOCITY_LIMIT ACCEL= applies to all moves, so the effective cap
// is the minimum of the extruding limit and the per-axis X/Y limits.
// This ensures user-configured Motion Ability limits are honoured (#12244).
unsigned int extruding_limit = std::lrint(print_config.machine_max_acceleration_extruding.values.front());
if (print_config.gcode_flavor.value == gcfKlipper) {
unsigned int x_limit = std::lrint(print_config.machine_max_acceleration_x.values.front());
unsigned int y_limit = std::lrint(print_config.machine_max_acceleration_y.values.front());
if (x_limit > 0) extruding_limit = std::min(extruding_limit, x_limit);
if (y_limit > 0) extruding_limit = std::min(extruding_limit, y_limit);
}
m_max_acceleration = extruding_limit;
} else {
m_max_acceleration = 0;
}
m_max_travel_acceleration = static_cast<unsigned int>(
std::round((use_mach_limits && supports_separate_travel_acceleration(print_config.gcode_flavor.value)) ?
print_config.machine_max_acceleration_travel.values.front() :
+87 -1
View File
@@ -19,7 +19,7 @@
#include <CGAL/Polygon_mesh_processing/orient_polygon_soup.h>
#include <CGAL/Polygon_mesh_processing/repair.h>
#include <CGAL/Polygon_mesh_processing/remesh.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
#include <CGAL/Polygon_mesh_processing/repair_polygon_soup.h>
#include <CGAL/Polygon_mesh_processing/orientation.h>
// BBS: for segment
#include <CGAL/mesh_segmentation.h>
@@ -475,6 +475,92 @@ bool empty(const CGALMesh &mesh)
return mesh.m.is_empty();
}
bool repair(TriangleMesh& mesh, RepairedMeshErrors* repaired_errors, std::string* error)
{
using namespace CGAL;
namespace PMP = CGAL::Polygon_mesh_processing;
if (mesh.empty())
return true;
try {
// 1) Convert to polygon soup
std::vector<_EpicMesh::Point> points;
std::vector<std::vector<std::size_t>> polygons;
points.reserve(mesh.its.vertices.size());
polygons.reserve(mesh.its.indices.size());
for (const auto& v : mesh.its.vertices)
points.emplace_back(v.x(), v.y(), v.z());
for (const auto& f : mesh.its.indices)
polygons.push_back({size_t(f[0]), size_t(f[1]), size_t(f[2])});
// 2) Aggressive soup cleanup
PMP::repair_polygon_soup(points, polygons);
// 3) Convert soup → mesh
_EpicMesh cgal_mesh;
PMP::polygon_soup_to_polygon_mesh(points, polygons, cgal_mesh);
// 4) Remove degenerate geometry
PMP::remove_degenerate_faces(cgal_mesh);
PMP::remove_isolated_vertices(cgal_mesh);
// 5) Fix remaining non-manifold vertices
PMP::duplicate_non_manifold_vertices(cgal_mesh);
// 6) Boolean union (keeps only outer shell)
_EpicMesh tmp;
if (PMP::corefine_and_compute_union(cgal_mesh, cgal_mesh, tmp)) {
cgal_mesh = std::move(tmp);
}
// If it fails, continue anyway with previous mesh
// 7) Fill holes
if (!CGAL::is_closed(cgal_mesh)) {
using halfedge_descriptor = boost::graph_traits<_EpicMesh>::halfedge_descriptor;
std::vector<halfedge_descriptor> borders;
PMP::extract_boundary_cycles(cgal_mesh, std::back_inserter(borders));
for (halfedge_descriptor h : borders) {
PMP::triangulate_and_refine_hole(cgal_mesh, h);
}
}
// 8) Final validity check
if (!CGAL::is_closed(cgal_mesh)) {
if (error)
*error = "Repair failed: mesh still open after hole filling.";
return false;
}
// 9) Ensure outward orientation
if (!PMP::does_bound_a_volume(cgal_mesh))
PMP::orient_to_bound_a_volume(cgal_mesh);
// 10) Convert back
indexed_triangle_set its = cgal_to_indexed_triangle_set(cgal_mesh);
RepairedMeshErrors errs{};
errs.facets_removed = 0;
errs.edges_fixed = 0;
mesh = TriangleMesh(std::move(its), errs);
if (repaired_errors)
*repaired_errors = errs;
return true;
} catch (const std::exception& e) {
if (error)
*error = e.what();
return false;
}
}
CGALMeshPtr clone(const CGALMesh &m)
{
return CGALMeshPtr{new CGALMesh{m}};
+3
View File
@@ -70,6 +70,9 @@ TriangleMesh merge(std::vector<TriangleMesh> meshes);
bool does_bound_a_volume(const CGALMesh &mesh);
bool empty(const CGALMesh &mesh);
// Repair a mesh using CGAL. Returns true on success. Optionally returns a summary of repairs and an error string.
bool repair(TriangleMesh &mesh, RepairedMeshErrors *repaired_errors = nullptr, std::string *error = nullptr);
}
namespace mcut {
+17 -7
View File
@@ -14,6 +14,7 @@
#include "Format/AMF.hpp"
#include "Format/svg.hpp"
#include "Format/bbs_3mf.hpp"
#include "Format/DRC.hpp"
// BBS
#include "FaceDetector.hpp"
@@ -322,7 +323,7 @@ Model Model::read_from_file(const std::string&
// 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, plate_id);
result = load_bbs_3mf(input_file.c_str(), config, config_substitutions, &model, plate_data, project_presets, is_xxx, nullptr, file_version, proFn, options, project, plate_id);
#ifdef __APPLE__
else if (boost::algorithm::iends_with(input_file, ".usd") || boost::algorithm::iends_with(input_file, ".usda") ||
boost::algorithm::iends_with(input_file, ".usdc") || boost::algorithm::iends_with(input_file, ".usdz") ||
@@ -381,7 +382,8 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
Model model;
bool result = false;
bool is_bbl_3mf;
bool is_bbl_3mf = false;
bool is_orca_3mf = false;
if (boost::algorithm::iends_with(input_file, ".3mf")) {
PrusaFileParser prusa_file_parser;
if (prusa_file_parser.check_3mf_from_prusa(input_file)) {
@@ -391,7 +393,7 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
} else {
// BBS: add part plate related logic
// BBS: backup & restore
result = load_bbs_3mf(input_file.c_str(), config, config_substitutions, &model, plate_data, project_presets, &is_bbl_3mf, file_version, proFn, options, project);
result = load_bbs_3mf(input_file.c_str(), config, config_substitutions, &model, plate_data, project_presets, &is_bbl_3mf, &is_orca_3mf, file_version, proFn, options, project);
}
}
else if (boost::algorithm::iends_with(input_file, ".zip.amf"))
@@ -400,7 +402,10 @@ Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig
throw Slic3r::RuntimeError(_L("Unknown file format. Input file must have .3mf or .zip.amf extension."));
if (out_file_type != En3mfType::From_Prusa) {
out_file_type = is_bbl_3mf ? En3mfType::From_BBS : En3mfType::From_Other;
if (is_orca_3mf)
out_file_type = En3mfType::From_Orca;
else
out_file_type = is_bbl_3mf ? En3mfType::From_BBS : En3mfType::From_Other;
}
if (!result)
@@ -1088,7 +1093,6 @@ bool Model::is_fuzzy_skin_painted() const
return std::any_of(this->objects.cbegin(), this->objects.cend(), [](const ModelObject *mo) { return mo->is_fuzzy_skin_painted(); });
}
static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix, const std::string& suffix = {}, ModelVolumeType type = ModelVolumeType::MODEL_PART)
{
if (mesh.empty())
@@ -1734,8 +1738,14 @@ void ModelObject::ensure_on_bed(bool allow_negative_z)
else
z_offset = -this->min_z();
if (z_offset != 0.0)
translate_instances(z_offset * Vec3d::UnitZ());
if (z_offset != 0.0) {
for (size_t i = 0; i < instances.size(); ++i) {
if (!instances[i]->auto_drop)
continue;
translate_instance(i, z_offset * Vec3d::UnitZ());
}
}
}
void ModelObject::translate_instances(const Vec3d& vector)
+9 -2
View File
@@ -719,6 +719,7 @@ enum class ConversionType : int {
};
enum class En3mfType : int {
From_Orca,
From_BBS,
From_Prusa,
From_Other
@@ -1251,6 +1252,7 @@ public:
ModelInstanceEPrintVolumeState print_volume_state;
// Whether or not this instance is printable
bool printable;
bool auto_drop;
bool use_loaded_id_for_label {false};
int arrange_order = 0; // BBS
size_t loaded_id = 0; // BBS
@@ -1379,7 +1381,11 @@ private:
Polygon convex_hull; // BBS
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject* object) : print_volume_state(ModelInstancePVS_Inside), printable(true), object(object), m_assemble_initialized(false) { assert(this->id().valid()); }
explicit ModelInstance(ModelObject* object)
: print_volume_state(ModelInstancePVS_Inside), printable(true), auto_drop(true), object(object), m_assemble_initialized(false)
{
assert(this->id().valid());
}
// Constructor, which assigns a new unique ID.
explicit ModelInstance(ModelObject *object, const ModelInstance &other) :
m_transformation(other.m_transformation)
@@ -1387,6 +1393,7 @@ private:
, m_offset_to_assembly(other.m_offset_to_assembly)
, print_volume_state(ModelInstancePVS_Inside)
, printable(other.printable)
, auto_drop(other.auto_drop)
, object(object)
, m_assemble_initialized(false) { assert(this->id().valid() && this->id() != other.id()); }
@@ -1400,7 +1407,7 @@ private:
ModelInstance() : ObjectBase(-1), object(nullptr) { assert(this->id().invalid()); }
// BBS. Add added members to archive.
template<class Archive> void serialize(Archive& ar) {
ar(m_transformation, print_volume_state, printable, m_assemble_transformation, m_offset_to_assembly, m_assemble_initialized);
ar(m_transformation, print_volume_state, printable, auto_drop, m_assemble_transformation, m_offset_to_assembly, m_assemble_initialized);
}
};
+24 -6
View File
@@ -1571,9 +1571,9 @@ void PerimeterGenerator::process_classic()
} // for each loop of an island
// fill gaps
if (! gaps.empty()) {
// collapse
double min = 0.2 * perimeter_width * (1 - INSET_OVERLAP_TOLERANCE);
if (! gaps.empty()) { // collapse
// ORCA: Use the smaller width as the lower bound to avoid overestimating safe overlap
double min = 0.2 * std::min(perimeter_width, ext_perimeter_width) * (1 - INSET_OVERLAP_TOLERANCE);
double max = 2. * perimeter_spacing;
ExPolygons gaps_ex = diff_ex(
//FIXME offset2 would be enough and cheaper.
@@ -1738,8 +1738,12 @@ void PerimeterGenerator::process_no_bridge(Surfaces& all_surfaces, coord_t perim
//compute our unsupported surface
ExPolygons unsupported = diff_ex(last, *this->lower_slices, ApplySafetyOffset::Yes);
if (!unsupported.empty()) {
//remove small overhangs
ExPolygons unsupported_filtered = offset2_ex(unsupported, double(-perimeter_spacing), double(perimeter_spacing));
// remove small overhangs (when using chbFilled we need to be less aggressive in removing small overhangs,
// to avoid affecting bridging detection.)
const int outset_divisor = this->config->counterbore_hole_bridging.value == chbFilled ? 2 : 1;
ExPolygons unsupported_filtered = offset2_ex(unsupported, double(-perimeter_spacing),
double(perimeter_spacing) / outset_divisor);
if (!unsupported_filtered.empty()) {
//to_draw.insert(to_draw.end(), last.begin(), last.end());
//extract only the useful part of the lower layer. The safety offset is really needed here.
@@ -1782,7 +1786,7 @@ void PerimeterGenerator::process_no_bridge(Surfaces& all_surfaces, coord_t perim
}
}
unsupported_filtered = intersection_ex(last,
offset2_ex(unsupported_filtered, double(-perimeter_spacing / 2), double(bridged_infill_margin + perimeter_spacing / 2)));
offset_ex(unsupported_filtered, 0.5 * double(bridged_infill_margin)));
if (this->config->counterbore_hole_bridging.value == chbFilled) {
for (ExPolygon& expol : unsupported_filtered) {
//check if the holes won't be covered by the upper layer
@@ -1879,6 +1883,20 @@ void PerimeterGenerator::process_no_bridge(Surfaces& all_surfaces, coord_t perim
bridges_temp = diff_ex(bridges_temp, unbridgeable);
unsupported_filtered = offset_ex(bridges_temp, offset_to_do);
unsupported_filtered = intersection_ex(unsupported_filtered, reference);
// Normalize anchor size for partial bridges:
// derive the bridge core first, then add a fixed overlap into support.
const coordf_t anchor_overlap = bridged_infill_margin;
ExPolygons bridge_core = diff_ex(unsupported_filtered, support, ApplySafetyOffset::Yes);
if (bridge_core.empty()) {
bridge_core = unsupported_filtered;
}
ExPolygons anchor_overlap_area = intersection_ex(
offset_ex(bridge_core, anchor_overlap),
support,
ApplySafetyOffset::Yes);
unsupported_filtered = union_ex(bridge_core, anchor_overlap_area);
unsupported_filtered = intersection_ex(unsupported_filtered, reference);
// } else {
// ExPolygons unbridgeable = intersection_ex(unsupported, diff_ex(unsupported_filtered, offset_ex(bridgeable_simplified, ext_perimeter_width / 2)));
// unbridgeable = offset2_ex(unbridgeable, -ext_perimeter_width, ext_perimeter_width);
+3 -3
View File
@@ -962,7 +962,7 @@ static std::vector<std::string> s_Preset_print_options{
"top_surface_speed", "support_speed", "support_object_xy_distance", "support_object_first_layer_gap", "support_interface_speed",
"bridge_speed", "internal_bridge_speed", "gap_infill_speed", "travel_speed", "travel_speed_z", "initial_layer_speed",
"outer_wall_acceleration", "initial_layer_acceleration", "top_surface_acceleration", "default_acceleration", "skirt_type", "skirt_loops", "skirt_speed","min_skirt_length", "skirt_distance", "skirt_start_angle", "skirt_height","single_loop_draft_shield", "draft_shield",
"brim_width", "brim_object_gap", "brim_use_efc_outline", "combine_brims", "brim_type", "brim_ears_max_angle", "brim_ears_detection_length", "enable_support", "support_type", "support_threshold_angle", "support_threshold_overlap","enforce_support_layers",
"brim_width", "brim_object_gap", "brim_flow_ratio", "brim_use_efc_outline", "combine_brims", "brim_type", "brim_ears_max_angle", "brim_ears_detection_length", "enable_support", "support_type", "support_threshold_angle", "support_threshold_overlap","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_expansion", "support_style",
// BBS
@@ -1189,9 +1189,9 @@ static std::vector<std::string> s_Preset_filament_options {/*"filament_colour",
//BBS:temperature_vitrification
"temperature_vitrification", "reduce_fan_stop_start_freq","dont_slow_down_outer_wall", "slow_down_for_layer_cooling", "fan_min_speed",
"fan_max_speed", "enable_overhang_bridge_fan", "overhang_fan_speed", "overhang_fan_threshold", "close_fan_the_first_x_layers", "full_fan_speed_layer", "fan_cooling_layer_time", "slow_down_layer_time", "slow_down_min_speed",
"filament_start_gcode", "filament_end_gcode",
"filament_start_gcode", "filament_end_gcode", "filament_change_extrusion_role_gcode",
//exhaust fan control
"activate_air_filtration","during_print_exhaust_fan_speed","complete_print_exhaust_fan_speed",
"activate_air_filtration","activate_air_filtration_during_print","activate_air_filtration_on_completion","during_print_exhaust_fan_speed","complete_print_exhaust_fan_speed",
// Retract overrides
"filament_retraction_length", "filament_z_hop", "filament_z_hop_types", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_lift_enforce", "filament_retraction_speed", "filament_deretraction_speed", "filament_retract_restart_extra", "filament_retraction_minimum_travel",
"filament_retract_when_changing_layer", "filament_wipe", "filament_retract_before_wipe",
+16
View File
@@ -2197,16 +2197,32 @@ void PresetBundle::set_num_filaments(unsigned int n, std::vector<std::string> ne
filament_presets.resize(n);
}
ConfigOptionStrings* filament_color = project_config.option<ConfigOptionStrings>("filament_colour");
ConfigOptionStrings *filament_multi_color = project_config.option<ConfigOptionStrings>("filament_multi_colour");
ConfigOptionStrings* filament_color_type = project_config.option<ConfigOptionStrings>("filament_colour_type");
ConfigOptionInts* filament_map = project_config.option<ConfigOptionInts>("filament_map");
filament_color->resize(n);
// Sync filament multi colour
filament_multi_color->values.resize(n);
for (size_t i = 0; i < n; i++) {
filament_multi_color->values[i] = filament_color->values[i];
}
filament_color_type->resize(n);
filament_map->values.resize(n, 1);
ams_multi_color_filment.resize(n);
// BBS set new filament color to new_color
if (old_filament_count < n) {
if (!new_colors.empty()) {
for (int i = old_filament_count; i < n; i++) {
filament_color->values[i] = new_colors[i - old_filament_count];
filament_multi_color->values[i] = new_colors[i - old_filament_count];
filament_color_type->values[i] = "1"; // default color type
}
}
}
update_multi_material_filament_presets();
}
void PresetBundle::set_num_filaments(unsigned int n, std::string new_color)
+74 -41
View File
@@ -219,6 +219,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"ironing_fan_speed",
"single_extruder_multi_material_priming",
"activate_air_filtration",
"activate_air_filtration_during_print",
"activate_air_filtration_on_completion",
"during_print_exhaust_fan_speed",
"complete_print_exhaust_fan_speed",
"activate_chamber_temp_control",
@@ -343,6 +345,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|| opt_key == "travel_speed_z"
|| opt_key == "initial_layer_speed"
|| opt_key == "initial_layer_travel_speed"
|| opt_key == "initial_layer_travel_acceleration"
|| opt_key == "initial_layer_travel_jerk"
|| opt_key == "slow_down_layers"
|| opt_key == "idle_temperature"
|| opt_key == "wipe_tower_cone_angle"
@@ -610,7 +614,6 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
std::for_each(exclude_polys.begin(), exclude_polys.end(),
[&print_origin](Polygon& p) { p.translate(scale_(print_origin.x()), scale_(print_origin.y())); });
std::map<ObjectID, Polygon> map_model_object_to_convex_hull;
struct print_instance_info
{
const PrintInstance *print_instance;
@@ -645,27 +648,13 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
for (const PrintObject *print_object : print.objects()) {
assert(! print_object->model_object()->instances.empty());
assert(! print_object->instances().empty());
ObjectID model_object_id = print_object->model_object()->id();
auto it_convex_hull = map_model_object_to_convex_hull.find(model_object_id);
// Get convex hull of all printable volumes assigned to this print object.
ModelInstance *model_instance0 = print_object->model_object()->instances.front();
if (it_convex_hull == map_model_object_to_convex_hull.end()) {
// Calculate the convex hull of a printable object.
// Grow convex hull with the clearance margin.
// FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2)
// which causes that the warning will be showed after arrangement with the
// appropriate object distance. Even if I set this to jtMiter the warning still shows up.
it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, model_object_id,
print_object->model_object()->convex_hull_2d(Geometry::assemble_transform(
{ 0.0, 0.0, model_instance0->get_offset().z() }, model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror())));
}
// Make a copy, so it may be rotated for instances.
Polygon convex_hull0 = it_convex_hull->second;
const double z_diff = Geometry::rotation_diff_z(model_instance0->get_rotation(), print_object->instances().front().model_instance->get_rotation());
if (std::abs(z_diff) > EPSILON)
convex_hull0.rotate(z_diff);
// Orca: check convex hull intersection for each instance individually to handle rotation/offset differences correctly
// Now we check that no instance of convex_hull intersects any of the previously checked object instances.
for (const PrintInstance &instance : print_object->instances()) {
Polygon convex_hull0 = print_object->model_object()->convex_hull_2d(Geometry::assemble_transform(
{ 0.0, 0.0, instance.model_instance->get_offset().z() }, instance.model_instance->get_rotation(), instance.model_instance->get_scaling_factor(), instance.model_instance->get_mirror()));
Polygon convex_hull_no_offset = convex_hull0, convex_hull;
auto tmp = offset(convex_hull_no_offset, obj_distance, jtRound, scale_(0.1));
if (!tmp.empty()) { // tmp may be empty due to clipper's bug, see STUDIO-2452
@@ -679,7 +668,9 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
if (!intersection(exclude_polys, convex_hull_no_offset).empty()) {
if (single_object_exception.string.empty()) {
single_object_exception.string = (boost::format(L("%1% is too close to exclusion area, there may be collisions when printing.")) %instance.model_instance->get_object()->name).str();
single_object_exception.object = instance.model_instance->get_object();
// single_object_exception.object = instance.model_instance->get_object();
//ORCA: Pass ModelInstance instead of ModelObject
single_object_exception.object = instance.model_instance;
}
else {
single_object_exception.string += "\n"+(boost::format(L("%1% is too close to exclusion area, there may be collisions when printing.")) %instance.model_instance->get_object()->name).str();
@@ -696,12 +687,16 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
bool has_exception = false;
if (single_object_exception.string.empty()) {
single_object_exception.string = (boost::format(L("%1% is too close to others, and collisions may be caused.")) %instance.model_instance->get_object()->name).str();
single_object_exception.object = instance.model_instance->get_object();
// single_object_exception.object = instance.model_instance->get_object();
//ORCA: Pass ModelInstance instead of ModelObject for better selection
single_object_exception.object = instance.model_instance;
has_exception = true;
}
else {
single_object_exception.string += "\n"+(boost::format(L("%1% is too close to others, and collisions may be caused.")) %instance.model_instance->get_object()->name).str();
single_object_exception.object = nullptr;
// single_object_exception.object = nullptr;
// ORCA: Keep the first object so jump works
// has_exception = true;
has_exception = true;
}
@@ -948,33 +943,52 @@ static StringObjectException layered_print_cleareance_valid(const Print &print,
wrapping_poly.points.emplace_back(scale_(pt.x() + print_origin.x()), scale_(pt.y() + print_origin.y()));
}
std::map<const ModelVolume*, Polygon> map_model_volume_to_convex_hull;
Polygons convex_hulls_other;
// Orca: check convex hull intersection for each instance individually
for (auto& inst : print_instances_ordered) {
Polygons current_instance_hulls;
for (const ModelVolume *v : inst->print_object->model_object()->volumes) {
if (!v->is_model_part()) continue;
auto it_convex_hull = map_model_volume_to_convex_hull.find(v);
if (it_convex_hull == map_model_volume_to_convex_hull.end()) {
auto volume_hull = v->get_convex_hull_2d(Geometry::assemble_transform(Vec3d::Zero(), inst->model_instance->get_rotation(),
inst->model_instance->get_scaling_factor(), inst->model_instance->get_mirror()));
volume_hull.translate(inst->shift - inst->print_object->center_offset());
auto volume_hull = v->get_convex_hull_2d(Geometry::assemble_transform(Vec3d::Zero(), inst->model_instance->get_rotation(),
inst->model_instance->get_scaling_factor(), inst->model_instance->get_mirror()));
volume_hull.translate(inst->shift - inst->print_object->center_offset());
it_convex_hull = map_model_volume_to_convex_hull.emplace_hint(it_convex_hull, v, volume_hull);
}
Polygon &convex_hull = it_convex_hull->second;
Polygons convex_hulls_temp;
convex_hulls_temp.push_back(convex_hull);
if (!intersection(exclude_polys, convex_hull).empty()) {
if (!intersection(exclude_polys, volume_hull).empty()) {
// return {inst->model_instance->get_object()->name + L(" is too close to exclusion area, there may be collisions when printing.") + "\n",
// inst->model_instance->get_object()};
//ORCA: Pass ModelInstance instead of ModelObject
return {inst->model_instance->get_object()->name + L(" is too close to exclusion area, there may be collisions when printing.") + "\n",
inst->model_instance->get_object()};
inst->model_instance};
}
if (print_config.enable_wrapping_detection.value && !intersection(wrapping_poly, convex_hull).empty()) {
if (print_config.enable_wrapping_detection.value && !intersection(wrapping_poly, volume_hull).empty()) {
// return {inst->model_instance->get_object()->name + L(" is too close to clumping detection area, there may be collisions when printing.") + "\n",
// inst->model_instance->get_object()};
//ORCA: Pass ModelInstance instead of ModelObject
return {inst->model_instance->get_object()->name + L(" is too close to clumping detection area, there may be collisions when printing.") + "\n",
inst->model_instance->get_object()};
inst->model_instance};
}
convex_hulls_other.emplace_back(convex_hull);
current_instance_hulls.emplace_back(volume_hull);
}
if (!intersection(convex_hulls_other, current_instance_hulls).empty()) {
if (warning) {
if (warning->string.empty()) {
warning->string = (boost::format(L("%1% is too close to others, and collisions may be caused.")) % inst->model_instance->get_object()->name).str();
// warning->object = inst->model_instance->get_object();
//ORCA: Pass ModelInstance instead of ModelObject for better selection
warning->object = inst->model_instance;
} else {
warning->string += "\n" + (boost::format(L("%1% is too close to others, and collisions may be caused.")) % inst->model_instance->get_object()->name).str();
// ORCA: Keep the first object so jump works
if (!warning->object) warning->object = inst->model_instance;
}
warning->is_warning = true;
warning->type = STRING_EXCEPT_OBJECT_COLLISION_IN_LAYER_PRINT;
}
}
append(convex_hulls_other, current_instance_hulls);
}
//BBS: add the wipe tower check logic
@@ -4612,7 +4626,7 @@ int Print::export_cached_data(const std::string& directory, bool with_space)
/*boost::nowide::ofstream c;
c.open(file_name, std::ios::out | std::ios::trunc);
if (with_space)
c << std::setw(4) << root_json << std::endl;
c << root_json.dump(1, '\t') << std::endl;
else
c << root_json.dump(0) << std::endl;
c.close();*/
@@ -4634,7 +4648,7 @@ int Print::export_cached_data(const std::string& directory, bool with_space)
boost::nowide::ofstream c;
c.open(filename_vector[object_index], std::ios::out | std::ios::trunc);
if (with_space)
c << std::setw(4) << json_vector[object_index] << std::endl;
c << json_vector[object_index].dump(1, '\t') << std::endl;
else
c << json_vector[object_index].dump(0) << std::endl;
c.close();
@@ -4908,6 +4922,25 @@ ExtrusionLayers FakeWipeTower::getTrueExtrusionLayersFromWipeTower() const
{
ExtrusionLayers wtels;
wtels.type = ExtrusionLayersType::WIPE_TOWER;
//ORCA: Fallback for WipeTower2 if outer_wall is empty
if (outer_wall.empty()) {
auto fake_paths = getFakeExtrusionPathsFromWipeTower2();
float current_z = 0.f;
for (auto& layer_paths : fake_paths) {
if (layer_paths.empty()) continue;
ExtrusionLayer el;
float lh = layer_paths.front().height;
el.height = lh;
el.bottom_z = current_z;
el.layer = nullptr;
el.paths = std::move(layer_paths);
wtels.push_back(std::move(el));
current_z += lh;
}
return wtels;
}
std::vector<float> layer_heights;
layer_heights.reserve(outer_wall.size());
auto pre = outer_wall.begin();
+2 -1
View File
@@ -1163,7 +1163,8 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
m_ori_full_print_config = new_full_config;
new_full_config.update_values_to_printer_extruders_for_multiple_filaments(new_full_config, filament_options_with_variant, "filament_self_index", "filament_extruder_variant");
std::vector<int> filament_maps = new_full_config.option<ConfigOptionInts>("filament_map")->values;
auto opt_filament_map = new_full_config.option<ConfigOptionInts>("filament_map");
std::vector<int> filament_maps = opt_filament_map ? opt_filament_map->values : std::vector<int>();
// Find modified keys of the various configs. Resolve overrides extruder retract values by filament profiles.
DynamicPrintConfig filament_overrides;
File diff suppressed because it is too large Load Diff
+10
View File
@@ -888,6 +888,7 @@ PRINT_CONFIG_CLASS_DEFINE(
PrintObjectConfig,
((ConfigOptionFloat, brim_object_gap))
((ConfigOptionFloat, brim_flow_ratio))
((ConfigOptionBool, brim_use_efc_outline))
((ConfigOptionEnum<BrimType>, brim_type))
((ConfigOptionFloat, brim_width))
@@ -897,6 +898,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, bridge_no_support))
((ConfigOptionFloat, elefant_foot_compensation))
((ConfigOptionInt, elefant_foot_compensation_layers))
((ConfigOptionPercent, elefant_foot_layers_density))
((ConfigOptionFloat, max_bridge_length))
((ConfigOptionFloatOrPercent, line_width))
// Force the generation of solid shells between adjacent materials/volumes.
@@ -995,6 +997,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionPercent, min_bead_width))
// Orca
((ConfigOptionFloat, wall_maximum_resolution))
((ConfigOptionFloat, wall_maximum_deviation))
((ConfigOptionFloat, make_overhang_printable_angle))
((ConfigOptionFloat, make_overhang_printable_hole_size))
((ConfigOptionFloat, tree_support_branch_distance_organic))
@@ -1269,6 +1273,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, wipe_tower_no_sparse_layers))
((ConfigOptionString, change_filament_gcode))
((ConfigOptionString, change_extrusion_role_gcode))
((ConfigOptionString, process_change_extrusion_role_gcode))
((ConfigOptionStrings, filament_change_extrusion_role_gcode))
((ConfigOptionFloat, travel_speed))
((ConfigOptionFloat, travel_speed_z))
((ConfigOptionBool, silent_mode))
@@ -1295,6 +1301,8 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionBool, accel_to_decel_enable))
((ConfigOptionPercent, accel_to_decel_factor))
((ConfigOptionFloatOrPercent, initial_layer_travel_speed))
((ConfigOptionFloatOrPercent, initial_layer_travel_acceleration))
((ConfigOptionFloatOrPercent, initial_layer_travel_jerk))
((ConfigOptionBool, bbl_calib_mark_logo))
((ConfigOptionBool, disable_m73))
@@ -1389,6 +1397,8 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionBools, dont_slow_down_outer_wall))
((ConfigOptionFloats, fan_cooling_layer_time))
((ConfigOptionBools, activate_air_filtration))
((ConfigOptionBools, activate_air_filtration_during_print))
((ConfigOptionBools, activate_air_filtration_on_completion))
((ConfigOptionInts, during_print_exhaust_fan_speed))
((ConfigOptionInts, complete_print_exhaust_fan_speed))
((ConfigOptionFloatOrPercent, initial_layer_line_width))
+21 -2
View File
@@ -1162,6 +1162,7 @@ bool PrintObject::invalidate_state_by_config_options(
} else if (
opt_key == "elefant_foot_compensation"
|| opt_key == "elefant_foot_compensation_layers"
|| opt_key == "elefant_foot_layers_density"
|| opt_key == "support_top_z_distance"
|| opt_key == "support_bottom_z_distance"
|| opt_key == "xy_hole_compensation"
@@ -1359,6 +1360,8 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "wall_transition_filter_deviation"
|| opt_key == "wall_transition_angle"
|| opt_key == "wall_distribution_count"
|| opt_key == "wall_maximum_resolution"
|| opt_key == "wall_maximum_deviation"
|| opt_key == "min_feature_size"
|| opt_key == "min_length_factor"
|| opt_key == "min_bead_width") {
@@ -1370,7 +1373,6 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "scarf_angle_threshold"
|| opt_key == "scarf_overhang_threshold"
|| opt_key == "scarf_joint_speed"
|| opt_key == "scarf_joint_flow_ratio"
|| opt_key == "seam_slope_start_height"
|| opt_key == "seam_slope_entire_loop"
|| opt_key == "seam_slope_min_length"
@@ -1394,7 +1396,24 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "bed_mesh_min"
|| opt_key == "bed_mesh_max"
|| opt_key == "adaptive_bed_mesh_margin"
|| opt_key == "bed_mesh_probe_distance") {
|| opt_key == "bed_mesh_probe_distance"
|| opt_key == "print_flow_ratio"
|| opt_key == "first_layer_flow_ratio"
|| opt_key == "top_solid_infill_flow_ratio"
|| opt_key == "bottom_solid_infill_flow_ratio"
|| opt_key == "outer_wall_flow_ratio"
|| opt_key == "inner_wall_flow_ratio"
|| opt_key == "overhang_flow_ratio"
|| opt_key == "sparse_infill_flow_ratio"
|| opt_key == "internal_solid_infill_flow_ratio"
|| opt_key == "gap_fill_flow_ratio"
|| opt_key == "support_flow_ratio"
|| opt_key == "support_interface_flow_ratio"
|| opt_key == "brim_flow_ratio"
|| opt_key == "filament_flow_ratio"
|| opt_key == "scarf_joint_flow_ratio"
|| opt_key == "spiral_starting_flow_ratio"
|| opt_key == "spiral_finishing_flow_ratio") {
invalidated |= m_print->invalidate_step(psGCodeExport);
} else if (
opt_key == "flush_into_infill"
+6 -3
View File
@@ -2884,8 +2884,9 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp
intermediate_layers.push_back(&layer_new);
}
} else {
// Insert intermediate layers.
size_t n_layers_extra = size_t(ceil(dist / m_slicing_params.max_suport_layer_height));
// ORCA: Bias by EPSILON so a gap effectively equal to
// max_suport_layer_height is not split by floating-point noise.
size_t n_layers_extra = size_t(ceil((dist - EPSILON) / m_slicing_params.max_suport_layer_height));
assert(n_layers_extra > 0);
coordf_t step = dist / coordf_t(n_layers_extra);
if (extr1 != nullptr && extr1->layer_type == SupporLayerType::TopContact &&
@@ -2900,7 +2901,9 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp
layer_new.height = extr1->height;
intermediate_layers.push_back(&layer_new);
dist = extr2z - extr1z;
n_layers_extra = size_t(ceil(dist / m_slicing_params.max_suport_layer_height));
// ORCA: Recalculate with the same EPSILON bias after re-anchoring at the top
// contact layer so near-equal gaps do not gain an extra split here either.
n_layers_extra = size_t(ceil((dist - EPSILON) / m_slicing_params.max_suport_layer_height));
if (n_layers_extra == 0)
continue;
// Continue printing the other layers up to extr2z.
+26 -9
View File
@@ -1157,12 +1157,17 @@ void TreeSupport::create_tree_support_layers()
// Layers between the raft contacts and bottom of the object.
double dist_to_go = m_slicing_params.object_print_z_min - raft_print_z;
auto nsteps = int(ceil(dist_to_go / m_slicing_params.max_suport_layer_height));
double height = dist_to_go / nsteps;
for (int i = 0; i < nsteps; ++i) {
raft_print_z += height;
raft_slice_z = raft_print_z - height / 2;
m_object->add_tree_support_layer(layer_id++, height, raft_print_z, raft_slice_z);
// ORCA: Guard tiny residual raft-to-object gaps so the EPSILON-biased ceil()
// below cannot turn them into zero steps after floating-point accumulation.
if (dist_to_go > EPSILON) {
// ORCA: Bias by EPSILON so near-equal gaps do not get an extra split from FP noise.
auto nsteps = int(ceil((dist_to_go - EPSILON) / m_slicing_params.max_suport_layer_height));
double height = dist_to_go / nsteps;
for (int i = 0; i < nsteps; ++i) {
raft_print_z += height;
raft_slice_z = raft_print_z - height / 2;
m_object->add_tree_support_layer(layer_id++, height, raft_print_z, raft_slice_z);
}
}
m_raft_layers = layer_id;
}
@@ -2076,9 +2081,10 @@ void TreeSupport::draw_circles()
if (!area.empty()) has_circle_node = true;
if (node.need_extra_wall) need_extra_wall = true;
// merge overhang to get a smoother interface surface
// Do not merge when buildplate_only is on, because some underneath nodes may have been deleted.
if (top_interface_layers > 0 && node.support_roof_layers_below > 0 && !on_buildplate_only && !node.is_sharp_tail) {
// Merge the overhang into the roof area so tree tips can still produce
// a continuous support interface. Suppressing this for build-plate-only
// support drops the roof polygons entirely in valid tree branches.
if (top_interface_layers > 0 && node.support_roof_layers_below > 0 && !node.is_sharp_tail) {
ExPolygons overhang_expanded;
if (node.overhang.contour.size() > 100 || node.overhang.holes.size()>1)
overhang_expanded.emplace_back(node.overhang);
@@ -2121,6 +2127,17 @@ void TreeSupport::draw_circles()
roof_1st_layer = diff_ex(roof_1st_layer, ClipperUtils::clip_clipper_polygons_with_subject_bbox(roof_areas,get_extents(roof_1st_layer)));
roof_1st_layer = intersection_ex(roof_1st_layer, m_machine_border);
// Build-plate-only pruning can collapse the roof stack down to a single
// printable layer. In that case we still need to emit an interface layer
// instead of downgrading the last roof-adjacent layer to base support.
if (on_buildplate_only && top_interface_layers > 0 && roof_areas.empty() && !roof_1st_layer.empty()) {
append(roof_areas, roof_1st_layer);
roof_1st_layer.clear();
max_layers_above_roof = std::max(max_layers_above_roof, max_layers_above_roof1);
max_layers_above_roof1 = 0;
interface_id = obj_layer_nr % top_interface_layers;
}
ExPolygons roofs; append(roofs, roof_1st_layer); append(roofs, roof_areas);append(roofs, roof_gap_areas);
base_areas = diff_ex(base_areas, ClipperUtils::clip_clipper_polygons_with_subject_bbox(roofs, get_extents(base_areas)));
base_areas = intersection_ex(base_areas, m_machine_border);
+49 -30
View File
@@ -898,6 +898,7 @@ public:
size_t dtt_roof_tip;
for (dtt_roof_tip = 0; dtt_roof_tip < roof_tip_layers && insert_layer_idx - dtt_roof_tip >= 1; ++ dtt_roof_tip) {
size_t this_layer_idx = insert_layer_idx - dtt_roof_tip;
const size_t roof_recovery_depth = dtt_roof_tip + supports_roof_layers;
auto evaluateRoofWillGenerate = [&](const std::pair<Point, LineStatus> &p) {
//FIXME Vojtech: The circle is just shifted, it has a known size, the infill should fit all the time!
#if 0
@@ -927,7 +928,9 @@ public:
// don't move until
roof_tip_layers - dtt_roof_tip,
// supports roof
dtt_roof_tip + supports_roof_layers > 0,
roof_recovery_depth > 0,
// recovered roof/contact depth for this slice
roof_recovery_depth,
// disable ovalization
false);
}
@@ -942,9 +945,10 @@ public:
roof_circle.translate(p.first);
new_roofs.emplace_back(std::move(roof_circle));
}
this->add_roof(std::move(new_roofs), this_layer_idx, dtt_roof_tip + supports_roof_layers);
this->add_roof(std::move(new_roofs), this_layer_idx, roof_recovery_depth);
}
const size_t roof_recovery_depth = dtt_roof_tip + supports_roof_layers;
for (const LineInformation &line : lines) {
// If a line consists of enough tips, the assumption is that it is not a single tip, but part of a simulated support pattern.
// Ovalisation should be disabled for these to improve the quality of the lines when tip_diameter=line_width
@@ -954,14 +958,16 @@ public:
// don't move until
dont_move_until > dtt_roof_tip ? dont_move_until - dtt_roof_tip : 0,
// supports roof
dtt_roof_tip + supports_roof_layers > 0,
roof_recovery_depth > 0,
// recovered roof/contact depth for this slice
roof_recovery_depth,
disable_ovalistation);
}
}
private:
// called by this->add_points_along_lines()
void add_point_as_influence_area(std::pair<Point, LineStatus> p, LayerIndex insert_layer, size_t dont_move_until, bool roof, bool skip_ovalisation)
void add_point_as_influence_area(std::pair<Point, LineStatus> p, LayerIndex insert_layer, size_t dont_move_until, bool roof, size_t roof_recovery_dtt, bool skip_ovalisation)
{
bool to_bp = p.second == LineStatus::TO_BP || p.second == LineStatus::TO_BP_SAFE;
bool gracious = to_bp || p.second == LineStatus::TO_MODEL_GRACIOUS || p.second == LineStatus::TO_MODEL_GRACIOUS_SAFE;
@@ -997,7 +1003,7 @@ private:
state.supports_roof = roof;
state.dont_move_until = dont_move_until;
state.can_use_safe_radius = safe_radius;
state.missing_roof_layers = force_tip_to_roof ? dont_move_until : 0;
state.set_pending_roof_recovery(force_tip_to_roof ? dont_move_until : 0, roof_recovery_dtt);
state.skip_ovalisation = skip_ovalisation;
move_bounds[insert_layer].emplace_back(state, std::move(circle));
}
@@ -1095,10 +1101,9 @@ void finalize_raft_contact(
// 1) Maximum num_support_roof_layers roof (top interface & contact) layers.
// 2) Tree tips supporting either the roof layers or the object itself.
// num_support_roof_layers should always be respected:
// If num_support_roof_layers contact layers could not be produced, then the tree tip
// is augmented with SupportElementState::missing_roof_layers
// and the top "missing_roof_layers" of such particular tree tips are supposed to be coverted to
// roofs aka interface layers by the tool path generator.
// If the requested roof/contact stack cannot be generated directly, the affected tree tips
// carry explicit pending roof recovery metadata so the sliced branch geometry can later be
// promoted back to top contacts / interfaces at the correct contact depth.
void sample_overhang_area(
// Area to support
Polygons &&overhang_area,
@@ -1606,7 +1611,6 @@ static Point move_inside_if_outside(const Polygons &polygons, Point from, int di
if (settings.increase_radius)
current_elem.effective_radius_height += 1;
coord_t radius = support_element_collision_radius(config, current_elem);
const auto _tiny_area_threshold = tiny_area_threshold();
if (settings.move) {
increased = relevant_offset;
@@ -2059,7 +2063,10 @@ static void increase_areas_one_layer(
out.supports_roof = first.supports_roof || second.supports_roof;
out.dont_move_until = std::max(first.dont_move_until, second.dont_move_until);
out.can_use_safe_radius = first.can_use_safe_radius || second.can_use_safe_radius;
out.missing_roof_layers = std::min(first.missing_roof_layers, second.missing_roof_layers);
// Preserve the deepest outstanding roof recovery request across merged sub-branches.
out.set_pending_roof_recovery(
std::max(first.missing_roof_layers, second.missing_roof_layers),
std::max(first.roof_recovery_dtt, second.roof_recovery_dtt));
out.skip_ovalisation = false;
if (first.target_height > second.target_height) {
out.target_height = first.target_height;
@@ -3473,7 +3480,6 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons
// value is the area where support may be placed. As this is calculated in CreateLayerPathing it is saved and reused in draw_areas
std::vector<SupportElements> move_bounds(num_support_layers);
// ### Place tips of the support tree
for (size_t mesh_idx : processing.second)
generate_initial_areas(*print.get_object(mesh_idx), volumes, config, overhangs,
@@ -3591,7 +3597,33 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons
// storage.support.generated = true;
}
// Organic specific: Smooth branches and produce one cummulative mesh to be sliced.
static void recover_pending_branch_roofs(
InterfacePlacer &interface_placer,
const std::vector<const SupportElement*> &branch_path,
const LayerIndex layer_begin,
std::vector<Polygons> &slices)
{
if (! interface_placer.support_parameters.has_top_contacts)
return;
for (auto it = branch_path.rbegin(); it != branch_path.rend(); ++ it) {
const SupportElement &el = **it;
if (! el.state.has_pending_roof_recovery())
break;
const LayerIndex slice_idx = el.state.layer_idx - layer_begin;
if (slice_idx < 0 || slice_idx >= LayerIndex(slices.size()))
continue;
if (slices[size_t(slice_idx)].empty())
continue;
if (el.state.roof_recovery_dtt > interface_placer.support_parameters.num_top_interface_layers)
continue;
interface_placer.add_roof(std::move(slices[size_t(slice_idx)]), el.state.layer_idx, el.state.roof_recovery_dtt);
}
}
// Organic specific: Smooth branches and produce one cumulative mesh to be sliced.
void organic_draw_branches(
PrintObject &print_object,
TreeModelVolumes &volumes,
@@ -3770,13 +3802,12 @@ void organic_draw_branches(
// ++ ielement;
}
}
const SlicingParameters &slicing_params = print_object.slicing_parameters();
MeshSlicingParams mesh_slicing_params;
mesh_slicing_params.mode = MeshSlicingParams::SlicingMode::Positive;
tbb::parallel_for(tbb::blocked_range<size_t>(0, trees.size(), 1),
[&trees, &volumes, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
[&trees, &volumes, &config, &slicing_params, &move_bounds, &mesh_slicing_params, &interface_placer, &throw_on_cancel](const tbb::blocked_range<size_t> &range) {
indexed_triangle_set partial_mesh;
std::vector<float> slice_z;
std::vector<Polygons> bottom_contacts;
@@ -3811,7 +3842,7 @@ void organic_draw_branches(
num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin();
} else {
if (branch.has_root) {
if (branch.path.front()->state.to_model_gracious) {
if (config.support_rests_on_model && branch.path.front()->state.to_model_gracious) {
if (config.settings.support_floor_layers > 0)
//FIXME one may just take the whole tree slice as bottom interface.
bottom_contacts.emplace_back(intersection_clipped(slices.front(), volumes.getPlaceableAreas(0, layer_begin, [] {})));
@@ -3860,7 +3891,7 @@ void organic_draw_branches(
}
}
#endif
if (config.settings.support_floor_layers > 0)
if (config.support_rests_on_model && config.settings.support_floor_layers > 0)
for (int i = int(bottom_extra_slices.size()) - 2; i >= 0; -- i)
bottom_contacts.emplace_back(
intersection_clipped(bottom_extra_slices[i].polygons, volumes.getPlaceableAreas(0, layer_begin - i - 1, [] {})));
@@ -3872,19 +3903,7 @@ void organic_draw_branches(
}
}
#if 0
//FIXME branch.has_tip seems to not be reliable.
if (branch.has_tip && interface_placer.support_parameters.has_top_contacts)
// Add top slices to top contacts / interfaces / base interfaces.
for (int i = int(branch.path.size()) - 1; i >= 0; -- i) {
const SupportElement &el = *branch.path[i];
if (el.state.missing_roof_layers == 0)
break;
//FIXME Move or not?
interface_placer.add_roof(std::move(slices[int(slices.size()) - i - 1]), el.state.layer_idx,
interface_placer.support_parameters.num_top_interface_layers + 1 - el.state.missing_roof_layers);
}
#endif
recover_pending_branch_roofs(interface_placer, branch.path, layer_begin, slices);
}
layer_begin += LayerIndex(num_empty);
+18 -2
View File
@@ -199,9 +199,20 @@ struct SupportElementState : public SupportElementStateBits
AreaIncreaseSettings last_area_increase;
/*!
* \brief Amount of roof layers that were not yet added, because the branch needed to move.
* \brief Number of pending roof/contact recovery slices from this node downward, including this node.
*/
uint32_t missing_roof_layers;
uint32_t missing_roof_layers = 0;
/*!
* \brief Contact/interface depth that this node should recover when missing_roof_layers > 0.
*/
uint32_t roof_recovery_dtt = 0;
void set_pending_roof_recovery(uint32_t pending_layers, uint32_t recovery_depth)
{
this->missing_roof_layers = pending_layers;
this->roof_recovery_dtt = pending_layers > 0 ? recovery_depth : 0;
}
// called by increase_single_area() and increaseAreas()
[[nodiscard]] static SupportElementState propagate_down(const SupportElementState &src)
@@ -209,6 +220,10 @@ struct SupportElementState : public SupportElementStateBits
SupportElementState dst{ src };
++ dst.distance_to_top;
-- dst.layer_idx;
if (dst.has_pending_roof_recovery()) {
-- dst.missing_roof_layers;
++ dst.roof_recovery_dtt;
}
// set to invalid as we are a new node on a new layer
dst.result_on_layer_reset();
dst.skip_ovalisation = false;
@@ -216,6 +231,7 @@ struct SupportElementState : public SupportElementStateBits
}
[[nodiscard]] bool locked() const { return this->distance_to_top < this->dont_move_until; }
[[nodiscard]] bool has_pending_roof_recovery() const { return this->missing_roof_layers > 0; }
};
/*!
+3 -2
View File
@@ -343,7 +343,8 @@ public:
}
if (double dist_to_go = slicing_params.object_print_z_min - z; dist_to_go > EPSILON) {
// Layers between the raft contacts and bottom of the object.
auto nsteps = int(ceil(dist_to_go / slicing_params.max_suport_layer_height));
// ORCA: Bias by EPSILON so near-equal gaps do not get an extra split from FP noise.
auto nsteps = int(ceil((dist_to_go - EPSILON) / slicing_params.max_suport_layer_height));
double step = dist_to_go / nsteps;
for (int i = 0; i < nsteps; ++ i) {
z += step;
@@ -754,4 +755,4 @@ enum class LineStatus
} // namespace Slic3r
#endif // slic3r_TreeSupportCommon_hpp
#endif // slic3r_TreeSupportCommon_hpp
+2 -2
View File
@@ -71,8 +71,8 @@ namespace Timing {
static TimeLimitAlarm new_nanos(uint64_t time_limit_nanoseconds, std::string_view limit_exceeded_message) {
return TimeLimitAlarm(time_limit_nanoseconds, limit_exceeded_message);
}
static TimeLimitAlarm new_milis(uint64_t time_limit_milis, std::string_view limit_exceeded_message) {
return TimeLimitAlarm(uint64_t(time_limit_milis) * 1000000l, limit_exceeded_message);
static TimeLimitAlarm new_millis(uint64_t time_limit_millis, std::string_view limit_exceeded_message) {
return TimeLimitAlarm(uint64_t(time_limit_millis) * 1000000l, limit_exceeded_message);
}
static TimeLimitAlarm new_seconds(uint64_t time_limit_seconds, std::string_view limit_exceeded_message) {
return TimeLimitAlarm(uint64_t(time_limit_seconds) * 1000000000l, limit_exceeded_message);