This commit is contained in:
enricoturri1966 2023-05-25 10:39:54 +02:00
commit 7ee770baef
71 changed files with 110117 additions and 68558 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

@ -288,8 +288,12 @@ set(SLIC3R_SOURCES
Support/SupportMaterial.hpp
Support/SupportParameters.cpp
Support/SupportParameters.hpp
Support/OrganicSupport.cpp
Support/OrganicSupport.hpp
Support/TreeSupport.cpp
Support/TreeSupport.hpp
Support/TreeSupportCommon.cpp
Support/TreeSupportCommon.hpp
Support/TreeModelVolumes.cpp
Support/TreeModelVolumes.hpp
SupportSpotsGenerator.cpp

View File

@ -274,21 +274,53 @@ public:
const ExtendedPoint &curr = extended_points[i];
const ExtendedPoint &next = extended_points[i + 1 < extended_points.size() ? i + 1 : i];
// The following code artifically increases the distance to provide slowdown for extrusions that are over curled lines
float artificial_distance_to_curled_lines = 0.0;
const double dist_limit = 10.0 * path.width;
{
Vec2d middle = 0.5 * (curr.position + next.position);
auto line_indices = prev_curled_extrusions[current_object].all_lines_in_radius(Point::new_scale(middle),
scale_(10.0 * path.width));
auto line_indices = prev_curled_extrusions[current_object].all_lines_in_radius(Point::new_scale(middle), scale_(dist_limit));
if (!line_indices.empty()) {
double len = (next.position - curr.position).norm();
// For long lines, there is a problem with the additional slowdown. If by accident, there is small curled line near the middle of this long line
// The whole segment gets slower unnecesarily. For these long lines, we do additional check whether it is worth slowing down.
// NOTE that this is still quite rough approximation, e.g. we are still checking lines only near the middle point
// TODO maybe split the lines into smaller segments before running this alg? but can be demanding, and GCode will be huge
if (len > 8) {
Vec2d dir = Vec2d(next.position - curr.position) / len;
Vec2d right = Vec2d(-dir.y(), dir.x());
Polygon box_of_influence = {
scaled(Vec2d(curr.position + right * dist_limit)),
scaled(Vec2d(next.position + right * dist_limit)),
scaled(Vec2d(next.position - right * dist_limit)),
scaled(Vec2d(curr.position - right * dist_limit)),
};
double projected_lengths_sum = 0;
for (size_t idx : line_indices) {
const CurledLine &line = prev_curled_extrusions[current_object].get_line(idx);
Lines inside = intersection_ln({{line.a, line.b}}, {box_of_influence});
if (inside.empty())
continue;
double projected_length = abs(dir.dot(unscaled(Vec2d((inside.back().b - inside.back().a).cast<double>()))));
projected_lengths_sum += projected_length;
}
if (projected_lengths_sum < 0.4 * len) {
line_indices.clear();
}
}
for (size_t idx : line_indices) {
const CurledLine &line = prev_curled_extrusions[current_object].get_line(idx);
float distance_from_curled = unscaled(line_alg::distance_to(line, Point::new_scale(middle)));
float dist = path.width * (1.0 - (distance_from_curled / (10.0 * path.width))) *
(1.0 - (distance_from_curled / (10.0 * path.width))) *
float dist = path.width * (1.0 - (distance_from_curled / dist_limit)) *
(1.0 - (distance_from_curled / dist_limit)) *
(line.curled_height / (path.height * 10.0f)); // max_curled_height_factor from SupportSpotGenerator
artificial_distance_to_curled_lines = std::max(artificial_distance_to_curled_lines, dist);
}
}
}
auto interpolate_speed = [](const std::map<float, float> &values, float distance) {
auto upper_dist = values.lower_bound(distance);

View File

@ -10,6 +10,7 @@
#include "SVG.hpp"
#include "Algorithm/RegionExpansion.hpp"
#include <algorithm>
#include <string>
#include <map>
@ -143,12 +144,12 @@ void LayerRegion::make_perimeters(
#if 1
// Extract surfaces of given type from surfaces, extract fill (layer) thickness of one of the surfaces.
static ExPolygons fill_surfaces_extract_expolygons(Surfaces &surfaces, SurfaceType surface_type, double &thickness)
static ExPolygons fill_surfaces_extract_expolygons(Surfaces &surfaces, std::initializer_list<SurfaceType> surface_types, double &thickness)
{
size_t cnt = 0;
for (const Surface &surface : surfaces)
if (surface.surface_type == surface_type) {
++ cnt;
if (std::find(surface_types.begin(), surface_types.end(), surface.surface_type) != surface_types.end()) {
++cnt;
thickness = surface.thickness;
}
if (cnt == 0)
@ -157,7 +158,7 @@ static ExPolygons fill_surfaces_extract_expolygons(Surfaces &surfaces, SurfaceTy
ExPolygons out;
out.reserve(cnt);
for (Surface &surface : surfaces)
if (surface.surface_type == surface_type)
if (std::find(surface_types.begin(), surface_types.end(), surface.surface_type) != surface_types.end())
out.emplace_back(std::move(surface.expolygon));
return out;
}
@ -173,7 +174,7 @@ Surfaces expand_bridges_detect_orientations(
using namespace Slic3r::Algorithm;
double thickness;
ExPolygons bridges_ex = fill_surfaces_extract_expolygons(surfaces, stBottomBridge, thickness);
ExPolygons bridges_ex = fill_surfaces_extract_expolygons(surfaces, {stBottomBridge}, thickness);
if (bridges_ex.empty())
return {};
@ -329,7 +330,7 @@ static Surfaces expand_merge_surfaces(
const double bridge_angle = -1.)
{
double thickness;
ExPolygons src = fill_surfaces_extract_expolygons(surfaces, surface_type, thickness);
ExPolygons src = fill_surfaces_extract_expolygons(surfaces, {surface_type}, thickness);
if (src.empty())
return {};
@ -375,7 +376,7 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
// Expand the top / bottom / bridge surfaces into the shell thickness solid infills.
double layer_thickness;
ExPolygons shells = union_ex(fill_surfaces_extract_expolygons(m_fill_surfaces.surfaces, stInternalSolid, layer_thickness));
ExPolygons shells = union_ex(fill_surfaces_extract_expolygons(m_fill_surfaces.surfaces, {stInternalSolid}, layer_thickness));
SurfaceCollection bridges;
{

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
#ifndef slic3r_OrganicSupport_hpp
#define slic3r_OrganicSupport_hpp
#include "SupportCommon.hpp"
#include "TreeSupport.hpp"
namespace Slic3r
{
class PrintObject;
namespace FFFTreeSupport
{
class TreeModelVolumes;
// Organic specific: Smooth branches and produce one cummulative mesh to be sliced.
void organic_draw_branches(
PrintObject &print_object,
TreeModelVolumes &volumes,
const TreeSupportSettings &config,
std::vector<SupportElements> &move_bounds,
// I/O:
SupportGeneratorLayersPtr &bottom_contacts,
SupportGeneratorLayersPtr &top_contacts,
InterfacePlacer &interface_placer,
// Output:
SupportGeneratorLayersPtr &intermediate_layers,
SupportGeneratorLayerStorage &layer_storage,
std::function<void()> throw_on_cancel);
} // namespace FFFTreeSupport
} // namespace Slic3r
#endif // slic3r_OrganicSupport_hpp

View File

@ -1,7 +1,9 @@
#ifndef slic3r_SupportCommon_hpp_
#define slic3r_SupportCommon_hpp_
#include "../Layer.hpp"
#include "../Polygon.hpp"
#include "../Print.hpp"
#include "SupportLayer.hpp"
#include "SupportParameters.hpp"

View File

@ -5,8 +5,8 @@
#include <oneapi/tbb/spin_mutex.h>
// for Slic3r::deque
#include "../libslic3r.h"
#include "ClipperUtils.hpp"
#include "Polygon.hpp"
#include "../ClipperUtils.hpp"
#include "../Polygon.hpp"
namespace Slic3r::FFFSupport {

View File

@ -7,7 +7,7 @@
// CuraEngine is released under the terms of the AGPLv3 or higher.
#include "TreeModelVolumes.hpp"
#include "TreeSupport.hpp"
#include "TreeSupportCommon.hpp"
#include "../BuildVolume.hpp"
#include "../ClipperUtils.hpp"
@ -35,75 +35,6 @@ using namespace std::literals;
// had to use a define beacuse the macro processing inside macro BOOST_LOG_TRIVIAL()
#define error_level_not_in_cache error
static constexpr const bool polygons_strictly_simple = false;
TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &print_object)
{
const PrintConfig &print_config = print_object.print()->config();
const PrintObjectConfig &config = print_object.config();
const SlicingParameters &slicing_params = print_object.slicing_parameters();
// const std::vector<unsigned int> printing_extruders = print_object.object_extruders();
// Support must be enabled and set to Tree style.
assert(config.support_material || config.support_material_enforce_layers > 0);
assert(config.support_material_style == smsTree || config.support_material_style == smsOrganic);
// Calculate maximum external perimeter width over all printing regions, taking into account the default layer height.
coordf_t external_perimeter_width = 0.;
for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) {
const PrintRegion &region = print_object.printing_region(region_id);
external_perimeter_width = std::max<coordf_t>(external_perimeter_width, region.flow(print_object, frExternalPerimeter, config.layer_height).width());
}
this->layer_height = scaled<coord_t>(config.layer_height.value);
this->resolution = scaled<coord_t>(print_config.gcode_resolution.value);
// Arache feature
this->min_feature_size = scaled<coord_t>(config.min_feature_size.value);
// +1 makes the threshold inclusive
this->support_angle = 0.5 * M_PI - std::clamp<double>((config.support_material_threshold + 1) * M_PI / 180., 0., 0.5 * M_PI);
this->support_line_width = support_material_flow(&print_object, config.layer_height).scaled_width();
this->support_roof_line_width = support_material_interface_flow(&print_object, config.layer_height).scaled_width();
//FIXME add it to SlicingParameters and reuse in both tree and normal supports?
this->support_bottom_enable = config.support_material_interface_layers.value > 0 && config.support_material_bottom_interface_layers.value != 0;
this->support_bottom_height = this->support_bottom_enable ?
(config.support_material_bottom_interface_layers.value > 0 ?
config.support_material_bottom_interface_layers.value :
config.support_material_interface_layers.value) * this->layer_height :
0;
this->support_material_buildplate_only = config.support_material_buildplate_only;
this->support_xy_distance = scaled<coord_t>(config.support_material_xy_spacing.get_abs_value(external_perimeter_width));
// Separation of interfaces, it is likely smaller than support_xy_distance.
this->support_xy_distance_overhang = std::min(this->support_xy_distance, scaled<coord_t>(0.5 * external_perimeter_width));
this->support_top_distance = scaled<coord_t>(slicing_params.gap_support_object);
this->support_bottom_distance = scaled<coord_t>(slicing_params.gap_object_support);
// this->support_interface_skip_height =
// this->support_infill_angles =
this->support_roof_enable = config.support_material_interface_layers.value > 0;
this->support_roof_layers = this->support_roof_enable ? config.support_material_interface_layers.value : 0;
this->support_floor_enable = config.support_material_interface_layers.value > 0 && config.support_material_bottom_interface_layers.value > 0;
this->support_floor_layers = this->support_floor_enable ? config.support_material_bottom_interface_layers.value : 0;
// this->minimum_roof_area =
// this->support_roof_angles =
this->support_roof_pattern = config.support_material_interface_pattern;
this->support_pattern = config.support_material_pattern;
this->support_line_spacing = scaled<coord_t>(config.support_material_spacing.value);
// this->support_bottom_offset =
// this->support_wall_count = config.support_material_with_sheath ? 1 : 0;
this->support_wall_count = 1;
this->support_roof_line_distance = scaled<coord_t>(config.support_material_interface_spacing.value) + this->support_roof_line_width;
// this->minimum_support_area =
// this->minimum_bottom_area =
// this->support_offset =
this->support_tree_branch_distance = scaled<coord_t>(config.support_tree_branch_distance.value);
this->support_tree_angle = std::clamp<double>(config.support_tree_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
this->support_tree_angle_slow = std::clamp<double>(config.support_tree_angle_slow * M_PI / 180., 0., this->support_tree_angle - EPSILON);
this->support_tree_branch_diameter = scaled<coord_t>(config.support_tree_branch_diameter.value);
this->support_tree_branch_diameter_angle = std::clamp<double>(config.support_tree_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
this->support_tree_top_rate = config.support_tree_top_rate.value; // percent
// this->support_tree_tip_diameter = this->support_line_width;
this->support_tree_tip_diameter = std::clamp(scaled<coord_t>(config.support_tree_tip_diameter.value), 0, this->support_tree_branch_diameter);
}
//FIXME Machine border is currently ignored.
static Polygons calculateMachineBorderCollision(Polygon machine_border)
{

View File

@ -14,6 +14,8 @@
#include <boost/functional/hash.hpp>
#include "TreeSupportCommon.hpp"
#include "../Point.hpp"
#include "../Polygon.hpp"
#include "../PrintConfig.hpp"
@ -27,175 +29,10 @@ class PrintObject;
namespace FFFTreeSupport
{
using LayerIndex = int;
struct TreeSupportMeshGroupSettings {
TreeSupportMeshGroupSettings() = default;
explicit TreeSupportMeshGroupSettings(const PrintObject &print_object);
/*********************************************************************/
/* Print parameters, not support specific: */
/*********************************************************************/
coord_t layer_height { scaled<coord_t>(0.15) };
// Maximum Deviation (meshfix_maximum_deviation)
// The maximum deviation allowed when reducing the resolution for the Maximum Resolution setting. If you increase this,
// the print will be less accurate, but the g-code will be smaller. Maximum Deviation is a limit for Maximum Resolution,
// so if the two conflict the Maximum Deviation will always be held true.
coord_t resolution { scaled<coord_t>(0.025) };
// Minimum Feature Size (aka minimum line width) - Arachne specific
// Minimum thickness of thin features. Model features that are thinner than this value will not be printed, while features thicker
// than the Minimum Feature Size will be widened to the Minimum Wall Line Width.
coord_t min_feature_size { scaled<coord_t>(0.1) };
/*********************************************************************/
/* General support parameters: */
/*********************************************************************/
// Support Overhang Angle
// The minimum angle of overhangs for which support is added. At a value of 0° all overhangs are supported, 90° will not provide any support.
double support_angle { 50. * M_PI / 180. };
// Support Line Width
// Width of a single support structure line.
coord_t support_line_width { scaled<coord_t>(0.4) };
// Support Roof Line Width: Width of a single support roof line.
coord_t support_roof_line_width { scaled<coord_t>(0.4) };
// Enable Support Floor (aka bottom interfaces)
// Generate a dense slab of material between the bottom of the support and the model. This will create a skin between the model and support.
bool support_bottom_enable { false };
// Support Floor Thickness
// The thickness of the support floors. This controls the number of dense layers that are printed on top of places of a model on which support rests.
coord_t support_bottom_height { scaled<coord_t>(1.) };
bool support_material_buildplate_only { false };
// Support X/Y Distance
// Distance of the support structure from the print in the X/Y directions.
// minimum: 0, maximum warning: 1.5 * machine_nozzle_tip_outer_diameter
coord_t support_xy_distance { scaled<coord_t>(0.7) };
// Minimum Support X/Y Distance
// Distance of the support structure from the overhang in the X/Y directions.
// minimum_value: 0, minimum warning": support_xy_distance - support_line_width * 2, maximum warning: support_xy_distance
coord_t support_xy_distance_overhang { scaled<coord_t>(0.2) };
// Support Top Distance
// Distance from the top of the support to the print.
coord_t support_top_distance { scaled<coord_t>(0.1) };
// Support Bottom Distance
// Distance from the print to the bottom of the support.
coord_t support_bottom_distance { scaled<coord_t>(0.1) };
//FIXME likely not needed, optimization for clipping of interface layers
// When checking where there's model above and below the support, take steps of the given height. Lower values will slice slower, while higher values
// may cause normal support to be printed in some places where there should have been support interface.
coord_t support_interface_skip_height { scaled<coord_t>(0.3) };
// Support Infill Line Directions
// A list of integer line directions to use. Elements from the list are used sequentially as the layers progress and when the end
// of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained
// in square brackets. Default is an empty list which means use the default angle 0 degrees.
// std::vector<double> support_infill_angles {};
// Enable Support Roof
// Generate a dense slab of material between the top of support and the model. This will create a skin between the model and support.
bool support_roof_enable { false };
// Support Roof Thickness
// The thickness of the support roofs. This controls the amount of dense layers at the top of the support on which the model rests.
coord_t support_roof_layers { 2 };
bool support_floor_enable { false };
coord_t support_floor_layers { 2 };
// Minimum Support Roof Area
// Minimum area size for the roofs of the support. Polygons which have an area smaller than this value will be printed as normal support.
double minimum_roof_area { scaled<double>(scaled<double>(1.)) };
// A list of integer line directions to use. Elements from the list are used sequentially as the layers progress
// and when the end of the list is reached, it starts at the beginning again. The list items are separated
// by commas and the whole list is contained in square brackets. Default is an empty list which means
// use the default angles (alternates between 45 and 135 degrees if interfaces are quite thick or 90 degrees).
std::vector<double> support_roof_angles {};
// Support Roof Pattern (aka top interface)
// The pattern with which the roofs of the support are printed.
SupportMaterialInterfacePattern support_roof_pattern { smipAuto };
// Support Pattern
// The pattern of the support structures of the print. The different options available result in sturdy or easy to remove support.
SupportMaterialPattern support_pattern { smpRectilinear };
// Support Line Distance
// Distance between the printed support structure lines. This setting is calculated by the support density.
coord_t support_line_spacing { scaled<coord_t>(2.66 - 0.4) };
// Support Floor Horizontal Expansion
// Amount of offset applied to the floors of the support.
coord_t support_bottom_offset { scaled<coord_t>(0.) };
// Support Wall Line Count
// The number of walls with which to surround support infill. Adding a wall can make support print more reliably
// and can support overhangs better, but increases print time and material used.
// tree: 1, zig-zag: 0, concentric: 1
int support_wall_count { 1 };
// Support Roof Line Distance
// Distance between the printed support roof lines. This setting is calculated by the Support Roof Density, but can be adjusted separately.
coord_t support_roof_line_distance { scaled<coord_t>(0.4) };
// Minimum Support Area
// Minimum area size for support polygons. Polygons which have an area smaller than this value will not be generated.
coord_t minimum_support_area { scaled<coord_t>(0.) };
// Minimum Support Floor Area
// Minimum area size for the floors of the support. Polygons which have an area smaller than this value will be printed as normal support.
coord_t minimum_bottom_area { scaled<coord_t>(1.0) };
// Support Horizontal Expansion
// Amount of offset applied to all support polygons in each layer. Positive values can smooth out the support areas and result in more sturdy support.
coord_t support_offset { scaled<coord_t>(0.) };
/*********************************************************************/
/* Parameters for the Cura tree supports implementation: */
/*********************************************************************/
// Tree Support Maximum Branch Angle
// The maximum angle of the branches, when the branches have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach.
// minimum: 0, minimum warning: 20, maximum: 89, maximum warning": 85
double support_tree_angle { 60. * M_PI / 180. };
// Tree Support Branch Diameter Angle
// The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length.
// A bit of an angle can increase stability of the tree support.
// minimum: 0, maximum: 89.9999, maximum warning: 15
double support_tree_branch_diameter_angle { 5. * M_PI / 180. };
// Tree Support Branch Distance
// How far apart the branches need to be when they touch the model. Making this distance small will cause
// the tree support to touch the model at more points, causing better overhang but making support harder to remove.
coord_t support_tree_branch_distance { scaled<coord_t>(1.) };
// Tree Support Branch Diameter
// The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this.
// minimum: 0.001, minimum warning: support_line_width * 2
coord_t support_tree_branch_diameter { scaled<coord_t>(2.) };
/*********************************************************************/
/* Parameters new to the Thomas Rahm's tree supports implementation: */
/*********************************************************************/
// Tree Support Preferred Branch Angle
// The preferred angle of the branches, when they do not have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle for branches to merge faster.
// minimum: 0, minimum warning: 10, maximum: support_tree_angle, maximum warning: support_tree_angle-1
double support_tree_angle_slow { 50. * M_PI / 180. };
// Tree Support Diameter Increase To Model
// The most the diameter of a branch that has to connect to the model may increase by merging with branches that could reach the buildplate.
// Increasing this reduces print time, but increases the area of support that rests on model
// minimum: 0
coord_t support_tree_max_diameter_increase_by_merges_when_support_to_model { scaled<coord_t>(1.0) };
// Tree Support Minimum Height To Model
// How tall a branch has to be if it is placed on the model. Prevents small blobs of support. This setting is ignored when a branch is supporting a support roof.
// minimum: 0, maximum warning: 5
coord_t support_tree_min_height_to_model { scaled<coord_t>(1.0) };
// Tree Support Inital Layer Diameter
// Diameter every branch tries to achieve when reaching the buildplate. Improves bed adhesion.
// minimum: 0, maximum warning: 20
coord_t support_tree_bp_diameter { scaled<coord_t>(7.5) };
// Tree Support Branch Density
// Adjusts the density of the support structure used to generate the tips of the branches. A higher value results in better overhangs,
// but the supports are harder to remove. Use Support Roof for very high values or ensure support density is similarly high at the top.
// ->
// Adjusts the density of the support structure used to generate the tips of the branches.
// A higher value results in better overhangs but the supports are harder to remove, thus it is recommended to enable top support interfaces
// instead of a high branch density value if dense interfaces are needed.
// 5%-35%
double support_tree_top_rate { 15. };
// Tree Support Tip Diameter
// The diameter of the top of the tip of the branches of tree support.
// minimum: min_wall_line_width, minimum warning: min_wall_line_width+0.05, maximum_value: support_tree_branch_diameter, value: support_line_width
coord_t support_tree_tip_diameter { scaled<coord_t>(0.4) };
// Support Interface Priority
// How support interface and support will interact when they overlap. Currently only implemented for support roof.
//enum support_interface_priority { support_lines_overwrite_interface_area };
};
static constexpr const double SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5;
static constexpr const coord_t SUPPORT_TREE_EXPONENTIAL_THRESHOLD = scaled<coord_t>(1. * SUPPORT_TREE_EXPONENTIAL_FACTOR);
static constexpr const coord_t SUPPORT_TREE_COLLISION_RESOLUTION = scaled<coord_t>(0.5);
static constexpr const bool SUPPORT_TREE_AVOID_SUPPORT_BLOCKER = true;
class TreeModelVolumes
{

File diff suppressed because it is too large Load Diff

View File

@ -9,14 +9,16 @@
#ifndef slic3r_TreeSupport_hpp
#define slic3r_TreeSupport_hpp
#include "SupportLayer.hpp"
#include "TreeModelVolumes.hpp"
#include "Point.hpp"
#include "Support/SupportLayer.hpp"
#include "TreeSupportCommon.hpp"
#include "../BoundingBox.hpp"
#include "../Point.hpp"
#include "../Utils.hpp"
#include <boost/container/small_vector.hpp>
#include "BoundingBox.hpp"
#include "Utils.hpp"
// #define TREE_SUPPORT_SHOW_ERRORS
@ -45,24 +47,8 @@ struct SlicingParameters;
namespace FFFTreeSupport
{
using LayerIndex = int;
static constexpr const double SUPPORT_TREE_EXPONENTIAL_FACTOR = 1.5;
static constexpr const coord_t SUPPORT_TREE_EXPONENTIAL_THRESHOLD = scaled<coord_t>(1. * SUPPORT_TREE_EXPONENTIAL_FACTOR);
static constexpr const coord_t SUPPORT_TREE_COLLISION_RESOLUTION = scaled<coord_t>(0.5);
// The number of vertices in each circle.
static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25;
static constexpr const bool SUPPORT_TREE_AVOID_SUPPORT_BLOCKER = true;
enum class InterfacePreference
{
InterfaceAreaOverwritesSupport,
SupportAreaOverwritesInterface,
InterfaceLinesOverwriteSupport,
SupportLinesOverwriteInterface,
Nothing
};
struct AreaIncreaseSettings
{
@ -89,8 +75,6 @@ struct AreaIncreaseSettings
}
};
struct TreeSupportSettings;
#define TREE_SUPPORTS_TRACK_LOST
// C++17 does not support in place initializers of bit values, thus a constructor zeroing the bits is provided.
@ -232,6 +216,38 @@ struct SupportElementState : public SupportElementStateBits
[[nodiscard]] bool locked() const { return this->distance_to_top < this->dont_move_until; }
};
/*!
* \brief Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of the branch.
* \param elem[in] The SupportElement one wants to know the effectiveDTT
* \return The Effective DTT.
*/
[[nodiscard]] inline size_t getEffectiveDTT(const TreeSupportSettings &settings, const SupportElementState &elem)
{
return elem.effective_radius_height < settings.increase_radius_until_layer ?
(elem.distance_to_top < settings.increase_radius_until_layer ? elem.distance_to_top : settings.increase_radius_until_layer) :
elem.effective_radius_height;
}
/*!
* \brief Get the Radius, that this element will have.
* \param elem[in] The Element.
* \return The radius the element has.
*/
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElementState &elem)
{
return settings.getRadius(getEffectiveDTT(settings, elem), elem.elephant_foot_increases);
}
/*!
* \brief Get the collision Radius of this Element. This can be smaller then the actual radius, as the drawAreas will cut off areas that may collide with the model.
* \param elem[in] The Element.
* \return The collision radius the element has.
*/
[[nodiscard]] inline coord_t support_element_collision_radius(const TreeSupportSettings &settings, const SupportElementState &elem)
{
return settings.getRadius(elem.effective_radius_height, elem.elephant_foot_increases);
}
struct SupportElement
{
using ParentIndices =
@ -262,281 +278,17 @@ struct SupportElement
Polygons influence_area;
};
/*!
* \brief This struct contains settings used in the tree support. Thanks to this most functions do not need to know of meshes etc. Also makes the code shorter.
*/
struct TreeSupportSettings
using SupportElements = std::deque<SupportElement>;
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElement &elem)
{
TreeSupportSettings() = default; // required for the definition of the config variable in the TreeSupportGenerator class.
explicit TreeSupportSettings(const TreeSupportMeshGroupSettings &mesh_group_settings, const SlicingParameters &slicing_params);
return support_element_radius(settings, elem.state);
}
private:
double angle;
double angle_slow;
std::vector<coord_t> known_z;
public:
// some static variables dependent on other meshes that are not currently processed.
// Has to be static because TreeSupportConfig will be used in TreeModelVolumes as this reduces redundancy.
inline static bool soluble = false;
/*!
* \brief Width of a single line of support.
*/
coord_t support_line_width;
/*!
* \brief Height of a single layer
*/
coord_t layer_height;
/*!
* \brief Radius of a branch when it has left the tip.
*/
coord_t branch_radius;
/*!
* \brief smallest allowed radius, required to ensure that even at DTT 0 every circle will still be printed
*/
coord_t min_radius;
/*!
* \brief How far an influence area may move outward every layer at most.
*/
coord_t maximum_move_distance;
/*!
* \brief How far every influence area will move outward every layer if possible.
*/
coord_t maximum_move_distance_slow;
/*!
* \brief Amount of bottom layers. 0 if disabled.
*/
size_t support_bottom_layers;
/*!
* \brief Amount of effectiveDTT increases are required to reach branch radius.
*/
size_t tip_layers;
/*!
* \brief How much a branch radius increases with each layer to guarantee the prescribed tree widening.
*/
double branch_radius_increase_per_layer;
/*!
* \brief How much a branch resting on the model may grow in radius by merging with branches that can reach the buildplate.
*/
coord_t max_to_model_radius_increase;
/*!
* \brief If smaller (in layers) than that, all branches to model will be deleted
*/
size_t min_dtt_to_model;
/*!
* \brief Increase radius in the resulting drawn branches, even if the avoidance does not allow it. Will be cut later to still fit.
*/
coord_t increase_radius_until_radius;
/*!
* \brief Same as increase_radius_until_radius, but contains the DTT at which the radius will be reached.
*/
size_t increase_radius_until_layer;
/*!
* \brief True if the branches may connect to the model.
*/
bool support_rests_on_model;
/*!
* \brief How far should support be from the model.
*/
coord_t xy_distance;
/*!
* \brief A minimum radius a tree trunk should expand to at the buildplate if possible.
*/
coord_t bp_radius;
/*!
* \brief The layer index at which an increase in radius may be required to reach the bp_radius.
*/
LayerIndex layer_start_bp_radius;
/*!
* \brief How much one is allowed to increase the tree branch radius close to print bed to reach the required bp_radius at layer 0.
* Note that this radius increase will not happen in the tip, to ensure the tip is structurally sound.
*/
double bp_radius_increase_per_layer;
/*!
* \brief minimum xy_distance. Only relevant when Z overrides XY, otherwise equal to xy_distance-
*/
coord_t xy_min_distance;
/*!
* \brief Amount of layers distance required the top of the support to the model
*/
size_t z_distance_top_layers;
/*!
* \brief Amount of layers distance required from the top of the model to the bottom of a support structure.
*/
size_t z_distance_bottom_layers;
/*!
* \brief User specified angles for the support infill.
*/
// std::vector<double> support_infill_angles;
/*!
* \brief User specified angles for the support roof infill.
*/
std::vector<double> support_roof_angles;
/*!
* \brief Pattern used in the support roof. May contain non relevant data if support roof is disabled.
*/
SupportMaterialInterfacePattern roof_pattern;
/*!
* \brief Pattern used in the support infill.
*/
SupportMaterialPattern support_pattern;
/*!
* \brief Line width of the support roof.
*/
coord_t support_roof_line_width;
/*!
* \brief Distance between support infill lines.
*/
coord_t support_line_spacing;
/*!
* \brief Offset applied to the support floor area.
*/
coord_t support_bottom_offset;
/*
* \brief Amount of walls the support area will have.
*/
int support_wall_count;
/*
* \brief Maximum allowed deviation when simplifying.
*/
coord_t resolution;
/*
* \brief Distance between the lines of the roof.
*/
coord_t support_roof_line_distance;
/*
* \brief How overlaps of an interface area with a support area should be handled.
*/
InterfacePreference interface_preference;
/*
* \brief The infill class wants a settings object. This one will be the correct one for all settings it uses.
*/
TreeSupportMeshGroupSettings settings;
/*
* \brief Minimum thickness of any model features.
*/
coord_t min_feature_size;
// Extra raft layers below the object.
std::vector<coordf_t> raft_layers;
public:
bool operator==(const TreeSupportSettings& other) const
{
return branch_radius == other.branch_radius && tip_layers == other.tip_layers && branch_radius_increase_per_layer == other.branch_radius_increase_per_layer && layer_start_bp_radius == other.layer_start_bp_radius && bp_radius == other.bp_radius &&
// as a recalculation of the collision areas is required to set a new min_radius.
bp_radius_increase_per_layer == other.bp_radius_increase_per_layer && min_radius == other.min_radius && xy_min_distance == other.xy_min_distance &&
xy_distance - xy_min_distance == other.xy_distance - other.xy_min_distance && // if the delta of xy_min_distance and xy_distance is different the collision areas have to be recalculated.
support_rests_on_model == other.support_rests_on_model && increase_radius_until_layer == other.increase_radius_until_layer && min_dtt_to_model == other.min_dtt_to_model && max_to_model_radius_increase == other.max_to_model_radius_increase && maximum_move_distance == other.maximum_move_distance && maximum_move_distance_slow == other.maximum_move_distance_slow && z_distance_bottom_layers == other.z_distance_bottom_layers && support_line_width == other.support_line_width &&
support_line_spacing == other.support_line_spacing && support_roof_line_width == other.support_roof_line_width && // can not be set on a per-mesh basis currently, so code to enable processing different roof line width in the same iteration seems useless.
support_bottom_offset == other.support_bottom_offset && support_wall_count == other.support_wall_count && support_pattern == other.support_pattern && roof_pattern == other.roof_pattern && // can not be set on a per-mesh basis currently, so code to enable processing different roof patterns in the same iteration seems useless.
support_roof_angles == other.support_roof_angles &&
//support_infill_angles == other.support_infill_angles &&
increase_radius_until_radius == other.increase_radius_until_radius && support_bottom_layers == other.support_bottom_layers && layer_height == other.layer_height && z_distance_top_layers == other.z_distance_top_layers && resolution == other.resolution && // Infill generation depends on deviation and resolution.
support_roof_line_distance == other.support_roof_line_distance && interface_preference == other.interface_preference
&& min_feature_size == other.min_feature_size // interface_preference should be identical to ensure the tree will correctly interact with the roof.
// The infill class now wants the settings object and reads a lot of settings, and as the infill class is used to calculate support roof lines for interface-preference. Not all of these may be required to be identical, but as I am not sure, better safe than sorry
#if 0
&& (interface_preference == InterfacePreference::InterfaceAreaOverwritesSupport || interface_preference == InterfacePreference::SupportAreaOverwritesInterface
// Perimeter generator parameters
||
(settings.get<bool>("fill_outline_gaps") == other.settings.get<bool>("fill_outline_gaps") &&
settings.get<coord_t>("min_bead_width") == other.settings.get<coord_t>("min_bead_width") &&
settings.get<double>("wall_transition_angle") == other.settings.get<double>("wall_transition_angle") &&
settings.get<coord_t>("wall_transition_length") == other.settings.get<coord_t>("wall_transition_length") &&
settings.get<Ratio>("wall_split_middle_threshold") == other.settings.get<Ratio>("wall_split_middle_threshold") &&
settings.get<Ratio>("wall_add_middle_threshold") == other.settings.get<Ratio>("wall_add_middle_threshold") &&
settings.get<int>("wall_distribution_count") == other.settings.get<int>("wall_distribution_count") &&
settings.get<coord_t>("wall_transition_filter_distance") == other.settings.get<coord_t>("wall_transition_filter_distance") &&
settings.get<coord_t>("wall_transition_filter_deviation") == other.settings.get<coord_t>("wall_transition_filter_deviation") &&
settings.get<coord_t>("wall_line_width_x") == other.settings.get<coord_t>("wall_line_width_x") &&
settings.get<int>("meshfix_maximum_extrusion_area_deviation") == other.settings.get<int>("meshfix_maximum_extrusion_area_deviation"))
)
#endif
&& raft_layers == other.raft_layers
;
}
/*!
* \brief Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of the branch.
* \param elem[in] The SupportElement one wants to know the effectiveDTT
* \return The Effective DTT.
*/
[[nodiscard]] inline size_t getEffectiveDTT(const SupportElementState &elem) const
{
return elem.effective_radius_height < increase_radius_until_layer ? (elem.distance_to_top < increase_radius_until_layer ? elem.distance_to_top : increase_radius_until_layer) : elem.effective_radius_height;
}
/*!
* \brief Get the Radius part will have based on numeric values.
* \param distance_to_top[in] The effective distance_to_top of the element
* \param elephant_foot_increases[in] The elephant_foot_increases of the element.
* \return The radius an element with these attributes would have.
*/
[[nodiscard]] inline coord_t getRadius(size_t distance_to_top, const double elephant_foot_increases = 0) const
{
return (distance_to_top <= tip_layers ? min_radius + (branch_radius - min_radius) * distance_to_top / tip_layers : // tip
branch_radius + // base
(distance_to_top - tip_layers) * branch_radius_increase_per_layer)
+ // gradual increase
elephant_foot_increases * (std::max(bp_radius_increase_per_layer - branch_radius_increase_per_layer, 0.0));
}
/*!
* \brief Get the Radius, that this element will have.
* \param elem[in] The Element.
* \return The radius the element has.
*/
[[nodiscard]] inline coord_t getRadius(const SupportElementState &elem) const
{ return getRadius(getEffectiveDTT(elem), elem.elephant_foot_increases); }
[[nodiscard]] inline coord_t getRadius(const SupportElement &elem) const
{ return this->getRadius(elem.state); }
/*!
* \brief Get the collision Radius of this Element. This can be smaller then the actual radius, as the drawAreas will cut off areas that may collide with the model.
* \param elem[in] The Element.
* \return The collision radius the element has.
*/
[[nodiscard]] inline coord_t getCollisionRadius(const SupportElementState &elem) const
{
return getRadius(elem.effective_radius_height, elem.elephant_foot_increases);
}
/*!
* \brief Get the Radius an element should at least have at a given layer.
* \param layer_idx[in] The layer.
* \return The radius every element should aim to achieve.
*/
[[nodiscard]] inline coord_t recommendedMinRadius(LayerIndex layer_idx) const
{
double num_layers_widened = layer_start_bp_radius - layer_idx;
return num_layers_widened > 0 ? branch_radius + num_layers_widened * bp_radius_increase_per_layer : 0;
}
/*!
* \brief Return on which z in microns the layer will be printed. Used only for support infill line generation.
* \param layer_idx[in] The layer.
* \return The radius every element should aim to achieve.
*/
[[nodiscard]] inline coord_t getActualZ(LayerIndex layer_idx)
{
return layer_idx < coord_t(known_z.size()) ? known_z[layer_idx] : (layer_idx - known_z.size()) * layer_height + known_z.size() ? known_z.back() : 0;
}
/*!
* \brief Set the z every Layer is printed at. Required for getActualZ to work
* \param z[in] The z every LayerIndex is printed. Vector is used as a map<LayerIndex,coord_t> with the index of each element being the corresponding LayerIndex
* \return The radius every element should aim to achieve.
*/
void setActualZ(std::vector<coord_t>& z)
{
known_z = z;
}
};
void tree_supports_show_error(std::string_view message, bool critical);
[[nodiscard]] inline coord_t support_element_collision_radius(const TreeSupportSettings &settings, const SupportElement &elem)
{
return support_element_collision_radius(settings, elem.state);
}
} // namespace FFFTreeSupport

View File

@ -0,0 +1,190 @@
// Tree supports by Thomas Rahm, losely based on Tree Supports by CuraEngine.
// Original source of Thomas Rahm's tree supports:
// https://github.com/ThomasRahm/CuraEngine
//
// Original CuraEngine copyright:
// Copyright (c) 2021 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.
#include "TreeSupportCommon.hpp"
namespace Slic3r::FFFTreeSupport {
TreeSupportMeshGroupSettings::TreeSupportMeshGroupSettings(const PrintObject &print_object)
{
const PrintConfig &print_config = print_object.print()->config();
const PrintObjectConfig &config = print_object.config();
const SlicingParameters &slicing_params = print_object.slicing_parameters();
// const std::vector<unsigned int> printing_extruders = print_object.object_extruders();
// Support must be enabled and set to Tree style.
assert(config.support_material || config.support_material_enforce_layers > 0);
assert(config.support_material_style == smsTree || config.support_material_style == smsOrganic);
// Calculate maximum external perimeter width over all printing regions, taking into account the default layer height.
coordf_t external_perimeter_width = 0.;
for (size_t region_id = 0; region_id < print_object.num_printing_regions(); ++ region_id) {
const PrintRegion &region = print_object.printing_region(region_id);
external_perimeter_width = std::max<coordf_t>(external_perimeter_width, region.flow(print_object, frExternalPerimeter, config.layer_height).width());
}
this->layer_height = scaled<coord_t>(config.layer_height.value);
this->resolution = scaled<coord_t>(print_config.gcode_resolution.value);
// Arache feature
this->min_feature_size = scaled<coord_t>(config.min_feature_size.value);
// +1 makes the threshold inclusive
this->support_angle = 0.5 * M_PI - std::clamp<double>((config.support_material_threshold + 1) * M_PI / 180., 0., 0.5 * M_PI);
this->support_line_width = support_material_flow(&print_object, config.layer_height).scaled_width();
this->support_roof_line_width = support_material_interface_flow(&print_object, config.layer_height).scaled_width();
//FIXME add it to SlicingParameters and reuse in both tree and normal supports?
this->support_bottom_enable = config.support_material_interface_layers.value > 0 && config.support_material_bottom_interface_layers.value != 0;
this->support_bottom_height = this->support_bottom_enable ?
(config.support_material_bottom_interface_layers.value > 0 ?
config.support_material_bottom_interface_layers.value :
config.support_material_interface_layers.value) * this->layer_height :
0;
this->support_material_buildplate_only = config.support_material_buildplate_only;
this->support_xy_distance = scaled<coord_t>(config.support_material_xy_spacing.get_abs_value(external_perimeter_width));
// Separation of interfaces, it is likely smaller than support_xy_distance.
this->support_xy_distance_overhang = std::min(this->support_xy_distance, scaled<coord_t>(0.5 * external_perimeter_width));
this->support_top_distance = scaled<coord_t>(slicing_params.gap_support_object);
this->support_bottom_distance = scaled<coord_t>(slicing_params.gap_object_support);
// this->support_interface_skip_height =
// this->support_infill_angles =
this->support_roof_enable = config.support_material_interface_layers.value > 0;
this->support_roof_layers = this->support_roof_enable ? config.support_material_interface_layers.value : 0;
this->support_floor_enable = config.support_material_interface_layers.value > 0 && config.support_material_bottom_interface_layers.value > 0;
this->support_floor_layers = this->support_floor_enable ? config.support_material_bottom_interface_layers.value : 0;
// this->minimum_roof_area =
// this->support_roof_angles =
this->support_roof_pattern = config.support_material_interface_pattern;
this->support_pattern = config.support_material_pattern;
this->support_line_spacing = scaled<coord_t>(config.support_material_spacing.value);
// this->support_bottom_offset =
// this->support_wall_count = config.support_material_with_sheath ? 1 : 0;
this->support_wall_count = 1;
this->support_roof_line_distance = scaled<coord_t>(config.support_material_interface_spacing.value) + this->support_roof_line_width;
// this->minimum_support_area =
// this->minimum_bottom_area =
// this->support_offset =
this->support_tree_branch_distance = scaled<coord_t>(config.support_tree_branch_distance.value);
this->support_tree_angle = std::clamp<double>(config.support_tree_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
this->support_tree_angle_slow = std::clamp<double>(config.support_tree_angle_slow * M_PI / 180., 0., this->support_tree_angle - EPSILON);
this->support_tree_branch_diameter = scaled<coord_t>(config.support_tree_branch_diameter.value);
this->support_tree_branch_diameter_angle = std::clamp<double>(config.support_tree_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON);
this->support_tree_top_rate = config.support_tree_top_rate.value; // percent
// this->support_tree_tip_diameter = this->support_line_width;
this->support_tree_tip_diameter = std::clamp(scaled<coord_t>(config.support_tree_tip_diameter.value), 0, this->support_tree_branch_diameter);
}
TreeSupportSettings::TreeSupportSettings(const TreeSupportMeshGroupSettings &mesh_group_settings, const SlicingParameters &slicing_params)
: angle(mesh_group_settings.support_tree_angle),
angle_slow(mesh_group_settings.support_tree_angle_slow),
support_line_width(mesh_group_settings.support_line_width),
layer_height(mesh_group_settings.layer_height),
branch_radius(mesh_group_settings.support_tree_branch_diameter / 2),
min_radius(mesh_group_settings.support_tree_tip_diameter / 2), // The actual radius is 50 microns larger as the resulting branches will be increased by 50 microns to avoid rounding errors effectively increasing the xydistance
maximum_move_distance((angle < M_PI / 2.) ? (coord_t)(tan(angle) * layer_height) : std::numeric_limits<coord_t>::max()),
maximum_move_distance_slow((angle_slow < M_PI / 2.) ? (coord_t)(tan(angle_slow) * layer_height) : std::numeric_limits<coord_t>::max()),
support_bottom_layers(mesh_group_settings.support_bottom_enable ? (mesh_group_settings.support_bottom_height + layer_height / 2) / layer_height : 0),
tip_layers(std::max((branch_radius - min_radius) / (support_line_width / 3), branch_radius / layer_height)), // Ensure lines always stack nicely even if layer height is large
branch_radius_increase_per_layer(tan(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height),
max_to_model_radius_increase(mesh_group_settings.support_tree_max_diameter_increase_by_merges_when_support_to_model / 2),
min_dtt_to_model(round_up_divide(mesh_group_settings.support_tree_min_height_to_model, layer_height)),
increase_radius_until_radius(mesh_group_settings.support_tree_branch_diameter / 2),
increase_radius_until_layer(increase_radius_until_radius <= branch_radius ? tip_layers * (increase_radius_until_radius / branch_radius) : (increase_radius_until_radius - branch_radius) / branch_radius_increase_per_layer),
support_rests_on_model(! mesh_group_settings.support_material_buildplate_only),
xy_distance(mesh_group_settings.support_xy_distance),
xy_min_distance(std::min(mesh_group_settings.support_xy_distance, mesh_group_settings.support_xy_distance_overhang)),
bp_radius(mesh_group_settings.support_tree_bp_diameter / 2),
// Increase by half a line overlap, but not faster than 40 degrees angle (0 degrees means zero increase in radius).
bp_radius_increase_per_layer(std::min(tan(0.7) * layer_height, 0.5 * support_line_width)),
z_distance_bottom_layers(size_t(round(double(mesh_group_settings.support_bottom_distance) / double(layer_height)))),
z_distance_top_layers(size_t(round(double(mesh_group_settings.support_top_distance) / double(layer_height)))),
// support_infill_angles(mesh_group_settings.support_infill_angles),
support_roof_angles(mesh_group_settings.support_roof_angles),
roof_pattern(mesh_group_settings.support_roof_pattern),
support_pattern(mesh_group_settings.support_pattern),
support_roof_line_width(mesh_group_settings.support_roof_line_width),
support_line_spacing(mesh_group_settings.support_line_spacing),
support_bottom_offset(mesh_group_settings.support_bottom_offset),
support_wall_count(mesh_group_settings.support_wall_count),
resolution(mesh_group_settings.resolution),
support_roof_line_distance(mesh_group_settings.support_roof_line_distance), // in the end the actual infill has to be calculated to subtract interface from support areas according to interface_preference.
settings(mesh_group_settings),
min_feature_size(mesh_group_settings.min_feature_size)
{
layer_start_bp_radius = (bp_radius - branch_radius) / bp_radius_increase_per_layer;
if (TreeSupportSettings::soluble) {
// safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely
// When for all meshes the z bottom and top distance is more than one layer though the worst case is xy_min_distance + min_feature_size
// This is not the best solution, but the only one to ensure areas can not lag though walls at high maximum_move_distance.
xy_min_distance = std::max(xy_min_distance, scaled<coord_t>(0.1));
xy_distance = std::max(xy_distance, xy_min_distance);
}
// const std::unordered_map<std::string, InterfacePreference> interface_map = { { "support_area_overwrite_interface_area", InterfacePreference::SupportAreaOverwritesInterface }, { "interface_area_overwrite_support_area", InterfacePreference::InterfaceAreaOverwritesSupport }, { "support_lines_overwrite_interface_area", InterfacePreference::SupportLinesOverwriteInterface }, { "interface_lines_overwrite_support_area", InterfacePreference::InterfaceLinesOverwriteSupport }, { "nothing", InterfacePreference::Nothing } };
// interface_preference = interface_map.at(mesh_group_settings.get<std::string>("support_interface_priority"));
//FIXME this was the default
// interface_preference = InterfacePreference::SupportLinesOverwriteInterface;
//interface_preference = InterfacePreference::SupportAreaOverwritesInterface;
interface_preference = InterfacePreference::InterfaceAreaOverwritesSupport;
if (slicing_params.raft_layers() > 0) {
// Fill in raft_layers with the heights of the layers below the first object layer.
// First layer
double z = slicing_params.first_print_layer_height;
this->raft_layers.emplace_back(z);
// Raft base layers
for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) {
z += slicing_params.base_raft_layer_height;
this->raft_layers.emplace_back(z);
}
// Raft interface layers
for (size_t i = 0; i + 1 < slicing_params.interface_raft_layers; ++ i) {
z += slicing_params.interface_raft_layer_height;
this->raft_layers.emplace_back(z);
}
// Raft contact layer
if (slicing_params.raft_layers() > 1) {
z = slicing_params.raft_contact_top_z;
this->raft_layers.emplace_back(z);
}
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));
double step = dist_to_go / nsteps;
for (size_t i = 0; i < nsteps; ++ i) {
z += step;
this->raft_layers.emplace_back(z);
}
}
}
}
#if defined(TREE_SUPPORT_SHOW_ERRORS) && defined(_WIN32)
#define TREE_SUPPORT_SHOW_ERRORS_WIN32
#include <windows.h>
#endif
// Shared with generate_support_areas()
bool g_showed_critical_error = false;
bool g_showed_performance_warning = false;
void tree_supports_show_error(std::string_view message, bool critical)
{ // todo Remove! ONLY FOR PUBLIC BETA!!
printf("Error: %s, critical: %d\n", message.data(), int(critical));
#ifdef TREE_SUPPORT_SHOW_ERRORS_WIN32
static bool showed_critical = false;
static bool showed_performance = false;
auto bugtype = std::string(critical ? " This is a critical bug. It may cause missing or malformed branches.\n" : "This bug should only decrease performance.\n");
bool show = (critical && !g_showed_critical_error) || (!critical && !g_showed_performance_warning);
(critical ? g_showed_critical_error : g_showed_performance_warning) = true;
if (show)
MessageBoxA(nullptr, std::string("TreeSupport_2 MOD detected an error while generating the tree support.\nPlease report this back to me with profile and model.\nRevision 5.0\n" + std::string(message) + "\n" + bugtype).c_str(),
"Bug detected!", MB_OK | MB_SYSTEMMODAL | MB_SETFOREGROUND | MB_ICONWARNING);
#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
}
} // namespace Slic3r::FFFTreeSupport

View File

@ -0,0 +1,593 @@
// Tree supports by Thomas Rahm, losely based on Tree Supports by CuraEngine.
// Original source of Thomas Rahm's tree supports:
// https://github.com/ThomasRahm/CuraEngine
//
// Original CuraEngine copyright:
// Copyright (c) 2021 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef slic3r_TreeSupportCommon_hpp
#define slic3r_TreeSupportCommon_hpp
#include "../libslic3r.h"
#include "../Polygon.hpp"
#include "SupportCommon.hpp"
#include <string_view>
using namespace Slic3r::FFFSupport;
namespace Slic3r
{
namespace FFFTreeSupport
{
using LayerIndex = int;
enum class InterfacePreference
{
InterfaceAreaOverwritesSupport,
SupportAreaOverwritesInterface,
InterfaceLinesOverwriteSupport,
SupportLinesOverwriteInterface,
Nothing
};
struct TreeSupportMeshGroupSettings {
TreeSupportMeshGroupSettings() = default;
explicit TreeSupportMeshGroupSettings(const PrintObject &print_object);
/*********************************************************************/
/* Print parameters, not support specific: */
/*********************************************************************/
coord_t layer_height { scaled<coord_t>(0.15) };
// Maximum Deviation (meshfix_maximum_deviation)
// The maximum deviation allowed when reducing the resolution for the Maximum Resolution setting. If you increase this,
// the print will be less accurate, but the g-code will be smaller. Maximum Deviation is a limit for Maximum Resolution,
// so if the two conflict the Maximum Deviation will always be held true.
coord_t resolution { scaled<coord_t>(0.025) };
// Minimum Feature Size (aka minimum line width) - Arachne specific
// Minimum thickness of thin features. Model features that are thinner than this value will not be printed, while features thicker
// than the Minimum Feature Size will be widened to the Minimum Wall Line Width.
coord_t min_feature_size { scaled<coord_t>(0.1) };
/*********************************************************************/
/* General support parameters: */
/*********************************************************************/
// Support Overhang Angle
// The minimum angle of overhangs for which support is added. At a value of 0° all overhangs are supported, 90° will not provide any support.
double support_angle { 50. * M_PI / 180. };
// Support Line Width
// Width of a single support structure line.
coord_t support_line_width { scaled<coord_t>(0.4) };
// Support Roof Line Width: Width of a single support roof line.
coord_t support_roof_line_width { scaled<coord_t>(0.4) };
// Enable Support Floor (aka bottom interfaces)
// Generate a dense slab of material between the bottom of the support and the model. This will create a skin between the model and support.
bool support_bottom_enable { false };
// Support Floor Thickness
// The thickness of the support floors. This controls the number of dense layers that are printed on top of places of a model on which support rests.
coord_t support_bottom_height { scaled<coord_t>(1.) };
bool support_material_buildplate_only { false };
// Support X/Y Distance
// Distance of the support structure from the print in the X/Y directions.
// minimum: 0, maximum warning: 1.5 * machine_nozzle_tip_outer_diameter
coord_t support_xy_distance { scaled<coord_t>(0.7) };
// Minimum Support X/Y Distance
// Distance of the support structure from the overhang in the X/Y directions.
// minimum_value: 0, minimum warning": support_xy_distance - support_line_width * 2, maximum warning: support_xy_distance
coord_t support_xy_distance_overhang { scaled<coord_t>(0.2) };
// Support Top Distance
// Distance from the top of the support to the print.
coord_t support_top_distance { scaled<coord_t>(0.1) };
// Support Bottom Distance
// Distance from the print to the bottom of the support.
coord_t support_bottom_distance { scaled<coord_t>(0.1) };
//FIXME likely not needed, optimization for clipping of interface layers
// When checking where there's model above and below the support, take steps of the given height. Lower values will slice slower, while higher values
// may cause normal support to be printed in some places where there should have been support interface.
coord_t support_interface_skip_height { scaled<coord_t>(0.3) };
// Support Infill Line Directions
// A list of integer line directions to use. Elements from the list are used sequentially as the layers progress and when the end
// of the list is reached, it starts at the beginning again. The list items are separated by commas and the whole list is contained
// in square brackets. Default is an empty list which means use the default angle 0 degrees.
// std::vector<double> support_infill_angles {};
// Enable Support Roof
// Generate a dense slab of material between the top of support and the model. This will create a skin between the model and support.
bool support_roof_enable { false };
// Support Roof Thickness
// The thickness of the support roofs. This controls the amount of dense layers at the top of the support on which the model rests.
coord_t support_roof_layers { 2 };
bool support_floor_enable { false };
coord_t support_floor_layers { 2 };
// Minimum Support Roof Area
// Minimum area size for the roofs of the support. Polygons which have an area smaller than this value will be printed as normal support.
double minimum_roof_area { scaled<double>(scaled<double>(1.)) };
// A list of integer line directions to use. Elements from the list are used sequentially as the layers progress
// and when the end of the list is reached, it starts at the beginning again. The list items are separated
// by commas and the whole list is contained in square brackets. Default is an empty list which means
// use the default angles (alternates between 45 and 135 degrees if interfaces are quite thick or 90 degrees).
std::vector<double> support_roof_angles {};
// Support Roof Pattern (aka top interface)
// The pattern with which the roofs of the support are printed.
SupportMaterialInterfacePattern support_roof_pattern { smipAuto };
// Support Pattern
// The pattern of the support structures of the print. The different options available result in sturdy or easy to remove support.
SupportMaterialPattern support_pattern { smpRectilinear };
// Support Line Distance
// Distance between the printed support structure lines. This setting is calculated by the support density.
coord_t support_line_spacing { scaled<coord_t>(2.66 - 0.4) };
// Support Floor Horizontal Expansion
// Amount of offset applied to the floors of the support.
coord_t support_bottom_offset { scaled<coord_t>(0.) };
// Support Wall Line Count
// The number of walls with which to surround support infill. Adding a wall can make support print more reliably
// and can support overhangs better, but increases print time and material used.
// tree: 1, zig-zag: 0, concentric: 1
int support_wall_count { 1 };
// Support Roof Line Distance
// Distance between the printed support roof lines. This setting is calculated by the Support Roof Density, but can be adjusted separately.
coord_t support_roof_line_distance { scaled<coord_t>(0.4) };
// Minimum Support Area
// Minimum area size for support polygons. Polygons which have an area smaller than this value will not be generated.
coord_t minimum_support_area { scaled<coord_t>(0.) };
// Minimum Support Floor Area
// Minimum area size for the floors of the support. Polygons which have an area smaller than this value will be printed as normal support.
coord_t minimum_bottom_area { scaled<coord_t>(1.0) };
// Support Horizontal Expansion
// Amount of offset applied to all support polygons in each layer. Positive values can smooth out the support areas and result in more sturdy support.
coord_t support_offset { scaled<coord_t>(0.) };
/*********************************************************************/
/* Parameters for the Cura tree supports implementation: */
/*********************************************************************/
// Tree Support Maximum Branch Angle
// The maximum angle of the branches, when the branches have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle to be able to have more reach.
// minimum: 0, minimum warning: 20, maximum: 89, maximum warning": 85
double support_tree_angle { 60. * M_PI / 180. };
// Tree Support Branch Diameter Angle
// The angle of the branches' diameter as they gradually become thicker towards the bottom. An angle of 0 will cause the branches to have uniform thickness over their length.
// A bit of an angle can increase stability of the tree support.
// minimum: 0, maximum: 89.9999, maximum warning: 15
double support_tree_branch_diameter_angle { 5. * M_PI / 180. };
// Tree Support Branch Distance
// How far apart the branches need to be when they touch the model. Making this distance small will cause
// the tree support to touch the model at more points, causing better overhang but making support harder to remove.
coord_t support_tree_branch_distance { scaled<coord_t>(1.) };
// Tree Support Branch Diameter
// The diameter of the thinnest branches of tree support. Thicker branches are more sturdy. Branches towards the base will be thicker than this.
// minimum: 0.001, minimum warning: support_line_width * 2
coord_t support_tree_branch_diameter { scaled<coord_t>(2.) };
/*********************************************************************/
/* Parameters new to the Thomas Rahm's tree supports implementation: */
/*********************************************************************/
// Tree Support Preferred Branch Angle
// The preferred angle of the branches, when they do not have to avoid the model. Use a lower angle to make them more vertical and more stable. Use a higher angle for branches to merge faster.
// minimum: 0, minimum warning: 10, maximum: support_tree_angle, maximum warning: support_tree_angle-1
double support_tree_angle_slow { 50. * M_PI / 180. };
// Tree Support Diameter Increase To Model
// The most the diameter of a branch that has to connect to the model may increase by merging with branches that could reach the buildplate.
// Increasing this reduces print time, but increases the area of support that rests on model
// minimum: 0
coord_t support_tree_max_diameter_increase_by_merges_when_support_to_model { scaled<coord_t>(1.0) };
// Tree Support Minimum Height To Model
// How tall a branch has to be if it is placed on the model. Prevents small blobs of support. This setting is ignored when a branch is supporting a support roof.
// minimum: 0, maximum warning: 5
coord_t support_tree_min_height_to_model { scaled<coord_t>(1.0) };
// Tree Support Inital Layer Diameter
// Diameter every branch tries to achieve when reaching the buildplate. Improves bed adhesion.
// minimum: 0, maximum warning: 20
coord_t support_tree_bp_diameter { scaled<coord_t>(7.5) };
// Tree Support Branch Density
// Adjusts the density of the support structure used to generate the tips of the branches. A higher value results in better overhangs,
// but the supports are harder to remove. Use Support Roof for very high values or ensure support density is similarly high at the top.
// ->
// Adjusts the density of the support structure used to generate the tips of the branches.
// A higher value results in better overhangs but the supports are harder to remove, thus it is recommended to enable top support interfaces
// instead of a high branch density value if dense interfaces are needed.
// 5%-35%
double support_tree_top_rate { 15. };
// Tree Support Tip Diameter
// The diameter of the top of the tip of the branches of tree support.
// minimum: min_wall_line_width, minimum warning: min_wall_line_width+0.05, maximum_value: support_tree_branch_diameter, value: support_line_width
coord_t support_tree_tip_diameter { scaled<coord_t>(0.4) };
// Support Interface Priority
// How support interface and support will interact when they overlap. Currently only implemented for support roof.
//enum support_interface_priority { support_lines_overwrite_interface_area };
};
/*!
* \brief This struct contains settings used in the tree support. Thanks to this most functions do not need to know of meshes etc. Also makes the code shorter.
*/
struct TreeSupportSettings
{
public:
TreeSupportSettings() = default; // required for the definition of the config variable in the TreeSupportGenerator class.
explicit TreeSupportSettings(const TreeSupportMeshGroupSettings &mesh_group_settings, const SlicingParameters &slicing_params);
// some static variables dependent on other meshes that are not currently processed.
// Has to be static because TreeSupportConfig will be used in TreeModelVolumes as this reduces redundancy.
inline static bool soluble = false;
/*!
* \brief Width of a single line of support.
*/
coord_t support_line_width;
/*!
* \brief Height of a single layer
*/
coord_t layer_height;
/*!
* \brief Radius of a branch when it has left the tip.
*/
coord_t branch_radius;
/*!
* \brief smallest allowed radius, required to ensure that even at DTT 0 every circle will still be printed
*/
coord_t min_radius;
/*!
* \brief How far an influence area may move outward every layer at most.
*/
coord_t maximum_move_distance;
/*!
* \brief How far every influence area will move outward every layer if possible.
*/
coord_t maximum_move_distance_slow;
/*!
* \brief Amount of bottom layers. 0 if disabled.
*/
size_t support_bottom_layers;
/*!
* \brief Amount of effectiveDTT increases are required to reach branch radius.
*/
size_t tip_layers;
/*!
* \brief How much a branch radius increases with each layer to guarantee the prescribed tree widening.
*/
double branch_radius_increase_per_layer;
/*!
* \brief How much a branch resting on the model may grow in radius by merging with branches that can reach the buildplate.
*/
coord_t max_to_model_radius_increase;
/*!
* \brief If smaller (in layers) than that, all branches to model will be deleted
*/
size_t min_dtt_to_model;
/*!
* \brief Increase radius in the resulting drawn branches, even if the avoidance does not allow it. Will be cut later to still fit.
*/
coord_t increase_radius_until_radius;
/*!
* \brief Same as increase_radius_until_radius, but contains the DTT at which the radius will be reached.
*/
size_t increase_radius_until_layer;
/*!
* \brief True if the branches may connect to the model.
*/
bool support_rests_on_model;
/*!
* \brief How far should support be from the model.
*/
coord_t xy_distance;
/*!
* \brief A minimum radius a tree trunk should expand to at the buildplate if possible.
*/
coord_t bp_radius;
/*!
* \brief The layer index at which an increase in radius may be required to reach the bp_radius.
*/
LayerIndex layer_start_bp_radius;
/*!
* \brief How much one is allowed to increase the tree branch radius close to print bed to reach the required bp_radius at layer 0.
* Note that this radius increase will not happen in the tip, to ensure the tip is structurally sound.
*/
double bp_radius_increase_per_layer;
/*!
* \brief minimum xy_distance. Only relevant when Z overrides XY, otherwise equal to xy_distance-
*/
coord_t xy_min_distance;
/*!
* \brief Amount of layers distance required the top of the support to the model
*/
size_t z_distance_top_layers;
/*!
* \brief Amount of layers distance required from the top of the model to the bottom of a support structure.
*/
size_t z_distance_bottom_layers;
/*!
* \brief User specified angles for the support infill.
*/
// std::vector<double> support_infill_angles;
/*!
* \brief User specified angles for the support roof infill.
*/
std::vector<double> support_roof_angles;
/*!
* \brief Pattern used in the support roof. May contain non relevant data if support roof is disabled.
*/
SupportMaterialInterfacePattern roof_pattern;
/*!
* \brief Pattern used in the support infill.
*/
SupportMaterialPattern support_pattern;
/*!
* \brief Line width of the support roof.
*/
coord_t support_roof_line_width;
/*!
* \brief Distance between support infill lines.
*/
coord_t support_line_spacing;
/*!
* \brief Offset applied to the support floor area.
*/
coord_t support_bottom_offset;
/*
* \brief Amount of walls the support area will have.
*/
int support_wall_count;
/*
* \brief Maximum allowed deviation when simplifying.
*/
coord_t resolution;
/*
* \brief Distance between the lines of the roof.
*/
coord_t support_roof_line_distance;
/*
* \brief How overlaps of an interface area with a support area should be handled.
*/
InterfacePreference interface_preference;
/*
* \brief The infill class wants a settings object. This one will be the correct one for all settings it uses.
*/
TreeSupportMeshGroupSettings settings;
/*
* \brief Minimum thickness of any model features.
*/
coord_t min_feature_size;
// Extra raft layers below the object.
std::vector<coordf_t> raft_layers;
public:
bool operator==(const TreeSupportSettings& other) const
{
return branch_radius == other.branch_radius && tip_layers == other.tip_layers && branch_radius_increase_per_layer == other.branch_radius_increase_per_layer && layer_start_bp_radius == other.layer_start_bp_radius && bp_radius == other.bp_radius &&
// as a recalculation of the collision areas is required to set a new min_radius.
bp_radius_increase_per_layer == other.bp_radius_increase_per_layer && min_radius == other.min_radius && xy_min_distance == other.xy_min_distance &&
xy_distance - xy_min_distance == other.xy_distance - other.xy_min_distance && // if the delta of xy_min_distance and xy_distance is different the collision areas have to be recalculated.
support_rests_on_model == other.support_rests_on_model && increase_radius_until_layer == other.increase_radius_until_layer && min_dtt_to_model == other.min_dtt_to_model && max_to_model_radius_increase == other.max_to_model_radius_increase && maximum_move_distance == other.maximum_move_distance && maximum_move_distance_slow == other.maximum_move_distance_slow && z_distance_bottom_layers == other.z_distance_bottom_layers && support_line_width == other.support_line_width &&
support_line_spacing == other.support_line_spacing && support_roof_line_width == other.support_roof_line_width && // can not be set on a per-mesh basis currently, so code to enable processing different roof line width in the same iteration seems useless.
support_bottom_offset == other.support_bottom_offset && support_wall_count == other.support_wall_count && support_pattern == other.support_pattern && roof_pattern == other.roof_pattern && // can not be set on a per-mesh basis currently, so code to enable processing different roof patterns in the same iteration seems useless.
support_roof_angles == other.support_roof_angles &&
//support_infill_angles == other.support_infill_angles &&
increase_radius_until_radius == other.increase_radius_until_radius && support_bottom_layers == other.support_bottom_layers && layer_height == other.layer_height && z_distance_top_layers == other.z_distance_top_layers && resolution == other.resolution && // Infill generation depends on deviation and resolution.
support_roof_line_distance == other.support_roof_line_distance && interface_preference == other.interface_preference
&& min_feature_size == other.min_feature_size // interface_preference should be identical to ensure the tree will correctly interact with the roof.
// The infill class now wants the settings object and reads a lot of settings, and as the infill class is used to calculate support roof lines for interface-preference. Not all of these may be required to be identical, but as I am not sure, better safe than sorry
#if 0
&& (interface_preference == InterfacePreference::InterfaceAreaOverwritesSupport || interface_preference == InterfacePreference::SupportAreaOverwritesInterface
// Perimeter generator parameters
||
(settings.get<bool>("fill_outline_gaps") == other.settings.get<bool>("fill_outline_gaps") &&
settings.get<coord_t>("min_bead_width") == other.settings.get<coord_t>("min_bead_width") &&
settings.get<double>("wall_transition_angle") == other.settings.get<double>("wall_transition_angle") &&
settings.get<coord_t>("wall_transition_length") == other.settings.get<coord_t>("wall_transition_length") &&
settings.get<Ratio>("wall_split_middle_threshold") == other.settings.get<Ratio>("wall_split_middle_threshold") &&
settings.get<Ratio>("wall_add_middle_threshold") == other.settings.get<Ratio>("wall_add_middle_threshold") &&
settings.get<int>("wall_distribution_count") == other.settings.get<int>("wall_distribution_count") &&
settings.get<coord_t>("wall_transition_filter_distance") == other.settings.get<coord_t>("wall_transition_filter_distance") &&
settings.get<coord_t>("wall_transition_filter_deviation") == other.settings.get<coord_t>("wall_transition_filter_deviation") &&
settings.get<coord_t>("wall_line_width_x") == other.settings.get<coord_t>("wall_line_width_x") &&
settings.get<int>("meshfix_maximum_extrusion_area_deviation") == other.settings.get<int>("meshfix_maximum_extrusion_area_deviation"))
)
#endif
&& raft_layers == other.raft_layers
;
}
/*!
* \brief Get the Radius part will have based on numeric values.
* \param distance_to_top[in] The effective distance_to_top of the element
* \param elephant_foot_increases[in] The elephant_foot_increases of the element.
* \return The radius an element with these attributes would have.
*/
[[nodiscard]] inline coord_t getRadius(size_t distance_to_top, const double elephant_foot_increases = 0) const
{
return (distance_to_top <= tip_layers ? min_radius + (branch_radius - min_radius) * distance_to_top / tip_layers : // tip
branch_radius + // base
(distance_to_top - tip_layers) * branch_radius_increase_per_layer)
+ // gradual increase
elephant_foot_increases * (std::max(bp_radius_increase_per_layer - branch_radius_increase_per_layer, 0.0));
}
/*!
* \brief Get the Radius an element should at least have at a given layer.
* \param layer_idx[in] The layer.
* \return The radius every element should aim to achieve.
*/
[[nodiscard]] inline coord_t recommendedMinRadius(LayerIndex layer_idx) const
{
double num_layers_widened = layer_start_bp_radius - layer_idx;
return num_layers_widened > 0 ? branch_radius + num_layers_widened * bp_radius_increase_per_layer : 0;
}
#if 0
/*!
* \brief Return on which z in microns the layer will be printed. Used only for support infill line generation.
* \param layer_idx[in] The layer.
* \return The radius every element should aim to achieve.
*/
[[nodiscard]] inline coord_t getActualZ(LayerIndex layer_idx)
{
return layer_idx < coord_t(known_z.size()) ? known_z[layer_idx] : (layer_idx - known_z.size()) * layer_height + known_z.size() ? known_z.back() : 0;
}
/*!
* \brief Set the z every Layer is printed at. Required for getActualZ to work
* \param z[in] The z every LayerIndex is printed. Vector is used as a map<LayerIndex,coord_t> with the index of each element being the corresponding LayerIndex
* \return The radius every element should aim to achieve.
*/
void setActualZ(std::vector<coord_t>& z)
{
known_z = z;
}
#endif
private:
double angle;
double angle_slow;
// std::vector<coord_t> known_z;
};
static constexpr const bool polygons_strictly_simple = false;
static constexpr const auto tiny_area_threshold = sqr(scaled<double>(0.001));
void tree_supports_show_error(std::string_view message, bool critical);
inline double layer_z(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const size_t layer_idx)
{
return layer_idx >= config.raft_layers.size() ?
slicing_params.object_print_z_min + slicing_params.first_object_layer_height + (layer_idx - config.raft_layers.size()) * slicing_params.layer_height :
config.raft_layers[layer_idx];
}
// Lowest collision layer
inline LayerIndex layer_idx_ceil(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const double z)
{
return
LayerIndex(config.raft_layers.size()) +
std::max<LayerIndex>(0, ceil((z - slicing_params.object_print_z_min - slicing_params.first_object_layer_height) / slicing_params.layer_height));
}
// Highest collision layer
inline LayerIndex layer_idx_floor(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const double z)
{
return
LayerIndex(config.raft_layers.size()) +
std::max<LayerIndex>(0, floor((z - slicing_params.object_print_z_min - slicing_params.first_object_layer_height) / slicing_params.layer_height));
}
inline SupportGeneratorLayer& layer_initialize(
SupportGeneratorLayer &layer_new,
const SlicingParameters &slicing_params,
const TreeSupportSettings &config,
const size_t layer_idx)
{
layer_new.print_z = layer_z(slicing_params, config, layer_idx);
layer_new.bottom_z = layer_idx > 0 ? layer_z(slicing_params, config, layer_idx - 1) : 0;
layer_new.height = layer_new.print_z - layer_new.bottom_z;
return layer_new;
}
// Using the std::deque as an allocator.
inline SupportGeneratorLayer& layer_allocate_unguarded(
SupportGeneratorLayerStorage &layer_storage,
SupporLayerType layer_type,
const SlicingParameters &slicing_params,
const TreeSupportSettings &config,
size_t layer_idx)
{
SupportGeneratorLayer &layer = layer_storage.allocate_unguarded(layer_type);
return layer_initialize(layer, slicing_params, config, layer_idx);
}
inline SupportGeneratorLayer& layer_allocate(
SupportGeneratorLayerStorage &layer_storage,
SupporLayerType layer_type,
const SlicingParameters &slicing_params,
const TreeSupportSettings &config,
size_t layer_idx)
{
SupportGeneratorLayer &layer = layer_storage.allocate(layer_type);
return layer_initialize(layer, slicing_params, config, layer_idx);
}
// Used by generate_initial_areas() in parallel by multiple layers.
class InterfacePlacer {
public:
InterfacePlacer(
const SlicingParameters &slicing_parameters,
const SupportParameters &support_parameters,
const TreeSupportSettings &config,
SupportGeneratorLayerStorage &layer_storage,
SupportGeneratorLayersPtr &top_contacts,
SupportGeneratorLayersPtr &top_interfaces,
SupportGeneratorLayersPtr &top_base_interfaces)
:
slicing_parameters(slicing_parameters), support_parameters(support_parameters), config(config),
layer_storage(layer_storage), top_contacts(top_contacts), top_interfaces(top_interfaces), top_base_interfaces(top_base_interfaces)
{}
InterfacePlacer(const InterfacePlacer& rhs) :
slicing_parameters(rhs.slicing_parameters), support_parameters(rhs.support_parameters), config(rhs.config),
layer_storage(rhs.layer_storage), top_contacts(rhs.top_contacts), top_interfaces(rhs.top_interfaces), top_base_interfaces(rhs.top_base_interfaces)
{}
const SlicingParameters &slicing_parameters;
const SupportParameters &support_parameters;
const TreeSupportSettings &config;
SupportGeneratorLayersPtr& top_contacts_mutable() { return this->top_contacts; }
public:
// Insert the contact layer and some of the inteface and base interface layers below.
void add_roofs(std::vector<Polygons> &&new_roofs, const size_t insert_layer_idx)
{
if (! new_roofs.empty()) {
std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
for (size_t idx = 0; idx < new_roofs.size(); ++ idx)
if (! new_roofs[idx].empty())
add_roof_unguarded(std::move(new_roofs[idx]), insert_layer_idx - idx, idx);
}
}
void add_roof(Polygons &&new_roof, const size_t insert_layer_idx, const size_t dtt_tip)
{
std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
add_roof_unguarded(std::move(new_roof), insert_layer_idx, dtt_tip);
}
// called by sample_overhang_area()
void add_roof_build_plate(Polygons &&overhang_areas, size_t dtt_roof)
{
std::lock_guard<std::mutex> lock(m_mutex_layer_storage);
this->add_roof_unguarded(std::move(overhang_areas), 0, std::min(dtt_roof, this->support_parameters.num_top_interface_layers));
}
void add_roof_unguarded(Polygons &&new_roofs, const size_t insert_layer_idx, const size_t dtt_roof)
{
assert(support_parameters.has_top_contacts);
assert(dtt_roof <= support_parameters.num_top_interface_layers);
SupportGeneratorLayersPtr &layers =
dtt_roof == 0 ? this->top_contacts :
dtt_roof <= support_parameters.num_top_interface_layers_only() ? this->top_interfaces : this->top_base_interfaces;
SupportGeneratorLayer*& l = layers[insert_layer_idx];
if (l == nullptr)
l = &layer_allocate_unguarded(layer_storage, dtt_roof == 0 ? SupporLayerType::TopContact : SupporLayerType::TopInterface,
slicing_parameters, config, insert_layer_idx);
// will be unioned in finalize_interface_and_support_areas()
append(l->polygons, std::move(new_roofs));
}
private:
// Outputs
SupportGeneratorLayerStorage &layer_storage;
SupportGeneratorLayersPtr &top_contacts;
SupportGeneratorLayersPtr &top_interfaces;
SupportGeneratorLayersPtr &top_base_interfaces;
// Mutexes, guards
std::mutex m_mutex_layer_storage;
};
} // namespace FFFTreeSupport
} // namespace Slic3r
#endif // slic3r_TreeSupportCommon_hpp

View File

@ -2354,13 +2354,23 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
size_t last = path_id;
// check adjacent paths
while (first > 0 && path.sub_paths.front().first.position.isApprox(buffer.paths[first - 1].sub_paths.back().last.position)) {
while (first > 0) {
const Path& ref_path = buffer.paths[first - 1];
if (!path.sub_paths.front().first.position.isApprox(ref_path.sub_paths.back().last.position) ||
path.role != ref_path.role)
break;
path.sub_paths.front().first = ref_path.sub_paths.front().first;
--first;
path.sub_paths.front().first = buffer.paths[first].sub_paths.front().first;
}
while (last < buffer.paths.size() - 1 && path.sub_paths.back().last.position.isApprox(buffer.paths[last + 1].sub_paths.front().first.position)) {
while (last < buffer.paths.size() - 1) {
const Path& ref_path = buffer.paths[last + 1];
if (!path.sub_paths.back().last.position.isApprox(ref_path.sub_paths.front().first.position) ||
path.role != ref_path.role)
break;
path.sub_paths.back().last = ref_path.sub_paths.back().last;
++last;
path.sub_paths.back().last = buffer.paths[last].sub_paths.back().last;
}
const size_t min_s_id = m_layers.get_range_at(min_id).first;
@ -3570,7 +3580,7 @@ void GCodeViewer::render_legend(float& legend_height)
ImGui::PushStyleColor(ImGuiCol_FrameBg, { 0.1f, 0.1f, 0.1f, 0.8f });
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, { 0.2f, 0.2f, 0.2f, 0.8f });
imgui.combo("", { _u8L("Feature type"),
imgui.combo(std::string(), { _u8L("Feature type"),
_u8L("Height (mm)"),
_u8L("Width (mm)"),
_u8L("Speed (mm/s)"),
@ -3951,7 +3961,7 @@ void GCodeViewer::render_legend(float& legend_height)
const auto custom_it = std::find(m_roles.begin(), m_roles.end(), GCodeExtrusionRole::Custom);
if (custom_it != m_roles.end()) {
const bool custom_visible = is_visible(GCodeExtrusionRole::Custom);
const wxString btn_text = custom_visible ? _u8L("Hide Custom GCode") : _u8L("Show Custom GCode");
const wxString btn_text = custom_visible ? _u8L("Hide Custom G-code") : _u8L("Show Custom G-code");
ImGui::Separator();
if (imgui.button(btn_text, ImVec2(-1.0f, 0.0f), true)) {
m_extrusions.role_visibility_flags = custom_visible ? m_extrusions.role_visibility_flags & ~(1 << int(GCodeExtrusionRole::Custom)) :

View File

@ -3645,6 +3645,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
else if (evt.Leaving()) {
_deactivate_undo_redo_toolbar_items();
if (m_layers_editing.state != LayersEditing::Unknown)
m_layers_editing.state = LayersEditing::Paused;
// to remove hover on objects when the mouse goes out of this canvas
m_mouse.position = Vec2d(-1.0, -1.0);
m_dirty = true;
@ -6755,7 +6758,9 @@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt)
m_layers_editing.last_action =
evt->ShiftDown() ? (evt->RightIsDown() ? LAYER_HEIGHT_EDIT_ACTION_SMOOTH : LAYER_HEIGHT_EDIT_ACTION_REDUCE) :
(evt->RightIsDown() ? LAYER_HEIGHT_EDIT_ACTION_INCREASE : LAYER_HEIGHT_EDIT_ACTION_DECREASE);
}
if (m_layers_editing.state != LayersEditing::Paused) {
m_layers_editing.adjust_layer_height_profile();
_refresh_if_shown_on_screen();
}

View File

@ -193,6 +193,7 @@ class GLCanvas3D
Unknown,
Editing,
Completed,
Paused,
Num_States
};

View File

@ -955,7 +955,7 @@ void ObjectList::list_manipulation(const wxPoint& mouse_pos, bool evt_context_me
if (!item) {
if (col == nullptr) {
if (wxOSX && !multiple_selection())
if (wxOSX)
UnselectAll();
else if (!evt_context_menu)
// Case, when last item was deleted and under GTK was called wxEVT_DATAVIEW_SELECTION_CHANGED,
@ -972,12 +972,18 @@ void ObjectList::list_manipulation(const wxPoint& mouse_pos, bool evt_context_me
if (wxOSX && item && col) {
wxDataViewItemArray sels;
GetSelections(sels);
bool is_selection_changed = true;
for (const auto& sel_item : sels)
if (sel_item == item) {
// item is one oth the already selected items, so resection is no needed
is_selection_changed = false;
break;
}
if (is_selection_changed) {
UnselectAll();
if (sels.Count() > 1)
SetSelections(sels);
else
Select(item);
}
}
if (col != nullptr)
{
@ -1795,7 +1801,7 @@ void ObjectList::load_shape_object(const std::string& type_name)
// Create mesh
BoundingBoxf3 bb;
TriangleMesh mesh = create_mesh(type_name, bb);
load_mesh_object(mesh, _u8L("Shape") + "-" + type_name);
load_mesh_object(mesh, _u8L("Shape") + "-" + into_u8(_(type_name)));
if (!m_objects->empty())
m_objects->back()->volumes.front()->source.is_from_builtin_objects = true;
wxGetApp().mainframe->update_title();
@ -2064,7 +2070,7 @@ bool ObjectList::del_from_cut_object(bool is_cut_connector, bool is_model_part/*
InfoDialog dialog(wxGetApp().plater(), title,
_L("This action will break a cut information.\n"
"After that PrusaSlicer can't guarantee model consistency.") + "\n\n" +
_L("To manipulate with solid parts or negative volumes you have to invalidate cut information first." + msg_end ),
_L("To manipulate with solid parts or negative volumes you have to invalidate cut information first.") + msg_end,
false, buttons_style | wxCANCEL_DEFAULT | wxICON_WARNING);
dialog.SetButtonLabel(wxID_YES, _L("Invalidate cut info"));

View File

@ -22,6 +22,7 @@ namespace GUI {
static const ColorRGBA GRABBER_COLOR = ColorRGBA::YELLOW();
static const ColorRGBA UPPER_PART_COLOR = ColorRGBA::CYAN();
static const ColorRGBA LOWER_PART_COLOR = ColorRGBA::MAGENTA();
static const ColorRGBA MODIFIER_COLOR = ColorRGBA(0.75f, 0.75f, 0.75f, 0.5f);
// connector colors
static const ColorRGBA PLAG_COLOR = ColorRGBA::YELLOW();
@ -179,8 +180,8 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename,
: GLGizmoBase(parent, icon_filename, sprite_id)
, m_connectors_group_id (GrabberID::Count)
, m_connector_type (CutConnectorType::Plug)
, m_connector_style (size_t(CutConnectorStyle::Prism))
, m_connector_shape_id (size_t(CutConnectorShape::Circle))
, m_connector_style (int(CutConnectorStyle::Prism))
, m_connector_shape_id (int(CutConnectorShape::Circle))
{
// m_modes = { _u8L("Planar"), _u8L("Grid")
// , _u8L("Radial"), _u8L("Modular")
@ -465,41 +466,10 @@ void GLGizmoCut3D::set_center(const Vec3d& center, bool update_tbb /*=false*/)
update_clipper();
}
bool GLGizmoCut3D::render_combo(const std::string& label, const std::vector<std::string>& lines, size_t& selection_idx)
bool GLGizmoCut3D::render_combo(const std::string& label, const std::vector<std::string>& lines, int& selection_idx)
{
ImGui::AlignTextToFramePadding();
m_imgui->text(label);
ImGui::SameLine(m_label_width);
ImGui::PushItemWidth(m_control_width);
size_t selection_out = selection_idx;
// It is necessary to use BeginGroup(). Otherwise, when using SameLine() is called, then other items will be drawn inside the combobox.
ImGui::BeginGroup();
ImVec2 combo_pos = ImGui::GetCursorScreenPos();
if (ImGui::BeginCombo(("##"+label).c_str(), "")) {
for (size_t line_idx = 0; line_idx < lines.size(); ++line_idx) {
ImGui::PushID(int(line_idx));
if (ImGui::Selectable("", line_idx == selection_idx))
selection_out = line_idx;
ImGui::SameLine();
ImGui::Text("%s", lines[line_idx].c_str());
ImGui::PopID();
}
ImGui::EndCombo();
}
ImVec2 backup_pos = ImGui::GetCursorScreenPos();
ImGuiStyle& style = ImGui::GetStyle();
ImGui::SetCursorScreenPos(ImVec2(combo_pos.x + style.FramePadding.x, combo_pos.y + style.FramePadding.y));
ImGui::Text("%s", selection_out < lines.size() ? lines[selection_out].c_str() : UndefLabel.c_str());
ImGui::SetCursorScreenPos(backup_pos);
ImGui::EndGroup();
bool is_changed = selection_idx != selection_out;
selection_idx = selection_out;
const bool is_changed = m_imgui->combo(label, lines, selection_idx, 0, m_label_width, m_control_width);
if (is_changed)
update_connector_shape();
@ -1436,7 +1406,7 @@ GLGizmoCut3D::PartSelection::PartSelection(const ModelObject* mo, const Transfor
m_parts.clear();
for (const ModelVolume* volume : volumes) {
assert(volume != nullptr);
m_parts.emplace_back(Part{GLModel(), MeshRaycaster(volume->mesh()), true});
m_parts.emplace_back(Part{GLModel(), MeshRaycaster(volume->mesh()), true, !volume->is_model_part()});
m_parts.back().glmodel.set_color({ 0.f, 0.f, 1.f, 1.f });
m_parts.back().glmodel.init_from(volume->mesh());
@ -1515,13 +1485,19 @@ void GLGizmoCut3D::PartSelection::render(const Vec3d* normal, GLModel& sphere_mo
const bool is_looking_forward = normal && camera.get_dir_forward().dot(*normal) < 0.05;
for (size_t id=0; id<m_parts.size(); ++id) {
if (normal && (( is_looking_forward && m_parts[id].selected) ||
if (!m_parts[id].is_modifier && normal && ((is_looking_forward && m_parts[id].selected) ||
(!is_looking_forward && !m_parts[id].selected) ) )
continue;
const Vec3d volume_offset = model_object()->volumes[id]->get_offset();
shader->set_uniform("view_model_matrix", view_inst_matrix * translation_transform(volume_offset));
m_parts[id].glmodel.set_color(m_parts[id].selected ? UPPER_PART_COLOR : LOWER_PART_COLOR);
if (m_parts[id].is_modifier) {
glsafe(::glEnable(GL_BLEND));
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
}
m_parts[id].glmodel.set_color(m_parts[id].is_modifier ? MODIFIER_COLOR : (m_parts[id].selected ? UPPER_PART_COLOR : LOWER_PART_COLOR));
m_parts[id].glmodel.render();
if (m_parts[id].is_modifier)
glsafe(::glDisable(GL_BLEND));
}
shader->stop_using();
@ -1582,7 +1558,7 @@ bool GLGizmoCut3D::PartSelection::is_one_object() const
if (m_parts.size() < 2)
return true;
return std::all_of(m_parts.begin(), m_parts.end(), [this](const Part& part) {
return part.selected == m_parts.front().selected;
return part.is_modifier || part.selected == m_parts.front().selected;
});
}
@ -1803,7 +1779,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors)
m_imgui->disabled_begin(m_connector_type == CutConnectorType::Dowel);
if (type_changed && m_connector_type == CutConnectorType::Dowel) {
m_connector_style = size_t(CutConnectorStyle::Prism);
m_connector_style = int(CutConnectorStyle::Prism);
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); });
}
if (render_combo(m_labels_map["Style"], m_connector_styles, m_connector_style))
@ -1813,7 +1789,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors)
if (render_combo(m_labels_map["Shape"], m_connector_shapes, m_connector_shape_id))
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); });
if (render_slider_double_input(m_labels_map["Depth ratio"], m_connector_depth_ratio, m_connector_depth_ratio_tolerance))
if (render_slider_double_input(m_labels_map["Depth"], m_connector_depth_ratio, m_connector_depth_ratio_tolerance))
apply_selected_connectors([this, &connectors](size_t idx) {
if (m_connector_depth_ratio > 0)
connectors[idx].height = m_connector_depth_ratio;
@ -2136,10 +2112,10 @@ void GLGizmoCut3D::validate_connector_settings()
if (m_connector_type == CutConnectorType::Undef)
m_connector_type = CutConnectorType::Plug;
if (m_connector_style == size_t(CutConnectorStyle::Undef))
m_connector_style = size_t(CutConnectorStyle::Prism);
if (m_connector_shape_id == size_t(CutConnectorShape::Undef))
m_connector_shape_id = size_t(CutConnectorShape::Circle);
if (m_connector_style == int(CutConnectorStyle::Undef))
m_connector_style = int(CutConnectorStyle::Prism);
if (m_connector_shape_id == int(CutConnectorShape::Undef))
m_connector_shape_id = int(CutConnectorShape::Circle);
}
void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors)
@ -2197,8 +2173,8 @@ void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors)
m_connector_size = 2.f * radius;
m_connector_size_tolerance = 2.f * radius_tolerance;
m_connector_type = type;
m_connector_style = size_t(style);
m_connector_shape_id = size_t(shape);
m_connector_style = int(style);
m_connector_shape_id = int(shape);
}
if (m_label_width == 0.f) {
@ -2566,29 +2542,66 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
};
const size_t cut_parts_cnt = m_part_selection.parts().size();
bool has_modifiers = false;
// Distribute SolidParts to the Upper/Lower object
for (size_t id = 0; id < cut_parts_cnt; ++id) {
if (ModelObject* obj = (m_part_selection.parts()[id].selected ? upper : lower))
if (m_part_selection.parts()[id].is_modifier)
has_modifiers = true; // modifiers will be added later to the related parts
else if (ModelObject* obj = (m_part_selection.parts()[id].selected ? upper : lower))
obj->add_volume(*(cut_mo->volumes[id]));
}
if (has_modifiers) {
// Distribute Modifiers to the Upper/Lower object
auto upper_bb = upper ? upper->instance_bounding_box(instance_idx) : BoundingBoxf3();
auto lower_bb = lower ? lower->instance_bounding_box(instance_idx) : BoundingBoxf3();
const Transform3d inst_matrix = cut_mo->instances[instance_idx]->get_transformation().get_matrix();
for (size_t id = 0; id < cut_parts_cnt; ++id)
if (m_part_selection.parts()[id].is_modifier) {
ModelVolume* vol = cut_mo->volumes[id];
auto bb = vol->mesh().transformed_bounding_box(inst_matrix * vol->get_matrix());
// Don't add modifiers which are not intersecting with solid parts
if (upper_bb.intersects(bb))
upper->add_volume(*vol);
if (lower_bb.intersects(bb))
lower->add_volume(*vol);
}
}
ModelVolumePtrs& volumes = cut_mo->volumes;
if (volumes.size() == cut_parts_cnt)
if (volumes.size() == cut_parts_cnt) {
// Means that object is cut without connectors
// Just add Upper and Lower objects to cut_object_ptrs and invalidate any cut information
add_cut_objects(cut_object_ptrs, upper, lower);
}
else if (volumes.size() > cut_parts_cnt) {
// Means that object is cut with connectors
// All volumes are distributed to Upper / Lower object,
// So we dont need them anymore
for (size_t id = 0; id < cut_parts_cnt; id++)
delete *(volumes.begin() + id);
volumes.erase(volumes.begin(), volumes.begin() + cut_parts_cnt);
// Perform cut just to get connectors
const ModelObjectPtrs cut_connectors_obj = cut_mo->cut(instance_idx, get_cut_matrix(selection), attributes);
assert(create_dowels_as_separate_object ? cut_connectors_obj.size() >= 3 : cut_connectors_obj.size() == 2);
// Connectors from upper object
for (const ModelVolume* volume : cut_connectors_obj[0]->volumes)
upper->add_volume(*volume, volume->type());
// Connectors from lower object
for (const ModelVolume* volume : cut_connectors_obj[1]->volumes)
lower->add_volume(*volume, volume->type());
// Add Upper and Lower objects to cut_object_ptrs with saved cut information
add_cut_objects(cut_object_ptrs, upper, lower, false);
// Add Dowel-connectors as separate objects to cut_object_ptrs
if (cut_connectors_obj.size() >= 3)
for (size_t id = 2; id < cut_connectors_obj.size(); id++)
cut_object_ptrs.push_back(cut_connectors_obj[id]);
@ -2598,8 +2611,9 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
{
for (ModelObject* mo : cut_object_ptrs) {
TriangleMesh mesh;
// Merge all SolidPart but not Connectors
for (const ModelVolume* mv : mo->volumes) {
if (mv->is_model_part()) {
if (mv->is_model_part() && !mv->is_cut_connector()) {
TriangleMesh m = mv->mesh();
m.transform(mv->get_matrix());
mesh.merge(m);
@ -2607,13 +2621,17 @@ void GLGizmoCut3D::perform_cut(const Selection& selection)
}
if (! mesh.empty()) {
ModelVolume* new_volume = mo->add_volume(mesh);
for (int i=int(mo->volumes.size())-2; i>=0; --i)
if (mo->volumes[i]->type() == ModelVolumeType::MODEL_PART)
new_volume->name = mo->name;
// Delete all merged SolidPart but not Connectors
for (int i=int(mo->volumes.size())-2; i>=0; --i) {
const ModelVolume* mv = mo->volumes[i];
if (mv->is_model_part() && !mv->is_cut_connector())
mo->delete_volume(i);
}
}
}
}
}
else
cut_object_ptrs = mo->cut(instance_idx, cut_matrix, attributes);

View File

@ -149,6 +149,7 @@ class GLGizmoCut3D : public GLGizmoBase
GLModel glmodel;
MeshRaycaster raycaster;
bool selected;
bool is_modifier;
};
void render(const Vec3d* normal, GLModel& sphere_model);
@ -199,10 +200,10 @@ class GLGizmoCut3D : public GLGizmoBase
CutConnectorType m_connector_type;
std::vector<std::string> m_connector_styles;
size_t m_connector_style;
int m_connector_style;
std::vector<std::string> m_connector_shapes;
size_t m_connector_shape_id;
int m_connector_shape_id;
std::vector<std::string> m_axis_names;
@ -301,7 +302,7 @@ protected:
private:
void set_center(const Vec3d& center, bool update_tbb = false);
bool render_combo(const std::string& label, const std::vector<std::string>& lines, size_t& selection_idx);
bool render_combo(const std::string& label, const std::vector<std::string>& lines, int& selection_idx);
bool render_double_input(const std::string& label, double& value_in);
bool render_slider_double_input(const std::string& label, float& value_in, float& tolerance_in);
void render_move_center_input(int axis);

View File

@ -71,7 +71,7 @@ static std::vector<std::string> get_extruders_names()
std::vector<std::string> extruders_out;
extruders_out.reserve(extruders_count);
for (size_t extruder_idx = 1; extruder_idx <= extruders_count; ++extruder_idx)
extruders_out.emplace_back("Extruder " + std::to_string(extruder_idx));
extruders_out.emplace_back(_u8L("Extruder") + " " + std::to_string(extruder_idx));
return extruders_out;
}
@ -334,7 +334,8 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
const ColorRGBA& select_first_color = m_modified_extruders_colors[m_first_selected_extruder_idx];
ImVec4 first_color = ImGuiWrapper::to_ImVec4(select_first_color);
if (ImGui::ColorEdit4("First color##color_picker", (float*)&first_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel))
const std::string first_label = into_u8(m_desc.at("first_color")) + "##color_picker";
if (ImGui::ColorEdit4(first_label.c_str(), (float*)&first_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel))
m_modified_extruders_colors[m_first_selected_extruder_idx] = ImGuiWrapper::from_ImVec4(first_color);
ImGui::AlignTextToFramePadding();
@ -346,7 +347,8 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott
const ColorRGBA& select_second_color = m_modified_extruders_colors[m_second_selected_extruder_idx];
ImVec4 second_color = ImGuiWrapper::to_ImVec4(select_second_color);
if (ImGui::ColorEdit4("Second color##color_picker", (float*)&second_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel))
const std::string second_label = into_u8(m_desc.at("second_color")) + "##color_picker";
if (ImGui::ColorEdit4(second_label.c_str(), (float*)&second_color, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel))
m_modified_extruders_colors[m_second_selected_extruder_idx] = ImGuiWrapper::from_ImVec4(second_color);
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;

View File

@ -67,23 +67,10 @@ bool GLGizmoMove3D::on_is_activable() const
void GLGizmoMove3D::on_start_dragging()
{
assert(m_hover_id != -1);
m_displacement = Vec3d::Zero();
const Selection& selection = m_parent.get_selection();
const ECoordinatesType coordinates_type = wxGetApp().obj_manipul()->get_coordinates_type();
if (coordinates_type == ECoordinatesType::World)
m_starting_drag_position = m_center + m_grabbers[m_hover_id].center;
else if (coordinates_type == ECoordinatesType::Local && selection.is_single_volume_or_modifier()) {
const GLVolume& v = *selection.get_first_volume();
m_starting_drag_position = m_center + v.get_instance_transformation().get_rotation_matrix() * v.get_volume_transformation().get_rotation_matrix() * m_grabbers[m_hover_id].center;
}
else {
const GLVolume& v = *selection.get_first_volume();
m_starting_drag_position = m_center + v.get_instance_transformation().get_rotation_matrix() * m_grabbers[m_hover_id].center;
}
m_starting_drag_position = m_grabbers[m_hover_id].matrix * m_grabbers[m_hover_id].center;
m_starting_box_center = m_center;
m_starting_box_bottom_center = m_center;
m_starting_box_bottom_center.z() = m_bounding_box.min.z();
m_starting_box_bottom_center = Vec3d(m_center.x(), m_center.y(), m_bounding_box.min.z());
}
void GLGizmoMove3D::on_stop_dragging()
@ -122,7 +109,8 @@ void GLGizmoMove3D::on_render()
const auto& [box, box_trafo] = selection.get_bounding_box_in_current_reference_system();
m_bounding_box = box;
m_center = box_trafo.translation();
const Transform3d base_matrix = local_transform(m_parent.get_selection());
const Transform3d base_matrix = box_trafo;
for (int i = 0; i < 3; ++i) {
m_grabbers[i].matrix = base_matrix;
}
@ -230,10 +218,12 @@ void GLGizmoMove3D::on_render()
if (shader != nullptr) {
shader->start_using();
shader->set_uniform("emission_factor", 0.1f);
glsafe(::glDisable(GL_CULL_FACE));
// draw grabber
const Vec3d box_size = m_bounding_box.size();
const float mean_size = (float)((box_size.x() + box_size.y() + box_size.z()) / 3.0);
m_grabbers[m_hover_id].render(true, mean_size);
glsafe(::glEnable(GL_CULL_FACE));
shader->stop_using();
}
}

View File

@ -774,27 +774,33 @@ bool ImGuiWrapper::image_button(const wchar_t icon, const wxString& tooltip)
return res;
}
bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags)
bool ImGuiWrapper::combo(const wxString& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags/* = 0*/, float label_width/* = 0.0f*/, float item_width/* = 0.0f*/)
{
return combo(into_u8(label), options, selection, flags, label_width, item_width);
}
bool ImGuiWrapper::combo(const std::string& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags/* = 0*/, float label_width/* = 0.0f*/, float item_width/* = 0.0f*/)
{
// this is to force the label to the left of the widget:
if (!label.empty()) {
text(label);
ImGui::SameLine();
ImGui::SameLine(label_width);
}
ImGui::PushItemWidth(item_width);
int selection_out = selection;
bool res = false;
const char *selection_str = selection < int(options.size()) && selection >= 0 ? options[selection].c_str() : "";
if (ImGui::BeginCombo("", selection_str, flags)) {
if (ImGui::BeginCombo(("##" + label).c_str(), selection_str, flags)) {
for (int i = 0; i < (int)options.size(); i++) {
if (ImGui::Selectable(options[i].c_str(), i == selection)) {
selection_out = i;
res = true;
}
}
ImGui::EndCombo();
res = true;
}
selection = selection_out;

View File

@ -120,7 +120,8 @@ public:
bool image_button(const wchar_t icon, const wxString& tooltip = L"");
// Use selection = -1 to not mark any option as selected
bool combo(const wxString& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags = 0);
bool combo(const std::string& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags = 0, float label_width = 0.0f, float item_width = 0.0f);
bool combo(const wxString& label, const std::vector<std::string>& options, int& selection, ImGuiComboFlags flags = 0, float label_width = 0.0f, float item_width = 0.0f);
bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected, int& mouse_wheel);
void search_list(const ImVec2& size, bool (*items_getter)(int, const char** label, const char** tooltip), char* search_str,
Search::OptionViewParameters& view_params, int& selected, bool& edited, int& mouse_wheel, bool is_localized);

View File

@ -6067,36 +6067,36 @@ void Plater::remove_selected()
p->view3D->delete_selected();
}
void Plater::increase_instances(size_t num, int obj_idx, std::optional<Selection::ObjectIdxsToInstanceIdxsMap> selection_map)
void Plater::increase_instances(size_t num, int obj_idx, int inst_idx)
{
if (! can_increase_instances()) { return; }
Plater::TakeSnapshot snapshot(this, _L("Increase Instances"));
if (obj_idx < 0)
obj_idx = p->get_selected_object_idx();
if (obj_idx < 0) {
obj_idx = p->get_selected_object_idx();
if (obj_idx < 0) {
// It's a case of increasing per 1 instance, when multiple objects are selected
if (const auto obj_idxs = get_selection().get_object_idxs(); !obj_idxs.empty()) {
// we need a copy made here because the selection changes at every call of increase_instances()
const Selection::ObjectIdxsToInstanceIdxsMap content = selection_map.has_value() ? *selection_map : p->get_selection().get_content();
for (const size_t obj_id : obj_idxs) {
increase_instances(1, int(obj_id), content);
const Selection::ObjectIdxsToInstanceIdxsMap content = p->get_selection().get_content();
for (const size_t& obj_id : obj_idxs) {
if (auto obj_it = content.find(int(obj_id)); obj_it != content.end())
increase_instances(1, int(obj_id), *obj_it->second.rbegin());
}
}
return;
}
}
assert(obj_idx >= 0);
ModelObject* model_object = p->model.objects[obj_idx];
int inst_idx = -1;
if (selection_map.has_value()) {
auto obj_it = selection_map->find(obj_idx);
if (obj_it != selection_map->end() && obj_it->second.size() == 1)
inst_idx = *obj_it->second.begin();
}
else
inst_idx = p->get_selected_instance_idx();
if (inst_idx < 0 && get_selected_object_idx() >= 0) {
inst_idx = get_selection().get_instance_idx();
if (0 > inst_idx || inst_idx >= int(model_object->instances.size()))
inst_idx = -1;
}
ModelInstance* model_instance = (inst_idx >= 0) ? model_object->instances[inst_idx] : model_object->instances.back();
bool was_one_instance = model_object->instances.size()==1;
@ -6108,7 +6108,6 @@ void Plater::increase_instances(size_t num, int obj_idx, std::optional<Selection
Geometry::Transformation trafo = model_instance->get_transformation();
trafo.set_offset(offset_vec);
model_object->add_instance(trafo);
// p->print.get_object(obj_idx)->add_copy(Slic3r::to_2d(offset_vec));
}
if (p->get_config_bool("autocenter"))
@ -6192,11 +6191,16 @@ void Plater::set_number_of_copies()
return;
TakeSnapshot snapshot(this, wxString::Format(_L("Set numbers of copies to %d"), num));
for (const auto obj_idx : obj_idxs) {
// we need a copy made here because the selection changes at every call of increase_instances()
Selection::ObjectIdxsToInstanceIdxsMap content = p->get_selection().get_content();
for (const auto& obj_idx : obj_idxs) {
ModelObject* model_object = p->model.objects[obj_idx];
const int diff = num - (int)model_object->instances.size();
if (diff > 0)
increase_instances(diff, int(obj_idx));
if (diff > 0) {
if (auto obj_it = content.find(int(obj_idx)); obj_it != content.end())
increase_instances(diff, int(obj_idx), *obj_it->second.rbegin());
}
else if (diff < 0)
decrease_instances(-diff, int(obj_idx));
}

View File

@ -255,7 +255,7 @@ public:
void reset_with_confirm();
bool delete_object_from_model(size_t obj_idx);
void remove_selected();
void increase_instances(size_t num = 1, int obj_idx = -1, std::optional<Selection::ObjectIdxsToInstanceIdxsMap> selection_map = std::nullopt);
void increase_instances(size_t num = 1, int obj_idx = -1, int inst_idx = -1);
void decrease_instances(size_t num = 1, int obj_idx = -1);
void set_number_of_copies();
void fill_bed_with_instances();

View File

@ -1598,6 +1598,7 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field)
shader->start_using();
glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDisable(GL_CULL_FACE));
const Transform3d base_matrix = Geometry::translation_transform(get_bounding_box().center());
Transform3d orient_matrix = Transform3d::Identity();
@ -1615,8 +1616,8 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field)
if (!wxGetApp().obj_manipul()->is_world_coordinates()) {
if (wxGetApp().obj_manipul()->is_local_coordinates()) {
const GLVolume* v = (*m_volumes)[*m_list.begin()];
orient_matrix = v->get_instance_transformation().get_rotation_matrix() * v->get_volume_transformation().get_rotation_matrix();
axes_center = (*m_volumes)[*m_list.begin()]->world_matrix().translation();
orient_matrix = get_bounding_box_in_current_reference_system().second;
orient_matrix.translation() = Vec3d::Zero();
}
else {
orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_rotation_matrix();
@ -1650,6 +1651,7 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field)
m_axes.render(Geometry::translation_transform(axes_center) * orient_matrix, 0.25f);
}
glsafe(::glEnable(GL_CULL_FACE));
shader->stop_using();
}

View File

@ -5154,8 +5154,7 @@ void TabSLAPrint::build()
optgroup->append_single_option_line("support_tree_type");
optgroup->append_single_option_line("support_enforcers_only");
build_sla_support_params({{"", "Default"}, {"branching", "Branching"}}, page);
build_sla_support_params({{"", L("Default")}, {"branching", L("Branching")}}, page);
optgroup = page->new_optgroup(L("Automatic generation"));
optgroup->append_single_option_line("support_points_density_relative");