Merge branch 'master' of https://github.com/Prusa-Development/PrusaSlicerPrivate into et_transformations

This commit is contained in:
enricoturri1966 2023-02-27 12:57:39 +01:00
commit 81bd626273
23 changed files with 2373 additions and 2121 deletions

File diff suppressed because it is too large Load Diff

View File

@ -680,6 +680,13 @@ Slic3r::Polygons union_(const Slic3r::ExPolygons &subject)
{ return _clipper(ClipperLib::ctUnion, ClipperUtils::ExPolygonsProvider(subject), ClipperUtils::EmptyPathsProvider(), ApplySafetyOffset::No); }
Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2)
{ return _clipper(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(subject2), ApplySafetyOffset::No); }
Slic3r::Polygons union_(Slic3r::Polygons &&subject, const Slic3r::Polygons &subject2) {
if (subject.empty())
return subject2;
if (subject2.empty())
return std::move(subject);
return union_(subject, subject2);
}
Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::ExPolygon &subject2)
{ return _clipper(ClipperLib::ctUnion, ClipperUtils::PolygonsProvider(subject), ClipperUtils::ExPolygonProvider(subject2), ApplySafetyOffset::No); }

View File

@ -1241,7 +1241,7 @@ indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes conn
break;
}
if (connector_attributes.style == CutConnectorStyle::Prizm)
if (connector_attributes.style == CutConnectorStyle::Prism)
connector_mesh = its_make_cylinder(1.0, 1.0, (2 * PI / sectorCount));
else if (connector_attributes.type == CutConnectorType::Plug)
connector_mesh = its_make_cone(1.0, 1.0, (2 * PI / sectorCount));
@ -1347,12 +1347,7 @@ void ModelVolume::apply_tolerance()
return;
Vec3d sf = get_scaling_factor();
/*
// correct Z offset in respect to the new size
Vec3d pos = vol->get_offset();
pos[Z] += sf[Z] * 0.5 * vol->cut_info.height_tolerance;
vol->set_offset(pos);
*/
// make a "hole" wider
sf[X] += double(cut_info.radius_tolerance);
sf[Y] += double(cut_info.radius_tolerance);
@ -1361,9 +1356,39 @@ void ModelVolume::apply_tolerance()
sf[Z] += double(cut_info.height_tolerance);
set_scaling_factor(sf);
// correct offset in respect to the new depth
Vec3d rot_norm = Geometry::rotation_transform(get_rotation()) * Vec3d::UnitZ();
if (rot_norm.norm() != 0.0)
rot_norm.normalize();
double z_offset = 0.5 * static_cast<double>(cut_info.height_tolerance);
if (cut_info.connector_type == CutConnectorType::Plug)
z_offset -= 0.05; // add small Z offset to better preview
set_offset(get_offset() + rot_norm * z_offset);
}
void ModelObject::process_connector_cut(ModelVolume* volume, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix, const std::string& suffix = {}, ModelVolumeType type = ModelVolumeType::MODEL_PART)
{
if (mesh.empty())
return;
mesh.transform(cut_matrix);
ModelVolume* vol = object->add_volume(mesh);
vol->set_type(type);
vol->name = src_volume->name + suffix;
// Don't copy the config's ID.
vol->config.assign_config(src_volume->config);
assert(vol->config.id().valid());
assert(vol->config.id() != src_volume->config.id());
vol->set_material(src_volume->material_id(), *src_volume->material());
vol->cut_info = src_volume->cut_info;
}
void ModelObject::process_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
std::vector<ModelObject*>& dowels, Vec3d& local_dowels_displace)
{
assert(volume->cut_info.is_connector);
@ -1373,39 +1398,53 @@ void ModelObject::process_connector_cut(ModelVolume* volume, ModelObjectCutAttri
// ! Don't apply instance transformation for the conntectors.
// This transformation is already there
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
ModelVolume* vol = upper->add_volume(*volume);
vol->set_transformation(volume_matrix);
vol->apply_tolerance();
}
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
ModelVolume* vol = lower->add_volume(*volume);
vol->set_transformation(volume_matrix);
if (volume->cut_info.connector_type == CutConnectorType::Dowel)
if (volume->cut_info.connector_type != CutConnectorType::Dowel) {
if (attributes.has(ModelObjectCutAttribute::KeepUpper)) {
ModelVolume* vol = upper->add_volume(*volume);
vol->set_transformation(volume_matrix);
vol->apply_tolerance();
else
}
if (attributes.has(ModelObjectCutAttribute::KeepLower)) {
ModelVolume* vol = lower->add_volume(*volume);
vol->set_transformation(volume_matrix);
// for lower part change type of connector from NEGATIVE_VOLUME to MODEL_PART if this connector is a plug
vol->set_type(ModelVolumeType::MODEL_PART);
}
}
if (volume->cut_info.connector_type == CutConnectorType::Dowel &&
attributes.has(ModelObjectCutAttribute::CreateDowels)) {
ModelObject* dowel{ nullptr };
// Clone the object to duplicate instances, materials etc.
clone_for_cut(&dowel);
else {
if (attributes.has(ModelObjectCutAttribute::CreateDowels)) {
ModelObject* dowel{ nullptr };
// Clone the object to duplicate instances, materials etc.
clone_for_cut(&dowel);
// add one more solid part same as connector if this connector is a dowel
ModelVolume* vol = dowel->add_volume(*volume);
vol->set_type(ModelVolumeType::MODEL_PART);
// add one more solid part same as connector if this connector is a dowel
ModelVolume* vol = dowel->add_volume(*volume);
vol->set_type(ModelVolumeType::MODEL_PART);
// But discard rotation and Z-offset for this volume
vol->set_rotation(Vec3d::Zero());
vol->set_offset(Z, 0.0);
// But discard rotation and Z-offset for this volume
vol->set_rotation(Vec3d::Zero());
vol->set_offset(Z, 0.0);
// Compute the displacement (in instance coordinates) to be applied to place the dowels
local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0));
// Compute the displacement (in instance coordinates) to be applied to place the dowels
local_dowels_displace = lower->full_raw_mesh_bounding_box().size().cwiseProduct(Vec3d(1.0, 1.0, 0.0));
dowels.push_back(dowel);
dowels.push_back(dowel);
}
// Cut the dowel
volume->apply_tolerance();
// Perform cut
TriangleMesh upper_mesh, lower_mesh;
process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh);
// add small Z offset to better preview
upper_mesh.translate((-0.05 * Vec3d::UnitZ()).cast<float>());
lower_mesh.translate((0.05 * Vec3d::UnitZ()).cast<float>());
// Add cut parts to the related objects
add_cut_volume(upper_mesh, upper, volume, cut_matrix, "_A", volume->type());
add_cut_volume(lower_mesh, lower, volume, cut_matrix, "_B", volume->type());
}
}
@ -1427,25 +1466,8 @@ void ModelObject::process_modifier_cut(ModelVolume* volume, const Transform3d& i
lower->add_volume(*volume);
}
static void add_cut_volume(TriangleMesh& mesh, ModelObject* object, const ModelVolume* src_volume, const Transform3d& cut_matrix, const std::string& suffix = {})
{
if (mesh.empty())
return;
mesh.transform(cut_matrix);
ModelVolume* vol = object->add_volume(mesh);
vol->name = src_volume->name + suffix;
// Don't copy the config's ID.
vol->config.assign_config(src_volume->config);
assert(vol->config.id().valid());
assert(vol->config.id() != src_volume->config.id());
vol->set_material(src_volume->material_id(), *src_volume->material());
vol->cut_info = src_volume->cut_info;
}
void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace)
void ModelObject::process_volume_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, TriangleMesh& upper_mesh, TriangleMesh& lower_mesh)
{
const auto volume_matrix = volume->get_matrix();
@ -1459,23 +1481,20 @@ void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d&
TriangleMesh mesh(volume->mesh());
mesh.transform(invert_cut_matrix * instance_matrix * volume_matrix, true);
volume->reset_mesh();
// Reset volume transformation except for offset
const Vec3d offset = volume->get_offset();
volume->set_transformation(Geometry::Transformation());
volume->set_offset(offset);
indexed_triangle_set upper_its, lower_its;
cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its);
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
upper_mesh = TriangleMesh(upper_its);
if (attributes.has(ModelObjectCutAttribute::KeepLower))
lower_mesh = TriangleMesh(lower_its);
}
void ModelObject::process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace)
{
// Perform cut
TriangleMesh upper_mesh, lower_mesh;
{
indexed_triangle_set upper_its, lower_its;
cut_mesh(mesh.its, 0.0f, &upper_its, &lower_its);
if (attributes.has(ModelObjectCutAttribute::KeepUpper))
upper_mesh = TriangleMesh(upper_its);
if (attributes.has(ModelObjectCutAttribute::KeepLower))
lower_mesh = TriangleMesh(lower_its);
}
process_volume_cut(volume, instance_matrix, cut_matrix, attributes, upper_mesh, lower_mesh);
// Add required cut parts to the objects
@ -1606,7 +1625,7 @@ ModelObjectPtrs ModelObject::cut(size_t instance, const Transform3d& cut_matrix,
if (volume->cut_info.is_processed)
process_modifier_cut(volume, instance_matrix, inverse_cut_matrix, attributes, upper, lower);
else
process_connector_cut(volume, attributes, upper, lower, dowels, local_dowels_displace);
process_connector_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, dowels, local_dowels_displace);
}
else if (!volume->mesh().empty())
process_solid_part_cut(volume, instance_matrix, cut_matrix, attributes, upper, lower, local_displace);

View File

@ -228,7 +228,7 @@ enum class CutConnectorType : int {
};
enum class CutConnectorStyle : int {
Prizm
Prism
, Frustum
, Undef
//,Claw
@ -246,7 +246,7 @@ enum class CutConnectorShape : int {
struct CutConnectorAttributes
{
CutConnectorType type{ CutConnectorType::Plug };
CutConnectorStyle style{ CutConnectorStyle::Prizm };
CutConnectorStyle style{ CutConnectorStyle::Prism };
CutConnectorShape shape{ CutConnectorShape::Circle };
CutConnectorAttributes() {}
@ -459,10 +459,13 @@ public:
void synchronize_model_after_cut();
void apply_cut_attributes(ModelObjectCutAttributes attributes);
void clone_for_cut(ModelObject **obj);
void process_connector_cut(ModelVolume* volume, ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
void process_connector_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower,
std::vector<ModelObject*>& dowels, Vec3d& local_dowels_displace);
void process_modifier_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& inverse_cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower);
void process_volume_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, TriangleMesh& upper_mesh, TriangleMesh& lower_mesh);
void process_solid_part_cut(ModelVolume* volume, const Transform3d& instance_matrix, const Transform3d& cut_matrix,
ModelObjectCutAttributes attributes, ModelObject* upper, ModelObject* lower, Vec3d& local_displace);
ModelObjectPtrs cut(size_t instance, const Transform3d&cut_matrix, ModelObjectCutAttributes attributes);

View File

@ -2861,8 +2861,9 @@ void PrintConfigDef::init_fff_params()
def->label = L("Branch Density");
def->category = L("Support material");
def->tooltip = L("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.");
"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.");
def->sidetext = L("%");
def->min = 5;
def->max_literal = 35;
@ -3654,7 +3655,7 @@ void PrintConfigDef::init_sla_params()
def = this->add_nullable("idle_temperature", coInts);
def->label = L("Idle temperature");
def->tooltip = L("Nozzle temperature when the tool is currently not used in multi-tool setups."
"This is only used when 'Ooze prevention is active in Print Settings.'");
"This is only used when 'Ooze prevention' is active in Print Settings.");
def->sidetext = L("°C");
def->min = 0;
def->max = max_temp;

View File

@ -2226,9 +2226,11 @@ void PrintObject::combine_infill()
void PrintObject::_generate_support_material()
{
if (m_config.support_material_style == smsTree || m_config.support_material_style == smsOrganic) {
if (this->has_support() && (m_config.support_material_style == smsTree || m_config.support_material_style == smsOrganic)) {
fff_tree_support_generate(*this, std::function<void()>([this](){ this->throw_if_canceled(); }));
} else {
// If support style is set to Organic however only raft will be built but no support,
// build snug raft instead.
PrintObjectSupportMaterial support_material(this, m_slicing_params);
support_material.generate(*this);
}

View File

@ -799,10 +799,6 @@ public:
)
{
switch (m_style) {
case smsTree:
case smsOrganic:
assert(false);
[[fallthrough]];
case smsGrid:
{
#ifdef SUPPORT_USE_AGG_RASTERIZER
@ -893,6 +889,10 @@ public:
polygons_rotate(out, m_support_angle);
return out;
}
case smsTree:
case smsOrganic:
// assert(false);
[[fallthrough]];
case smsSnug:
// Merge the support polygons by applying morphological closing and inwards smoothing.
auto closing_distance = scaled<float>(m_support_material_closing_radius);
@ -1763,7 +1763,7 @@ static inline void fill_contact_layer(
#endif // SLIC3R_DEBUG
));
// 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra.
bool reduce_interfaces = object_config.support_material_style.value != smsSnug && layer_id > 0 && !slicing_params.soluble_interface;
bool reduce_interfaces = object_config.support_material_style.value == smsGrid && layer_id > 0 && !slicing_params.soluble_interface;
if (reduce_interfaces) {
// Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions.
Polygons dense_interface_polygons = diff(overhang_polygons, lower_layer_polygons_for_dense_interface());
@ -2011,11 +2011,11 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::top_contact_layers(
// Find the bottom contact layers above the top surfaces of this layer.
static inline SupportGeneratorLayer* detect_bottom_contacts(
const SlicingParameters &slicing_params,
const SupportParameters &support_params,
const SupportParameters &support_params,
const PrintObject &object,
const Layer &layer,
// Existing top contact layers, to which this newly created bottom contact layer will be snapped to guarantee a minimum layer height.
const SupportGeneratorLayersPtr &top_contacts,
const SupportGeneratorLayersPtr &top_contacts,
// First top contact layer index overlapping with this new bottom interface layer.
size_t contact_idx,
// To allocate a new layer from.
@ -2888,6 +2888,7 @@ SupportGeneratorLayersPtr generate_raft_base(
// If there is brim to be generated, calculate the trimming regions.
Polygons brim;
if (object.has_brim()) {
// The object does not have a raft.
// Calculate the area covered by the brim.
const BrimType brim_type = object.config().brim_type;
const bool brim_outer = brim_type == btOuterOnly || brim_type == btOuterAndInner;
@ -2948,12 +2949,20 @@ SupportGeneratorLayersPtr generate_raft_base(
if (slicing_params.raft_layers() > 1) {
Polygons base;
Polygons columns;
Polygons first_layer;
if (columns_base != nullptr) {
base = columns_base->polygons;
columns = base;
if (! interface_polygons.empty())
// Trim the 1st layer columns with the inflated interface polygons.
columns = diff(columns, interface_polygons);
if (columns_base->print_z > slicing_params.raft_contact_top_z - EPSILON) {
// Classic supports with colums above the raft interface.
base = columns_base->polygons;
columns = base;
if (! interface_polygons.empty())
// Trim the 1st layer columns with the inflated interface polygons.
columns = diff(columns, interface_polygons);
} else {
// Organic supports with raft on print bed.
assert(is_approx(columns_base->print_z, slicing_params.first_print_layer_height));
first_layer = columns_base->polygons;
}
}
if (! interface_polygons.empty()) {
// Merge the untrimmed columns base with the expanded raft interface, to be used for the support base and interface.
@ -2967,7 +2976,8 @@ SupportGeneratorLayersPtr generate_raft_base(
new_layer.print_z = slicing_params.first_print_layer_height;
new_layer.height = slicing_params.first_print_layer_height;
new_layer.bottom_z = 0.;
new_layer.polygons = inflate_factor_1st_layer > 0 ? expand(base, inflate_factor_1st_layer) : base;
first_layer = union_(std::move(first_layer), base);
new_layer.polygons = inflate_factor_1st_layer > 0 ? expand(first_layer, inflate_factor_1st_layer) : first_layer;
}
// Insert the base layers.
for (size_t i = 1; i < slicing_params.base_raft_layers; ++ i) {
@ -3045,7 +3055,7 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> PrintObjectSuppo
m_object_config->support_material_interface_extruder.value > 0 && m_print_config->filament_soluble.get_at(m_object_config->support_material_interface_extruder.value - 1) &&
// Base extruder: Either "print with active extruder" not soluble.
(m_object_config->support_material_extruder.value == 0 || ! m_print_config->filament_soluble.get_at(m_object_config->support_material_extruder.value - 1));
bool snug_supports = m_object_config->support_material_style.value == smsSnug;
bool snug_supports = m_object_config->support_material_style.value != smsGrid;
int num_interface_layers_top = m_object_config->support_material_interface_layers;
int num_interface_layers_bottom = m_object_config->support_material_bottom_interface_layers;
if (num_interface_layers_bottom < 0)
@ -3492,12 +3502,7 @@ static inline void fill_expolygons_with_sheath_generate_paths(
if (polygons.empty())
return;
if (with_sheath) {
if (density == 0) {
tree_supports_generate_paths(dst, polygons, flow);
return;
}
} else {
if (! with_sheath) {
fill_expolygons_generate_paths(dst, closing_ex(polygons, float(SCALED_EPSILON)), filler, density, role, flow);
return;
}
@ -4227,9 +4232,9 @@ void generate_support_toolpaths(
}
// Insert the raft base layers.
size_t n_raft_layers = size_t(std::max(0, int(slicing_params.raft_layers()) - 1));
auto n_raft_layers = std::min<size_t>(support_layers.size(), std::max(0, int(slicing_params.raft_layers()) - 1));
tbb::parallel_for(tbb::blocked_range<size_t>(0, n_raft_layers),
[&support_layers, &raft_layers, &config, &support_params, &slicing_params,
[&support_layers, &raft_layers, &intermediate_layers, &config, &support_params, &slicing_params,
&bbox_object, raft_angle_1st_layer, raft_angle_base, raft_angle_interface, link_max_length_factor]
(const tbb::blocked_range<size_t>& range) {
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id)
@ -4244,16 +4249,24 @@ void generate_support_toolpaths(
filler_interface->set_bounding_box(bbox_object);
filler_support->set_bounding_box(bbox_object);
// Print the tree supports cutting through the raft with the exception of the 1st layer, where a full support layer will be printed below
// both the raft and the trees.
// Trim the raft layers with the tree polygons.
const Polygons &tree_polygons =
support_layer_id > 0 && support_layer_id < intermediate_layers.size() && is_approx(intermediate_layers[support_layer_id]->print_z, support_layer.print_z) ?
intermediate_layers[support_layer_id]->polygons : Polygons();
// Print the support base below the support columns, or the support base for the support columns plus the contacts.
if (support_layer_id > 0) {
const Polygons &to_infill_polygons = (support_layer_id < slicing_params.base_raft_layers) ?
raft_layer.polygons :
//FIXME misusing contact_polygons for support columns.
((raft_layer.contact_polygons == nullptr) ? Polygons() : *raft_layer.contact_polygons);
// Trees may cut through the raft layers down to a print bed.
Flow flow(float(support_params.support_material_flow.width()), float(raft_layer.height), support_params.support_material_flow.nozzle_diameter());
assert(!raft_layer.bridging);
if (! to_infill_polygons.empty()) {
assert(! raft_layer.bridging);
Flow flow(float(support_params.support_material_flow.width()), float(raft_layer.height), support_params.support_material_flow.nozzle_diameter());
Fill * filler = filler_support.get();
Fill *filler = filler_support.get();
filler->angle = raft_angle_base;
filler->spacing = support_params.support_material_flow.spacing();
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.support_density));
@ -4261,13 +4274,15 @@ void generate_support_toolpaths(
// Destination
support_layer.support_fills.entities,
// Regions to fill
to_infill_polygons,
tree_polygons.empty() ? to_infill_polygons : diff(to_infill_polygons, tree_polygons),
// Filler and its parameters
filler, float(support_params.support_density),
// Extrusion parameters
ExtrusionRole::SupportMaterial, flow,
support_params.with_sheath, false);
}
if (! tree_polygons.empty())
tree_supports_generate_paths(support_layer.support_fills.entities, tree_polygons, flow);
}
Fill *filler = filler_interface.get();
@ -4293,7 +4308,7 @@ void generate_support_toolpaths(
// Destination
support_layer.support_fills.entities,
// Regions to fill
raft_layer.polygons,
tree_polygons.empty() ? raft_layer.polygons : diff(raft_layer.polygons, tree_polygons),
// Filler and its parameters
filler, density,
// Extrusion parameters
@ -4356,7 +4371,7 @@ void generate_support_toolpaths(
{
SupportLayer &support_layer = *support_layers[support_layer_id];
LayerCache &layer_cache = layer_caches[support_layer_id];
float interface_angle_delta = config.support_material_style.value == smsSnug || config.support_material_style.value == smsTree || config.support_material_style.value == smsOrganic ?
float interface_angle_delta = config.support_material_style.value != smsGrid ?
(support_layer.interface_id() & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.) :
0;
@ -4491,6 +4506,7 @@ void generate_support_toolpaths(
float density = float(support_params.support_density);
bool sheath = support_params.with_sheath;
bool no_sort = false;
bool done = false;
if (base_layer.layer->bottom_z < EPSILON) {
// Base flange (the 1st layer).
filler = filler_first_layer;
@ -4504,18 +4520,21 @@ void generate_support_toolpaths(
filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density));
sheath = true;
no_sort = true;
} else if (config.support_material_style == SupportMaterialStyle::smsOrganic) {
tree_supports_generate_paths(base_layer.extrusions, base_layer.polygons_to_extrude(), flow);
done = true;
}
fill_expolygons_with_sheath_generate_paths(
// Destination
base_layer.extrusions,
// Regions to fill
base_layer.polygons_to_extrude(),
// Filler and its parameters
filler, density,
// Extrusion parameters
ExtrusionRole::SupportMaterial, flow,
sheath, no_sort);
if (! done)
fill_expolygons_with_sheath_generate_paths(
// Destination
base_layer.extrusions,
// Regions to fill
base_layer.polygons_to_extrude(),
// Filler and its parameters
filler, density,
// Extrusion parameters
ExtrusionRole::SupportMaterial, flow,
sheath, no_sort);
}
// Merge base_interface_layers to base_layers to avoid unneccessary retractions
@ -4708,3 +4727,4 @@ sub clip_with_shape {
*/
} // namespace Slic3r

View File

@ -13,7 +13,7 @@ class PrintObjectConfig;
// Support layer type to be used by SupportGeneratorLayer. This type carries a much more detailed information
// about the support layer type than the final support layers stored in a PrintObject.
enum SupporLayerType {
enum class SupporLayerType {
Unknown = 0,
// Ratft base layer, to be printed with the support material.
RaftBase,

View File

@ -158,14 +158,24 @@ TreeModelVolumes::TreeModelVolumes(
{
m_anti_overhang = print_object.slice_support_blockers();
TreeSupportMeshGroupSettings mesh_settings(print_object);
m_layer_outlines.emplace_back(mesh_settings, std::vector<Polygons>{});
const TreeSupportSettings config{ mesh_settings, print_object.slicing_parameters() };
m_current_min_xy_dist = config.xy_min_distance;
m_current_min_xy_dist_delta = config.xy_distance - m_current_min_xy_dist;
assert(m_current_min_xy_dist_delta >= 0);
m_increase_until_radius = config.increase_radius_until_radius;
m_radius_0 = config.getRadius(0);
m_raft_layers = config.raft_layers;
m_current_outline_idx = 0;
m_layer_outlines.emplace_back(mesh_settings, std::vector<Polygons>{});
std::vector<Polygons> &outlines = m_layer_outlines.front().second;
outlines.assign(print_object.layer_count(), Polygons{});
tbb::parallel_for(tbb::blocked_range<size_t>(0, print_object.layer_count(), std::min<size_t>(1, std::max<size_t>(16, print_object.layer_count() / (8 * tbb::this_task_arena::max_concurrency())))),
size_t num_raft_layers = m_raft_layers.size();
size_t num_layers = print_object.layer_count() + num_raft_layers;
outlines.assign(num_layers, Polygons{});
tbb::parallel_for(tbb::blocked_range<size_t>(num_raft_layers, num_layers, std::min<size_t>(1, std::max<size_t>(16, num_layers / (8 * tbb::this_task_arena::max_concurrency())))),
[&](const tbb::blocked_range<size_t> &range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
outlines[layer_idx] = to_polygons(expolygons_simplify(print_object.get_layer(layer_idx)->lslices, mesh_settings.resolution));
outlines[layer_idx] = to_polygons(expolygons_simplify(print_object.get_layer(layer_idx - num_raft_layers)->lslices, mesh_settings.resolution));
});
}
#endif
@ -177,13 +187,6 @@ TreeModelVolumes::TreeModelVolumes(
m_min_resolution = std::min(m_min_resolution, data_pair.first.resolution);
}
const TreeSupportSettings config{ m_layer_outlines[m_current_outline_idx].first };
m_current_min_xy_dist = config.xy_min_distance;
m_current_min_xy_dist_delta = config.xy_distance - m_current_min_xy_dist;
assert(m_current_min_xy_dist_delta >= 0);
m_increase_until_radius = config.increase_radius_until_radius;
m_radius_0 = config.getRadius(0);
#if 0
for (size_t mesh_idx = 0; mesh_idx < storage.meshes.size(); mesh_idx++) {
SliceMeshStorage mesh = storage.meshes[mesh_idx];
@ -214,7 +217,7 @@ TreeModelVolumes::TreeModelVolumes(
#endif
}
void TreeModelVolumes::precalculate(const coord_t max_layer, std::function<void()> throw_on_cancel)
void TreeModelVolumes::precalculate(const PrintObject& print_object, const coord_t max_layer, std::function<void()> throw_on_cancel)
{
auto t_start = std::chrono::high_resolution_clock::now();
m_precalculated = true;
@ -222,7 +225,7 @@ void TreeModelVolumes::precalculate(const coord_t max_layer, std::function<void(
// Get the config corresponding to one mesh that is in the current group. Which one has to be irrelevant.
// Not the prettiest way to do this, but it ensures some calculations that may be a bit more complex
// like inital layer diameter are only done in once.
TreeSupportSettings config(m_layer_outlines[m_current_outline_idx].first);
TreeSupportSettings config(m_layer_outlines[m_current_outline_idx].first, print_object.slicing_parameters());
{
// calculate which radius each layer in the tip may have.

View File

@ -179,6 +179,10 @@ struct TreeSupportMeshGroupSettings {
// 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
@ -240,7 +244,7 @@ public:
* Knowledge about branch angle is used to only calculate avoidances and collisions that may actually be needed.
* Not calling precalculate() will cause the class to lazily calculate avoidances and collisions as needed, which will be a lot slower on systems with more then one or two cores!
*/
void precalculate(const coord_t max_layer, std::function<void()> throw_on_cancel);
void precalculate(const PrintObject& print_object, const coord_t max_layer, std::function<void()> throw_on_cancel);
/*!
* \brief Provides the areas that have to be avoided by the tree's branches to prevent collision with the model on this layer.
@ -614,6 +618,9 @@ private:
*/
coord_t m_radius_0;
// Z heights of the raft layers (additional layers below the object, last raft layer aligned with the bottom of the first object layer).
std::vector<double> m_raft_layers;
/*!
* \brief Caches for the collision, avoidance and areas on the model where support can be placed safely
* at given radius and layer indices.

View File

@ -59,6 +59,91 @@ namespace Slic3r
namespace FFFTreeSupport
{
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
diameter_angle_scale_factor(sin(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height / branch_radius),
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 * diameter_angle_scale_factor)),
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),
diameter_scale_bp_radius(std::min(sin(0.7) * layer_height / branch_radius, 1.0 / (branch_radius / (support_line_width / 2.0)))), // Either 40? or as much as possible so that 2 lines will overlap by at least 50%, whichever is smaller.
z_distance_top_layers(round_up_divide(mesh_group_settings.support_top_distance, layer_height)),
z_distance_bottom_layers(round_up_divide(mesh_group_settings.support_bottom_distance, layer_height)),
performance_interface_skip_layers(round_up_divide(mesh_group_settings.support_interface_skip_height, 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) / (branch_radius * diameter_scale_bp_radius);
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
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);
}
}
}
}
enum class LineStatus
{
INVALID,
@ -158,7 +243,7 @@ static std::vector<std::pair<TreeSupportSettings, std::vector<size_t>>> group_me
assert(object_config.support_material_style == smsTree || object_config.support_material_style == smsOrganic);
bool found_existing_group = false;
TreeSupportSettings next_settings{ TreeSupportMeshGroupSettings{ print_object } };
TreeSupportSettings next_settings{ TreeSupportMeshGroupSettings{ print_object }, print_object.slicing_parameters() };
//FIXME for now only a single object per group is enabled.
#if 0
for (size_t idx = 0; idx < grouped_meshes.size(); ++ idx)
@ -222,9 +307,12 @@ void tree_supports_show_error(std::string_view message, bool critical)
#endif // TREE_SUPPORT_SHOW_ERRORS_WIN32
}
[[nodiscard]] static const std::vector<Polygons> generate_overhangs(const PrintObject &print_object, std::function<void()> throw_on_cancel)
[[nodiscard]] static const std::vector<Polygons> generate_overhangs(const TreeSupportSettings &settings, const PrintObject &print_object, std::function<void()> throw_on_cancel)
{
std::vector<Polygons> out(print_object.layer_count(), Polygons{});
const size_t num_raft_layers = settings.raft_layers.size();
const size_t num_object_layers = print_object.layer_count();
const size_t num_layers = num_object_layers + num_raft_layers;
std::vector<Polygons> out(num_layers, Polygons{});
const PrintConfig &print_config = print_object.print()->config();
const PrintObjectConfig &config = print_object.config();
@ -241,10 +329,10 @@ void tree_supports_show_error(std::string_view message, bool critical)
//FIXME this is a fudge constant!
auto enforcer_overhang_offset = scaled<double>(config.support_tree_tip_diameter.value);
size_t num_overhang_layers = support_auto ? out.size() : std::max(size_t(support_enforce_layers), enforcers_layers.size());
size_t num_overhang_layers = support_auto ? num_object_layers : std::max(size_t(support_enforce_layers), enforcers_layers.size());
tbb::parallel_for(tbb::blocked_range<LayerIndex>(1, num_overhang_layers),
[&print_object, &config, &print_config, &enforcers_layers, &blockers_layers,
support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, &throw_on_cancel, &out]
support_auto, support_enforce_layers, support_threshold_auto, tan_threshold, enforcer_overhang_offset, num_raft_layers, &throw_on_cancel, &out]
(const tbb::blocked_range<LayerIndex> &range) {
for (LayerIndex layer_id = range.begin(); layer_id < range.end(); ++ layer_id) {
const Layer &current_layer = *print_object.get_layer(layer_id);
@ -314,11 +402,36 @@ void tree_supports_show_error(std::string_view message, bool critical)
//check_self_intersections(overhangs, "generate_overhangs - enforcers");
}
}
out[layer_id] = std::move(overhangs);
out[layer_id + num_raft_layers] = std::move(overhangs);
throw_on_cancel();
}
});
#if 0
if (num_raft_layers > 0) {
const Layer &first_layer = *print_object.get_layer(0);
// Final overhangs.
Polygons overhangs =
// Don't apply blockes on raft layer.
//(! blockers_layers.empty() && ! blockers_layers[layer_id].empty() ?
// diff(first_layer.lslices, blockers_layers[layer_id], ApplySafetyOffset::Yes) :
to_polygons(first_layer.lslices);
#if 0
if (! enforcers_layers.empty() && ! enforcers_layers[layer_id].empty()) {
if (Polygons enforced_overhangs = intersection(first_layer.lslices, enforcers_layers[layer_id] /*, ApplySafetyOffset::Yes */);
! enforced_overhangs.empty()) {
//FIXME this is a hack to make enforcers work on steep overhangs.
//FIXME enforcer_overhang_offset is a fudge constant!
enforced_overhangs = offset(union_ex(enforced_overhangs), enforcer_overhang_offset);
overhangs = overhangs.empty() ? std::move(enforced_overhangs) : union_(overhangs, enforced_overhangs);
}
}
#endif
out[num_raft_layers] = std::move(overhangs);
throw_on_cancel();
}
#endif
return out;
}
@ -333,16 +446,18 @@ void tree_supports_show_error(std::string_view message, bool critical)
// calculate top most layer that is relevant for support
LayerIndex max_layer = 0;
for (size_t object_id : object_ids) {
const PrintObject &print_object = *print.get_object(object_id);
int max_support_layer_id = 0;
for (int layer_id = 1; layer_id < int(print_object.layer_count()); ++ layer_id)
const PrintObject &print_object = *print.get_object(object_id);
const int num_raft_layers = int(config.raft_layers.size());
const int num_layers = int(print_object.layer_count()) + num_raft_layers;
int max_support_layer_id = 0;
for (int layer_id = std::max<int>(num_raft_layers, 1); layer_id < num_layers; ++ layer_id)
if (! overhangs[layer_id].empty())
max_support_layer_id = layer_id;
max_layer = std::max(max_support_layer_id - int(config.z_distance_top_layers), 0);
}
if (max_layer > 0)
// The actual precalculation happens in TreeModelVolumes.
volumes.precalculate(max_layer, throw_on_cancel);
volumes.precalculate(*print.get_object(object_ids.front()), max_layer, throw_on_cancel);
return max_layer;
}
@ -815,29 +930,38 @@ static std::optional<std::pair<Point, size_t>> polyline_sample_next_point_at_dis
return union_(ret);
}
static double layer_z(const SlicingParameters &slicing_params, const size_t layer_idx)
static double layer_z(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const size_t layer_idx)
{
return slicing_params.object_print_z_min + slicing_params.first_object_layer_height + layer_idx * slicing_params.layer_height;
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];
}
static LayerIndex layer_idx_ceil(const SlicingParameters &slicing_params, const double z)
// Lowest collision layer
static LayerIndex layer_idx_ceil(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const double z)
{
return LayerIndex(ceil((z - slicing_params.object_print_z_min - slicing_params.first_object_layer_height) / slicing_params.layer_height));
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));
}
static LayerIndex layer_idx_floor(const SlicingParameters &slicing_params, const double z)
// Highest collision layer
static LayerIndex layer_idx_floor(const SlicingParameters &slicing_params, const TreeSupportSettings &config, const double z)
{
return LayerIndex(floor((z - slicing_params.object_print_z_min - slicing_params.first_object_layer_height) / slicing_params.layer_height));
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));
}
static inline SupportGeneratorLayer& layer_initialize(
SupportGeneratorLayer &layer_new,
const SupporLayerType layer_type,
const SlicingParameters &slicing_params,
const TreeSupportSettings &config,
const size_t layer_idx)
{
layer_new.layer_type = layer_type;
layer_new.print_z = layer_z(slicing_params, layer_idx);
layer_new.height = layer_idx == 0 ? slicing_params.first_object_layer_height : slicing_params.layer_height;
layer_new.bottom_z = layer_idx == 0 ? slicing_params.object_print_z_min : layer_new.print_z - layer_new.height;
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;
}
@ -846,11 +970,12 @@ inline SupportGeneratorLayer& layer_allocate(
std::deque<SupportGeneratorLayer> &layer_storage,
SupporLayerType layer_type,
const SlicingParameters &slicing_params,
const TreeSupportSettings &config,
size_t layer_idx)
{
//FIXME take raft into account.
layer_storage.push_back(SupportGeneratorLayer());
return layer_initialize(layer_storage.back(), layer_type, slicing_params, layer_idx);
return layer_initialize(layer_storage.back(), layer_type, slicing_params, config, layer_idx);
}
inline SupportGeneratorLayer& layer_allocate(
@ -858,11 +983,12 @@ inline SupportGeneratorLayer& layer_allocate(
tbb::spin_mutex& layer_storage_mutex,
SupporLayerType layer_type,
const SlicingParameters &slicing_params,
const TreeSupportSettings &config,
size_t layer_idx)
{
tbb::spin_mutex::scoped_lock lock(layer_storage_mutex);
layer_storage.push_back(SupportGeneratorLayer());
return layer_initialize(layer_storage.back(), layer_type, slicing_params, layer_idx);
return layer_initialize(layer_storage.back(), layer_type, slicing_params, config, layer_idx);
}
using SupportElements = std::deque<SupportElement>;
@ -890,7 +1016,7 @@ static void generate_initial_areas(
static constexpr const auto base_radius = scaled<int>(0.01);
const Polygon base_circle = make_circle(base_radius, SUPPORT_TREE_CIRCLE_RESOLUTION);
TreeSupportMeshGroupSettings mesh_group_settings(print_object);
TreeSupportSettings mesh_config{ mesh_group_settings };
TreeSupportSettings mesh_config{ mesh_group_settings, print_object.slicing_parameters() };
SupportParameters support_params(print_object);
support_params.with_sheath = true;
support_params.support_density = 0;
@ -935,12 +1061,31 @@ static void generate_initial_areas(
std::max<coord_t>(round_up_divide(mesh_config.xy_distance, max_overhang_speed / 2), 2 * mesh_config.z_distance_top_layers) :
0;
//FIXME
size_t num_support_layers = print_object.layer_count();
std::vector<std::unordered_set<Point, PointHash>> already_inserted(num_support_layers - z_distance_delta);
const size_t num_raft_layers = config.raft_layers.size();
const size_t num_support_layers = size_t(std::max(0, int(print_object.layer_count()) + int(num_raft_layers) - int(z_distance_delta)));
const size_t first_support_layer = std::max(int(num_raft_layers) - int(z_distance_delta), 1);
size_t first_tree_layer = 0;
size_t raft_contact_layer_idx = std::numeric_limits<size_t>::max();
if (num_raft_layers > 0 && print_object.layer_count() > 0) {
// Produce raft contact layer outside of the tree support loop, so that no trees will be generated for the raft contact layer.
// Raft layers supporting raft contact interface will be produced by the classic raft generator.
// Find the raft contact layer.
raft_contact_layer_idx = config.raft_layers.size() - 1;
while (raft_contact_layer_idx > 0 && config.raft_layers[raft_contact_layer_idx] > print_object.slicing_parameters().raft_contact_top_z + EPSILON)
-- raft_contact_layer_idx;
// Create the raft contact layer.
SupportGeneratorLayer &raft_contact_layer = layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, raft_contact_layer_idx);
top_contacts[raft_contact_layer_idx] = &raft_contact_layer;
const ExPolygons &lslices = print_object.get_layer(0)->lslices;
double expansion = print_object.config().raft_expansion.value;
raft_contact_layer.polygons = expansion > 0 ? expand(lslices, scaled<float>(expansion)) : to_polygons(lslices);
first_tree_layer = print_object.slicing_parameters().raft_layers() - 1;
}
std::mutex mutex_layer_storage, mutex_movebounds;
tbb::parallel_for(tbb::blocked_range<size_t>(1, num_support_layers - z_distance_delta),
std::vector<std::unordered_set<Point, PointHash>> already_inserted(num_support_layers);
tbb::parallel_for(tbb::blocked_range<size_t>(first_support_layer, num_support_layers),
[&print_object, &volumes, &config, &overhangs, &mesh_config, &mesh_group_settings, &support_params,
z_distance_delta, min_xy_dist, force_tip_to_roof, roof_enabled, support_roof_layers, extra_outset, circle_length_to_half_linewidth_change, connect_length, max_overhang_insert_lag,
&base_circle, &mutex_layer_storage, &mutex_movebounds, &top_contacts, &layer_storage, &already_inserted,
@ -1060,7 +1205,7 @@ static void generate_initial_areas(
std::lock_guard<std::mutex> lock(mutex_layer_storage);
SupportGeneratorLayer *&l = top_contacts[insert_layer_idx - dtt_roof_tip];
if (l == nullptr)
l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), insert_layer_idx - dtt_roof_tip);
l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, insert_layer_idx - dtt_roof_tip);
append(l->polygons, std::move(added_roofs));
}
}
@ -1242,7 +1387,7 @@ static void generate_initial_areas(
if (! added_roofs[idx].empty()) {
SupportGeneratorLayer *&l = top_contacts[layer_idx - idx];
if (l == nullptr)
l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), layer_idx - idx);
l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, layer_idx - idx);
// will be unioned in finalize_interface_and_support_areas()
append(l->polygons, std::move(added_roofs[idx]));
}
@ -1280,7 +1425,7 @@ static void generate_initial_areas(
std::lock_guard<std::mutex> lock(mutex_layer_storage);
SupportGeneratorLayer*& l = top_contacts[0];
if (l == nullptr)
l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), 0);
l = &layer_allocate(layer_storage, SupporLayerType::TopContact, print_object.slicing_parameters(), config, 0);
append(l->polygons, std::move(overhang_outset));
} else // normal trees have to be generated
addLinesAsInfluenceAreas(overhang_lines, force_tip_to_roof ? support_roof_layers - dtt_roof : 0, layer_idx - dtt_roof, dtt_roof > 0, roof_enabled ? support_roof_layers - dtt_roof : 0);
@ -1288,6 +1433,49 @@ static void generate_initial_areas(
}
}
});
// Remove tree tips that start below the raft contact,
// remove interface layers below the raft contact.
for (size_t i = 0; i < first_tree_layer; ++i) {
top_contacts[i] = nullptr;
move_bounds[i].clear();
}
if (raft_contact_layer_idx != std::numeric_limits<coord_t>::max() && print_object.config().raft_expansion.value > 0) {
// If any tips at first_tree_layer now are completely inside the expanded raft layer, remove them as well before they are propagated to the ground.
Polygons &raft_polygons = top_contacts[raft_contact_layer_idx]->polygons;
EdgeGrid::Grid grid(get_extents(raft_polygons).inflated(SCALED_EPSILON));
grid.create(raft_polygons, Polylines{}, coord_t(scale_(10.)));
SupportElements &first_layer_move_bounds = move_bounds[first_tree_layer];
double threshold = scaled<double>(print_object.config().raft_expansion.value) * 2.;
first_layer_move_bounds.erase(std::remove_if(first_layer_move_bounds.begin(), first_layer_move_bounds.end(),
[&grid, threshold](const SupportElement &el) {
coordf_t dist;
if (grid.signed_distance_edges(el.state.result_on_layer, threshold, dist)) {
assert(std::abs(dist) < threshold + SCALED_EPSILON);
// Support point is inside the expanded raft, remove it.
return dist < - 0.;
}
return false;
}), first_layer_move_bounds.end());
#if 0
// Remove the remaining tips from the raft: Closing operation on tip circles.
if (! first_layer_move_bounds.empty()) {
const double eps = 0.1;
// All tips supporting this layer are expected to have the same radius.
double radius = config.getRadius(first_layer_move_bounds.front().state);
// Connect the tips with the following closing radius.
double closing_distance = radius;
Polygon circle = make_circle(radius + closing_distance, eps);
Polygons circles;
circles.reserve(first_layer_move_bounds.size());
for (const SupportElement &el : first_layer_move_bounds) {
circles.emplace_back(circle);
circles.back().translate(el.state.result_on_layer);
}
raft_polygons = diff(raft_polygons, offset(union_(circles), - closing_distance));
}
#endif
}
}
static unsigned int move_inside(const Polygons &polygons, Point &from, int distance = 0, int64_t maxDist2 = std::numeric_limits<int64_t>::max())
@ -3047,7 +3235,7 @@ static void finalize_interface_and_support_areas(
}
if (! floor_layer.empty()) {
if (support_bottom == nullptr)
support_bottom = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::BottomContact, print_object.slicing_parameters(), layer_idx);
support_bottom = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::BottomContact, print_object.slicing_parameters(), config, layer_idx);
support_bottom->polygons = union_(floor_layer, support_bottom->polygons);
base_layer_polygons = diff_clipped(base_layer_polygons, offset(support_bottom->polygons, scaled<float>(0.01), jtMiter, 1.2)); // Subtract the support floor from the normal support.
}
@ -3055,11 +3243,11 @@ static void finalize_interface_and_support_areas(
if (! support_roof_polygons.empty()) {
if (support_roof == nullptr)
support_roof = top_contacts[layer_idx] = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::TopContact, print_object.slicing_parameters(), layer_idx);
support_roof = top_contacts[layer_idx] = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::TopContact, print_object.slicing_parameters(), config, layer_idx);
support_roof->polygons = union_(support_roof_polygons);
}
if (! base_layer_polygons.empty()) {
SupportGeneratorLayer *base_layer = intermediate_layers[layer_idx] = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::Base, print_object.slicing_parameters(), layer_idx);
SupportGeneratorLayer *base_layer = intermediate_layers[layer_idx] = &layer_allocate(layer_storage, layer_storage_mutex, SupporLayerType::Base, print_object.slicing_parameters(), config, layer_idx);
base_layer->polygons = union_(base_layer_polygons);
}
@ -3391,8 +3579,8 @@ static void extrude_branch(
const SupportElement &prev = *path[ipath - 1];
const SupportElement &current = *path[ipath];
assert(prev.state.layer_idx + 1 == current.state.layer_idx);
p1 = to_3d(unscaled<double>(prev .state.result_on_layer), layer_z(slicing_params, prev .state.layer_idx));
p2 = to_3d(unscaled<double>(current.state.result_on_layer), layer_z(slicing_params, current.state.layer_idx));
p1 = to_3d(unscaled<double>(prev .state.result_on_layer), layer_z(slicing_params, config, prev .state.layer_idx));
p2 = to_3d(unscaled<double>(current.state.result_on_layer), layer_z(slicing_params, config, current.state.layer_idx));
v1 = (p2 - p1).normalized();
if (ipath == 1) {
nprev = v1;
@ -3439,7 +3627,7 @@ static void extrude_branch(
} else {
const SupportElement &next = *path[ipath + 1];
assert(current.state.layer_idx + 1 == next.state.layer_idx);
p3 = to_3d(unscaled<double>(next.state.result_on_layer), layer_z(slicing_params, next.state.layer_idx));
p3 = to_3d(unscaled<double>(next.state.result_on_layer), layer_z(slicing_params, config, next.state.layer_idx));
v2 = (p3 - p2).normalized();
ncurrent = (v1 + v2).normalized();
float radius = unscaled<float>(config.getRadius(current.state));
@ -3549,7 +3737,7 @@ static void organic_smooth_branches_avoid_collisions(
element.parents.empty() || (link_down == -1 && element.state.layer_idx > 0),
unscaled<float>(config.getRadius(element.state)),
// 3D position
to_3d(unscaled<float>(element.state.result_on_layer), float(layer_z(slicing_params, element.state.layer_idx)))
to_3d(unscaled<float>(element.state.result_on_layer), float(layer_z(slicing_params, config, element.state.layer_idx)))
});
// Update min_z coordinate to min_z of the tree below.
CollisionSphere &collision_sphere = collision_spheres.back();
@ -3580,8 +3768,9 @@ static void organic_smooth_branches_avoid_collisions(
//FIXME limit the collision span by the tree slope.
collision_sphere.min_z = std::max(collision_sphere.min_z, collision_sphere.position.z() - collision_sphere.radius);
collision_sphere.max_z = std::min(collision_sphere.max_z, collision_sphere.position.z() + collision_sphere.radius);
collision_sphere.layer_begin = std::min(collision_sphere.element.state.layer_idx, layer_idx_ceil(slicing_params, collision_sphere.min_z));
collision_sphere.layer_end = std::max(collision_sphere.element.state.layer_idx, layer_idx_floor(slicing_params, collision_sphere.max_z)) + 1;
collision_sphere.layer_begin = std::min(collision_sphere.element.state.layer_idx, layer_idx_ceil(slicing_params, config, collision_sphere.min_z));
assert(collision_sphere.layer_begin < layer_collision_cache.size());
collision_sphere.layer_end = std::min(LayerIndex(layer_collision_cache.size()), std::max(collision_sphere.element.state.layer_idx, layer_idx_floor(slicing_params, config, collision_sphere.max_z)) + 1);
}
throw_on_cancel();
@ -3596,7 +3785,7 @@ static void organic_smooth_branches_avoid_collisions(
collision_sphere.prev_position = collision_sphere.position;
std::atomic<size_t> num_moved{ 0 };
tbb::parallel_for(tbb::blocked_range<size_t>(0, collision_spheres.size()),
[&collision_spheres, &layer_collision_cache, &slicing_params, &move_bounds, &linear_data_layers, &num_moved, &throw_on_cancel](const tbb::blocked_range<size_t> range) {
[&collision_spheres, &layer_collision_cache, &slicing_params, &config, &move_bounds, &linear_data_layers, &num_moved, &throw_on_cancel](const tbb::blocked_range<size_t> range) {
for (size_t collision_sphere_id = range.begin(); collision_sphere_id < range.end(); ++ collision_sphere_id)
if (CollisionSphere &collision_sphere = collision_spheres[collision_sphere_id]; ! collision_sphere.locked) {
// Calculate collision of multiple 2D layers against a collision sphere.
@ -3613,7 +3802,7 @@ static void organic_smooth_branches_avoid_collisions(
double collision_depth = sqrt(r2) - dist;
if (collision_depth > collision_sphere.last_collision_depth) {
collision_sphere.last_collision_depth = collision_depth;
collision_sphere.last_collision = to_3d(hit_point_out.cast<float>(), float(layer_z(slicing_params, layer_id)));
collision_sphere.last_collision = to_3d(hit_point_out.cast<float>(), float(layer_z(slicing_params, config, layer_id)));
}
}
}
@ -3693,7 +3882,7 @@ static void organic_smooth_branches_avoid_collisions(
std::vector<openvdb::Vec3R> pts, prev, projections;
std::vector<float> distances;
for (const std::pair<SupportElement*, int>& element : elements_with_link_down) {
Vec3d pt = to_3d(unscaled<double>(element.first->state.result_on_layer), layer_z(print_object.slicing_parameters(), element.first->state.layer_idx)) * scale;
Vec3d pt = to_3d(unscaled<double>(element.first->state.result_on_layer), layer_z(print_object.slicing_parameters(), config, element.first->state.layer_idx)) * scale;
pts.push_back({ pt.x(), pt.y(), pt.z() });
}
@ -3918,9 +4107,9 @@ static void slice_branches(
const SlicingParameters &slicing_params = print_object.slicing_parameters();
std::vector<float> slice_z;
for (size_t layer_idx = 0; layer_idx < move_bounds.size(); ++ layer_idx) {
double print_z = slicing_params.object_print_z_min + slicing_params.first_object_layer_height + layer_idx * slicing_params.layer_height;
double layer_height = layer_idx == 0 ? slicing_params.first_object_layer_height : slicing_params.layer_height;
slice_z.emplace_back(float(print_z - layer_height * 0.5));
const double print_z = layer_z(print_object.slicing_parameters(), config, layer_idx);
const double bottom_z = layer_idx > 0 ? layer_z(print_object.slicing_parameters(), config, layer_idx - 1) : 0.;
slice_z.emplace_back(float(0.5 * (bottom_z + print_z)));
}
// Remove the trailing slices.
while (! slice_z.empty())
@ -4009,7 +4198,7 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
//FIXME generating overhangs just for the furst mesh of the group.
assert(processing.second.size() == 1);
std::vector<Polygons> overhangs = generate_overhangs(*print.get_object(processing.second.front()), throw_on_cancel);
std::vector<Polygons> overhangs = generate_overhangs(config, *print.get_object(processing.second.front()), throw_on_cancel);
// ### Precalculate avoidances, collision etc.
size_t num_support_layers = precalculate(print, overhangs, processing.first, processing.second, volumes, throw_on_cancel);
@ -4096,8 +4285,6 @@ static void generate_support_areas(Print &print, const BuildVolume &build_volume
// Produce the support G-code.
// Used by both classic and tree supports.
SupportParameters support_params(print_object);
support_params.with_sheath = true;
support_params.support_density = 0;
SupportGeneratorLayersPtr interface_layers, base_interface_layers;
SupportGeneratorLayersPtr raft_layers = generate_raft_base(print_object, support_params, print_object.slicing_parameters(), top_contacts, interface_layers, base_interface_layers, intermediate_layers, layer_storage);
#if 1 //#ifdef SLIC3R_DEBUG

View File

@ -40,6 +40,7 @@ namespace Slic3r
class Print;
class PrintObject;
class SupportGeneratorLayer;
struct SlicingParameters;
using SupportGeneratorLayerStorage = std::deque<SupportGeneratorLayer>;
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
@ -255,64 +256,7 @@ struct SupportElement
struct TreeSupportSettings
{
TreeSupportSettings() = default; // required for the definition of the config variable in the TreeSupportGenerator class.
explicit TreeSupportSettings(const TreeSupportMeshGroupSettings& mesh_group_settings)
: 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
diameter_angle_scale_factor(sin(mesh_group_settings.support_tree_branch_diameter_angle) * layer_height / branch_radius),
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 * diameter_angle_scale_factor)),
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),
diameter_scale_bp_radius(std::min(sin(0.7) * layer_height / branch_radius, 1.0 / (branch_radius / (support_line_width / 2.0)))), // Either 40? or as much as possible so that 2 lines will overlap by at least 50%, whichever is smaller.
z_distance_top_layers(round_up_divide(mesh_group_settings.support_top_distance, layer_height)),
z_distance_bottom_layers(round_up_divide(mesh_group_settings.support_bottom_distance, layer_height)),
performance_interface_skip_layers(round_up_divide(mesh_group_settings.support_interface_skip_height, 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) / (branch_radius * diameter_scale_bp_radius);
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;
}
explicit TreeSupportSettings(const TreeSupportMeshGroupSettings &mesh_group_settings, const SlicingParameters &slicing_params);
private:
double angle;
@ -466,6 +410,9 @@ public:
*/
coord_t min_feature_size;
// Extra raft layers below the object.
std::vector<coordf_t> raft_layers;
public:
bool operator==(const TreeSupportSettings& other) const
{
@ -497,6 +444,7 @@ public:
settings.get<int>("meshfix_maximum_extrusion_area_deviation") == other.settings.get<int>("meshfix_maximum_extrusion_area_deviation"))
)
#endif
&& raft_layers == other.raft_layers
;
}

View File

@ -140,8 +140,8 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
}
} else {
if ((config->opt_int("support_material_extruder") != 0 || config->opt_int("support_material_interface_extruder") != 0)) {
wxString msg_text = _(L("The Wipe Tower currently supports the non-soluble supports only\n"
"if they are printed with the current extruder without triggering a tool change.\n"
wxString msg_text = _(L("The Wipe Tower currently supports the non-soluble supports only "
"if they are printed with the current extruder without triggering a tool change. "
"(both support_material_extruder and support_material_interface_extruder need to be set to 0)."));
if (is_global_config)
msg_text += "\n\n" + _(L("Shall I adjust those settings in order to enable the Wipe Tower?"));

View File

@ -1414,7 +1414,7 @@ PageDownloader::PageDownloader(ConfigWizard* parent)
append_spacer(VERTICAL_SPACING);
auto* box_allow_downloads = new wxCheckBox(this, wxID_ANY, _L("Allow build-in downloader"));
auto* box_allow_downloads = new wxCheckBox(this, wxID_ANY, _L("Allow built-in downloader"));
// TODO: Do we want it like this? The downloader is allowed for very first time the wizard is run.
bool box_allow_value = (app_config->has("downloader_url_registered") ? app_config->get_bool("downloader_url_registered") : true);
box_allow_downloads->SetValue(box_allow_value);

View File

@ -2060,10 +2060,10 @@ bool ObjectList::del_from_cut_object(bool is_cut_connector, bool is_model_part/*
is_model_part ? _L("Delete solid part from object which is a part of cut") :
is_negative_volume ? _L("Delete negative volume from object which is a part of cut") : "";
const wxString msg_end = is_cut_connector ? ("\n" + _L("To save cut correspondence you can delete all connectors from all related objects.")) : "";
const wxString msg_end = is_cut_connector ? ("\n" + _L("To save cut information you can delete all connectors from all related objects.")) : "";
InfoDialog dialog(wxGetApp().plater(), title,
_L("This action will break a cut correspondence.\n"
_L("This action will break a cut information.\n"
"After that PrusaSlicer can't guarantee model consistency.\n"
"\n"
"To manipulate with solid parts or negative volumes you have to invalidate cut infornation first." + msg_end ),

View File

@ -200,7 +200,7 @@ 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::Prizm))
, m_connector_style (size_t(CutConnectorStyle::Prism))
, m_connector_shape_id (size_t(CutConnectorShape::Circle))
{
m_modes = { _u8L("Planar")//, _u8L("Grid")
@ -219,7 +219,7 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename,
m_connector_types.push_back(type_label);
}
m_connector_styles = { _u8L("Prizm"), _u8L("Frustum")
m_connector_styles = { _u8L("Prism"), _u8L("Frustum")
// , _u8L("Claw")
};
@ -245,15 +245,17 @@ std::string GLGizmoCut3D::get_tooltip() const
double koef = m_imperial_units ? ObjectManipulation::mm_to_in : 1.0;
std::string unit_str = " " + (m_imperial_units ? _u8L("inch") : _u8L("mm"));
const BoundingBoxf3& tbb = m_transformed_bounding_box;
const std::string name = m_keep_as_parts ? _u8L("Part") : _u8L("Object");
if (tbb.max.z() >= 0.0) {
double top = (tbb.min.z() <= 0.0 ? tbb.max.z() : tbb.size().z()) * koef;
tooltip += format(top, 2) + " " + unit_str + " (" + _u8L("Top part") + ")";
tooltip += format(static_cast<float>(top), 2) + " " + unit_str + " (" + name + " A)";
if (tbb.min.z() <= 0.0)
tooltip += "\n";
}
if (tbb.min.z() <= 0.0) {
double bottom = (tbb.max.z() <= 0.0 ? tbb.size().z() : (tbb.min.z() * (-1))) * koef;
tooltip += format(bottom, 2) + " " + unit_str + " (" + _u8L("Bottom part") + ")";
tooltip += format(static_cast<float>(bottom), 2) + " " + unit_str + " (" + name + " B)";
}
return tooltip;
}
@ -362,11 +364,8 @@ bool GLGizmoCut3D::on_mouse(const wxMouseEvent &mouse_event)
void GLGizmoCut3D::shift_cut(double delta)
{
Vec3d starting_vec = m_rotation_m * Vec3d::UnitZ();
if (starting_vec.norm() != 0.0)
starting_vec.normalize();
Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Move cut plane"), UndoRedo::SnapshotType::GizmoAction);
set_center(m_plane_center + starting_vec * delta, true);
set_center(m_plane_center + m_cut_normal * delta, true);
m_ar_plane_center = m_plane_center;
}
@ -405,18 +404,15 @@ bool GLGizmoCut3D::is_looking_forward() const
void GLGizmoCut3D::update_clipper()
{
// update cut_normal
Vec3d beg, end = beg = m_bb_center;
beg[Z] -= m_radius;
end[Z] += m_radius;
rotate_vec3d_around_plane_center(beg);
rotate_vec3d_around_plane_center(end);
// calculate normal for cut plane
Vec3d normal = m_cut_normal = end - beg;
Vec3d normal = m_rotation_m * Vec3d::UnitZ();
normal.normalize();
m_cut_normal = normal;
// calculate normal and offset for clipping plane
normal.normalize();
Vec3d beg = m_bb_center;
beg[Z] -= m_radius;
rotate_vec3d_around_plane_center(beg);
m_clp_normal = normal;
double offset = normal.dot(m_plane_center);
double dist = normal.dot(beg);
@ -425,17 +421,13 @@ void GLGizmoCut3D::update_clipper()
if (!is_looking_forward()) {
// recalculate normal and offset for clipping plane, if camera is looking downward to cut plane
end = beg = m_bb_center;
beg[Z] += m_radius;
end[Z] -= m_radius;
rotate_vec3d_around_plane_center(beg);
rotate_vec3d_around_plane_center(end);
normal = end - beg;
if (normal == Vec3d::Zero())
return;
normal = m_rotation_m * (-1. * Vec3d::UnitZ());
normal.normalize();
beg = m_bb_center;
beg[Z] += m_radius;
rotate_vec3d_around_plane_center(beg);
m_clp_normal = normal;
offset = normal.dot(m_plane_center);
dist = normal.dot(beg);
@ -910,7 +902,7 @@ void GLGizmoCut3D::on_set_state()
update_bb();
m_connectors_editing = !m_selected.empty();
m_transformed_bounding_box = transformed_bounding_box(m_plane_center);
m_transformed_bounding_box = transformed_bounding_box(m_plane_center, m_rotation_m);
// initiate archived values
m_ar_plane_center = m_plane_center;
@ -1011,9 +1003,11 @@ void GLGizmoCut3D::update_raycasters_for_picking_transform()
// recalculate connector position to world position
Vec3d pos = connector.pos + instance_offset;
if (connector.attribs.type == CutConnectorType::Dowel &&
connector.attribs.style == CutConnectorStyle::Prizm) {
pos -= height * m_clp_normal;
height *= 2;
connector.attribs.style == CutConnectorStyle::Prism) {
if (is_looking_forward())
pos -= static_cast<double>(height - 0.05f) * m_clp_normal;
else
pos += 0.05 * m_clp_normal;
}
pos[Z] += sla_shift;
@ -1559,7 +1553,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::Prizm);
m_connector_style = size_t(CutConnectorStyle::Prism);
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); });
}
if (render_combo(_u8L("Style"), m_connector_styles, m_connector_style))
@ -1611,7 +1605,7 @@ void GLGizmoCut3D::render_build_size()
", Z: " + double_to_string(tbb_sz.z() * koef, 2) + unit_str;
ImGui::AlignTextToFramePadding();
m_imgui->text(_L("Build size"));
m_imgui->text(_L("Build Volume"));
ImGui::SameLine(m_label_width);
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, size);
}
@ -1860,7 +1854,7 @@ 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::Prizm);
m_connector_style = size_t(CutConnectorStyle::Prism);
if (m_connector_shape_id == size_t(CutConnectorShape::Undef))
m_connector_shape_id = size_t(CutConnectorShape::Circle);
}
@ -2096,12 +2090,20 @@ void GLGizmoCut3D::render_connectors()
const Camera& camera = wxGetApp().plater()->get_camera();
if (connector.attribs.type == CutConnectorType::Dowel &&
connector.attribs.style == CutConnectorStyle::Prizm) {
if (is_looking_forward())
pos -= height * m_clp_normal;
else
pos += height * m_clp_normal;
height *= 2;
connector.attribs.style == CutConnectorStyle::Prism) {
if (m_connectors_editing) {
if (is_looking_forward())
pos -= static_cast<double>(height-0.05f) * m_clp_normal;
else
pos += 0.05 * m_clp_normal;
}
else {
if (is_looking_forward())
pos -= static_cast<double>(height) * m_clp_normal;
else
pos += static_cast<double>(height) * m_clp_normal;
height *= 2;
}
}
else if (!is_looking_forward())
pos += 0.05 * m_clp_normal;
@ -2136,16 +2138,13 @@ void GLGizmoCut3D::apply_connectors_in_model(ModelObject* mo, bool &create_dowel
connector.rotation_m = m_rotation_m;
if (connector.attribs.type == CutConnectorType::Dowel) {
if (connector.attribs.style == CutConnectorStyle::Prizm)
if (connector.attribs.style == CutConnectorStyle::Prism)
connector.height *= 2;
create_dowels_as_separate_object = true;
}
else {
// calculate shift of the connector center regarding to the position on the cut plane
Vec3d shifted_center = m_plane_center + Vec3d::UnitZ();
rotate_vec3d_around_plane_center(shifted_center);
Vec3d norm = (shifted_center - m_plane_center).normalized();
connector.pos += norm * 0.5 * double(connector.height);
connector.pos += m_cut_normal * 0.5 * double(connector.height);
}
}
mo->apply_cut_connectors(_u8L("Connector"));
@ -2255,7 +2254,7 @@ void GLGizmoCut3D::reset_connectors()
void GLGizmoCut3D::init_connector_shapes()
{
for (const CutConnectorType& type : {CutConnectorType::Dowel, CutConnectorType::Plug})
for (const CutConnectorStyle& style : {CutConnectorStyle::Frustum, CutConnectorStyle::Prizm})
for (const CutConnectorStyle& style : {CutConnectorStyle::Frustum, CutConnectorStyle::Prism})
for (const CutConnectorShape& shape : {CutConnectorShape::Circle, CutConnectorShape::Hexagon, CutConnectorShape::Square, CutConnectorShape::Triangle}) {
const CutConnectorAttributes attribs = { type, style, shape };
const indexed_triangle_set its = ModelObject::get_connector_mesh(attribs);

View File

@ -1528,7 +1528,7 @@ void GLGizmoEmboss::draw_window()
if (m_is_unknown_font && m_is_advanced_edit_style)
ImGui::SetNextTreeNodeOpen(false);
if (ImGui::TreeNode(_u8L("advanced").c_str())) {
if (ImGui::TreeNode(_u8L("Advanced").c_str())) {
if (!m_is_advanced_edit_style) {
set_minimal_window_size(true);
} else {
@ -1592,8 +1592,8 @@ void GLGizmoEmboss::draw_window()
m_set_window_offset = priv::calc_fine_position(m_parent.get_selection(), get_minimal_window_size(), m_parent.get_canvas_size());
} else if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s", ((m_allow_open_near_volume) ?
_u8L("Fix settings possition"):
_u8L("Allow floating window near text")).c_str());
"Fix settings position":
"Allow floating window near text").c_str());
}
#endif // ALLOW_FLOAT_WINDOW
}
@ -1654,9 +1654,9 @@ void GLGizmoEmboss::draw_text_input()
auto &ff = m_style_manager.get_font_file_with_cache();
float imgui_size = StyleManager::get_imgui_font_size(prop, *ff.font_file, scale);
if (imgui_size > StyleManager::max_imgui_font_size)
append_warning(_u8L("To tall"), _u8L("Diminished font height inside text input."));
append_warning(_u8L("Too tall"), _u8L("Diminished font height inside text input."));
if (imgui_size < StyleManager::min_imgui_font_size)
append_warning(_u8L("To small"), _u8L("Enlarged font height inside text input."));
append_warning(_u8L("Too small"), _u8L("Enlarged font height inside text input."));
if (!who.empty()) warning = GUI::format(_L("%1% is NOT shown."), who);
}
@ -3175,7 +3175,8 @@ void GLGizmoEmboss::draw_advanced()
{
const auto &ff = m_style_manager.get_font_file_with_cache();
if (!ff.has_value()) {
ImGui::Text("%s", _u8L("Advanced font options could be change only for corect font.\nStart with select correct font.").c_str());
ImGui::Text("%s", _u8L("Advanced font options could be changed only for correct font.\n"
"Start with select correct font.").c_str());
return;
}
@ -3301,7 +3302,7 @@ void GLGizmoEmboss::draw_advanced()
bool use_inch = wxGetApp().app_config->get_bool("use_inches");
const std::string undo_move_tooltip = _u8L("Undo translation");
const wxString move_tooltip = _L("Distance center of text from model surface");
const wxString move_tooltip = _L("Distance of the center of text from model surface");
bool is_moved = false;
if (use_inch) {
std::optional<float> distance_inch;
@ -3597,7 +3598,7 @@ void GLGizmoEmboss::create_notification_not_valid_font(
std::string text =
GUI::format(_L("Can't load exactly same font(\"%1%\"), "
"Aplication select similar one(\"%2%\"). "
"Aplication selected a similar one(\"%2%\"). "
"You have to specify font for enable edit text."),
face_name_3mf, face_name);
auto notification_manager = wxGetApp().plater()->get_notification_manager();

View File

@ -521,7 +521,7 @@ void GLGizmoFdmSupports::auto_generate()
{
std::string err = wxGetApp().plater()->fff_print().validate();
if (!err.empty()) {
MessageDialog dlg(GUI::wxGetApp().plater(), _L("Automatic painting requires valid print setup. \n") + from_u8(err), _L("Warning"), wxOK);
MessageDialog dlg(GUI::wxGetApp().plater(), _L("Automatic painting requires valid print setup.") + " \n" + from_u8(err), _L("Warning"), wxOK);
dlg.ShowModal();
return;
}

View File

@ -463,7 +463,7 @@ TriangleMesh priv::create_mesh(DataBase &input, Fnc was_canceled, Job::Ctl& ctl)
// only info
ctl.call_on_main_thread([]() {
create_message(_u8L("It is used default volume for embossed "
"text, try to change text or font for fix it."));
"text, try to change text or font to fix it."));
});
}

View File

@ -4988,7 +4988,16 @@ bool Plater::priv::can_decrease_instances(int obj_idx /*= -1*/) const
if (obj_idx < 0)
obj_idx = get_selected_object_idx();
return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) &&
if (obj_idx < 0) {
if (const auto obj_ids = get_selection().get_object_idxs(); !obj_ids.empty())
for (const size_t obj_id : obj_ids)
if (can_decrease_instances(obj_id))
return true;
return false;
}
return obj_idx < (int)model.objects.size() &&
(model.objects[obj_idx]->instances.size() > 1) &&
!sidebar->obj_list()->has_selected_cut_object();
}
@ -6254,6 +6263,13 @@ void Plater::increase_instances(size_t num, int obj_idx/* = -1*/)
if (obj_idx < 0)
obj_idx = p->get_selected_object_idx();
if (obj_idx < 0) {
if (const auto obj_idxs = get_selection().get_object_idxs(); !obj_idxs.empty())
for (const size_t obj_id : obj_idxs)
increase_instances(1, int(obj_id));
return;
}
ModelObject* model_object = p->model.objects[obj_idx];
ModelInstance* model_instance = model_object->instances.back();
@ -6289,6 +6305,13 @@ void Plater::decrease_instances(size_t num, int obj_idx/* = -1*/)
if (obj_idx < 0)
obj_idx = p->get_selected_object_idx();
if (obj_idx < 0) {
if (const auto obj_ids = get_selection().get_object_idxs(); !obj_ids.empty())
for (const size_t obj_id : obj_ids)
decrease_instances(1, int(obj_id));
return;
}
ModelObject* model_object = p->model.objects[obj_idx];
if (model_object->instances.size() > num) {
for (size_t i = 0; i < num; ++ i)

View File

@ -598,7 +598,7 @@ void PreferencesDialog::build()
append_bool_option(m_optgroup_other, "downloader_url_registered",
L("Allow downloads from Printables.com"),
L("If enabled, PrusaSlicer will allow to download from Printables.com"),
L("If enabled, PrusaSlicer will be allowed to download from Printables.com"),
app_config->get_bool("downloader_url_registered"));
activate_options_tab(m_optgroup_other);
@ -645,7 +645,7 @@ void PreferencesDialog::build()
{
append_bool_option(m_optgroup_dark_mode, "sys_menu_enabled",
L("Use system menu for application"),
L("If enabled, application will use the standart Windows system menu,\n"
L("If enabled, application will use the standard Windows system menu,\n"
"but on some combination od display scales it can look ugly. If disabled, old UI will be used."),
app_config->get_bool("sys_menu_enabled"));
}

View File

@ -1593,7 +1593,7 @@ void DiffPresetDialog::create_buttons()
});
m_transfer_btn->Bind(wxEVT_ENTER_WINDOW, [this, show_in_bottom_info](wxMouseEvent& e) {
show_in_bottom_info(_L("Transfer the selected options from left preset to the right.\n"
"Note: New modified presets will be selected in setting stabs after close this dialog."), e); });
"Note: New modified presets will be selected in settings tabs after close this dialog."), e); });
// Save
m_save_btn = new ScalableButton(this, wxID_ANY, "save", _L("Save"), wxDefaultSize, wxDefaultPosition, wxBORDER_DEFAULT, 24);

View File

@ -1168,7 +1168,7 @@ void PresetUpdater::slic3r_update_notify()
static bool reload_configs_update_gui()
{
wxString header = _L("Configuration Updates causes a loss of preset modification.\n"
wxString header = _L("Configuration Update will cause the preset modification to be lost.\n"
"So, check unsaved changes and save them if necessary.");
if (!GUI::wxGetApp().check_and_save_current_preset_changes(_L("Updating"), header, false ))
return false;