Merge branch 'master' of https://github.com/Prusa-Development/PrusaSlicerPrivate into et_transformations
This commit is contained in:
commit
81bd626273
File diff suppressed because it is too large
Load Diff
@ -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); }
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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 ¤t_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 ¤t = *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
|
||||
|
@ -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
|
||||
;
|
||||
}
|
||||
|
||||
|
@ -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?"));
|
||||
|
@ -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);
|
||||
|
@ -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 ),
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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."));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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"));
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user