Fixed conflicts after merge with master

This commit is contained in:
enricoturri1966 2021-11-29 11:53:45 +01:00
commit 353a9e61f5
40 changed files with 5667 additions and 3462 deletions

View File

@ -13,9 +13,21 @@ This guide describes building PrusaSlicer statically against dependencies pulled
#### 0. Prerequisities
CMake, GNU build tools, git and m4 macro processor have to be installed. Unless that's already the case, install them as usual from your distribution packages. E.g. on Ubuntu, run `sudo apt-get install cmake build-essential git m4`. The names of the packages may be different on different distros.
GNU build tools, CMake, git and other libraries have to be installed on the build machine.
Unless that's already the case, install them as usual from your distribution packages.
E.g. on Ubuntu 20.10, run
```shell
sudo apt-get install -y \
git \
build-essential \
autoconf \
cmake \
libglu1-mesa-dev \
libgtk-3-dev \
libdbus-1-dev \
Although most of dependencies are handled by the build script, PrusaSlicer still expects that some libraries will be available in the system (GTK, MESA, gettext). E.g., on Ubuntu, install the required packages by running `sudo apt-get install libgtk-3-dev libglu1-mesa-dev gettext`. The names of the packages may be different on different distros.
```
The names of the packages may be different on different distros.
#### 1. Cloning the repository

View File

@ -192,7 +192,7 @@ documentation_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-
disabled_tags = SLA
[hint:Insert Custom G-code]
text = Insert Custom G-code\nDid you know that you can insert a custom G-code at a specific layer? Right-click the layer in the Preview and select Add custom G-code. With this function you can, for example, create a temperature tower. Read more in the documentation.
text = Insert Custom G-code\nDid you know that you can insert a custom G-code at a specific layer? Left-click the layer in the Preview, Right-click the plus icon and select Add custom G-code. With this function you can, for example, create a temperature tower. Read more in the documentation.
documentation_link = https://help.prusa3d.com/en/article/insert-pause-or-custom-g-code-at-layer_120490#insert-custom-g-code-at-layer
disabled_tags = SLA
@ -203,8 +203,8 @@ hypertext_type = menubar
hypertext_menubar_menu_name = Configuration
hypertext_menubar_item_name = Configuration Snapshots
[hint:Minimum wall thickness]
text = Minimum wall thickness\nDid you know that instead of the number of top and bottom layers, you can define the<a>Minimum shell thickness</a>in millimeters? This feature is especially useful when using the variable layer height function.
[hint:Minimum shell thickness]
text = Minimum shell thickness\nDid you know that instead of the number of top and bottom layers, you can define the<a>Minimum shell thickness</a>in millimeters? This feature is especially useful when using the variable layer height function.
hypertext_type = settings
hypertext_settings_opt = top_solid_min_thickness
hypertext_settings_type = 1

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,6 @@
min_slic3r_version = 2.4.0-beta0
min_slic3r_version = 2.4.0-beta2
1.4.0-beta2 Added SLA material colors. Updated BASF filament profiles.
min_slic3r_version = 2.4.0-beta0
1.4.0-beta1 Updated pad wall slope angle for SLA printers. Updated Filatech Filacarbon profile for Prusa MINI.
1.4.0-beta0 Added multiple Filatech and BASF filament profiles. Added material profiles for SL1S.
min_slic3r_version = 2.4.0-alpha0

5
src/fast_float/README.md Normal file
View File

@ -0,0 +1,5 @@
**The fast_float library provides fast header-only implementations for the C++ from_chars functions for float and double types.**
For more information go to https://github.com/fastfloat/fast_float.
THIS DIRECTORY CONTAINS THE fast_float-2.0.0 fe1ce58 SOURCE DISTRIBUTION.

View File

@ -269,15 +269,17 @@ BuildVolume::ObjectState object_state_templ(const indexed_triangle_set &its, con
return inside ? (outside ? BuildVolume::ObjectState::Colliding : BuildVolume::ObjectState::Inside) : BuildVolume::ObjectState::Outside;
}
BuildVolume::ObjectState BuildVolume::object_state(const indexed_triangle_set &its, const Transform3f &trafo, bool may_be_below_bed) const
BuildVolume::ObjectState BuildVolume::object_state(const indexed_triangle_set& its, const Transform3f& trafo, bool may_be_below_bed, bool ignore_bottom) const
{
switch (m_type) {
case Type::Rectangle:
{
BoundingBox3Base<Vec3d> build_volume = this->bounding_volume().inflated(SceneEpsilon);
BoundingBox3Base<Vec3f> build_volumef(build_volume.min.cast<float>(), build_volume.max.cast<float>());
if (m_max_print_height == 0)
if (m_max_print_height == 0.0)
build_volume.max.z() = std::numeric_limits<double>::max();
if (ignore_bottom)
build_volume.min.z() = -std::numeric_limits<double>::max();
BoundingBox3Base<Vec3f> build_volumef(build_volume.min.cast<float>(), build_volume.max.cast<float>());
// The following test correctly interprets intersection of a non-convex object with a rectangular build volume.
//return rectangle_test(its, trafo, to_2d(build_volume.min), to_2d(build_volume.max), build_volume.max.z());
//FIXME This test does NOT correctly interprets intersection of a non-convex object with a rectangular build volume.
@ -286,14 +288,14 @@ BuildVolume::ObjectState BuildVolume::object_state(const indexed_triangle_set &i
case Type::Circle:
{
Geometry::Circlef circle { unscaled<float>(m_circle.center), unscaled<float>(m_circle.radius + SceneEpsilon) };
return m_max_print_height == 0 ?
return m_max_print_height == 0.0 ?
object_state_templ(its, trafo, may_be_below_bed, [circle](const Vec3f &pt) { return circle.contains(to_2d(pt)); }) :
object_state_templ(its, trafo, may_be_below_bed, [circle, z = m_max_print_height + SceneEpsilon](const Vec3f &pt) { return pt.z() < z && circle.contains(to_2d(pt)); });
}
case Type::Convex:
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
case Type::Custom:
return m_max_print_height == 0 ?
return m_max_print_height == 0.0 ?
object_state_templ(its, trafo, may_be_below_bed, [this](const Vec3f &pt) { return Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_scene, to_2d(pt).cast<double>()); }) :
object_state_templ(its, trafo, may_be_below_bed, [this, z = m_max_print_height + SceneEpsilon](const Vec3f &pt) { return pt.z() < z && Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_scene, to_2d(pt).cast<double>()); });
case Type::Invalid:
@ -302,18 +304,20 @@ BuildVolume::ObjectState BuildVolume::object_state(const indexed_triangle_set &i
}
}
BuildVolume::ObjectState BuildVolume::volume_state_bbox(const BoundingBoxf3 &volume_bbox) const
BuildVolume::ObjectState BuildVolume::volume_state_bbox(const BoundingBoxf3& volume_bbox, bool ignore_bottom) const
{
assert(m_type == Type::Rectangle);
BoundingBox3Base<Vec3d> build_volume = this->bounding_volume().inflated(SceneEpsilon);
if (m_max_print_height == 0)
if (m_max_print_height == 0.0)
build_volume.max.z() = std::numeric_limits<double>::max();
if (ignore_bottom)
build_volume.min.z() = -std::numeric_limits<double>::max();
return build_volume.max.z() <= - SceneEpsilon ? ObjectState::Below :
build_volume.contains(volume_bbox) ? ObjectState::Inside :
build_volume.intersects(volume_bbox) ? ObjectState::Colliding : ObjectState::Outside;
}
bool BuildVolume::all_paths_inside(const GCodeProcessorResult &paths, const BoundingBoxf3 &paths_bbox) const
bool BuildVolume::all_paths_inside(const GCodeProcessorResult& paths, const BoundingBoxf3& paths_bbox, bool ignore_bottom) const
{
auto move_valid = [](const GCodeProcessorResult::MoveVertex &move) {
return move.type == EMoveType::Extrude && move.extrusion_role != erCustom && move.width != 0.f && move.height != 0.f;
@ -324,8 +328,10 @@ bool BuildVolume::all_paths_inside(const GCodeProcessorResult &paths, const Boun
case Type::Rectangle:
{
BoundingBox3Base<Vec3d> build_volume = this->bounding_volume().inflated(epsilon);
if (m_max_print_height == 0)
if (m_max_print_height == 0.0)
build_volume.max.z() = std::numeric_limits<double>::max();
if (ignore_bottom)
build_volume.min.z() = -std::numeric_limits<double>::max();
return build_volume.contains(paths_bbox);
}
case Type::Circle:
@ -333,7 +339,7 @@ bool BuildVolume::all_paths_inside(const GCodeProcessorResult &paths, const Boun
const Vec2f c = unscaled<float>(m_circle.center);
const float r = unscaled<double>(m_circle.radius) + epsilon;
const float r2 = sqr(r);
return m_max_print_height == 0 ?
return m_max_print_height == 0.0 ?
std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, c, r2](const GCodeProcessorResult::MoveVertex &move)
{ return ! move_valid(move) || (to_2d(move.position) - c).squaredNorm() <= r2; }) :
std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, c, r2, z = m_max_print_height + epsilon](const GCodeProcessorResult::MoveVertex& move)
@ -342,7 +348,7 @@ bool BuildVolume::all_paths_inside(const GCodeProcessorResult &paths, const Boun
case Type::Convex:
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
case Type::Custom:
return m_max_print_height == 0 ?
return m_max_print_height == 0.0 ?
std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, this](const GCodeProcessorResult::MoveVertex &move)
{ return ! move_valid(move) || Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_bed, to_2d(move.position).cast<double>()); }) :
std::all_of(paths.moves.begin(), paths.moves.end(), [move_valid, this, z = m_max_print_height + epsilon](const GCodeProcessorResult::MoveVertex &move)
@ -364,7 +370,7 @@ inline bool all_inside_vertices_normals_interleaved(const std::vector<float> &pa
return true;
}
bool BuildVolume::all_paths_inside_vertices_and_normals_interleaved(const std::vector<float> &paths, const Eigen::AlignedBox<float, 3> &paths_bbox) const
bool BuildVolume::all_paths_inside_vertices_and_normals_interleaved(const std::vector<float>& paths, const Eigen::AlignedBox<float, 3>& paths_bbox, bool ignore_bottom) const
{
assert(paths.size() % 6 == 0);
static constexpr const double epsilon = BedEpsilon;
@ -372,8 +378,10 @@ bool BuildVolume::all_paths_inside_vertices_and_normals_interleaved(const std::v
case Type::Rectangle:
{
BoundingBox3Base<Vec3d> build_volume = this->bounding_volume().inflated(epsilon);
if (m_max_print_height == 0)
if (m_max_print_height == 0.0)
build_volume.max.z() = std::numeric_limits<double>::max();
if (ignore_bottom)
build_volume.min.z() = -std::numeric_limits<double>::max();
return build_volume.contains(paths_bbox.min().cast<double>()) && build_volume.contains(paths_bbox.max().cast<double>());
}
case Type::Circle:
@ -381,14 +389,14 @@ bool BuildVolume::all_paths_inside_vertices_and_normals_interleaved(const std::v
const Vec2f c = unscaled<float>(m_circle.center);
const float r = unscaled<double>(m_circle.radius) + float(epsilon);
const float r2 = sqr(r);
return m_max_print_height == 0 ?
return m_max_print_height == 0.0 ?
all_inside_vertices_normals_interleaved(paths, [c, r2](Vec3f p) { return (to_2d(p) - c).squaredNorm() <= r2; }) :
all_inside_vertices_normals_interleaved(paths, [c, r2, z = m_max_print_height + epsilon](Vec3f p) { return (to_2d(p) - c).squaredNorm() <= r2 && p.z() <= z; });
}
case Type::Convex:
//FIXME doing test on convex hull until we learn to do test on non-convex polygons efficiently.
case Type::Custom:
return m_max_print_height == 0 ?
return m_max_print_height == 0.0 ?
all_inside_vertices_normals_interleaved(paths, [this](Vec3f p) { return Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_bed, to_2d(p).cast<double>()); }) :
all_inside_vertices_normals_interleaved(paths, [this, z = m_max_print_height + epsilon](Vec3f p) { return Geometry::inside_convex_polygon(m_top_bottom_convex_hull_decomposition_bed, to_2d(p).cast<double>()) && p.z() <= z; });
default:

View File

@ -80,19 +80,19 @@ public:
// Called by Plater to update Inside / Colliding / Outside state of ModelObjects before slicing.
// Called from Model::update_print_volume_state() -> ModelObject::update_instances_print_volume_state()
// Using SceneEpsilon
ObjectState object_state(const indexed_triangle_set &its, const Transform3f &trafo, bool may_be_below_bed) const;
ObjectState object_state(const indexed_triangle_set &its, const Transform3f &trafo, bool may_be_below_bed, bool ignore_bottom = true) const;
// Called by GLVolumeCollection::check_outside_state() after an object is manipulated with gizmos for example.
// Called for a rectangular bed:
ObjectState volume_state_bbox(const BoundingBoxf3 &volume_bbox) const;
ObjectState volume_state_bbox(const BoundingBoxf3& volume_bbox, bool ignore_bottom = true) const;
// 2) Test called on G-code paths.
// Using BedEpsilon for all tests.
static constexpr const double BedEpsilon = 3. * EPSILON;
// Called on final G-code paths.
//FIXME The test does not take the thickness of the extrudates into account!
bool all_paths_inside(const GCodeProcessorResult &paths, const BoundingBoxf3 &paths_bbox) const;
bool all_paths_inside(const GCodeProcessorResult& paths, const BoundingBoxf3& paths_bbox, bool ignore_bottom = true) const;
// Called on initial G-code preview on OpenGL vertex buffer interleaved normals and vertices.
bool all_paths_inside_vertices_and_normals_interleaved(const std::vector<float> &paths, const Eigen::AlignedBox<float, 3> &bbox) const;
bool all_paths_inside_vertices_and_normals_interleaved(const std::vector<float>& paths, const Eigen::AlignedBox<float, 3>& bbox, bool ignore_bottom = true) const;
private:
// Source definition of the print bed geometry (PrintConfig::bed_shape)

View File

@ -288,7 +288,7 @@ set(CGAL_DO_NOT_WARN_ABOUT_CMAKE_BUILD_TYPE ON CACHE BOOL "" FORCE)
cmake_policy(PUSH)
cmake_policy(SET CMP0011 NEW)
find_package(CGAL 4.13.2 REQUIRED)
find_package(CGAL 4.13 REQUIRED)
cmake_policy(POP)
add_library(libslic3r_cgal STATIC MeshBoolean.cpp MeshBoolean.hpp TryCatchSignal.hpp

View File

@ -1544,30 +1544,37 @@ void GCode::process_layers(
const size_t single_object_idx,
GCodeOutputStream &output_stream)
{
// The pipeline is fixed: Neither wipe tower nor vase mode are implemented for sequential print.
// The pipeline is variable: The vase mode filter is optional.
size_t layer_to_print_idx = 0;
tbb::parallel_pipeline(12,
tbb::make_filter<void, GCode::LayerResult>(
tbb::filter::serial_in_order,
[this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx](tbb::flow_control& fc) -> GCode::LayerResult {
if (layer_to_print_idx == layers_to_print.size()) {
fc.stop();
return {};
} else {
LayerToPrint &layer = layers_to_print[layer_to_print_idx ++];
print.throw_if_canceled();
return this->process_layer(print, { std::move(layer) }, tool_ordering.tools_for_layer(layer.print_z()), &layer == &layers_to_print.back(), nullptr, single_object_idx);
}
}) &
tbb::make_filter<GCode::LayerResult, std::string>(
tbb::filter::serial_in_order,
[&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in) -> std::string {
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
}) &
tbb::make_filter<std::string, void>(
tbb::filter::serial_in_order,
[&output_stream](std::string s) { output_stream.write(s); }
));
const auto generator = tbb::make_filter<void, GCode::LayerResult>(tbb::filter::serial_in_order,
[this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx](tbb::flow_control& fc) -> GCode::LayerResult {
if (layer_to_print_idx == layers_to_print.size()) {
fc.stop();
return {};
} else {
LayerToPrint &layer = layers_to_print[layer_to_print_idx ++];
print.throw_if_canceled();
return this->process_layer(print, { std::move(layer) }, tool_ordering.tools_for_layer(layer.print_z()), &layer == &layers_to_print.back(), nullptr, single_object_idx);
}
});
const auto spiral_vase = tbb::make_filter<GCode::LayerResult, GCode::LayerResult>(tbb::filter::serial_in_order,
[&spiral_vase = *this->m_spiral_vase.get()](GCode::LayerResult in)->GCode::LayerResult {
spiral_vase.enable(in.spiral_vase_enable);
return { spiral_vase.process_layer(std::move(in.gcode)), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush };
});
const auto cooling = tbb::make_filter<GCode::LayerResult, std::string>(tbb::filter::serial_in_order,
[&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in)->std::string {
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
});
const auto output = tbb::make_filter<std::string, void>(tbb::filter::serial_in_order,
[&output_stream](std::string s) { output_stream.write(s); }
);
// The pipeline elements are joined using const references, thus no copying is performed.
if (m_spiral_vase)
tbb::parallel_pipeline(12, generator & spiral_vase & cooling & output);
else
tbb::parallel_pipeline(12, generator & cooling & output);
}
std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override)

View File

@ -613,13 +613,25 @@ const PrintObjectRegions::BoundingBox* find_volume_extents(const PrintObjectRegi
}
// Find a bounding box of a topmost printable volume referenced by this modifier given this_region_id.
const PrintObjectRegions::BoundingBox* find_modifier_volume_extents(const PrintObjectRegions::LayerRangeRegions &layer_range, const int this_region_id)
PrintObjectRegions::BoundingBox find_modifier_volume_extents(const PrintObjectRegions::LayerRangeRegions &layer_range, const int this_region_id)
{
// Find the top-most printable volume of this modifier, or the printable volume itself.
int parent_region_id = this_region_id;
for (; ! layer_range.volume_regions[parent_region_id].model_volume->is_model_part(); parent_region_id = layer_range.volume_regions[parent_region_id].parent)
assert(parent_region_id >= 0);
return find_volume_extents(layer_range, *layer_range.volume_regions[parent_region_id].model_volume);
const PrintObjectRegions::VolumeRegion &this_region = layer_range.volume_regions[this_region_id];
const PrintObjectRegions::BoundingBox *this_extents = find_volume_extents(layer_range, *this_region.model_volume);
assert(this_extents);
PrintObjectRegions::BoundingBox out { *this_extents };
if (! this_region.model_volume->is_model_part())
for (int parent_region_id = this_region.parent;;) {
assert(parent_region_id >= 0);
const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id];
const PrintObjectRegions::BoundingBox *parent_extents = find_volume_extents(layer_range, *parent_region.model_volume);
assert(parent_extents);
out.extend(*parent_extents);
if (parent_region.model_volume->is_model_part())
break;
parent_region_id = parent_region.parent;
}
return out;
}
PrintRegionConfig region_config_from_model_volume(const PrintRegionConfig &default_or_parent_region_config, const DynamicPrintConfig *layer_range_config, const ModelVolume &volume, size_t num_extruders);
@ -662,11 +674,10 @@ bool verify_update_print_object_regions(
// if the visited modifier did not modify their properties. Now the visited modifier's configuration may have changed,
// which may require new regions to be created.
it_model_volume_modifier_last = it_model_volume;
int this_region_id = int(&region - layer_range.volume_regions.data());
int next_region_id = this_region_id + 1;
int next_region_id = int(&region - layer_range.volume_regions.data());
const PrintObjectRegions::BoundingBox *bbox = find_volume_extents(layer_range, *region.model_volume);
assert(bbox);
for (int parent_region_id = this_region_id - 1; parent_region_id >= 0; -- parent_region_id) {
for (int parent_region_id = next_region_id - 1; parent_region_id >= 0; -- parent_region_id) {
const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id];
assert(parent_region.model_volume != region.model_volume);
if (parent_region.model_volume->is_model_part() || parent_region.model_volume->is_modifier()) {
@ -680,17 +691,13 @@ bool verify_update_print_object_regions(
layer_range.volume_regions[next_region_id].parent == parent_region_id) {
// A parent region is already overridden.
++ next_region_id;
} else {
} else if (PrintObjectRegions::BoundingBox parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id); parent_bbox.intersects(*bbox))
// Such parent region does not exist. If it is needed, then we need to reslice.
const PrintObjectRegions::BoundingBox *parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id);
assert(parent_bbox != nullptr);
if (parent_bbox->intersects(*bbox))
// Only create new region for a modifier, which actually modifies config of it's parent.
if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, **it_model_volume, num_extruders);
config != parent_region.region->config())
// This modifier newly overrides a region, which it did not before. We need to reslice.
return false;
}
// Only create new region for a modifier, which actually modifies config of it's parent.
if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, **it_model_volume, num_extruders);
config != parent_region.region->config())
// This modifier newly overrides a region, which it did not before. We need to reslice.
return false;
}
}
}
@ -912,10 +919,8 @@ static PrintObjectRegions* generate_print_object_regions(
for (int parent_region_id = int(layer_range.volume_regions.size()) - 1; parent_region_id >= 0; -- parent_region_id) {
const PrintObjectRegions::VolumeRegion &parent_region = layer_range.volume_regions[parent_region_id];
const ModelVolume &parent_volume = *parent_region.model_volume;
if (parent_volume.is_model_part() || parent_volume.is_modifier()) {
const PrintObjectRegions::BoundingBox *parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id);
assert(parent_bbox != nullptr);
if (parent_bbox->intersects(*bbox))
if (parent_volume.is_model_part() || parent_volume.is_modifier())
if (PrintObjectRegions::BoundingBox parent_bbox = find_modifier_volume_extents(layer_range, parent_region_id); parent_bbox.intersects(*bbox))
// Only create new region for a modifier, which actually modifies config of it's parent.
if (PrintRegionConfig config = region_config_from_model_volume(parent_region.region->config(), nullptr, volume, num_extruders);
config != parent_region.region->config()) {
@ -923,7 +928,6 @@ static PrintObjectRegions* generate_print_object_regions(
layer_range.volume_regions.push_back({ &volume, parent_region_id, get_create_region(std::move(config)), bbox });
} else if (parent_model_part_id == -1 && parent_volume.is_model_part())
parent_model_part_id = parent_region_id;
}
}
if (! added && parent_model_part_id >= 0)
// This modifier does not override any printable volume's configuration, however it may in the future.

View File

@ -2688,7 +2688,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back(L("Rectilinear"));
def->enum_labels.push_back(L("Concentric"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<SupportMaterialPattern>(smpRectilinear));
def->set_default_value(new ConfigOptionEnum<SupportMaterialInterfacePattern>(smipRectilinear));
def = this->add("support_material_spacing", coFloat);
def->label = L("Pattern spacing");

View File

@ -91,14 +91,22 @@
// an additional button can be used to set the keyboard focus into the slider
// to allow the user to type in the desired value
#define ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT (1 && ENABLE_2_4_0_BETA2)
// Enable fit print volume command for circular printbeds
#define ENABLE_ENHANCED_PRINT_VOLUME_FIT (1 && ENABLE_2_4_0_BETA2)
//=================
// 2.4.0.rc techs
//=================
#define ENABLE_2_4_0_RC 1
// Enable changes in preview layout
#define ENABLE_PREVIEW_LAYOUT (1 && ENABLE_2_4_0_BETA2)
#define ENABLE_PREVIEW_LAYOUT (1 && ENABLE_2_4_0_RC)
// Enable drawing the items in legend toolbar using icons
#define ENABLE_LEGEND_TOOLBAR_ICONS (1 && ENABLE_PREVIEW_LAYOUT)
// Enable coloring of toolpaths in preview by layer time
#define ENABLE_PREVIEW_LAYER_TIME (1 && ENABLE_2_4_0_BETA2)
#define ENABLE_PREVIEW_LAYER_TIME (1 && ENABLE_2_4_0_RC)
// Enable showing time estimate for travel moves in legend
#define ENABLE_TRAVEL_TIME (1 && ENABLE_2_4_0_BETA2)
#define ENABLE_TRAVEL_TIME (1 && ENABLE_2_4_0_RC)
#endif // _prusaslicer_technologies_h_

View File

@ -1866,9 +1866,13 @@ std::vector<ExPolygons> slice_mesh_ex(
//FIXME simplify
if (this_mode == MeshSlicingParams::SlicingMode::PositiveLargestContour)
keep_largest_contour_only(expolygons);
if (resolution != 0.)
for (ExPolygon &ex : expolygons)
ex.simplify(resolution);
if (resolution != 0.) {
ExPolygons simplified;
simplified.reserve(expolygons.size());
for (const ExPolygon &ex : expolygons)
append(simplified, ex.simplify(resolution));
expolygons = std::move(simplified);
}
}
});
// BOOST_LOG_TRIVIAL(debug) << "slice_mesh make_expolygons in parallel - end";

View File

@ -174,7 +174,7 @@ bool Bed3D::set_shape(const Pointfs& bed_shape, const double max_print_height, c
}
if (m_build_volume.bed_shape() == bed_shape && m_type == type && m_texture_filename == texture_filename && m_model_filename == model_filename)
if (m_build_volume.bed_shape() == bed_shape && m_build_volume.max_print_height() == max_print_height && m_type == type && m_texture_filename == texture_filename && m_model_filename == model_filename)
// No change, no need to update the UI.
return false;

View File

@ -21,7 +21,13 @@ void ButtonsDescription::FillSizerWithTextColorDescriptions(wxSizer* sizer, wxWi
ScalableBitmap bmp_delete_focus = ScalableBitmap(parent, "cross_focus");
auto add_color = [grid_sizer, parent](wxColourPickerCtrl** color_picker, const wxColour& color, const wxColour& def_color, wxString label_text) {
//
// wrap the label_text to the max 80 characters
if (label_text.Len() > 80) {
size_t brack_pos = label_text.find_last_of(" ", 79);
if (brack_pos > 0 && brack_pos < 80)
label_text.insert(brack_pos + 1, "\n");
}
auto sys_label = new wxStaticText(parent, wxID_ANY, label_text);
sys_label->SetForegroundColour(color);

View File

@ -162,8 +162,8 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
if (config->opt_bool("support_material")) {
// Ask only once.
if (!support_material_overhangs_queried) {
support_material_overhangs_queried = true;
if (!m_support_material_overhangs_queried) {
m_support_material_overhangs_queried = true;
if (!config->opt_bool("overhangs")/* != 1*/) {
wxString msg_text = _(L("Supports work better, if the following feature is enabled:\n"
"- Detect bridging perimeters"));
@ -182,7 +182,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
}
}
else {
support_material_overhangs_queried = false;
m_support_material_overhangs_queried = false;
}
if (config->option<ConfigOptionPercent>("fill_density")->value == 100) {

View File

@ -17,15 +17,12 @@ class ModelConfig;
namespace GUI {
// This variable have to be static because of use its value from Preset configuration
// and from object/parts configuration from the Settings in sidebar
static bool support_material_overhangs_queried {false};
class ConfigManipulation
{
bool is_msg_dlg_already_exist{ false };
bool m_is_initialized_support_material_overhangs_queried{ false };
bool m_support_material_overhangs_queried{ false };
// function to loading of changed configuration
std::function<void()> load_config = nullptr;
@ -66,7 +63,7 @@ public:
void initialize_support_material_overhangs_queried(bool queried)
{
m_is_initialized_support_material_overhangs_queried = true;
support_material_overhangs_queried = queried;
m_support_material_overhangs_queried = queried;
}
};

View File

@ -26,6 +26,7 @@
#include <cmath>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <random>
#include "Field.hpp"
#include "format.hpp"
@ -1446,6 +1447,18 @@ wxString Control::get_tooltip(int tick/*=-1*/)
std::string space = " ";
tooltip = space;
auto format_gcode = [space](std::string gcode) {
// when the tooltip is too long, it starts to flicker, see: https://github.com/prusa3d/PrusaSlicer/issues/7368
// so we limit the number of lines shown
std::vector<std::string> lines;
boost::split(lines, gcode, boost::is_any_of("\n"), boost::token_compress_off);
static const size_t MAX_LINES = 10;
if (lines.size() > MAX_LINES) {
gcode = lines.front() + '\n';
for (size_t i = 1; i < MAX_LINES; ++i) {
gcode += lines[i] + '\n';
}
gcode += "[" + into_u8(_L("continue")) + "]\n";
}
boost::replace_all(gcode, "\n", "\n" + space);
return gcode;
};

View File

@ -3,6 +3,7 @@
#include "GUI.hpp"
#include "I18N.hpp"
#include "BitmapComboBox.hpp"
#include "Plater.hpp"
#include <wx/dc.h>
#ifdef wxHAS_GENERIC_DATAVIEWCTRL
@ -222,14 +223,8 @@ bool BitmapTextRenderer::GetValueFromEditorCtrl(wxWindow* ctrl, wxVariant& value
if (!text_editor || text_editor->GetValue().IsEmpty())
return false;
std::string chosen_name = into_u8(text_editor->GetValue());
const char* unusable_symbols = "<>:/\\|?*\"";
for (size_t i = 0; i < std::strlen(unusable_symbols); i++) {
if (chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos) {
m_was_unusable_symbol = true;
return false;
}
}
if (m_was_unusable_symbol = Slic3r::GUI::Plater::has_illegal_filename_characters(text_editor->GetValue()))
return false;
// The icon can't be edited so get its old value and reuse it.
wxVariant valueOld;

View File

@ -2519,7 +2519,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
return m_layers.get_range_at(min_id).first <= id && id <= m_layers.get_range_at(max_id).last;
};
return in_layers_range(path.sub_paths.front().first.s_id) || in_layers_range(path.sub_paths.back().last.s_id);
return in_layers_range(path.sub_paths.front().first.s_id) && in_layers_range(path.sub_paths.back().last.s_id);
};
auto is_travel_in_layers_range = [this](size_t path_id, size_t min_id, size_t max_id) {

View File

@ -95,6 +95,11 @@ RetinaHelper::~RetinaHelper() {}
float RetinaHelper::get_scale_factor() { return float(m_window->GetContentScaleFactor()); }
#endif // __WXGTK3__
// Fixed the collision between BuildVolume::Type::Convex and macro Convex defined inside /usr/include/X11/X.h that is included by WxWidgets 3.0.
#if defined(__linux__) && defined(Convex)
#undef Convex
#endif
Size::Size()
: m_width(0)
, m_height(0)
@ -3764,7 +3769,8 @@ Linef3 GLCanvas3D::mouse_ray(const Point& mouse_pos)
double GLCanvas3D::get_size_proportional_to_max_bed_size(double factor) const
{
return factor * m_bed.build_volume().bounding_volume().max_size();
const BoundingBoxf& bbox = m_bed.build_volume().bounding_volume2d();
return factor * std::max(bbox.size()[0], bbox.size()[1]);
}
void GLCanvas3D::set_cursor(ECursorType type)
@ -5135,10 +5141,12 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type)
break;
}
default:
case BuildVolume::Type::Convex:
case BuildVolume::Type::Custom: {
m_volumes.set_print_volume({ static_cast<int>(type),
{ 0.0f, 0.0f, 0.0f, 0.0f },
{ 0.0f, 0.0f } });
{ -FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX },
{ -FLT_MAX, FLT_MAX } }
);
}
}
if (m_requires_check_outside_state) {

View File

@ -786,8 +786,14 @@ void MenuFactory::append_menu_item_change_extruder(wxMenu* menu)
void MenuFactory::append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu)
{
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
append_menu_item(menu, wxID_ANY, _L("Scale to print volume"), _L("Scale the selected object to fit the print volume"),
[](wxCommandEvent&) { plater()->scale_selection_to_fit_print_volume(); }, "", menu,
[]() { return plater()->can_scale_to_print_volume(); }, m_parent);
#else
append_menu_item(menu, wxID_ANY, _L("Scale to print volume"), _L("Scale the selected object to fit the print volume"),
[](wxCommandEvent&) { plater()->scale_selection_to_fit_print_volume(); }, "", menu);
#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
}
void MenuFactory::append_menu_items_convert_unit(wxMenu* menu, int insert_pos/* = 1*/)

View File

@ -4016,18 +4016,8 @@ void ObjectList::rename_item()
if (new_name.IsEmpty())
return;
bool is_unusable_symbol = false;
std::string chosen_name = Slic3r::normalize_utf8_nfc(new_name.ToUTF8());
const char* unusable_symbols = "<>:/\\|?*\"";
for (size_t i = 0; i < std::strlen(unusable_symbols); i++) {
if (chosen_name.find_first_of(unusable_symbols[i]) != std::string::npos) {
is_unusable_symbol = true;
}
}
if (is_unusable_symbol) {
show_error(this, _(L("The supplied name is not valid;")) + "\n" +
_(L("the following characters are not allowed:")) + " <>:/\\|?*\"");
if (Plater::has_illegal_filename_characters(new_name)) {
Plater::show_illegal_characters_warning(this);
return;
}
@ -4247,10 +4237,7 @@ void ObjectList::OnEditingDone(wxDataViewEvent &event)
const auto renderer = dynamic_cast<BitmapTextRenderer*>(GetColumn(colName)->GetRenderer());
if (renderer->WasCanceled())
wxTheApp->CallAfter([this]{
show_error(this, _(L("The supplied name is not valid;")) + "\n" +
_(L("the following characters are not allowed:")) + " <>:/\\|?*\"");
});
wxTheApp->CallAfter([this]{ Plater::show_illegal_characters_warning(this); });
#ifdef __WXMSW__
// Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected.

View File

@ -109,7 +109,7 @@ void GLGizmoSimplify::add_simplify_suggestion_notification(
if (big_ids.empty()) return;
for (size_t object_id : big_ids) {
std::string t = GUI::format(_u8L(
std::string t = GUI::format(_L(
"Processing model '%1%' with more than 1M triangles "
"could be slow. It is highly recommend to reduce "
"amount of triangles."), objects[object_id]->name);

View File

@ -538,14 +538,14 @@ bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float
{
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
std::string str_label;
if (boost::algorithm::istarts_with(label, "##"))
str_label = std::string(label);
else {
str_label = std::string("##") + std::string(label);
this->text(label);
ImGui::SameLine();
}
// let the label string start with "##" to hide the automatic label from ImGui::SliderFloat()
bool label_visible = !boost::algorithm::istarts_with(label, "##");
std::string str_label = label_visible ? std::string("##") + std::string(label) : std::string(label);
// removes 2nd evenience of "##", if present
std::string::size_type pos = str_label.find("##", 2);
if (pos != std::string::npos)
str_label = str_label.substr(0, pos) + str_label.substr(pos + 2);
// the current slider edit state needs to be detected here before calling SliderFloat()
bool slider_editing = ImGui::GetCurrentWindow()->GetID(str_label.c_str()) == ImGui::GetActiveID();
@ -557,8 +557,8 @@ bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float
if (clamp)
*v = std::clamp(*v, v_min, v_max);
const ImGuiStyle& style = ImGui::GetStyle();
if (show_edit_btn) {
const ImGuiStyle& style = ImGui::GetStyle();
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 1, style.ItemSpacing.y });
ImGui::SameLine();
ImGuiIO& io = ImGui::GetIO();
@ -592,6 +592,19 @@ bool ImGuiWrapper::slider_float(const char* label, float* v, float v_min, float
ImGui::PopStyleVar();
}
if (label_visible) {
// if the label is visible, hide the part of it that should be hidden
std::string out_label = std::string(label);
std::string::size_type pos = out_label.find("##");
if (pos != std::string::npos)
out_label = out_label.substr(0, pos);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, { 1, style.ItemSpacing.y });
ImGui::SameLine();
this->text(out_label.c_str());
ImGui::PopStyleVar();
}
return ret;
}

View File

@ -468,9 +468,9 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const
ImGui::Separator();
imgui.text_colored(color, _L("Speed:"));
float translation_scale = (float)params_copy.translation.scale / Params::DefaultTranslationScale;
if (imgui.slider_float(_L("Translation") + "##1", &translation_scale, Params::MinTranslationScale, Params::MaxTranslationScale, "%.1f")) {
params_copy.translation.scale = Params::DefaultTranslationScale * (double)translation_scale;
float translation_scale = float(params_copy.translation.scale) / float(Params::DefaultTranslationScale);
if (imgui.slider_float(_L("Translation"), &translation_scale, float(Params::MinTranslationScale), float(Params::MaxTranslationScale), "%.1f")) {
params_copy.translation.scale = Params::DefaultTranslationScale * double(translation_scale);
params_changed = true;
}
@ -517,13 +517,13 @@ void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const
imgui.text_colored(color, "Vectors:");
Vec3f translation = m_state.get_first_vector_of_type(State::QueueItem::TranslationType).cast<float>();
Vec3f rotation = m_state.get_first_vector_of_type(State::QueueItem::RotationType).cast<float>();
ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat3("Translation##2", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
imgui.text_colored(color, "Queue size:");
int input_queue_size_current[2] = { int(m_state.input_queue_size_current()), int(m_state.input_queue_max_size_achieved) };
ImGui::InputInt2("Current##4", input_queue_size_current, ImGuiInputTextFlags_ReadOnly);
ImGui::InputInt2("Current", input_queue_size_current, ImGuiInputTextFlags_ReadOnly);
int input_queue_size_param = int(params_copy.input_queue_max_size);
if (ImGui::InputInt("Max size", &input_queue_size_param, 1, 1, ImGuiInputTextFlags_ReadOnly)) {

View File

@ -119,6 +119,30 @@ wxDEFINE_EVENT(EVT_SLICING_COMPLETED, wxCommandEvent);
wxDEFINE_EVENT(EVT_PROCESS_COMPLETED, SlicingProcessCompletedEvent);
wxDEFINE_EVENT(EVT_EXPORT_BEGAN, wxCommandEvent);
bool Plater::has_illegal_filename_characters(const wxString& wxs_name)
{
std::string name = into_u8(wxs_name);
return has_illegal_filename_characters(name);
}
bool Plater::has_illegal_filename_characters(const std::string& name)
{
const char* illegal_characters = "<>:/\\|?*\"";
for (size_t i = 0; i < std::strlen(illegal_characters); i++)
if (name.find_first_of(illegal_characters[i]) != std::string::npos)
return true;
return false;
}
void Plater::show_illegal_characters_warning(wxWindow* parent)
{
show_error(parent, _L("The supplied name is not valid;") + "\n" +
_L("the following characters are not allowed:") + " <>:/\\|?*\"");
}
// Sidebar widgets
// struct InfoBox : public wxStaticBox
@ -1893,6 +1917,9 @@ struct Plater::priv
bool can_reload_from_disk() const;
bool can_replace_with_stl() const;
bool can_split(bool to_objects) const;
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
bool can_scale_to_print_volume() const;
#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, Camera::EType camera_type);
ThumbnailsList generate_thumbnails(const ThumbnailsParams& params, Camera::EType camera_type);
@ -3058,7 +3085,11 @@ void Plater::priv::split_volume()
void Plater::priv::scale_selection_to_fit_print_volume()
{
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
this->view3D->get_canvas3d()->get_selection().scale_to_fit_print_volume(this->bed.build_volume());
#else
this->view3D->get_canvas3d()->get_selection().scale_to_fit_print_volume(*config);
#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
}
void Plater::priv::schedule_background_process()
@ -4528,6 +4559,14 @@ bool Plater::priv::can_split(bool to_objects) const
return sidebar->obj_list()->is_splittable(to_objects);
}
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
bool Plater::priv::can_scale_to_print_volume() const
{
const BuildVolume::Type type = this->bed.build_volume().type();
return !view3D->get_canvas3d()->get_selection().is_empty() && (type == BuildVolume::Type::Rectangle || type == BuildVolume::Type::Circle);
}
#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
bool Plater::priv::layers_height_allowed() const
{
if (printer_technology != ptFFF)
@ -5053,7 +5092,7 @@ void Plater::new_project()
wxString header = _L("Creating a new project while some presets are modified.") + "\n" +
(saved_project == wxID_YES ? _L("You can keep presets modifications to the new project or discard them") :
_L("You can keep presets modifications to the new project, discard them or save changes as new presets.\n"
"Note, if changes will be saved than new project wouldn't keep them"));
"Note, if changes will be saved then new project wouldn't keep them"));
using ab = UnsavedChangesDialog::ActionButtons;
int act_buttons = ab::KEEP;
if (saved_project == wxID_NO)
@ -5699,8 +5738,15 @@ void Plater::export_gcode(bool prefer_removable)
GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : boost::iequals(ext, ".sl1s") ? FT_SL1S : FT_SL1, ext),
wxFD_SAVE | wxFD_OVERWRITE_PROMPT
);
if (dlg.ShowModal() == wxID_OK)
if (dlg.ShowModal() == wxID_OK) {
output_path = into_path(dlg.GetPath());
while (has_illegal_filename_characters(output_path.filename().string())) {
show_illegal_characters_warning(this);
dlg.SetFilename(from_path(output_path.filename()));
if (dlg.ShowModal() == wxID_OK)
output_path = into_path(dlg.GetPath());
}
}
}
if (! output_path.empty()) {
@ -6267,8 +6313,10 @@ void Plater::on_config_change(const DynamicPrintConfig &config)
update_scheduled = true;
p->sidebar->obj_list()->update_extruder_colors();
}
else if(opt_key == "max_print_height")
else if (opt_key == "max_print_height") {
bed_shape_changed = true;
update_scheduled = true;
}
else if (opt_key == "printer_model") {
p->reset_gcode_toolpaths();
// update to force bed selection(for texturing)
@ -6855,6 +6903,10 @@ bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); }
bool Plater::can_replace_with_stl() const { return p->can_replace_with_stl(); }
bool Plater::can_mirror() const { return p->can_mirror(); }
bool Plater::can_split(bool to_objects) const { return p->can_split(to_objects); }
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
bool Plater::can_scale_to_print_volume() const { return p->can_scale_to_print_volume(); }
#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); }
void Plater::clear_undo_redo_stack_main() { p->undo_redo_stack_main().clear(); }
void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); }

View File

@ -327,6 +327,9 @@ public:
bool can_replace_with_stl() const;
bool can_mirror() const;
bool can_split(bool to_objects) const;
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
bool can_scale_to_print_volume() const;
#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
void msw_rescale();
void sys_color_changed();
@ -437,6 +440,10 @@ public:
wxMenu* layer_menu();
wxMenu* multi_selection_menu();
static bool has_illegal_filename_characters(const wxString& name);
static bool has_illegal_filename_characters(const std::string& name);
static void show_illegal_characters_warning(wxWindow* parent);
private:
struct priv;
std::unique_ptr<priv> p;

View File

@ -483,8 +483,8 @@ void PreferencesDialog::build(size_t selected_tab)
def.label = L("Use system menu for application");
def.type = coBool;
def.tooltip = L("If enabled, application will use standart Windows system menu,\n"
"but on some combination od display scales it can looks ugly. "
def.tooltip = L("If enabled, application will use the standart Windows system menu,\n"
"but on some combination od display scales it can look ugly. "
"If disabled, old UI will be used.");
def.set_default_value(new ConfigOptionBool{ app_config->get("sys_menu_enabled") == "1" });
option = Option(def, "sys_menu_enabled");
@ -519,7 +519,11 @@ void PreferencesDialog::build(size_t selected_tab)
void PreferencesDialog::update_ctrls_alignment()
{
int max_ctrl_width{ 0 };
std::initializer_list<ConfigOptionsGroup*> og_list = { m_optgroup_general.get(), m_optgroup_camera.get(), m_optgroup_gui.get() };
std::initializer_list<ConfigOptionsGroup*> og_list = { m_optgroup_general.get(), m_optgroup_camera.get(), m_optgroup_gui.get()
#ifdef _WIN32
, m_optgroup_dark_mode.get()
#endif // _WIN32
};
for (auto og : og_list) {
if (int max = og->custom_ctrl->get_max_win_width();
max_ctrl_width < max)

View File

@ -96,7 +96,7 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path, bool can_start_pr
}
add_button(wxID_CANCEL);
if (auto* btn_ok = get_button(wxID_NO); btn_ok != NULL) {
if (auto* btn_ok = get_button(wxID_OK); btn_ok != NULL) {
btn_ok->SetLabel(_L("Upload"));
btn_ok->Bind(wxEVT_BUTTON, [this, suffix](wxCommandEvent&) {
wxString path = txt_filename->GetValue();

View File

@ -15,6 +15,9 @@
#include "libslic3r/LocalesUtils.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/PresetBundle.hpp"
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
#include "libslic3r/BuildVolume.hpp"
#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
#include <GL/glew.h>
@ -948,6 +951,94 @@ void Selection::scale(const Vec3d& scale, TransformationType transformation_type
wxGetApp().plater()->canvas3D()->requires_check_outside_state();
}
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
void Selection::scale_to_fit_print_volume(const BuildVolume& volume)
{
auto fit = [this](double s, const Vec3d& offset) {
if (s <= 0.0 || s == 1.0)
return;
wxGetApp().plater()->take_snapshot(_L("Scale To Fit"));
TransformationType type;
type.set_world();
type.set_relative();
type.set_joint();
// apply scale
start_dragging();
scale(s * Vec3d::Ones(), type);
wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot
// center selection on print bed
start_dragging();
translate(offset);
wxGetApp().plater()->canvas3D()->do_move(""); // avoid storing another snapshot
wxGetApp().obj_manipul()->set_dirty();
};
auto fit_rectangle = [this, fit](const BuildVolume& volume) {
const BoundingBoxf3 print_volume = volume.bounding_volume();
const Vec3d print_volume_size = print_volume.size();
// 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();
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;
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());
};
auto fit_circle = [this, fit](const BuildVolume& volume) {
const Geometry::Circled& print_circle = volume.circle();
double print_circle_radius = unscale<double>(print_circle.radius);
if (print_circle_radius == 0.0)
return;
Points points;
double max_z = 0.0;
for (unsigned int i : m_list) {
const GLVolume& v = *(*m_volumes)[i];
TriangleMesh hull_3d = *v.convex_hull();
hull_3d.transform(v.world_matrix());
max_z = std::max(max_z, hull_3d.bounding_box().size().z());
const Polygon hull_2d = hull_3d.convex_hull();
points.insert(points.end(), hull_2d.begin(), hull_2d.end());
}
if (points.empty())
return;
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;
const double s = std::min(print_circle_radius / circle_radius, volume.max_print_height() / 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));
};
if (is_empty() || m_mode == Volume)
return;
switch (volume.type())
{
case BuildVolume::Type::Rectangle: { fit_rectangle(volume); break; }
case BuildVolume::Type::Circle: { fit_circle(volume); break; }
default: { break; }
}
}
#else
void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config)
{
if (is_empty() || m_mode == Volume)
@ -990,6 +1081,7 @@ void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config)
}
}
}
#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
void Selection::mirror(Axis axis)
{

View File

@ -17,6 +17,9 @@ class GLArrow;
class GLCurvedArrow;
class DynamicPrintConfig;
class GLShaderProgram;
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
class BuildVolume;
#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
using GLVolumePtrs = std::vector<GLVolume*>;
using ModelObjectPtrs = std::vector<ModelObject*>;
@ -320,7 +323,11 @@ public:
void rotate(const Vec3d& rotation, TransformationType transformation_type);
void flattening_rotate(const Vec3d& normal);
void scale(const Vec3d& scale, TransformationType transformation_type);
#if ENABLE_ENHANCED_PRINT_VOLUME_FIT
void scale_to_fit_print_volume(const BuildVolume& volume);
#else
void scale_to_fit_print_volume(const DynamicPrintConfig& config);
#endif // ENABLE_ENHANCED_PRINT_VOLUME_FIT
void mirror(Axis axis);
void translate(unsigned int object_idx, const Vec3d& displacement);

View File

@ -3569,6 +3569,7 @@ void Tab::save_preset(std::string name /*= ""*/, bool detach)
on_presets_changed();
// If current profile is saved, "delete preset" button have to be enabled
m_btn_delete_preset->Show();
m_btn_delete_preset->GetParent()->Layout();
if (m_type == Preset::TYPE_PRINTER)
static_cast<TabPrinter*>(this)->m_initial_extruders_count = static_cast<TabPrinter*>(this)->m_extruders_count;

View File

@ -127,6 +127,7 @@ struct Http::priv
Http::CompleteFn completefn;
Http::ErrorFn errorfn;
Http::ProgressFn progressfn;
Http::IPResolveFn ipresolvefn;
priv(const std::string &url);
~priv();
@ -390,6 +391,13 @@ void Http::priv::http_perform()
if (errorfn) { errorfn(std::move(buffer), std::string(), http_status); }
} else {
if (completefn) { completefn(std::move(buffer), http_status); }
if (ipresolvefn) {
char* ct;
res = curl_easy_getinfo(curl, CURLINFO_PRIMARY_IP, &ct);
if ((CURLE_OK == res) && ct) {
ipresolvefn(ct);
}
}
}
}
}
@ -554,6 +562,12 @@ Http& Http::on_progress(ProgressFn fn)
return *this;
}
Http& Http::on_ip_resolve(IPResolveFn fn)
{
if (p) { p->ipresolvefn = std::move(fn); }
return *this;
}
Http::Ptr Http::perform()
{
auto self = std::make_shared<Http>(std::move(*this));

View File

@ -41,6 +41,8 @@ public:
// Writing true to the `cancel` reference cancels the request in progress.
typedef std::function<void(Progress, bool& /* cancel */)> ProgressFn;
typedef std::function<void(std::string/* address */)> IPResolveFn;
Http(Http &&other);
// Note: strings are expected to be UTF-8-encoded
@ -113,6 +115,9 @@ public:
// See the `Progress` structure for description of the data passed.
// Writing a true-ish value into the cancel reference parameter cancels the request.
Http& on_progress(ProgressFn fn);
// Callback called after succesful HTTP request (after on_complete callback)
// Called if curl_easy_getinfo resolved just used IP address.
Http& on_ip_resolve(IPResolveFn fn);
// Starts performing the request in a background thread
Ptr perform();

View File

@ -76,6 +76,9 @@ bool OctoPrint::test(wxString &msg) const
})
#ifdef WIN32
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
.on_ip_resolve([&](std::string address) {
msg = boost::nowide::widen(address);
})
#endif
.perform_sync();
@ -108,9 +111,25 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro
return false;
}
std::string url;
bool res = true;
auto url = make_url("api/files/local");
if (m_host.find("https://") == 0 || test_msg.empty())
{
// If https is entered we assume signed ceritificate is being used
// IP resolving will not happen - it could resolve into address not being specified in cert
url = make_url("api/files/local");
} else {
// Curl uses easy_getinfo to get ip address of last successful transaction.
// If it got the address use it instead of the stored in "host" variable.
// This new address returns in "test_msg" variable.
// Solves troubles of uploades failing with name address.
std::string resolved_addr = boost::nowide::narrow(test_msg);
// put ipv6 into [] brackets (there shouldn't be any http:// if its resolved addr)
if (resolved_addr.find(':') != std::string::npos && resolved_addr.at(0) != '[')
resolved_addr = "[" + resolved_addr + "]";
url = make_url("api/files/local", resolved_addr);
}
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Uploading file %2% at %3%, filename: %4%, path: %5%, print: %6%")
% name
@ -176,6 +195,21 @@ std::string OctoPrint::make_url(const std::string &path) const
}
}
std::string OctoPrint::make_url(const std::string& path, const std::string& addr) const
{
std::string hst = addr.empty() ? m_host : addr;
if (hst.find("http://") == 0 || hst.find("https://") == 0) {
if (hst.back() == '/') {
return (boost::format("%1%%2%") % hst % path).str();
}
else {
return (boost::format("%1%/%2%") % hst % path).str();
}
} else {
return (boost::format("http://%1%/%2%") % hst % path).str();
}
}
SL1Host::SL1Host(DynamicPrintConfig *config) :
OctoPrint(config),
m_authorization_type(dynamic_cast<const ConfigOptionEnum<AuthorizationType>*>(config->option("printhost_authorization_type"))->value),

View File

@ -44,6 +44,7 @@ private:
virtual void set_auth(Http &http) const;
std::string make_url(const std::string &path) const;
std::string make_url(const std::string& path, const std::string& addr) const;
};
class SL1Host: public OctoPrint

View File

@ -3,7 +3,7 @@
set(SLIC3R_APP_NAME "PrusaSlicer")
set(SLIC3R_APP_KEY "PrusaSlicer")
set(SLIC3R_VERSION "2.4.0-beta1")
set(SLIC3R_VERSION "2.4.0-beta2")
set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN")
set(SLIC3R_RC_VERSION "2,4,0,0")
set(SLIC3R_RC_VERSION_DOTS "2.4.0.0")