Merge branch 'master' into fs_emboss

This commit is contained in:
Filip Sykala - NTB T15p 2023-03-08 07:37:13 +01:00
commit 6adc2cf733
89 changed files with 79724 additions and 64817 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,5 @@
min_slic3r_version = 2.4.1-beta3
0.0.7 Correct missing profile improvement from 0.0.6 to set ensure_vertical_shell_thickness = 0 (off).
0.0.6 Correct start gcode, match profile and firmware settings, and other profile improvements.
0.0.5 Correct Marlin Error accumulation for Ditto printer profiles.
0.0.4 Correct Marlin Error accumulation

View file

@ -5,7 +5,7 @@
name = BIBO
# Configuration version of this file. Config file will only be installed, if the config_version differs.
# This means, the server may force the PrusaSlicer configuration to be downgraded.
config_version = 0.0.6
config_version = 0.0.7
# Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/BIBO/
@ -38,7 +38,7 @@ compatible_printers =
complete_objects = 0
dont_support_bridges = 1
elefant_foot_compensation = 0.3
ensure_vertical_shell_thickness = 1
ensure_vertical_shell_thickness = 0
external_fill_pattern = rectilinear
external_perimeters_first = 0
external_perimeter_extrusion_width = 0.40

View file

@ -85,7 +85,13 @@ template<class PConf>
void fill_config(PConf& pcfg, const ArrangeParams &params) {
// Align the arranged pile into the center of the bin
pcfg.alignment = PConf::Alignment::CENTER;
switch (params.alignment) {
case Pivots::Center: pcfg.alignment = PConf::Alignment::CENTER; break;
case Pivots::BottomLeft: pcfg.alignment = PConf::Alignment::BOTTOM_LEFT; break;
case Pivots::BottomRight: pcfg.alignment = PConf::Alignment::BOTTOM_RIGHT; break;
case Pivots::TopLeft: pcfg.alignment = PConf::Alignment::TOP_LEFT; break;
case Pivots::TopRight: pcfg.alignment = PConf::Alignment::TOP_RIGHT; break;
}
// Start placing the items from the center of the print bed
pcfg.starting_point = PConf::Alignment::CENTER;
@ -593,23 +599,27 @@ template<class Fn> auto call_with_bed(const Points &bed, Fn &&fn)
auto parea = poly_area(bed);
if ((1.0 - parea / area(bb)) < 1e-3)
return fn(bb);
return fn(RectangleBed{bb});
else if (!std::isnan(circ.radius()))
return fn(circ);
else
return fn(Polygon(bed));
return fn(IrregularBed{ExPolygon(bed)});
}
}
bool is_box(const Points &bed)
{
return !bed.empty() &&
((1.0 - poly_area(bed) / area(BoundingBox(bed))) < 1e-3);
}
template<>
void arrange(ArrangePolygons & items,
const ArrangePolygons &excludes,
const Points & bed,
const ArrangeParams & params)
{
call_with_bed(bed, [&](const auto &bin) {
arrange(items, excludes, bin, params);
});
arrange(items, excludes, to_arrange_bed(bed), params);
}
template<class BedT>
@ -649,5 +659,97 @@ template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, c
template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Polygon &bed, const ArrangeParams &params);
template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const InfiniteBed &bed, const ArrangeParams &params);
ArrangeBed to_arrange_bed(const Points &bedpts)
{
ArrangeBed ret;
call_with_bed(bedpts, [&](const auto &bed) {
ret = bed;
});
return ret;
}
void arrange(ArrangePolygons &items,
const ArrangePolygons &excludes,
const SegmentedRectangleBed &bed,
const ArrangeParams &params)
{
arrange(items, excludes, bed.bb, params);
if (! excludes.empty())
return;
auto it = std::max_element(items.begin(), items.end(),
[](auto &i1, auto &i2) {
return i1.bed_idx < i2.bed_idx;
});
size_t beds = 0;
if (it != items.end())
beds = it->bed_idx + 1;
std::vector<BoundingBox> pilebb(beds);
for (auto &itm : items) {
if (itm.bed_idx >= 0)
pilebb[itm.bed_idx].merge(get_extents(itm.transformed_poly()));
}
auto piecesz = unscaled(bed.bb).size();
piecesz.x() /= bed.segments.x();
piecesz.y() /= bed.segments.y();
for (size_t bedidx = 0; bedidx < beds; ++bedidx) {
BoundingBox bb;
auto pilesz = unscaled(pilebb[bedidx]).size();
bb.max.x() = scaled(std::ceil(pilesz.x() / piecesz.x()) * piecesz.x());
bb.max.y() = scaled(std::ceil(pilesz.y() / piecesz.y()) * piecesz.y());
switch (params.alignment) {
case Pivots::BottomLeft:
bb.translate(bed.bb.min - bb.min);
break;
case Pivots::TopRight:
bb.translate(bed.bb.max - bb.max);
break;
case Pivots::BottomRight: {
Point bedref{bed.bb.max.x(), bed.bb.min.y()};
Point bbref {bb.max.x(), bb.min.y()};
bb.translate(bedref - bbref);
break;
}
case Pivots::TopLeft: {
Point bedref{bed.bb.min.x(), bed.bb.max.y()};
Point bbref {bb.min.x(), bb.max.y()};
bb.translate(bedref - bbref);
break;
}
case Pivots::Center: {
bb.translate(bed.bb.center() - bb.center());
break;
}
}
Vec2crd d = bb.center() - pilebb[bedidx].center();
auto bedbb = bed.bb;
bedbb.offset(-params.min_bed_distance);
auto pilebbx = pilebb[bedidx];
pilebbx.translate(d);
Point corr{0, 0};
corr.x() = -std::min(0, pilebbx.min.x() - bedbb.min.x())
-std::max(0, pilebbx.max.x() - bedbb.max.x());
corr.y() = -std::min(0, pilebbx.min.y() - bedbb.min.y())
-std::max(0, pilebbx.max.y() - bedbb.max.y());
d += corr;
for (auto &itm : items)
if (itm.bed_idx == bedidx)
itm.translation += d;
}
}
} // namespace arr
} // namespace Slic3r

View file

@ -3,12 +3,25 @@
#include "ExPolygon.hpp"
#include <boost/variant.hpp>
#include <libslic3r/BoundingBox.hpp>
namespace Slic3r {
class BoundingBox;
namespace arrangement {
/// Representing an unbounded bed.
struct InfiniteBed {
Point center;
explicit InfiniteBed(const Point &p = {0, 0}): center{p} {}
};
struct RectangleBed {
BoundingBox bb;
};
/// A geometry abstraction for a circular print bed. Similarly to BoundingBox.
class CircleBed {
Point center_;
@ -22,12 +35,28 @@ public:
inline const Point& center() const { return center_; }
};
/// Representing an unbounded bed.
struct InfiniteBed {
Point center;
explicit InfiniteBed(const Point &p = {0, 0}): center{p} {}
struct SegmentedRectangleBed {
Vec<2, size_t> segments;
BoundingBox bb;
SegmentedRectangleBed (const BoundingBox &bb,
size_t segments_x,
size_t segments_y)
: segments{segments_x, segments_y}
, bb{bb}
{}
};
struct IrregularBed {
ExPolygon poly;
};
//enum BedType { Infinite, Rectangle, Circle, SegmentedRectangle, Irregular };
using ArrangeBed = boost::variant<InfiniteBed, RectangleBed, CircleBed, SegmentedRectangleBed, IrregularBed>;
ArrangeBed to_arrange_bed(const Points &bedpts);
/// A logical bed representing an object not being arranged. Either the arrange
/// has not yet successfully run on this ArrangePolygon or it could not fit the
/// object due to overly large size or invalid geometry.
@ -75,6 +104,10 @@ struct ArrangePolygon {
using ArrangePolygons = std::vector<ArrangePolygon>;
enum class Pivots {
Center, TopLeft, BottomLeft, BottomRight, TopRight
};
struct ArrangeParams {
/// The minimum distance which is allowed for any
@ -93,6 +126,12 @@ struct ArrangeParams {
bool allow_rotations = false;
/// Final alignment of the merged pile after arrangement
Pivots alignment = Pivots::Center;
/// Starting position hint for the arrangement
Pivots starting_point = Pivots::Center;
/// Progress indicator callback called when an object gets packed.
/// The unsigned argument is the number of items remaining to pack.
std::function<void(unsigned)> progressind;
@ -127,12 +166,32 @@ extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excl
extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Polygon &bed, const ArrangeParams &params);
extern template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const InfiniteBed &bed, const ArrangeParams &params);
inline void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const RectangleBed &bed, const ArrangeParams &params)
{
arrange(items, excludes, bed.bb, params);
}
inline void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const IrregularBed &bed, const ArrangeParams &params)
{
arrange(items, excludes, bed.poly.contour, params);
}
void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const SegmentedRectangleBed &bed, const ArrangeParams &params);
inline void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const ArrangeBed &bed, const ArrangeParams &params)
{
auto call_arrange = [&](const auto &realbed) { arrange(items, excludes, realbed, params); };
boost::apply_visitor(call_arrange, bed);
}
inline void arrange(ArrangePolygons &items, const Points &bed, const ArrangeParams &params = {}) { arrange(items, {}, bed, params); }
inline void arrange(ArrangePolygons &items, const BoundingBox &bed, const ArrangeParams &params = {}) { arrange(items, {}, bed, params); }
inline void arrange(ArrangePolygons &items, const CircleBed &bed, const ArrangeParams &params = {}) { arrange(items, {}, bed, params); }
inline void arrange(ArrangePolygons &items, const Polygon &bed, const ArrangeParams &params = {}) { arrange(items, {}, bed, params); }
inline void arrange(ArrangePolygons &items, const InfiniteBed &bed, const ArrangeParams &params = {}) { arrange(items, {}, bed, params); }
bool is_box(const Points &bed);
}} // namespace Slic3r::arrangement
#endif // MODELARRANGE_HPP

View file

@ -235,9 +235,18 @@ inline bool empty(const BoundingBox3Base<VT> &bb)
}
inline BoundingBox scaled(const BoundingBoxf &bb) { return {scaled(bb.min), scaled(bb.max)}; }
inline BoundingBox3 scaled(const BoundingBoxf3 &bb) { return {scaled(bb.min), scaled(bb.max)}; }
inline BoundingBoxf unscaled(const BoundingBox &bb) { return {unscaled(bb.min), unscaled(bb.max)}; }
inline BoundingBoxf3 unscaled(const BoundingBox3 &bb) { return {unscaled(bb.min), unscaled(bb.max)}; }
template<class T = coord_t>
BoundingBoxBase<Vec<2, T>> scaled(const BoundingBoxf &bb) { return {scaled<T>(bb.min), scaled<T>(bb.max)}; }
template<class T = coord_t>
BoundingBox3Base<Vec<3, T>> scaled(const BoundingBoxf3 &bb) { return {scaled<T>(bb.min), scaled<T>(bb.max)}; }
template<class T = double>
BoundingBoxBase<Vec<2, T>> unscaled(const BoundingBox &bb) { return {unscaled<T>(bb.min), unscaled<T>(bb.max)}; }
template<class T = double>
BoundingBox3Base<Vec<3, T>> unscaled(const BoundingBox3 &bb) { return {unscaled<T>(bb.min), unscaled<T>(bb.max)}; }
template<class Tout, class Tin>
auto cast(const BoundingBoxBase<Tin> &b)

View file

@ -1736,7 +1736,7 @@ private:
void set_values(const std::initializer_list<std::string_view> il) {
m_values.clear();
m_values.reserve(il.size());
for (const std::string_view p : il)
for (const std::string_view& p : il)
m_values.emplace_back(p);
assert(m_labels.empty() || m_labels.size() == m_values.size());
}
@ -1745,7 +1745,7 @@ private:
m_values.reserve(il.size());
m_labels.clear();
m_labels.reserve(il.size());
for (const std::pair<std::string_view, std::string_view> p : il) {
for (const std::pair<std::string_view, std::string_view>& p : il) {
m_values.emplace_back(p.first);
m_labels.emplace_back(p.second);
}
@ -1753,7 +1753,7 @@ private:
void set_labels(const std::initializer_list<std::string_view> il) {
m_labels.clear();
m_labels.reserve(il.size());
for (const std::string_view p : il)
for (const std::string_view& p : il)
m_labels.emplace_back(p);
assert(m_values.empty() || m_labels.size() == m_values.size());
}
@ -1762,9 +1762,9 @@ private:
// Check whether def.enum_values contains all the values of def.enum_keys_map and
// that they are sorted by their ordinary values.
m_values_ordinary = true;
for (const std::pair<std::string, int>& key : *m_enum_keys_map) {
assert(key.second >= 0);
if (key.second >= this->values().size() || this->value(key.second) != key.first) {
for (const auto& [enum_name, enum_int] : *m_enum_keys_map) {
assert(enum_int >= 0);
if (enum_int >= int(this->values().size()) || this->value(enum_int) != enum_name) {
m_values_ordinary = false;
break;
}

View file

@ -1098,7 +1098,13 @@ namespace priv {
/// Track source of intersection
/// Help for anotate inner and outer faces
/// </summary>
struct Visitor {
struct Visitor : public CGAL::Polygon_mesh_processing::Corefinement::Default_visitor<CutMesh> {
Visitor(const CutMesh &object, const CutMesh &shape, EdgeShapeMap edge_shape_map,
FaceShapeMap face_shape_map, VertexShapeMap vert_shape_map, bool* is_valid) :
object(object), shape(shape), edge_shape_map(edge_shape_map), face_shape_map(face_shape_map),
vert_shape_map(vert_shape_map), is_valid(is_valid)
{}
const CutMesh &object;
const CutMesh &shape;
@ -1160,16 +1166,6 @@ struct Visitor {
/// <param name="v">New added vertex</param>
/// <param name="tm">Affected mesh</param>
void new_vertex_added(std::size_t i_id, VI v, const CutMesh &tm);
// Not used visitor functions
void before_subface_creations(FI /* f_old */, CutMesh &/* mesh */){}
void after_subface_created(FI /* f_new */, CutMesh &/* mesh */) {}
void after_subface_creations(CutMesh&) {}
void before_subface_created(CutMesh&) {}
void before_edge_split(HI /* h */, CutMesh& /* tm */) {}
void edge_split(HI /* hnew */, CutMesh& /* tm */) {}
void after_edge_split() {}
void add_retriangulation_edge(HI /* h */, CutMesh& /* tm */) {}
};
/// <summary>

View file

@ -1005,18 +1005,6 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
std::sort(zs.begin(), zs.end());
m_layer_count += (unsigned int)(object->instances().size() * (std::unique(zs.begin(), zs.end()) - zs.begin()));
}
} else {
// Print all objects with the same print_z together.
std::vector<coordf_t> zs;
for (auto object : print.objects()) {
zs.reserve(zs.size() + object->layers().size() + object->support_layers().size());
for (auto layer : object->layers())
zs.push_back(layer->print_z);
for (auto layer : object->support_layers())
zs.push_back(layer->print_z);
}
std::sort(zs.begin(), zs.end());
m_layer_count = (unsigned int)(std::unique(zs.begin(), zs.end()) - zs.begin());
}
print.throw_if_canceled();
@ -1144,6 +1132,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
this->set_extruders(tool_ordering.all_extruders());
// Order object instances using a nearest neighbor search.
print_object_instances_ordering = chain_print_object_instances(print);
m_layer_count = tool_ordering.layer_tools().size();
}
if (initial_extruder_id == (unsigned int)-1) {
// Nothing to print!

View file

@ -798,6 +798,7 @@ std::string CoolingBuffer::apply_layer_cooldown(
m_fan_speed = fan_speed_new;
new_gcode += GCodeWriter::set_fan(m_config.gcode_flavor, m_config.gcode_comments, m_fan_speed);
}
custom_fan_speed_limits.first = std::min(custom_fan_speed_limits.first, custom_fan_speed_limits.second);
return custom_fan_speed_limits;
};

View file

@ -929,7 +929,7 @@ void WipeTower::toolchange_Unload(
writer.retract(-m_cooling_tube_length/2.f+m_parking_pos_retraction-m_cooling_tube_retraction, 2000);
}
// this is to align ramming and future wiping extrusions, so the future y-steps can be uniform from the start:
// this is to align ramming and future wiping extrusions, so the future y-steps can be uniform from the start:
// the perimeter_width will later be subtracted, it is there to not load while moving over just extruded material
Vec2f pos = Vec2f(end_of_ramming.x(), end_of_ramming.y() + (y_step/m_extra_spacing-m_perimeter_width) / 2.f + m_perimeter_width);
if (m_semm)
@ -937,8 +937,8 @@ void WipeTower::toolchange_Unload(
else
writer.set_position(pos);
writer.resume_preview()
.flush_planner_queue();
writer.resume_preview()
.flush_planner_queue();
}
// Change the tool, set a speed override for soluble and flex materials.

View file

@ -444,7 +444,9 @@ private:
MedialAxis::MedialAxis(double min_width, double max_width, const ExPolygon &expolygon) :
m_expolygon(expolygon), m_lines(expolygon.lines()), m_min_width(min_width), m_max_width(max_width)
{}
{
(void)m_expolygon; // supress unused variable warning
}
void MedialAxis::build(ThickPolylines* polylines)
{

View file

@ -355,6 +355,8 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
shell_width += 0.5f * external_perimeter_flow.scaled_width() + external_perimeter_flow.scaled_spacing();
shell_width += perimeter_flow.scaled_spacing() * (num_perimeters - 1);
} else {
// TODO: Maybe there is better solution when printing with zero perimeters, but this works reasonably well, given the situation
shell_width = float(SCALED_EPSILON);
}
// Scaled expansions of the respective external surfaces.

View file

@ -889,7 +889,6 @@ const BoundingBoxf3& ModelObject::bounding_box_exact() const
if (! m_bounding_box_exact_valid) {
m_bounding_box_exact_valid = true;
m_min_max_z_valid = true;
BoundingBoxf3 raw_bbox = this->raw_mesh_bounding_box();
m_bounding_box_exact.reset();
for (size_t i = 0; i < this->instances.size(); ++ i)
m_bounding_box_exact.merge(this->instance_bounding_box(i));
@ -1309,7 +1308,7 @@ indexed_triangle_set ModelObject::get_connector_mesh(CutConnectorAttributes conn
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));
connector_mesh = its_make_frustum(1.0, 1.0, (2 * PI / sectorCount));
else
connector_mesh = its_make_frustum_dowel(1.0, 1.0, sectorCount);
@ -1522,6 +1521,11 @@ void ModelObject::process_modifier_cut(ModelVolume* volume, const Transform3d& i
// to the modifier volume transformation to preserve their shape properly.
volume->set_transformation(Geometry::Transformation(volume_matrix));
if (attributes.has(ModelObjectCutAttribute::KeepAsParts)) {
upper->add_volume(*volume);
return;
}
// Some logic for the negative volumes/connectors. Add only needed modifiers
auto bb = volume->mesh().transformed_bounding_box(inverse_cut_matrix * volume_matrix);
bool is_crossed_by_cut = bb.min[Z] <= 0 && bb.max[Z] >= 0;

View file

@ -431,7 +431,7 @@ void Preset::set_visible_from_appconfig(const AppConfig &app_config)
static std::vector<std::string> s_Preset_print_options {
"layer_height", "first_layer_height", "perimeters", "spiral_vase", "slice_closing_radius", "slicing_mode",
"top_solid_layers", "top_solid_min_thickness", "bottom_solid_layers", "bottom_solid_min_thickness",
"extra_perimeters", "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "avoid_crossing_curled_overhangs", "avoid_crossing_perimeters", "thin_walls", "overhangs",
"extra_perimeters", "extra_perimeters_on_overhangs", "avoid_crossing_curled_overhangs", "avoid_crossing_perimeters", "thin_walls", "overhangs",
"seam_position","staggered_inner_seams", "external_perimeters_first", "fill_density", "fill_pattern", "top_fill_pattern", "bottom_fill_pattern",
"infill_every_layers", "infill_only_where_needed", "solid_infill_every_layers", "fill_angle", "bridge_angle",
"solid_infill_below_area", "only_retract_when_crossing_perimeters", "infill_first",

View file

@ -544,7 +544,7 @@ void PrintConfigDef::init_fff_params()
"If set as percentage, the speed is calculated over the external perimeter speed.");
def = this->add("overhang_speed_0", coFloatOrPercent);
def->label = L("speed for 0\% overlap (bridge)");
def->label = L("speed for 0% overlap (bridge)");
def->category = L("Speed");
def->tooltip = overhang_speed_setting_description;
def->sidetext = L("mm/s or %");
@ -553,7 +553,7 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloatOrPercent(15, false));
def = this->add("overhang_speed_1", coFloatOrPercent);
def->label = L("speed for 25\% overlap");
def->label = L("speed for 25% overlap");
def->category = L("Speed");
def->tooltip = overhang_speed_setting_description;
def->sidetext = L("mm/s or %");
@ -562,7 +562,7 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloatOrPercent(15, false));
def = this->add("overhang_speed_2", coFloatOrPercent);
def->label = L("speed for 50\% overlap");
def->label = L("speed for 50% overlap");
def->category = L("Speed");
def->tooltip = overhang_speed_setting_description;
def->sidetext = L("mm/s or %");
@ -571,7 +571,7 @@ void PrintConfigDef::init_fff_params()
def->set_default_value(new ConfigOptionFloatOrPercent(20, false));
def = this->add("overhang_speed_3", coFloatOrPercent);
def->label = L("speed for 75\% overlap");
def->label = L("speed for 75% overlap");
def->category = L("Speed");
def->tooltip = overhang_speed_setting_description;
def->sidetext = L("mm/s or %");
@ -806,14 +806,6 @@ void PrintConfigDef::init_fff_params()
def->mode = comExpert;
def->set_default_value(new ConfigOptionStrings { "; Filament-specific end gcode \n;END gcode for filament\n" });
def = this->add("ensure_vertical_shell_thickness", coBool);
def->label = L("Ensure vertical shell thickness");
def->category = L("Layers and Perimeters");
def->tooltip = L("Add solid infill near sloping surfaces to guarantee the vertical shell thickness "
"(top+bottom solid layers).");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
auto def_top_fill_pattern = def = this->add("top_fill_pattern", coEnum);
def->label = L("Top fill pattern");
def->category = L("Infill");
@ -4097,6 +4089,8 @@ static std::set<std::string> PrintConfigDef_ignore = {
"fuzzy_skin_perimeter_mode", "fuzzy_skin_shape",
// Introduced in PrusaSlicer 2.3.0-alpha2, later replaced by automatic calculation based on extrusion width.
"wall_add_middle_threshold", "wall_split_middle_threshold",
// Replaced by new concentric ensuring in 2.6.0-alpha5
"ensure_vertical_shell_thickness",
};
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
@ -4828,6 +4822,15 @@ Points get_bed_shape(const DynamicPrintConfig &config)
return to_points(bed_shape_opt->values);
}
void get_bed_shape(const DynamicPrintConfig &cfg, arrangement::ArrangeBed &out)
{
if (is_XL_printer(cfg)) {
out = arrangement::SegmentedRectangleBed{get_extents(get_bed_shape(cfg)), 4, 4};
} else {
out = arrangement::to_arrange_bed(get_bed_shape(cfg));
}
}
Points get_bed_shape(const PrintConfig &cfg)
{
return to_points(cfg.bed_shape.values);
@ -4852,6 +4855,20 @@ std::string get_sla_suptree_prefix(const DynamicPrintConfig &config)
return slatree;
}
bool is_XL_printer(const DynamicPrintConfig &cfg)
{
static constexpr const char *ALIGN_ONLY_FOR = "XL";
bool ret = false;
auto *printer_model = cfg.opt<ConfigOptionString>("printer_model");
if (printer_model)
ret = boost::algorithm::contains(printer_model->value, ALIGN_ONLY_FOR);
return ret;
}
} // namespace Slic3r
#include <cereal/types/polymorphic.hpp>

View file

@ -19,6 +19,7 @@
#include "libslic3r.h"
#include "Config.hpp"
#include "SLA/SupportTreeStrategies.hpp"
#include "libslic3r/Arrange.hpp"
#include <boost/preprocessor/facilities/empty.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
@ -569,7 +570,6 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionFloat, bottom_solid_min_thickness))
((ConfigOptionFloat, bridge_flow_ratio))
((ConfigOptionFloat, bridge_speed))
((ConfigOptionBool, ensure_vertical_shell_thickness))
((ConfigOptionEnum<InfillPattern>, top_fill_pattern))
((ConfigOptionEnum<InfillPattern>, bottom_fill_pattern))
((ConfigOptionFloatOrPercent, external_perimeter_extrusion_width))
@ -1186,10 +1186,14 @@ private:
static PrintAndCLIConfigDef s_def;
};
bool is_XL_printer(const DynamicPrintConfig &cfg);
Points get_bed_shape(const DynamicPrintConfig &cfg);
Points get_bed_shape(const PrintConfig &cfg);
Points get_bed_shape(const SLAPrinterConfig &cfg);
void get_bed_shape(const DynamicPrintConfig &cfg, arrangement::ArrangeBed &out);
std::string get_sla_suptree_prefix(const DynamicPrintConfig &config);
// ModelConfig is a wrapper around DynamicPrintConfig with an addition of a timestamp.

View file

@ -719,7 +719,6 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "infill_extruder"
|| opt_key == "solid_infill_extruder"
|| opt_key == "infill_extrusion_width"
|| opt_key == "ensure_vertical_shell_thickness"
|| opt_key == "bridge_angle") {
steps.emplace_back(posPrepareInfill);
} else if (
@ -1204,7 +1203,7 @@ void PrintObject::discover_vertical_shells()
bool has_extra_layers = false;
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
const PrintRegionConfig &config = this->printing_region(region_id).config();
if (config.ensure_vertical_shell_thickness.value && has_extra_layers_fn(config)) {
if (has_extra_layers_fn(config)) {
has_extra_layers = true;
break;
}
@ -1282,9 +1281,6 @@ void PrintObject::discover_vertical_shells()
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
const PrintRegion &region = this->printing_region(region_id);
if (! region.config().ensure_vertical_shell_thickness.value)
// This region will be handled by discover_horizontal_shells().
continue;
if (! has_extra_layers_fn(region.config()))
// Zero or 1 layer, there is no additional vertical wall thickness enforced.
continue;
@ -1598,7 +1594,7 @@ void PrintObject::bridge_over_infill()
std::unordered_map<const LayerSlice *, std::vector<ModifiedSurface>> bridging_surfaces;
tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = this,
tbb::parallel_for(tbb::blocked_range<size_t>(0, this->layers().size()), [po = static_cast<const PrintObject*>(this),
&bridging_surfaces](tbb::blocked_range<size_t> r) {
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
const Layer *layer = po->get_layer(lidx);
@ -1778,7 +1774,7 @@ void PrintObject::bridge_over_infill()
Polygons infill_region = to_polygons(r->fill_expolygons());
Polygons deep_infill_area = closing(infill_region, scale_(0.01), scale_(0.01) + 4.0 * flow.scaled_spacing());
Polygons solid_supported_area = expand(not_sparse_infill, 4.0 * flow.scaled_spacing());
infill_and_deep_infill_polygons_per_region[r] = {closing(infill_region, scale_(0.1)),
infill_and_deep_infill_polygons_per_region[r] = {closing(infill_region, float(scale_(0.1))),
intersection(lower_layers_sparse_infill,
diff(deep_infill_area, solid_supported_area))};
}
@ -2104,6 +2100,7 @@ void PrintObject::bridge_over_infill()
&bridging_surfaces](tbb::blocked_range<size_t> r) {
for (size_t lidx = r.begin(); lidx < r.end(); lidx++) {
Layer *layer = po->get_layer(lidx);
std::unordered_map<const LayerRegion*, Surfaces> new_surfaces;
for (const LayerSlice &slice : layer->lslices_ex) {
if (const auto &modified_surfaces = bridging_surfaces.find(&slice);
@ -2126,42 +2123,40 @@ void PrintObject::bridge_over_infill()
cut_from_infill.insert(cut_from_infill.end(), surface.new_polys.begin(), surface.new_polys.end());
}
for (LayerRegion *region : regions_to_check) {
Surfaces new_surfaces;
for (const LayerRegion *region : regions_to_check) {
for (const ModifiedSurface &s : modified_surfaces->second) {
for (Surface &surface : region->m_fill_surfaces.surfaces) {
for (const Surface &surface : region->m_fill_surfaces.surfaces) {
if (s.original_surface == &surface) {
Surface tmp(surface, {});
for (const ExPolygon &expoly : diff_ex(surface.expolygon, s.new_polys)) {
if (expoly.area() > region->flow(frSolidInfill).scaled_width() * scale_(4.0)) {
new_surfaces.emplace_back(tmp, expoly);
new_surfaces[region].emplace_back(tmp, expoly);
}
}
tmp.surface_type = stInternalBridge;
tmp.bridge_angle = s.bridge_angle;
for (const ExPolygon &expoly : union_ex(s.new_polys)) {
new_surfaces.emplace_back(tmp, expoly);
new_surfaces[region].emplace_back(tmp, expoly);
}
surface.clear();
} else if (surface.surface_type == stInternal) {
Surface tmp(surface, {});
for (const ExPolygon &expoly : diff_ex(surface.expolygon, cut_from_infill)) {
new_surfaces.emplace_back(tmp, expoly);
new_surfaces[region].emplace_back(tmp, expoly);
}
surface.clear();
} else {
new_surfaces[region].push_back(surface);
}
}
}
region->m_fill_surfaces.surfaces.insert(region->m_fill_surfaces.surfaces.end(), new_surfaces.begin(),
new_surfaces.end());
region->m_fill_surfaces.surfaces.erase(std::remove_if(region->m_fill_surfaces.surfaces.begin(),
region->m_fill_surfaces.surfaces.end(),
[](const Surface &s) { return s.empty(); }),
region->m_fill_surfaces.surfaces.end());
}
}
}
for (LayerRegion *region : layer->regions()) {
if (new_surfaces.find(region) != new_surfaces.end()) {
region->m_fill_surfaces = new_surfaces[region];
}
}
}
});
@ -2440,195 +2435,35 @@ void PrintObject::clip_fill_surfaces()
void PrintObject::discover_horizontal_shells()
{
BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()";
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (size_t i = 0; i < m_layers.size(); ++ i) {
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
for (size_t i = 0; i < m_layers.size(); ++i) {
m_print->throw_if_canceled();
Layer *layer = m_layers[i];
LayerRegion *layerm = layer->regions()[region_id];
Layer *layer = m_layers[i];
LayerRegion *layerm = layer->regions()[region_id];
const PrintRegionConfig &region_config = layerm->region().config();
if (region_config.solid_infill_every_layers.value > 0 && region_config.fill_density.value > 0 &&
(i % region_config.solid_infill_every_layers) == 0) {
// Insert a solid internal layer. Mark stInternal surfaces as stInternalSolid or stInternalBridge.
SurfaceType type = (region_config.fill_density == 100 || region_config.solid_infill_every_layers == 1) ? stInternalSolid : stInternalBridge;
SurfaceType type = (region_config.fill_density == 100 || region_config.solid_infill_every_layers == 1) ? stInternalSolid :
stInternalBridge;
for (Surface &surface : layerm->m_fill_surfaces.surfaces)
if (surface.surface_type == stInternal)
surface.surface_type = type;
}
// If ensure_vertical_shell_thickness, then the rest has already been performed by discover_vertical_shells().
if (region_config.ensure_vertical_shell_thickness.value)
continue;
coordf_t print_z = layer->print_z;
coordf_t bottom_z = layer->bottom_z();
for (size_t idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) {
m_print->throw_if_canceled();
SurfaceType type = (idx_surface_type == 0) ? stTop : (idx_surface_type == 1) ? stBottom : stBottomBridge;
int num_solid_layers = (type == stTop) ? region_config.top_solid_layers.value : region_config.bottom_solid_layers.value;
if (num_solid_layers == 0)
continue;
// Find slices of current type for current layer.
// Use slices instead of fill_surfaces, because they also include the perimeter area,
// which needs to be propagated in shells; we need to grow slices like we did for
// fill_surfaces though. Using both ungrown slices and grown fill_surfaces will
// not work in some situations, as there won't be any grown region in the perimeter
// area (this was seen in a model where the top layer had one extra perimeter, thus
// its fill_surfaces were thinner than the lower layer's infill), however it's the best
// solution so far. Growing the external slices by EXTERNAL_INFILL_MARGIN will put
// too much solid infill inside nearly-vertical slopes.
// Surfaces including the area of perimeters. Everything, that is visible from the top / bottom
// (not covered by a layer above / below).
// This does not contain the areas covered by perimeters!
Polygons solid;
for (const Surface &surface : layerm->slices())
if (surface.surface_type == type)
polygons_append(solid, to_polygons(surface.expolygon));
// Infill areas (slices without the perimeters).
for (const Surface &surface : layerm->fill_surfaces())
if (surface.surface_type == type)
polygons_append(solid, to_polygons(surface.expolygon));
if (solid.empty())
continue;
// Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == stTop) ? 'top' : 'bottom';
// Scatter top / bottom regions to other layers. Scattering process is inherently serial, it is difficult to parallelize without locking.
for (int n = (type == stTop) ? int(i) - 1 : int(i) + 1;
(type == stTop) ?
(n >= 0 && (int(i) - n < num_solid_layers ||
print_z - m_layers[n]->print_z < region_config.top_solid_min_thickness.value - EPSILON)) :
(n < int(m_layers.size()) && (n - int(i) < num_solid_layers ||
m_layers[n]->bottom_z() - bottom_z < region_config.bottom_solid_min_thickness.value - EPSILON));
(type == stTop) ? -- n : ++ n)
{
// Slic3r::debugf " looking for neighbors on layer %d...\n", $n;
// Reference to the lower layer of a TOP surface, or an upper layer of a BOTTOM surface.
LayerRegion *neighbor_layerm = m_layers[n]->regions()[region_id];
// find intersection between neighbor and current layer's surfaces
// intersections have contours and holes
// we update $solid so that we limit the next neighbor layer to the areas that were
// found on this one - in other words, solid shells on one layer (for a given external surface)
// are always a subset of the shells found on the previous shell layer
// this approach allows for DWIM in hollow sloping vases, where we want bottom
// shells to be generated in the base but not in the walls (where there are many
// narrow bottom surfaces): reassigning $solid will consider the 'shadow' of the
// upper perimeter as an obstacle and shell will not be propagated to more upper layers
//FIXME How does it work for stInternalBRIDGE? This is set for sparse infill. Likely this does not work.
Polygons new_internal_solid;
{
Polygons internal;
for (const Surface &surface : neighbor_layerm->fill_surfaces())
if (surface.surface_type == stInternal || surface.surface_type == stInternalSolid)
polygons_append(internal, to_polygons(surface.expolygon));
new_internal_solid = intersection(solid, internal, ApplySafetyOffset::Yes);
}
if (new_internal_solid.empty()) {
// No internal solid needed on this layer. In order to decide whether to continue
// searching on the next neighbor (thus enforcing the configured number of solid
// layers, use different strategies according to configured infill density:
if (region_config.fill_density.value == 0) {
// If user expects the object to be void (for example a hollow sloping vase),
// don't continue the search. In this case, we only generate the external solid
// shell if the object would otherwise show a hole (gap between perimeters of
// the two layers), and internal solid shells are a subset of the shells found
// on each previous layer.
goto EXTERNAL;
} else {
// If we have internal infill, we can generate internal solid shells freely.
continue;
}
}
if (region_config.fill_density.value == 0) {
// if we're printing a hollow object we discard any solid shell thinner
// than a perimeter width, since it's probably just crossing a sloping wall
// and it's not wanted in a hollow print even if it would make sense when
// obeying the solid shell count option strictly (DWIM!)
float margin = float(neighbor_layerm->flow(frExternalPerimeter).scaled_width());
Polygons too_narrow = diff(
new_internal_solid,
opening(new_internal_solid, margin, margin + ClipperSafetyOffset, jtMiter, 5));
// Trim the regularized region by the original region.
if (! too_narrow.empty())
new_internal_solid = solid = diff(new_internal_solid, too_narrow);
}
// make sure the new internal solid is wide enough, as it might get collapsed
// when spacing is added in Fill.pm
{
//FIXME Vojtech: Disable this and you will be sorry.
// https://github.com/prusa3d/PrusaSlicer/issues/26 bottom
float margin = 3.f * layerm->flow(frSolidInfill).scaled_width(); // require at least this size
// we use a higher miterLimit here to handle areas with acute angles
// in those cases, the default miterLimit would cut the corner and we'd
// get a triangle in $too_narrow; if we grow it below then the shell
// would have a different shape from the external surface and we'd still
// have the same angle, so the next shell would be grown even more and so on.
Polygons too_narrow = diff(
new_internal_solid,
opening(new_internal_solid, margin, margin + ClipperSafetyOffset, ClipperLib::jtMiter, 5));
if (! too_narrow.empty()) {
// grow the collapsing parts and add the extra area to the neighbor layer
// as well as to our original surfaces so that we support this
// additional area in the next shell too
// make sure our grown surfaces don't exceed the fill area
Polygons internal;
for (const Surface &surface : neighbor_layerm->fill_surfaces())
if (surface.is_internal() && !surface.is_bridge())
polygons_append(internal, to_polygons(surface.expolygon));
polygons_append(new_internal_solid,
intersection(
expand(too_narrow, +margin),
// Discard bridges as they are grown for anchoring and we can't
// remove such anchors. (This may happen when a bridge is being
// anchored onto a wall where little space remains after the bridge
// is grown, and that little space is an internal solid shell so
// it triggers this too_narrow logic.)
internal));
// see https://github.com/prusa3d/PrusaSlicer/pull/3426
// solid = new_internal_solid;
}
}
// internal-solid are the union of the existing internal-solid surfaces
// and new ones
SurfaceCollection backup = std::move(neighbor_layerm->m_fill_surfaces);
polygons_append(new_internal_solid, to_polygons(backup.filter_by_type(stInternalSolid)));
ExPolygons internal_solid = union_ex(new_internal_solid);
// assign new internal-solid surfaces to layer
neighbor_layerm->m_fill_surfaces.set(internal_solid, stInternalSolid);
// subtract intersections from layer surfaces to get resulting internal surfaces
Polygons polygons_internal = to_polygons(std::move(internal_solid));
ExPolygons internal = diff_ex(backup.filter_by_type(stInternal), polygons_internal, ApplySafetyOffset::Yes);
// assign resulting internal surfaces to layer
neighbor_layerm->m_fill_surfaces.append(internal, stInternal);
polygons_append(polygons_internal, to_polygons(std::move(internal)));
// assign top and bottom surfaces to layer
backup.keep_types({ stTop, stBottom, stBottomBridge });
std::vector<SurfacesPtr> top_bottom_groups;
backup.group(&top_bottom_groups);
for (SurfacesPtr &group : top_bottom_groups)
neighbor_layerm->m_fill_surfaces.append(
diff_ex(group, polygons_internal),
// Use an existing surface as a template, it carries the bridge angle etc.
*group.front());
}
EXTERNAL:;
} // foreach type (stTop, stBottom, stBottomBridge)
// The rest has already been performed by discover_vertical_shells().
} // for each layer
} // for each region
} // for each region
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++ region_id) {
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id) {
for (const Layer *layer : m_layers) {
const LayerRegion *layerm = layer->m_regions[region_id];
layerm->export_region_slices_to_svg_debug("5_discover_horizontal_shells");
layerm->export_region_fill_surfaces_to_svg_debug("5_discover_horizontal_shells");
} // for each layer
} // for each region
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
} // for each region
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
} // void PrintObject::discover_horizontal_shells()
// combine fill surfaces across layers to honor the "infill every N layers" option

View file

@ -1077,7 +1077,7 @@ SLAPrintObject::get_parts_to_slice(SLAPrintObjectStep untilstep) const
std::vector<csg::CSGPart> ret;
for (int step = 0; step < s; ++step) {
for (unsigned int step = 0; step < s; ++step) {
auto r = m_mesh_to_slice.equal_range(SLAPrintObjectStep(step));
csg::copy_csgrange_shallow(Range{r.first, r.second}, std::back_inserter(ret));
}

View file

@ -180,7 +180,7 @@ indexed_triangle_set SLAPrint::Steps::generate_preview_vdb(
auto r = range(po.m_mesh_to_slice);
auto grid = csg::voxelize_csgmesh(r, voxparams);
auto m = grid ? grid_to_mesh(*grid, 0., 0.01) : indexed_triangle_set{};
float loss_less_max_error = 1e-6;
float loss_less_max_error = float(1e-6);
its_quadric_edge_collapse(m, 0U, &loss_less_max_error);
return m;

View file

@ -965,6 +965,51 @@ indexed_triangle_set its_make_cylinder(double r, double h, double fa)
return mesh;
}
indexed_triangle_set its_make_frustum(double r, double h, double fa)
{
indexed_triangle_set mesh;
size_t n_steps = (size_t)ceil(2. * PI / fa);
double angle_step = 2. * PI / n_steps;
auto &vertices = mesh.vertices;
auto &facets = mesh.indices;
vertices.reserve(2 * n_steps + 2);
facets.reserve(4 * n_steps);
// 2 special vertices, top and bottom center, rest are relative to this
vertices.emplace_back(Vec3f(0.f, 0.f, 0.f));
vertices.emplace_back(Vec3f(0.f, 0.f, float(h)));
// for each line along the polygon approximating the top/bottom of the
// circle, generate four points and four facets (2 for the wall, 2 for the
// top and bottom.
// Special case: Last line shares 2 vertices with the first line.
Vec2f vec_top = Eigen::Rotation2Df(0.f) * Eigen::Vector2f(0, 0.5f*r);
Vec2f vec_botton = Eigen::Rotation2Df(0.f) * Eigen::Vector2f(0, r);
vertices.emplace_back(Vec3f(vec_botton(0), vec_botton(1), 0.f));
vertices.emplace_back(Vec3f(vec_top(0), vec_top(1), float(h)));
for (size_t i = 1; i < n_steps; ++i) {
vec_top = Eigen::Rotation2Df(angle_step * i) * Eigen::Vector2f(0, 0.5f*float(r));
vec_botton = Eigen::Rotation2Df(angle_step * i) * Eigen::Vector2f(0, float(r));
vertices.emplace_back(Vec3f(vec_botton(0), vec_botton(1), 0.f));
vertices.emplace_back(Vec3f(vec_top(0), vec_top(1), float(h)));
int id = (int)vertices.size() - 1;
facets.emplace_back( 0, id - 1, id - 3); // top
facets.emplace_back(id, 1, id - 2); // bottom
facets.emplace_back(id, id - 2, id - 3); // upper-right of side
facets.emplace_back(id, id - 3, id - 1); // bottom-left of side
}
// Connect the last set of vertices with the first.
int id = (int)vertices.size() - 1;
facets.emplace_back( 0, 2, id - 1);
facets.emplace_back( 3, 1, id);
facets.emplace_back(id, 2, 3);
facets.emplace_back(id, id - 1, 2);
return mesh;
}
indexed_triangle_set its_make_cone(double r, double h, double fa)
{
indexed_triangle_set mesh;
@ -1069,7 +1114,7 @@ indexed_triangle_set its_make_sphere(double radius, double fa)
std::vector<std::array<DividedEdge, 3>> divided_triangles(indices.size());
std::vector<Vec3i> new_neighbors(4*indices.size());
size_t orig_indices_size = indices.size();
int orig_indices_size = int(indices.size());
for (int i=0; i<orig_indices_size; ++i) { // iterate over all old triangles
// We are going to split this triangle. Let's foresee what will be the indices

View file

@ -317,6 +317,7 @@ indexed_triangle_set its_make_cube(double x, double y, double z);
indexed_triangle_set its_make_prism(float width, float length, float height);
indexed_triangle_set its_make_cylinder(double r, double h, double fa=(2*PI/360));
indexed_triangle_set its_make_cone(double r, double h, double fa=(2*PI/360));
indexed_triangle_set its_make_frustum(double r, double h, double fa=(2*PI/360));
indexed_triangle_set its_make_frustum_dowel(double r, double h, int sectorCount);
indexed_triangle_set its_make_pyramid(float base, float height);
indexed_triangle_set its_make_sphere(double radius, double fa);

View file

@ -62,7 +62,11 @@ public:
// Inherits coord_t x, y
};
#define DEBUG_INTERSECTIONLINE (! defined(NDEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING))
#if (! defined(NDEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING))
#define DEBUG_INTERSECTION_LINE 1
#else
#define DEBUG_INTERSECTION_LINE 0
#endif
class IntersectionLine : public Line
{

View file

@ -172,11 +172,11 @@ void Bed3D::render_internal(GLCanvas3D& canvas, const Transform3d& view_matrix,
{
m_scale_factor = scale_factor;
glsafe(::glEnable(GL_DEPTH_TEST));
if (show_axes)
render_axes();
glsafe(::glEnable(GL_DEPTH_TEST));
m_model.model.set_color(picking ? PICKING_MODEL_COLOR : DEFAULT_MODEL_COLOR);
switch (m_type)
@ -366,6 +366,9 @@ std::tuple<Bed3D::Type, std::string, std::string> Bed3D::detect_type(const Point
void Bed3D::render_axes()
{
if (!m_show_axes)
return;
if (m_build_volume.valid())
#if ENABLE_WORLD_COORDINATE
m_axes.render(Transform3d::Identity(), 0.25f);

View file

@ -84,6 +84,7 @@ private:
#endif // ENABLE_WORLD_COORDINATE
float m_scale_factor{ 1.0f };
bool m_show_axes{ true };
public:
Bed3D() = default;
@ -111,6 +112,8 @@ public:
bool contains(const Point& point) const;
Point point_projection(const Point& point) const;
void toggle_show_axes() { m_show_axes = !m_show_axes; }
void render(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor, bool show_axes, bool show_texture);
void render_for_picking(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, float scale_factor);

View file

@ -77,7 +77,6 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
fill_density == 0 &&
! config->opt_bool("support_material") &&
config->opt_int("support_material_enforce_layers") == 0 &&
config->opt_bool("ensure_vertical_shell_thickness") &&
! config->opt_bool("thin_walls")))
{
wxString msg_text = _(L("The Spiral Vase mode requires:\n"
@ -85,7 +84,6 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
"- no top solid layers\n"
"- 0% fill density\n"
"- no support material\n"
"- Ensure vertical shell thickness enabled\n"
"- Detect thin walls disabled"));
if (is_global_config)
msg_text += "\n\n" + _(L("Shall I adjust those settings in order to enable Spiral Vase?"));
@ -100,7 +98,6 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
new_conf.set_key_value("fill_density", new ConfigOptionPercent(0));
new_conf.set_key_value("support_material", new ConfigOptionBool(false));
new_conf.set_key_value("support_material_enforce_layers", new ConfigOptionInt(0));
new_conf.set_key_value("ensure_vertical_shell_thickness", new ConfigOptionBool(true));
new_conf.set_key_value("thin_walls", new ConfigOptionBool(false));
fill_density = 0;
support = false;
@ -219,7 +216,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
{
bool have_perimeters = config->opt_int("perimeters") > 0;
for (auto el : { "extra_perimeters","extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "thin_walls", "overhangs",
for (auto el : { "extra_perimeters","extra_perimeters_on_overhangs", "thin_walls", "overhangs",
"seam_position","staggered_inner_seams", "external_perimeters_first", "external_perimeter_extrusion_width",
"perimeter_speed", "small_perimeter_speed", "external_perimeter_speed", "enable_dynamic_overhang_speeds"})
toggle_field(el, have_perimeters);

View file

@ -598,11 +598,11 @@ void TextCtrl::propagate_value()
if (m_opt.nullable && val != na_value())
m_last_meaningful_value = val;
if (!is_defined_input_value<wxTextCtrl>(window, m_opt.type) )
// on_kill_focus() cause a call of OptionsGroup::reload_config(),
// Thus, do it only when it's really needed (when undefined value was input)
if (!is_defined_input_value<wxTextCtrl>(window, m_opt.type) )
// on_kill_focus() cause a call of OptionsGroup::reload_config(),
// Thus, do it only when it's really needed (when undefined value was input)
on_kill_focus();
else if (value_was_changed())
else if (value_was_changed())
on_change_field();
}
@ -931,8 +931,8 @@ boost::any& SpinCtrl::get_value()
if (spin->GetTextValue() == na_value(true))
return m_value;
int value = spin->GetValue();
return m_value = value;
int value = spin->GetValue();
return m_value = value;
}
void SpinCtrl::propagate_value()
@ -946,7 +946,7 @@ void SpinCtrl::propagate_value()
if (tmp_value == UNDEF_VALUE) {
on_kill_focus();
} else {
} else {
#ifdef __WXOSX__
// check input value for minimum
if (m_opt.min > 0 && tmp_value < m_opt.min) {
@ -988,7 +988,6 @@ void Choice::BUILD() {
choice_ctrl* temp;
if (m_opt.gui_type != ConfigOptionDef::GUIType::undefined
&& m_opt.gui_type != ConfigOptionDef::GUIType::select_open
&& m_opt.gui_type != ConfigOptionDef::GUIType::select_close) {
m_is_editable = true;
temp = new choice_ctrl(m_parent, wxID_ANY, wxString(""), wxDefaultPosition, size, 0, nullptr, wxTE_PROCESS_ENTER);
@ -1152,7 +1151,7 @@ void Choice::set_value(const std::string& value, bool change_event) //! Redunda
field->SetSelection(*opt);
else
field->SetValue(value);
m_disable_change_event = false;
m_disable_change_event = false;
}
void Choice::set_value(const boost::any& value, bool change_event)

View file

@ -293,6 +293,20 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas)
return { w - thickness_bar_width(canvas), 0.0f, w, h };
}
std::pair<SlicingParameters, const std::vector<double>> GLCanvas3D::LayersEditing::get_layers_height_data()
{
if (m_slicing_parameters != nullptr)
return { *m_slicing_parameters, m_layer_height_profile };
assert(m_model_object != nullptr);
this->update_slicing_parameters();
PrintObject::update_layer_height_profile(*m_model_object, *m_slicing_parameters, m_layer_height_profile);
std::pair<SlicingParameters, const std::vector<double>> ret = { *m_slicing_parameters, m_layer_height_profile };
delete m_slicing_parameters;
m_slicing_parameters = nullptr;
return ret;
}
bool GLCanvas3D::LayersEditing::is_initialized() const
{
return wxGetApp().get_shader("variable_layer_height") != nullptr;
@ -999,6 +1013,19 @@ void GLCanvas3D::load_arrange_settings()
std::string en_rot_sla_str =
wxGetApp().app_config->get("arrange", "enable_rotation_sla");
// std::string alignment_fff_str =
// wxGetApp().app_config->get("arrange", "alignment_fff");
// std::string alignment_fff_seqp_str =
// wxGetApp().app_config->get("arrange", "alignment_fff_seq_pring");
// std::string alignment_sla_str =
// wxGetApp().app_config->get("arrange", "alignment_sla");
// Override default alignment and save save/load it to a temporary slot "alignment_xl"
std::string alignment_xl_str =
wxGetApp().app_config->get("arrange", "alignment_xl");
if (!dist_fff_str.empty())
m_arrange_settings_fff.distance = string_to_float_decimal_point(dist_fff_str);
@ -1025,6 +1052,24 @@ void GLCanvas3D::load_arrange_settings()
if (!en_rot_sla_str.empty())
m_arrange_settings_sla.enable_rotation = (en_rot_sla_str == "1" || en_rot_sla_str == "yes");
// if (!alignment_sla_str.empty())
// m_arrange_settings_sla.alignment = std::stoi(alignment_sla_str);
// if (!alignment_fff_str.empty())
// m_arrange_settings_fff.alignment = std::stoi(alignment_fff_str);
// if (!alignment_fff_seqp_str.empty())
// m_arrange_settings_fff_seq_print.alignment = std::stoi(alignment_fff_seqp_str);
// Override default alignment and save save/load it to a temporary slot "alignment_xl"
int arr_alignment = static_cast<int>(arrangement::Pivots::BottomLeft);
if (!alignment_xl_str.empty())
arr_alignment = std::stoi(alignment_xl_str);
m_arrange_settings_sla.alignment = arr_alignment ;
m_arrange_settings_fff.alignment = arr_alignment ;
m_arrange_settings_fff_seq_print.alignment = arr_alignment ;
}
PrinterTechnology GLCanvas3D::current_printer_technology() const
@ -1032,6 +1077,11 @@ PrinterTechnology GLCanvas3D::current_printer_technology() const
return m_process->current_printer_technology();
}
bool GLCanvas3D::is_arrange_alignment_enabled() const
{
return m_config ? is_XL_printer(*m_config) : false;
}
GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed)
: m_canvas(canvas)
, m_context(nullptr)
@ -2299,6 +2349,23 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
#endif /* __APPLE__ */
post_event(SimpleEvent(EVT_GLTOOLBAR_COPY));
break;
#ifdef __APPLE__
case 'd':
case 'D':
#else /* __APPLE__ */
case WXK_CONTROL_D:
#endif /* __APPLE__ */
m_bed.toggle_show_axes();
m_dirty = true;
break;
#ifdef __APPLE__
case 'f':
case 'F':
#else /* __APPLE__ */
case WXK_CONTROL_F:
#endif /* __APPLE__ */
_activate_search_toolbar_item();
break;
#ifdef __APPLE__
case 'm':
case 'M':
@ -2335,18 +2402,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
#endif /* __APPLE__ */
post_event(SimpleEvent(EVT_GLTOOLBAR_PASTE));
break;
#ifdef __APPLE__
case 'f':
case 'F':
#else /* __APPLE__ */
case WXK_CONTROL_F:
#endif /* __APPLE__ */
_activate_search_toolbar_item();
break;
#ifdef __APPLE__
case 'y':
case 'Y':
@ -4046,6 +4101,14 @@ void GLCanvas3D::apply_retina_scale(Vec2d &screen_coordinate) const
#endif // ENABLE_RETINA_GL
}
std::pair<SlicingParameters, const std::vector<double>> GLCanvas3D::get_layers_height_data(int object_id)
{
m_layers_editing.select_object(*m_model, object_id);
std::pair<SlicingParameters, const std::vector<double>> ret = m_layers_editing.get_layers_height_data();
m_layers_editing.select_object(*m_model, -1);
return ret;
}
bool GLCanvas3D::_is_shown_on_screen() const
{
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
@ -4160,7 +4223,7 @@ bool GLCanvas3D::_render_arrange_menu(float pos_x)
imgui->begin(_L("Arrange options"), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
ArrangeSettings settings = get_arrange_settings();
ArrangeSettings &settings_out = get_arrange_settings();
ArrangeSettings &settings_out = get_arrange_settings_ref(this);
auto &appcfg = wxGetApp().app_config;
PrinterTechnology ptech = current_printer_technology();
@ -4171,6 +4234,7 @@ bool GLCanvas3D::_render_arrange_menu(float pos_x)
std::string dist_key = "min_object_distance";
std::string dist_bed_key = "min_bed_distance";
std::string rot_key = "enable_rotation";
std::string align_key = "alignment";
std::string postfix;
if (ptech == ptSLA) {
@ -4189,6 +4253,7 @@ bool GLCanvas3D::_render_arrange_menu(float pos_x)
dist_key += postfix;
dist_bed_key += postfix;
rot_key += postfix;
align_key += postfix;
imgui->text(GUI::format_wxstr(_L("Press %1%left mouse button to enter the exact value"), shortkey_ctrl_prefix()));
@ -4212,11 +4277,28 @@ bool GLCanvas3D::_render_arrange_menu(float pos_x)
settings_changed = true;
}
Points bed = m_config ? get_bed_shape(*m_config) : Points{};
if (arrangement::is_box(bed) && settings.alignment >= 0 &&
imgui->combo(_L("Alignment"), {_u8L("Center"), _u8L("Rear left"), _u8L("Front left"), _u8L("Front right"), _u8L("Rear right"), _u8L("Random") }, settings.alignment)) {
settings_out.alignment = settings.alignment;
appcfg->set("arrange", align_key.c_str(), std::to_string(settings_out.alignment));
settings_changed = true;
}
ImGui::Separator();
if (imgui->button(_L("Reset"))) {
auto alignment = settings_out.alignment;
settings_out = ArrangeSettings{};
settings_out.distance = std::max(dist_min, settings_out.distance);
// Default alignment for XL printers set explicitly:
if (is_arrange_alignment_enabled())
settings_out.alignment = static_cast<int>(arrangement::Pivots::BottomLeft);
else
settings_out.alignment = alignment;
appcfg->set("arrange", dist_key.c_str(), float_to_string_decimal_point(settings_out.distance));
appcfg->set("arrange", dist_bed_key.c_str(), float_to_string_decimal_point(settings_out.distance_from_bed));
appcfg->set("arrange", rot_key.c_str(), settings_out.enable_rotation? "1" : "0");

View file

@ -291,6 +291,8 @@ class GLCanvas3D
std::string get_tooltip(const GLCanvas3D& canvas) const;
std::pair<SlicingParameters, const std::vector<double>> get_layers_height_data();
private:
bool is_initialized() const;
void generate_layer_height_texture();
@ -463,6 +465,7 @@ public:
// float distance_sla = 6.;
float accuracy = 0.65f; // Unused currently
bool enable_rotation = false;
int alignment = 0;
};
private:
@ -549,8 +552,10 @@ private:
PrinterTechnology current_printer_technology() const;
bool is_arrange_alignment_enabled() const;
template<class Self>
static auto & get_arrange_settings(Self *self) {
static auto & get_arrange_settings_ref(Self *self) {
PrinterTechnology ptech = self->current_printer_technology();
auto *ptr = &self->m_arrange_settings_fff;
@ -568,8 +573,22 @@ private:
return *ptr;
}
ArrangeSettings &get_arrange_settings() { return get_arrange_settings(this); }
public:
ArrangeSettings get_arrange_settings() const {
const ArrangeSettings &settings = get_arrange_settings_ref(this);
ArrangeSettings ret = settings;
if (&settings == &m_arrange_settings_fff_seq_print) {
ret.distance = std::max(ret.distance,
float(min_object_distance(*m_config)));
}
if (!is_arrange_alignment_enabled())
ret.alignment = -1;
return ret;
}
private:
void load_arrange_settings();
class SequentialPrintClearance
@ -901,17 +920,6 @@ public:
void highlight_toolbar_item(const std::string& item_name);
void highlight_gizmo(const std::string& gizmo_name);
ArrangeSettings get_arrange_settings() const {
const ArrangeSettings &settings = get_arrange_settings(this);
ArrangeSettings ret = settings;
if (&settings == &m_arrange_settings_fff_seq_print) {
ret.distance = std::max(ret.distance,
float(min_object_distance(*m_config)));
}
return ret;
}
// Timestamp for FPS calculation and notification fade-outs.
static int64_t timestamp_now() {
#ifdef _WIN32
@ -952,6 +960,8 @@ public:
void apply_retina_scale(Vec2d &screen_coordinate) const;
std::pair<SlicingParameters, const std::vector<double>> get_layers_height_data(int object_id);
private:
bool _is_shown_on_screen() const;

View file

@ -1840,6 +1840,7 @@ void ObjectList::load_mesh_object(
const TextConfiguration *text_config /* = nullptr*/,
const Transform3d * transformation /* = nullptr*/)
{
PlaterAfterLoadAutoArrange plater_after_load_auto_arrange;
// Add mesh to model as a new object
Model& model = wxGetApp().plater()->model();

View file

@ -64,26 +64,6 @@ static GLModel::Geometry its_make_line(Vec3f beg_pos, Vec3f end_pos)
return init_data;
}
// Generates mesh for a square plane
static GLModel::Geometry its_make_square_plane(float radius)
{
GLModel::Geometry init_data;
init_data.format = { GLModel::Geometry::EPrimitiveType::Triangles, GLModel::Geometry::EVertexLayout::P3 };
init_data.reserve_vertices(4);
init_data.reserve_indices(6);
// vertices
init_data.add_vertex(Vec3f(-radius, -radius, 0.0));
init_data.add_vertex(Vec3f(radius , -radius, 0.0));
init_data.add_vertex(Vec3f(radius , radius , 0.0));
init_data.add_vertex(Vec3f(-radius, radius , 0.0));
// indices
init_data.add_triangle(0, 1, 2);
init_data.add_triangle(2, 3, 0);
return init_data;
}
//! -- #ysFIXME those functions bodies are ported from GizmoRotation
// Generates mesh for a circle
static void init_from_circle(GLModel& model, double radius)
@ -235,6 +215,15 @@ GLGizmoCut3D::GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename,
{"flip", _L("Flip upside down")},
};
m_labels_map = {
{"Connectors" , _u8L("Connectors")},
{"Type" , _u8L("Type")},
{"Style" , _u8L("Style")},
{"Shape" , _u8L("Shape")},
{"Depth ratio" , _u8L("Depth ratio")},
{"Size" , _u8L("Size")},
};
update_connector_shape();
}
@ -576,9 +565,9 @@ void GLGizmoCut3D::render_move_center_input(int axis)
bool GLGizmoCut3D::render_connect_type_radio_button(CutConnectorType type)
{
ImGui::SameLine(type == CutConnectorType::Plug ? m_label_width : 2*m_label_width);
ImGui::SameLine(type == CutConnectorType::Plug ? m_label_width : 0);
ImGui::PushItemWidth(m_control_width);
if (m_imgui->radio_button(m_connector_types[size_t(type)], m_connector_type == type)) {
if (ImGui::RadioButton(m_connector_types[size_t(type)].c_str(), m_connector_type == type)) {
m_connector_type = type;
update_connector_shape();
return true;
@ -588,9 +577,9 @@ bool GLGizmoCut3D::render_connect_type_radio_button(CutConnectorType type)
void GLGizmoCut3D::render_connect_mode_radio_button(CutConnectorMode mode)
{
ImGui::SameLine(mode == CutConnectorMode::Auto ? m_label_width : 2*m_label_width);
ImGui::SameLine(mode == CutConnectorMode::Auto ? m_label_width : 2 * m_label_width);
ImGui::PushItemWidth(m_control_width);
if (m_imgui->radio_button(m_connector_modes[int(mode)], m_connector_mode == mode))
if (ImGui::RadioButton(m_connector_modes[int(mode)].c_str(), m_connector_mode == mode))
m_connector_mode = mode;
}
@ -618,36 +607,6 @@ bool GLGizmoCut3D::render_reset_button(const std::string& label_id, const std::s
return revert;
}
static Vec2d ndc_to_ss(const Vec3d& ndc, const std::array<int, 4>& viewport) {
const double half_w = 0.5 * double(viewport[2]);
const double half_h = 0.5 * double(viewport[3]);
return { half_w * ndc.x() + double(viewport[0]) + half_w, half_h * ndc.y() + double(viewport[1]) + half_h };
};
static Vec3d clip_to_ndc(const Vec4d& clip) {
return Vec3d(clip.x(), clip.y(), clip.z()) / clip.w();
}
static Vec4d world_to_clip(const Vec3d& world, const Matrix4d& projection_view_matrix) {
return projection_view_matrix * Vec4d(world.x(), world.y(), world.z(), 1.0);
}
static Vec2d world_to_ss(const Vec3d& world, const Matrix4d& projection_view_matrix, const std::array<int, 4>& viewport) {
return ndc_to_ss(clip_to_ndc(world_to_clip(world, projection_view_matrix)), viewport);
}
static wxString get_label(Vec3d vec)
{
wxString str = "x=" + double_to_string(vec.x(), 2) +
", y=" + double_to_string(vec.y(), 2) +
", z=" + double_to_string(vec.z(), 2);
return str;
}
static wxString get_label(Vec2d vec)
{
wxString str = "x=" + double_to_string(vec.x(), 2) +
", y=" + double_to_string(vec.y(), 2);
return str;
}
void GLGizmoCut3D::render_cut_plane()
{
if (cut_line_processing())
@ -1534,7 +1493,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors)
// render_connect_mode_radio_button(CutConnectorMode::Manual);
ImGui::AlignTextToFramePadding();
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, _L("Connectors"));
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, m_labels_map["Connectors"]);
m_imgui->disabled_begin(connectors.empty());
ImGui::SameLine(m_label_width);
@ -1547,7 +1506,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors)
render_flip_plane_button(m_connectors_editing && connectors.empty());
m_imgui->text(_L("Type"));
m_imgui->text(m_labels_map["Type"]);
bool type_changed = render_connect_type_radio_button(CutConnectorType::Plug);
type_changed |= render_connect_type_radio_button(CutConnectorType::Dowel);
if (type_changed)
@ -1558,14 +1517,14 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors)
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))
if (render_combo(m_labels_map["Style"], m_connector_styles, m_connector_style))
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.style = CutConnectorStyle(m_connector_style); });
m_imgui->disabled_end();
if (render_combo(_u8L("Shape"), m_connector_shapes, m_connector_shape_id))
if (render_combo(m_labels_map["Shape"], m_connector_shapes, m_connector_shape_id))
apply_selected_connectors([this, &connectors](size_t idx) { connectors[idx].attribs.shape = CutConnectorShape(m_connector_shape_id); });
if (render_slider_double_input(_u8L("Depth ratio"), m_connector_depth_ratio, m_connector_depth_ratio_tolerance))
if (render_slider_double_input(m_labels_map["Depth ratio"], m_connector_depth_ratio, m_connector_depth_ratio_tolerance))
apply_selected_connectors([this, &connectors](size_t idx) {
if (m_connector_depth_ratio > 0)
connectors[idx].height = m_connector_depth_ratio;
@ -1573,7 +1532,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors)
connectors[idx].height_tolerance = m_connector_depth_ratio_tolerance;
});
if (render_slider_double_input(_u8L("Size"), m_connector_size, m_connector_size_tolerance))
if (render_slider_double_input(m_labels_map["Size"], m_connector_size, m_connector_size_tolerance))
apply_selected_connectors([this, &connectors](size_t idx) {
if (m_connector_size > 0)
connectors[idx].radius = 0.5f * m_connector_size;
@ -1588,7 +1547,7 @@ void GLGizmoCut3D::render_connectors_input_window(CutConnectors &connectors)
set_connectors_editing(false);
}
ImGui::SameLine(2.75f * m_label_width);
ImGui::SameLine(m_label_width + 1.15f * m_control_width);
if (m_imgui->button(_L("Cancel"))) {
reset_connectors();
@ -1608,7 +1567,7 @@ void GLGizmoCut3D::render_build_size()
ImGui::AlignTextToFramePadding();
m_imgui->text(_L("Build Volume"));
ImGui::SameLine(m_label_width);
ImGui::SameLine();
m_imgui->text_colored(ImGuiWrapper::COL_ORANGE_LIGHT, size);
}
@ -1657,7 +1616,7 @@ void GLGizmoCut3D::flip_cut_plane()
void GLGizmoCut3D::render_flip_plane_button(bool disable_pred /*=false*/)
{
ImGui::SameLine(2.5 * m_label_width);
ImGui::SameLine();
if (m_hover_id == CutPlane)
ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_ButtonHovered));
@ -1714,7 +1673,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
ImGui::AlignTextToFramePadding();
ImGuiWrapper::text(_L("Cut position: "));
ImGui::SameLine(m_label_width);
ImGui::SameLine();
render_move_center_input(Z);
ImGui::SameLine();
@ -1738,7 +1697,7 @@ void GLGizmoCut3D::render_cut_plane_input_window(CutConnectors &connectors)
set_connectors_editing(true);
m_imgui->disabled_end();
ImGui::SameLine(2.5f * m_label_width);
ImGui::SameLine(1.5f * m_control_width);
m_imgui->disabled_begin(is_cut_plane_init && !has_connectors);
act_name = _L("Reset cut");
@ -1864,7 +1823,6 @@ void GLGizmoCut3D::validate_connector_settings()
void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors)
{
m_imperial_units = wxGetApp().app_config->get_bool("use_inches");
m_label_width = m_imgui->get_font_size() * 6.f;
m_control_width = m_imgui->get_font_size() * 9.f;
if (m_connectors_editing && m_selected_count > 0) {
@ -1920,6 +1878,15 @@ void GLGizmoCut3D::init_input_window_data(CutConnectors &connectors)
m_connector_style = size_t(style);
m_connector_shape_id = size_t(shape);
}
if (m_label_width == 0.f) {
for (const auto& item : m_labels_map) {
const float width = ImGuiWrapper::calc_text_size(item.second).x;
if (m_label_width < width)
m_label_width = width;
}
m_label_width += m_imgui->scaled(1.f);
}
}
void GLGizmoCut3D::render_input_window_warning() const
@ -2257,13 +2224,16 @@ void GLGizmoCut3D::reset_connectors()
void GLGizmoCut3D::init_connector_shapes()
{
for (const CutConnectorType& type : {CutConnectorType::Dowel, CutConnectorType::Plug})
for (const CutConnectorStyle& style : {CutConnectorStyle::Frustum, CutConnectorStyle::Prism})
for (const CutConnectorStyle& style : {CutConnectorStyle::Frustum, CutConnectorStyle::Prism}) {
if (type == CutConnectorType::Dowel && style == CutConnectorStyle::Frustum)
continue;
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);
m_shapes[attribs].model.init_from(its);
m_shapes[attribs].mesh_raycaster = std::make_unique<MeshRaycaster>(std::make_shared<const TriangleMesh>(std::move(its)));
}
}
}
void GLGizmoCut3D::update_connector_shape()

View file

@ -118,8 +118,8 @@ class GLGizmoCut3D : public GLGizmoBase
float m_connector_depth_ratio_tolerance{ 0.1f };
float m_connector_size_tolerance{ 0.f };
float m_label_width{ 150.0 };
float m_control_width{ 200.0 };
float m_label_width{ 0.f };
float m_control_width{ 200.f };
bool m_imperial_units{ false };
float m_contour_width{ 0.4f };
@ -169,6 +169,8 @@ class GLGizmoCut3D : public GLGizmoBase
std::map<std::string, wxString> m_part_orientation_names;
std::map<std::string, std::string> m_labels_map;
public:
GLGizmoCut3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);

View file

@ -1993,6 +1993,13 @@ void GLGizmoMeasure::on_render_input_window(float x, float y, float bottom_limit
radius = ObjectManipulation::mm_to_in * radius;
text += " (" + _u8L("Diameter") + ": " + format_double(2.0 * radius) + units + ")";
}
else if (item.feature.has_value() && item.feature->get_type() == Measure::SurfaceFeatureType::Edge) {
auto [start, end] = item.feature->get_edge();
double length = (end - start).norm();
if (use_inches)
length = ObjectManipulation::mm_to_in * length;
text += " (" + _u8L("Length") + ": " + format_double(length) + units + ")";
}
return text;
};

View file

@ -14,6 +14,7 @@
#include "libnest2d/common.hpp"
#include <numeric>
#include <random>
namespace Slic3r { namespace GUI {
@ -168,11 +169,11 @@ void ArrangeJob::process(Ctl &ctl)
static const auto arrangestr = _u8L("Arranging");
arrangement::ArrangeParams params;
Points bedpts;
ctl.call_on_main_thread([this, &params, &bedpts]{
arrangement::ArrangeBed bed;
ctl.call_on_main_thread([this, &params, &bed]{
prepare();
params = get_arrange_params(m_plater);
bedpts = get_bed_shape(*m_plater->config());
get_bed_shape(*m_plater->config(), bed);
}).wait();
auto count = unsigned(m_selected.size() + m_unprintable.size());
@ -191,13 +192,13 @@ void ArrangeJob::process(Ctl &ctl)
ctl.update_status(0, arrangestr);
arrangement::arrange(m_selected, m_unselected, bedpts, params);
arrangement::arrange(m_selected, m_unselected, bed, params);
params.progressind = [this, count, &ctl](unsigned st) {
if (st > 0) ctl.update_status(int(count - st) * 100 / status_range(), arrangestr);
};
arrangement::arrange(m_unprintable, {}, bedpts, params);
arrangement::arrange(m_unprintable, {}, bed, params);
// finalize just here.
ctl.update_status(int(count) * 100 / status_range(), ctl.was_canceled() ?
@ -291,13 +292,30 @@ arrangement::ArrangePolygon get_arrange_poly(ModelInstance *inst,
arrangement::ArrangeParams get_arrange_params(Plater *p)
{
const GLCanvas3D::ArrangeSettings &settings =
static_cast<const GLCanvas3D*>(p->canvas3D())->get_arrange_settings();
p->canvas3D()->get_arrange_settings();
arrangement::ArrangeParams params;
params.allow_rotations = settings.enable_rotation;
params.min_obj_distance = scaled(settings.distance);
params.min_bed_distance = scaled(settings.distance_from_bed);
arrangement::Pivots pivot = arrangement::Pivots::Center;
int pivot_max = static_cast<int>(arrangement::Pivots::TopRight);
if (settings.alignment < 0) {
pivot = arrangement::Pivots::Center;
} else if (settings.alignment > pivot_max) {
// means it should be random
std::random_device rd{};
std::mt19937 rng(rd());
std::uniform_int_distribution<std::mt19937::result_type> dist(0, pivot_max);
pivot = static_cast<arrangement::Pivots>(dist(rng));
} else {
pivot = static_cast<arrangement::Pivots>(settings.alignment);
}
params.alignment = pivot;
return params;
}

View file

@ -106,18 +106,15 @@ void FillBedJob::prepare()
void FillBedJob::process(Ctl &ctl)
{
auto statustxt = _u8L("Filling bed");
ctl.call_on_main_thread([this] { prepare(); }).wait();
arrangement::ArrangeParams params;
ctl.call_on_main_thread([this, &params] {
prepare();
params = get_arrange_params(m_plater);
}).wait();
ctl.update_status(0, statustxt);
if (m_object_idx == -1 || m_selected.empty()) return;
const GLCanvas3D::ArrangeSettings &settings =
static_cast<const GLCanvas3D*>(m_plater->canvas3D())->get_arrange_settings();
arrangement::ArrangeParams params;
params.allow_rotations = settings.enable_rotation;
params.min_obj_distance = scaled(settings.distance);
params.min_bed_distance = scaled(settings.distance_from_bed);
if (m_object_idx == -1 || m_selected.empty())
return;
bool do_stop = false;
params.stopcondition = [&ctl, &do_stop]() {

View file

@ -151,6 +151,7 @@ void KBShortcutsDialog::fill_shortcuts()
{ L("Arrow Right"), L("Move selection 10 mm in positive X direction") },
{ std::string("Shift+") + L("Any arrow"), L("Movement step set to 1 mm") },
{ ctrl + L("Any arrow"), L("Movement in camera space") },
{ ctrl + "D", L("Show/hide reference axes") },
{ L("Page Up"), L("Rotate selection 45 degrees CCW") },
{ L("Page Down"), L("Rotate selection 45 degrees CW") },
{ "M", L("Gizmo move") },
@ -231,6 +232,7 @@ void KBShortcutsDialog::fill_shortcuts()
{ "X", L("On/Off one layer mode of the vertical slider") },
{ "L", L("Show/Hide legend") },
{ "C", L("Show/Hide G-code window") },
{ ctrl + "D", L("Show/hide reference axes") },
};
m_full_shortcuts.push_back({ { _L("Preview"), "" }, preview_shortcuts });

View file

@ -1293,7 +1293,7 @@ void MainFrame::init_menubar_as_editor()
[this](wxCommandEvent&) { if (m_plater) m_plater->import_sl1_archive(); }, "import_plater", nullptr,
[this](){return m_plater != nullptr && m_plater->get_ui_job_worker().is_idle(); }, this);
append_menu_item(import_menu, wxID_ANY, _L("Import ZIP Achive") + dots, _L("Load a zip achive"),
append_menu_item(import_menu, wxID_ANY, _L("Import ZIP Archive") + dots, _L("Load a ZIP archive"),
[this](wxCommandEvent&) { if (m_plater) m_plater->import_zip_archive(); }, "import_plater", nullptr,
[this]() {return m_plater != nullptr; }, this);

View file

@ -9,6 +9,7 @@
#include <future>
#include <boost/algorithm/string.hpp>
#include <boost/optional.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/log/trivial.hpp>
@ -2439,6 +2440,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
auto *nozzle_dmrs = config->opt<ConfigOptionFloats>("nozzle_diameter");
PlaterAfterLoadAutoArrange plater_after_load_auto_arrange;
bool one_by_one = input_files.size() == 1 || printer_technology == ptSLA || nozzle_dmrs->values.size() <= 1;
if (! one_by_one) {
for (const auto &path : input_files) {
@ -2698,6 +2701,9 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
new_model->add_object(*model_object);
}
}
if (is_project_file)
plater_after_load_auto_arrange.disable();
}
}
@ -7587,4 +7593,18 @@ SuppressBackgroundProcessingUpdate::~SuppressBackgroundProcessingUpdate()
wxGetApp().plater()->schedule_background_process(m_was_scheduled);
}
PlaterAfterLoadAutoArrange::PlaterAfterLoadAutoArrange()
{
Plater* plater = wxGetApp().plater();
m_enabled = plater->model().objects.empty() &&
plater->printer_technology() == ptFFF &&
plater->fff_print().config().printer_model.value == "XL";
}
PlaterAfterLoadAutoArrange::~PlaterAfterLoadAutoArrange()
{
if (m_enabled)
wxGetApp().plater()->arrange();
}
}} // namespace Slic3r::GUI

View file

@ -507,6 +507,16 @@ private:
bool m_was_scheduled;
};
class PlaterAfterLoadAutoArrange
{
bool m_enabled{ false };
public:
PlaterAfterLoadAutoArrange();
~PlaterAfterLoadAutoArrange();
void disable() { m_enabled = false; }
};
} // namespace GUI
} // namespace Slic3r

View file

@ -197,7 +197,7 @@ std::string PrintHostSendDialog::storage() const
{
if (!combo_storage)
return GUI::format("%1%", m_preselected_storage);
if (combo_storage->GetSelection() < 0 || combo_storage->GetSelection() >= m_paths.size())
if (combo_storage->GetSelection() < 0 || combo_storage->GetSelection() >= int(m_paths.size()))
return {};
return boost::nowide::narrow(m_paths[combo_storage->GetSelection()]);
}

View file

@ -1302,11 +1302,12 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
void Selection::scale_to_fit_print_volume(const BuildVolume& volume)
{
auto fit = [this](double s, Vec3d offset) {
auto fit = [this](double s, Vec3d offset, bool undoredo_snapshot) {
if (s <= 0.0 || s == 1.0)
return;
return false;
wxGetApp().plater()->take_snapshot(_L("Scale To Fit"));
if (undoredo_snapshot)
wxGetApp().plater()->take_snapshot(_L("Scale To Fit"));
TransformationType type;
type.set_world();
@ -1331,29 +1332,32 @@ void Selection::scale_to_fit_print_volume(const BuildVolume& volume)
wxGetApp().plater()->canvas3D()->do_move(""); // avoid storing another snapshot
wxGetApp().obj_manipul()->set_dirty();
return undoredo_snapshot;
};
auto fit_rectangle = [this, fit](const BuildVolume& volume) {
auto fit_rectangle = [this, fit](const BuildVolume& volume, bool undoredo_snapshot, double* max_height = nullptr) {
const BoundingBoxf3 print_volume = volume.bounding_volume();
const Vec3d print_volume_size = print_volume.size();
Vec3d print_volume_size = print_volume.size();
print_volume_size.z() = (max_height != nullptr) ? *max_height : volume.max_print_height();
// adds 1/100th of a mm on all sides to avoid false out of print volume detections due to floating-point roundings
const Vec3d box_size = get_bounding_box().size() + 0.02 * Vec3d::Ones();
// adds 1/100th of a mm on both xy sides to avoid false out of print volume detections due to floating-point roundings
Vec3d box_size = get_bounding_box().size();
box_size.x() += 0.02;
box_size.y() += 0.02;
const double sx = (box_size.x() != 0.0) ? print_volume_size.x() / box_size.x() : 0.0;
const double sy = (box_size.y() != 0.0) ? print_volume_size.y() / box_size.y() : 0.0;
const double sz = (box_size.z() != 0.0) ? print_volume_size.z() / box_size.z() : 0.0;
const double sx = print_volume_size.x() / box_size.x();
const double sy = print_volume_size.y() / box_size.y();
const double sz = print_volume_size.z() / box_size.z();
if (sx != 0.0 && sy != 0.0 && sz != 0.0)
fit(std::min(sx, std::min(sy, sz)), print_volume.center() - get_bounding_box().center());
return fit(std::min(sx, std::min(sy, sz)), print_volume.center() - get_bounding_box().center(), undoredo_snapshot);
};
auto fit_circle = [this, fit](const BuildVolume& volume) {
auto fit_circle = [this, fit](const BuildVolume& volume, bool undoredo_snapshot, double* max_height = nullptr) {
const Geometry::Circled& print_circle = volume.circle();
double print_circle_radius = unscale<double>(print_circle.radius);
if (print_circle_radius == 0.0)
return;
return false;
Points points;
double max_z = 0.0;
@ -1367,31 +1371,56 @@ void Selection::scale_to_fit_print_volume(const BuildVolume& volume)
}
if (points.empty())
return;
return false;
const Geometry::Circled circle = Geometry::smallest_enclosing_circle_welzl(points);
// adds 1/100th of a mm on all sides to avoid false out of print volume detections due to floating-point roundings
const double circle_radius = unscale<double>(circle.radius) + 0.01;
if (circle_radius == 0.0 || max_z == 0.0)
return;
return false;
const double s = std::min(print_circle_radius / circle_radius, volume.max_print_height() / max_z);
const double print_volume_max_z = (max_height != nullptr) ? *max_height : volume.max_print_height();
const double s = std::min(print_circle_radius / circle_radius, print_volume_max_z / max_z);
const Vec3d sel_center = get_bounding_box().center();
const Vec3d offset = s * (Vec3d(unscale<double>(circle.center.x()), unscale<double>(circle.center.y()), 0.5 * max_z) - sel_center);
const Vec3d print_center = { unscale<double>(print_circle.center.x()), unscale<double>(print_circle.center.y()), 0.5 * volume.max_print_height() };
fit(s, print_center - (sel_center + offset));
return fit(s, print_center - (sel_center + offset), undoredo_snapshot);
};
if (is_empty() || m_mode == Volume)
return;
assert(is_single_full_instance());
// used to keep track whether the undo/redo snapshot has already been taken
bool undoredo_snapshot = false;
switch (volume.type())
{
case BuildVolume::Type::Rectangle: { fit_rectangle(volume); break; }
case BuildVolume::Type::Circle: { fit_circle(volume); break; }
case BuildVolume::Type::Rectangle: { undoredo_snapshot = fit_rectangle(volume, !undoredo_snapshot); break; }
case BuildVolume::Type::Circle: { undoredo_snapshot = fit_circle(volume, !undoredo_snapshot); break; }
default: { break; }
}
if (wxGetApp().plater()->printer_technology() == ptFFF) {
// check whether the top layer exceeds the maximum height of the print volume
// and, in case, reduce the scale accordingly
const auto [slicing_parameters, profile] = wxGetApp().plater()->canvas3D()->get_layers_height_data(get_object_idx());
auto layers = generate_object_layers(slicing_parameters, profile);
auto layers_it = layers.rbegin();
while (layers_it != layers.rend() && *layers_it > volume.bounding_volume().max.z()) {
++layers_it;
}
if (layers_it != layers.rbegin() && layers_it != layers.rend()) {
switch (volume.type())
{
case BuildVolume::Type::Rectangle: { fit_rectangle(volume, !undoredo_snapshot, &(*layers_it)); break; }
case BuildVolume::Type::Circle: { fit_circle(volume, !undoredo_snapshot, &(*layers_it)); break; }
default: { break; }
}
}
}
}
#if ENABLE_WORLD_COORDINATE

View file

@ -1427,7 +1427,6 @@ void TabPrint::build()
optgroup = page->new_optgroup(L("Quality (slower slicing)"));
optgroup->append_single_option_line("extra_perimeters", category_path + "extra-perimeters-if-needed");
optgroup->append_single_option_line("extra_perimeters_on_overhangs", category_path + "extra-perimeters-on-overhangs");
optgroup->append_single_option_line("ensure_vertical_shell_thickness", category_path + "ensure-vertical-shell-thickness");
optgroup->append_single_option_line("avoid_crossing_curled_overhangs", category_path + "avoid-crossing-curled-overhangs");
optgroup->append_single_option_line("avoid_crossing_perimeters", category_path + "avoid-crossing-perimeters");
optgroup->append_single_option_line("avoid_crossing_perimeters_max_detour", category_path + "avoid_crossing_perimeters_max_detour");

View file

@ -458,44 +458,6 @@ wxBitmapBundle* get_solid_bmp_bundle(int width, int height, const std::string& c
#endif // __WXGTK2__
}
// win is used to get a correct em_unit value
// It's important for bitmaps of dialogs.
// if win == nullptr, em_unit value of MainFrame will be used
wxBitmap create_scaled_bitmap( const std::string& bmp_name_in,
wxWindow *win/* = nullptr*/,
const int px_cnt/* = 16*/,
const bool grayscale/* = false*/,
const std::string& new_color/* = std::string()*/, // color witch will used instead of orange
const bool menu_bitmap/* = false*/)
{
static Slic3r::GUI::BitmapCache cache;
unsigned int width = 0;
unsigned int height = (unsigned int)(em_unit(win) * px_cnt * 0.1f + 0.5f);
std::string bmp_name = bmp_name_in;
boost::replace_last(bmp_name, ".png", "");
bool dark_mode =
#ifdef _WIN32
menu_bitmap ? Slic3r::GUI::check_dark_mode() :
#endif
Slic3r::GUI::wxGetApp().dark_mode();
// Try loading an SVG first, then PNG if SVG is not found:
wxBitmap *bmp = cache.load_svg(bmp_name, width, height, grayscale, dark_mode, new_color);
if (bmp == nullptr) {
bmp = cache.load_png(bmp_name, width, height, grayscale);
}
if (bmp == nullptr) {
// Neither SVG nor PNG has been found, raise error
throw Slic3r::RuntimeError("Could not load bitmap: " + bmp_name);
}
return *bmp;
}
std::vector<wxBitmapBundle*> get_extruder_color_icons(bool thin_icon/* = false*/)
{
// Create the bitmap with color bars.
@ -742,7 +704,6 @@ void ModeButton::sys_color_changed()
ModeSizer::ModeSizer(wxWindow *parent, int hgap/* = 0*/) :
wxFlexGridSizer(3, 0, hgap),
m_parent(parent),
m_hgap_unscaled((double)(hgap)/em_unit(parent))
{
SetFlexibleDirection(wxHORIZONTAL);

View file

@ -54,11 +54,6 @@ wxBitmapBundle* get_bmp_bundle(const std::string& bmp_name, int px_cnt = 16, con
wxBitmapBundle* get_empty_bmp_bundle(int width, int height);
wxBitmapBundle* get_solid_bmp_bundle(int width, int height, const std::string& color);
wxBitmap create_scaled_bitmap(const std::string& bmp_name, wxWindow *win = nullptr,
const int px_cnt = 16, const bool grayscale = false,
const std::string& new_color = std::string(), // color witch will used instead of orange
const bool menu_bitmap = false);
std::vector<wxBitmapBundle*> get_extruder_color_icons(bool thin_icon = false);
namespace Slic3r {
@ -172,7 +167,6 @@ private:
wxBitmap m_bitmap = wxBitmap();
std::string m_icon_name = "";
int m_px_cnt {16};
bool m_grayscale {false};
};
@ -338,7 +332,6 @@ public:
private:
std::vector<ModeButton*> m_mode_btns;
wxWindow* m_parent {nullptr};
double m_hgap_unscaled;
};

View file

@ -11,7 +11,7 @@
#include <boost/date_time/posix_time/posix_time_duration.hpp>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
#include <boost/bind.hpp>
#include <boost/bind/bind.hpp>
using boost::optional;
using boost::system::error_code;

View file

@ -4,7 +4,7 @@
#include <boost/asio/read_until.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/write.hpp>
#include <boost/bind.hpp>
#include <boost/bind/bind.hpp>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
#include <boost/algorithm/string.hpp>
@ -41,7 +41,7 @@ void TCPConsole::transmit_next_command()
boost::asio::async_write(
m_socket,
boost::asio::buffer(m_send_buffer),
boost::bind(&TCPConsole::handle_write, this, _1, _2)
boost::bind(&TCPConsole::handle_write, this, boost::placeholders::_1, boost::placeholders::_2)
);
}
@ -52,7 +52,7 @@ void TCPConsole::wait_next_line()
m_socket,
m_recv_buffer,
m_newline,
boost::bind(&TCPConsole::handle_read, this, _1, _2)
boost::bind(&TCPConsole::handle_read, this, boost::placeholders::_1, boost::placeholders::_2)
);
}
@ -157,7 +157,7 @@ bool TCPConsole::run_queue()
auto endpoints = m_resolver.resolve(m_host_name, m_port_name);
m_socket.async_connect(endpoints->endpoint(),
boost::bind(&TCPConsole::handle_connect, this, _1)
boost::bind(&TCPConsole::handle_connect, this, boost::placeholders::_1)
);
// Loop until we get any reasonable result. Negative result is also result.

View file

@ -312,24 +312,24 @@ SCENARIO("Infill only where needed", "[Fill]")
double tolerance = 5; // mm^2
GIVEN("solid_infill_below_area == 0") {
config.opt_float("solid_infill_below_area") = 0;
WHEN("pyramid is sliced ") {
auto area = test();
THEN("no infill is generated when using infill_only_where_needed on a pyramid") {
REQUIRE(area < tolerance);
}
}
}
GIVEN("solid_infill_below_area == 70") {
config.opt_float("solid_infill_below_area") = 70;
WHEN("pyramid is sliced ") {
auto area = test();
THEN("infill is only generated under the forced solid shells") {
REQUIRE(std::abs(area - 70) < tolerance);
}
}
}
// GIVEN("solid_infill_below_area == 0") {
// config.opt_float("solid_infill_below_area") = 0;
// WHEN("pyramid is sliced ") {
// auto area = test();
// THEN("no infill is generated when using infill_only_where_needed on a pyramid") {
// REQUIRE(area < tolerance);
// }
// }
// }
// GIVEN("solid_infill_below_area == 70") {
// config.opt_float("solid_infill_below_area") = 70;
// WHEN("pyramid is sliced ") {
// auto area = test();
// THEN("infill is only generated under the forced solid shells") {
// REQUIRE(std::abs(area - 70) < tolerance);
// }
// }
// }
}
SCENARIO("Combine infill", "[Fill]")

View file

@ -556,9 +556,9 @@ SCENARIO("Perimeters3", "[Perimeters]")
GIVEN("V shape, unscaled") {
int n = test(Vec3d(1., 1., 1.));
// except for the two internal solid layers above void
// One bridge layer under the V middle and one layer (two briding areas) under tops
THEN("no overhangs printed with bridge speed") {
REQUIRE(n == 1);
REQUIRE(n == 2);
}
}
GIVEN("V shape, scaled 3x in X") {

View file

@ -147,63 +147,65 @@ SCENARIO("Shells (from Perl)", "[Shells]") {
REQUIRE(n == 3);
}
}
GIVEN("V shape") {
// we need to check against one perimeter because this test is calibrated
// (shape, extrusion_width) so that perimeters cover the bottom surfaces of
// their lower layer - the test checks that shells are not generated on the
// above layers (thus 'across' the shadow perimeter)
// the test is actually calibrated to leave a narrow bottom region for each
// layer - we test that in case of fill_density = 0 such narrow shells are
// discarded instead of grown
int bottom_solid_layers = 3;
auto config = Slic3r::DynamicPrintConfig::full_print_config_with({
{ "perimeters", 1 },
{ "fill_density", 0 },
// to prevent speeds from being altered
{ "cooling", "0" },
// to prevent speeds from being altered
{ "first_layer_speed", "100%" },
// prevent speed alteration
{ "enable_dynamic_overhang_speeds", 0 },
{ "layer_height", 0.4 },
{ "first_layer_height", 0.4 },
{ "extrusion_width", 0.55 },
{ "bottom_solid_layers", bottom_solid_layers },
{ "top_solid_layers", 0 },
{ "solid_infill_speed", 99 }
});
THEN("shells are not propagated across perimeters of the neighbor layer") {
std::string gcode = Slic3r::Test::slice({TestMesh::V}, config);
REQUIRE(layers_with_speed(gcode, 99).size() == bottom_solid_layers);
}
}
GIVEN("sloping_hole") {
int bottom_solid_layers = 3;
int top_solid_layers = 3;
int solid_speed = 99;
auto config = Slic3r::DynamicPrintConfig::full_print_config_with({
{ "perimeters", 3 },
// to prevent speeds from being altered
{ "cooling", "0" },
// to prevent speeds from being altered
{ "first_layer_speed", "100%" },
// prevent speed alteration
{ "enable_dynamic_overhang_speeds", 0 },
{ "layer_height", 0.4 },
{ "first_layer_height", 0.4 },
{ "bottom_solid_layers", bottom_solid_layers },
{ "top_solid_layers", top_solid_layers },
{ "solid_infill_speed", solid_speed },
{ "top_solid_infill_speed", solid_speed },
{ "bridge_speed", solid_speed },
{ "filament_diameter", 3. },
{ "nozzle_diameter", 0.5 }
});
THEN("no superfluous shells are generated") {
std::string gcode = Slic3r::Test::slice({TestMesh::sloping_hole}, config);
REQUIRE(layers_with_speed(gcode, solid_speed).size() == bottom_solid_layers + top_solid_layers);
}
}
//TODO CHECK AFTER REMOVAL OF "ensure_vertical_wall_thickness"
// GIVEN("V shape") {
// // we need to check against one perimeter because this test is calibrated
// // (shape, extrusion_width) so that perimeters cover the bottom surfaces of
// // their lower layer - the test checks that shells are not generated on the
// // above layers (thus 'across' the shadow perimeter)
// // the test is actually calibrated to leave a narrow bottom region for each
// // layer - we test that in case of fill_density = 0 such narrow shells are
// // discarded instead of grown
// int bottom_solid_layers = 3;
// auto config = Slic3r::DynamicPrintConfig::full_print_config_with({
// { "perimeters", 1 },
// { "fill_density", 0 },
// // to prevent speeds from being altered
// { "cooling", "0" },
// // to prevent speeds from being altered
// { "first_layer_speed", "100%" },
// // prevent speed alteration
// { "enable_dynamic_overhang_speeds", 0 },
// { "layer_height", 0.4 },
// { "first_layer_height", 0.4 },
// { "extrusion_width", 0.55 },
// { "bottom_solid_layers", bottom_solid_layers },
// { "top_solid_layers", 0 },
// { "solid_infill_speed", 99 }
// });
// THEN("shells are not propagated across perimeters of the neighbor layer") {
// std::string gcode = Slic3r::Test::slice({TestMesh::V}, config);
// REQUIRE(layers_with_speed(gcode, 99).size() == bottom_solid_layers);
// }
// }
// GIVEN("sloping_hole") {
// int bottom_solid_layers = 3;
// int top_solid_layers = 3;
// int solid_speed = 99;
// auto config = Slic3r::DynamicPrintConfig::full_print_config_with({
// { "perimeters", 3 },
// // to prevent speeds from being altered
// { "cooling", "0" },
// // to prevent speeds from being altered
// { "first_layer_speed", "100%" },
// // prevent speed alteration
// { "enable_dynamic_overhang_speeds", 0 },
// { "layer_height", 0.4 },
// { "first_layer_height", 0.4 },
// { "bottom_solid_layers", bottom_solid_layers },
// { "top_solid_layers", top_solid_layers },
// { "solid_infill_speed", solid_speed },
// { "top_solid_infill_speed", solid_speed },
// { "bridge_speed", solid_speed },
// { "filament_diameter", 3. },
// { "nozzle_diameter", 0.5 }
// });
// THEN("no superfluous shells are generated") {
// std::string gcode = Slic3r::Test::slice({TestMesh::sloping_hole}, config);
// REQUIRE(layers_with_speed(gcode, solid_speed).size() == bottom_solid_layers + top_solid_layers);
// }
// }
GIVEN("20mm_cube, spiral vase") {
double layer_height = 0.3;
auto config = Slic3r::DynamicPrintConfig::full_print_config_with({

View file

@ -848,7 +848,16 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
using EcmType = CGAL::internal::Dynamic<MyMesh, ecm_it>;
EcmType ecm = get(d_prop_bool(), cgal_object);
struct Visitor {
struct Visitor : public CGAL::Polygon_mesh_processing::Corefinement::Default_visitor<MyMesh> {
Visitor(const MyMesh &object, const MyMesh &shape,
MyMesh::Property_map<CGAL::SM_Edge_index, IntersectingElemnt> edge_shape_map,
MyMesh::Property_map<CGAL::SM_Face_index, IntersectingElemnt> face_shape_map,
MyMesh::Property_map<CGAL::SM_Face_index, int32_t> face_map,
MyMesh::Property_map<CGAL::SM_Vertex_index, IntersectingElemnt> vert_shape_map) :
object(object), shape(shape), edge_shape_map(edge_shape_map), face_shape_map(face_shape_map),
face_map(face_map), vert_shape_map(vert_shape_map)
{}
const MyMesh &object;
const MyMesh &shape;
// Properties of the shape mesh:
@ -946,13 +955,6 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
assert(glyph->point_index != -1);
vert_shape_map[vh] = glyph ? *glyph : IntersectingElemnt{};
}
void after_subface_creations(MyMesh&) {}
void before_subface_created(MyMesh&) {}
void before_edge_split(halfedge_descriptor /* h */, MyMesh& /* tm */) {}
void edge_split(halfedge_descriptor /* hnew */, MyMesh& /* tm */) {}
void after_edge_split() {}
void add_retriangulation_edge(halfedge_descriptor /* h */, MyMesh& /* tm */) {}
} visitor{cgal_object, cgal_shape, edge_shape_map, face_shape_map,
face_map, vert_shape_map};

View file

@ -3,7 +3,7 @@
set(SLIC3R_APP_NAME "PrusaSlicer")
set(SLIC3R_APP_KEY "PrusaSlicer")
set(SLIC3R_VERSION "2.6.0-alpha4")
set(SLIC3R_VERSION "2.6.0-alpha5")
set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN")
set(SLIC3R_RC_VERSION "2,6,0,0")
set(SLIC3R_RC_VERSION_DOTS "2.6.0.0")