Merge remote-tracking branch 'origin/master' into ys_cut

This commit is contained in:
YuSanka 2022-03-18 11:32:33 +01:00
commit 9658c9c6b7
37 changed files with 619 additions and 389 deletions

View file

@ -1,2 +1,4 @@
min_slic3r_version = 2.4.0-beta0 min_slic3r_version = 2.4.0-beta0
1.0.2 Fixed start g-code.
1.0.1 Updated start g-code.
1.0.0 Initial version 1.0.0 Initial version

View file

@ -9,7 +9,7 @@ name = Ultimaker
# Configuration version of this file. Config file will only be installed, if the config_version differs. # 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. # This means, the server may force the PrusaSlicer configuration to be downgraded.
config_version = 1.0.0 config_version = 1.0.2
# Where to get the updates from? # Where to get the updates from?
config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Ultimaker/ config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Ultimaker/
@ -396,5 +396,5 @@ printer_notes = Dont remove the following keywords! These keywords are used in t
nozzle_diameter = 0.4 nozzle_diameter = 0.4
default_print_profile = 0.20mm NORMAL @ULTIMAKER2 default_print_profile = 0.20mm NORMAL @ULTIMAKER2
default_filament_profile = Generic PLA @ULTIMAKER2 default_filament_profile = Generic PLA @ULTIMAKER2
start_gcode = ; Printer_Settings_ID: [printer_settings_id]\n\n; # # # # # # START Header\nG21 ; metric values\nG90 ; absolute positioning\nM82 ; set extruder to absolute mode\nM107 ; start with the fan off\n\nG28 X0 Y0 Z0 ; move X/Y/Z to endstops\nG1 X1 Y6 F15000 ; move X/Y to start position\nG1 Z35 F9000 ; move Z to start position\n\n; Heat up bed and nozzle\nM190 S{first_layer_bed_temperature[0] - 5} ; wait for bed temperature - 5\nM140 S[first_layer_bed_temperature] ; continue bed heating\nM109 S[first_layer_temperature] ; wait for nozzle temperature\n\nG92 E0 ; zero the extruded length\nG1 F150 E22 ; purge nozzle with filament\nG92 E0 ; zero the extruded length again\nG1 F75 E7 ; additional priming\nG92 E0 ; zero the extruded length again\n\n; # # # # # # END Header start_gcode = ; Printer_Settings_ID: [printer_settings_id]\n\n; # # # # # # START Header\nG21 ; metric values\nG90 ; absolute positioning\nM82 ; set extruder to absolute mode\nM107 ; start with the fan off\n\nM140 S[first_layer_bed_temperature] ; start bed heating\n\nG28 X0 Y0 Z0 ; move X/Y/Z to endstops\nG1 X1 Y6 F15000 ; move X/Y to start position\nG1 Z35 F9000 ; move Z to start position\n\n; Wait for bed and nozzle temperatures\nM190 S{first_layer_bed_temperature[0] - 5} ; wait for bed temperature - 5\nM140 S[first_layer_bed_temperature] ; continue bed heating\nM109 S[first_layer_temperature] ; wait for nozzle temperature\n\n; Purge and prime\nM83 ; set extruder to relative mode\nG92 E0 ; reset extrusion distance\nG0 X0 Y1 F10000\nG1 F150 E20 ; compress the bowden tube\nG1 E-8 F1200\nG0 X30 Y1 F5000 \nG0 F1200 Z{first_layer_height/2} ; Cut the connection to priming blob\nG0 X100 F10000 ; disconnect with the prime blob\nG0 X50 ; Avoid the metal clip holding the Ultimaker glass plate\nG0 Z0.2 F720\nG1 E8 F1200\nG1 X80 E3 F1000 ; intro line 1\nG1 X110 E4 F1000 ; intro line 2\nG1 X140 F600 ; drag filament to decompress bowden tube\nG1 X100 F3200 ; wipe backwards a bit\nG1 X150 F3200 ; back to where there is no plastic: avoid dragging\nG92 E0 ; reset extruder reference\nM82 ; set extruder to absolute mode\n\n; # # # # # # END Header
end_gcode = ; # # # # # # START Footer\nG91 ; relative coordinates\n;G1 E-1 F1200 ; retract the filament\nG1 Z+15 X-10 Y-10 E-7 F6000 ; move Z a bit\n; G1 X-10 Y-10 F6000 ; move XY a bit\nG1 E-5.5 F300 ; retract the filament\nG28 X0 Y0 ; move X/Y to min endstops, so the head is out of the way\nM104 S0 ; extruder heater off\nM140 S0 ; heated bed heater off (if you have it)\nM84 ; disable motors\n; # # # # # # END Footer\n end_gcode = ; # # # # # # START Footer\nG91 ; relative coordinates\n;G1 E-1 F1200 ; retract the filament\nG1 Z+15 X-10 Y-10 E-7 F6000 ; move Z a bit\n; G1 X-10 Y-10 F6000 ; move XY a bit\nG1 E-5.5 F300 ; retract the filament\nG28 X0 Y0 ; move X/Y to min endstops, so the head is out of the way\nM104 S0 ; extruder heater off\nM140 S0 ; heated bed heater off (if you have it)\nM84 ; disable motors\n; # # # # # # END Footer\n

View file

@ -11,6 +11,7 @@
#include "FillBase.hpp" #include "FillBase.hpp"
#include "FillRectilinear.hpp" #include "FillRectilinear.hpp"
#include "FillLightning.hpp"
namespace Slic3r { namespace Slic3r {
@ -318,7 +319,7 @@ void export_group_fills_to_svg(const char *path, const std::vector<SurfaceFill>
#endif #endif
// friend to Layer // friend to Layer
void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree) void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator)
{ {
for (LayerRegion *layerm : m_regions) for (LayerRegion *layerm : m_regions)
layerm->fills.clear(); layerm->fills.clear();
@ -348,6 +349,9 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
f->angle = surface_fill.params.angle; f->angle = surface_fill.params.angle;
f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree; f->adapt_fill_octree = (surface_fill.params.pattern == ipSupportCubic) ? support_fill_octree : adaptive_fill_octree;
if (surface_fill.params.pattern == ipLightning)
dynamic_cast<FillLightning::Filler*>(f.get())->generator = lightning_generator;
// calculate flow spacing for infill pattern generation // calculate flow spacing for infill pattern generation
bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge; bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.bridge;
double link_max_length = 0.; double link_max_length = 0.;

View file

@ -46,9 +46,7 @@ Fill* Fill::new_from_type(const InfillPattern type)
case ipAdaptiveCubic: return new FillAdaptive::Filler(); case ipAdaptiveCubic: return new FillAdaptive::Filler();
case ipSupportCubic: return new FillAdaptive::Filler(); case ipSupportCubic: return new FillAdaptive::Filler();
case ipSupportBase: return new FillSupportBase(); case ipSupportBase: return new FillSupportBase();
#if HAS_LIGHTNING_INFILL
case ipLightning: return new FillLightning::Filler(); case ipLightning: return new FillLightning::Filler();
#endif // HAS_LIGHTNING_INFILL
default: throw Slic3r::InvalidArgument("unknown type"); default: throw Slic3r::InvalidArgument("unknown type");
} }
} }

View file

@ -406,13 +406,15 @@ public:
// for the infill pattern, don't cut the corners. // for the infill pattern, don't cut the corners.
// default miterLimt = 3 // default miterLimt = 3
//double miterLimit = 10.; //double miterLimit = 10.;
assert(aoffset1 < 0); // FIXME: Resolve properly the cases when it is constructed with aoffset1 = 0 and aoffset2 = 0,
// that is used in sample_grid_pattern() for Lightning infill.
// assert(aoffset1 < 0);
assert(aoffset2 <= 0); assert(aoffset2 <= 0);
assert(aoffset2 == 0 || aoffset2 < aoffset1); // assert(aoffset2 == 0 || aoffset2 < aoffset1);
// bool sticks_removed = // bool sticks_removed =
remove_sticks(polygons_src); remove_sticks(polygons_src);
// if (sticks_removed) BOOST_LOG_TRIVIAL(error) << "Sticks removed!"; // if (sticks_removed) BOOST_LOG_TRIVIAL(error) << "Sticks removed!";
polygons_outer = offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, miterLimit); polygons_outer = aoffset1 == 0 ? polygons_src : offset(polygons_src, float(aoffset1), ClipperLib::jtMiter, miterLimit);
if (aoffset2 < 0) if (aoffset2 < 0)
polygons_inner = shrink(polygons_outer, float(aoffset1 - aoffset2), ClipperLib::jtMiter, miterLimit); polygons_inner = shrink(polygons_outer, float(aoffset1 - aoffset2), ClipperLib::jtMiter, miterLimit);
// Filter out contours with zero area or small area, contours with 2 points only. // Filter out contours with zero area or small area, contours with 2 points only.

View file

@ -10,27 +10,29 @@ namespace Slic3r::FillLightning
constexpr coord_t radius_per_cell_size = 6; // The cell-size should be small compared to the radius, but not so small as to be inefficient. constexpr coord_t radius_per_cell_size = 6; // The cell-size should be small compared to the radius, but not so small as to be inefficient.
DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outline, const Polygons& current_overhang) : DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outline, const BoundingBox& current_outlines_bbox, const Polygons& current_overhang) :
m_cell_size(radius / radius_per_cell_size), m_cell_size(radius / radius_per_cell_size),
m_supporting_radius(radius) m_supporting_radius(radius),
m_unsupported_points_bbox(current_outlines_bbox)
{ {
m_supporting_radius2 = double(radius) * double(radius); m_supporting_radius2 = Slic3r::sqr(int64_t(radius));
// Sample source polygons with a regular grid sampling pattern. // Sample source polygons with a regular grid sampling pattern.
for (const ExPolygon &expoly : union_ex(current_outline)) { for (const ExPolygon &expoly : union_ex(current_overhang)) {
for (const Point &p : sample_grid_pattern(expoly, m_cell_size)) { for (const Point &p : sample_grid_pattern(expoly, m_cell_size)) {
// Find a squared distance to the source expolygon boundary. // Find a squared distance to the source expolygon boundary.
double d2 = std::numeric_limits<double>::max(); double d2 = std::numeric_limits<double>::max();
for (size_t icontour = 0; icontour <= expoly.holes.size(); ++ icontour) { for (size_t icontour = 0; icontour <= expoly.holes.size(); ++icontour) {
const Polygon &contour = icontour == 0 ? expoly.contour : expoly.holes[icontour - 1]; const Polygon &contour = icontour == 0 ? expoly.contour : expoly.holes[icontour - 1];
if (contour.size() > 2) { if (contour.size() > 2) {
Point prev = contour.points.back(); Point prev = contour.points.back();
for (const Point &p2 : contour.points) { for (const Point &p2 : contour.points) {
d2 = std::min(d2, Line::distance_to_squared(p, prev, p2)); d2 = std::min(d2, Line::distance_to_squared(p, prev, p2));
prev = p2; prev = p2;
} }
} }
} }
m_unsupported_points.emplace_back(p, sqrt(d2)); m_unsupported_points.emplace_back(p, sqrt(d2));
assert(m_unsupported_points_bbox.contains(p));
} }
} }
m_unsupported_points.sort([&radius](const UnsupportedCell &a, const UnsupportedCell &b) { m_unsupported_points.sort([&radius](const UnsupportedCell &a, const UnsupportedCell &b) {
@ -41,8 +43,11 @@ DistanceField::DistanceField(const coord_t& radius, const Polygons& current_outl
}); });
for (auto it = m_unsupported_points.begin(); it != m_unsupported_points.end(); ++it) { for (auto it = m_unsupported_points.begin(); it != m_unsupported_points.end(); ++it) {
UnsupportedCell& cell = *it; UnsupportedCell& cell = *it;
m_unsupported_points_grid.emplace(Point{ cell.loc.x() / m_cell_size, cell.loc.y() / m_cell_size }, it); m_unsupported_points_grid.emplace(this->to_grid_point(cell.loc), it);
} }
// Because the distance between two points is at least one axis equal to m_cell_size, every cell
// in m_unsupported_points_grid contains exactly one point.
assert(m_unsupported_points.size() == m_unsupported_points_grid.size());
} }
void DistanceField::update(const Point& to_node, const Point& added_leaf) void DistanceField::update(const Point& to_node, const Point& added_leaf)
@ -60,17 +65,24 @@ void DistanceField::update(const Point& to_node, const Point& added_leaf)
grid.merge(to_node + iextent); grid.merge(to_node + iextent);
grid.merge(added_leaf - iextent); grid.merge(added_leaf - iextent);
grid.merge(added_leaf + iextent); grid.merge(added_leaf + iextent);
grid.min /= m_cell_size;
grid.max /= m_cell_size; // Clip grid by m_unsupported_points_bbox. Mainly to ensure that grid.min is a non-negative value.
grid.min.x() = std::max(grid.min.x(), m_unsupported_points_bbox.min.x());
grid.min.y() = std::max(grid.min.y(), m_unsupported_points_bbox.min.y());
grid.max.x() = std::min(grid.max.x(), m_unsupported_points_bbox.max.x());
grid.max.y() = std::min(grid.max.y(), m_unsupported_points_bbox.max.y());
grid.min = this->to_grid_point(grid.min);
grid.max = this->to_grid_point(grid.max);
} }
Point grid_addr;
Point grid_loc; Point grid_loc;
for (coord_t row = grid.min.y(); row <= grid.max.y(); ++ row) { for (grid_addr.y() = grid.min.y(); grid_addr.y() <= grid.max.y(); ++grid_addr.y()) {
grid_loc.y() = row * m_cell_size; for (grid_addr.x() = grid.min.x(); grid_addr.x() <= grid.max.x(); ++grid_addr.x()) {
for (coord_t col = grid.min.x(); col <= grid.max.y(); ++ col) { grid_loc = this->from_grid_point(grid_addr);
grid_loc.x() = col * m_cell_size;
// Test inside a circle at the new leaf. // Test inside a circle at the new leaf.
if ((grid_loc - added_leaf).cast<double>().squaredNorm() > m_supporting_radius2) { if ((grid_loc - added_leaf).cast<int64_t>().squaredNorm() > m_supporting_radius2) {
// Not inside a circle at the end of the new leaf. // Not inside a circle at the end of the new leaf.
// Test inside a rotated rectangle. // Test inside a rotated rectangle.
Vec2d vx = (grid_loc - to_node).cast<double>(); Vec2d vx = (grid_loc - to_node).cast<double>();
@ -84,10 +96,10 @@ void DistanceField::update(const Point& to_node, const Point& added_leaf)
} }
// Inside a circle at the end of the new leaf, or inside a rotated rectangle. // Inside a circle at the end of the new leaf, or inside a rotated rectangle.
// Remove unsupported leafs at this grid location. // Remove unsupported leafs at this grid location.
if (auto it = m_unsupported_points_grid.find(grid_loc); it != m_unsupported_points_grid.end()) { if (auto it = m_unsupported_points_grid.find(grid_addr); it != m_unsupported_points_grid.end()) {
std::list<UnsupportedCell>::iterator& list_it = it->second; std::list<UnsupportedCell>::iterator& list_it = it->second;
UnsupportedCell& cell = *list_it; UnsupportedCell& cell = *list_it;
if ((cell.loc - added_leaf).cast<double>().squaredNorm() <= m_supporting_radius2) { if ((cell.loc - added_leaf).cast<int64_t>().squaredNorm() <= m_supporting_radius2) {
m_unsupported_points.erase(list_it); m_unsupported_points.erase(list_it);
m_unsupported_points_grid.erase(it); m_unsupported_points_grid.erase(it);
} }
@ -96,4 +108,25 @@ void DistanceField::update(const Point& to_node, const Point& added_leaf)
} }
} }
#if 0
void DistanceField::update(const Point &to_node, const Point &added_leaf)
{
const Point supporting_radius_point(m_supporting_radius, m_supporting_radius);
const BoundingBox grid(this->to_grid_point(added_leaf - supporting_radius_point), this->to_grid_point(added_leaf + supporting_radius_point));
for (coord_t grid_y = grid.min.y(); grid_y <= grid.max.y(); ++grid_y) {
for (coord_t grid_x = grid.min.x(); grid_x <= grid.max.x(); ++grid_x) {
if (auto it = m_unsupported_points_grid.find({grid_x, grid_y}); it != m_unsupported_points_grid.end()) {
std::list<UnsupportedCell>::iterator &list_it = it->second;
UnsupportedCell &cell = *list_it;
if ((cell.loc - added_leaf).cast<int64_t>().squaredNorm() <= m_supporting_radius2) {
m_unsupported_points.erase(list_it);
m_unsupported_points_grid.erase(it);
}
}
}
}
}
#endif
} // namespace Slic3r::FillLightning } // namespace Slic3r::FillLightning

View file

@ -4,6 +4,7 @@
#ifndef LIGHTNING_DISTANCE_FIELD_H #ifndef LIGHTNING_DISTANCE_FIELD_H
#define LIGHTNING_DISTANCE_FIELD_H #define LIGHTNING_DISTANCE_FIELD_H
#include "../../BoundingBox.hpp"
#include "../../Point.hpp" #include "../../Point.hpp"
#include "../../Polygon.hpp" #include "../../Polygon.hpp"
@ -29,7 +30,7 @@ public:
* \param current_overhang The overhang that needs to be supported on this * \param current_overhang The overhang that needs to be supported on this
* layer. * layer.
*/ */
DistanceField(const coord_t& radius, const Polygons& current_outline, const Polygons& current_overhang); DistanceField(const coord_t& radius, const Polygons& current_outline, const BoundingBox& current_outlines_bbox, const Polygons& current_overhang);
/*! /*!
* Gets the next unsupported location to be supported by a new branch. * Gets the next unsupported location to be supported by a new branch.
@ -69,14 +70,14 @@ protected:
* branch of a tree. * branch of a tree.
*/ */
coord_t m_supporting_radius; coord_t m_supporting_radius;
double m_supporting_radius2; int64_t m_supporting_radius2;
/*! /*!
* Represents a small discrete area of infill that needs to be supported. * Represents a small discrete area of infill that needs to be supported.
*/ */
struct UnsupportedCell struct UnsupportedCell
{ {
UnsupportedCell(Point loc, coord_t dist_to_boundary) : loc(loc), dist_to_boundary(dist_to_boundary) {} UnsupportedCell(const Point &loc, coord_t dist_to_boundary) : loc(loc), dist_to_boundary(dist_to_boundary) {}
// The position of the center of this cell. // The position of the center of this cell.
Point loc; Point loc;
// How far this cell is removed from the ``current_outline`` polygon, the edge of the infill area. // How far this cell is removed from the ``current_outline`` polygon, the edge of the infill area.
@ -88,11 +89,30 @@ protected:
*/ */
std::list<UnsupportedCell> m_unsupported_points; std::list<UnsupportedCell> m_unsupported_points;
/*!
* BoundingBox of all points in m_unsupported_points. Used for mapping of sign integer numbers to positive integer numbers.
*/
const BoundingBox m_unsupported_points_bbox;
/*! /*!
* Links the unsupported points to a grid point, so that we can quickly look * Links the unsupported points to a grid point, so that we can quickly look
* up the cell belonging to a certain position in the grid. * up the cell belonging to a certain position in the grid.
*/ */
std::unordered_map<Point, std::list<UnsupportedCell>::iterator, PointHash> m_unsupported_points_grid; std::unordered_map<Point, std::list<UnsupportedCell>::iterator, PointHash> m_unsupported_points_grid;
/*!
* Maps the point to the grid coordinates.
*/
Point to_grid_point(const Point &point) const {
return (point - m_unsupported_points_bbox.min) / m_cell_size;
}
/*!
* Maps the point to the grid coordinates.
*/
Point from_grid_point(const Point &point) const {
return point * m_cell_size + m_unsupported_points_bbox.min;
}
}; };
} // namespace Slic3r::FillLightning } // namespace Slic3r::FillLightning

View file

@ -35,17 +35,17 @@ Generator::Generator(const PrintObject &print_object)
// const int infill_extruder = region_config.infill_extruder.value; // const int infill_extruder = region_config.infill_extruder.value;
const double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter)); const double default_infill_extrusion_width = Flow::auto_extrusion_width(FlowRole::frInfill, float(max_nozzle_diameter));
// Note: There's not going to be a layer below the first one, so the 'initial layer height' doesn't have to be taken into account. // Note: There's not going to be a layer below the first one, so the 'initial layer height' doesn't have to be taken into account.
const double layer_thickness = object_config.layer_height; const double layer_thickness = scaled<double>(object_config.layer_height.value);
m_infill_extrusion_width = scaled<float>(region_config.infill_extrusion_width.percent ? default_infill_extrusion_width * 0.01 * region_config.infill_extrusion_width : region_config.infill_extrusion_width); m_infill_extrusion_width = scaled<float>(region_config.infill_extrusion_width.percent ? default_infill_extrusion_width * 0.01 * region_config.infill_extrusion_width : region_config.infill_extrusion_width);
m_supporting_radius = scaled<coord_t>(m_infill_extrusion_width * 0.001 / region_config.fill_density); m_supporting_radius = coord_t(m_infill_extrusion_width) * 100 / coord_t(region_config.fill_density.value);
const double lightning_infill_overhang_angle = M_PI / 4; // 45 degrees const double lightning_infill_overhang_angle = M_PI / 4; // 45 degrees
const double lightning_infill_prune_angle = M_PI / 4; // 45 degrees const double lightning_infill_prune_angle = M_PI / 4; // 45 degrees
const double lightning_infill_straightening_angle = M_PI / 4; // 45 degrees const double lightning_infill_straightening_angle = M_PI / 4; // 45 degrees
m_wall_supporting_radius = layer_thickness * std::tan(lightning_infill_overhang_angle); m_wall_supporting_radius = coord_t(layer_thickness * std::tan(lightning_infill_overhang_angle));
m_prune_length = layer_thickness * std::tan(lightning_infill_prune_angle); m_prune_length = coord_t(layer_thickness * std::tan(lightning_infill_prune_angle));
m_straightening_max_distance = layer_thickness * std::tan(lightning_infill_straightening_angle); m_straightening_max_distance = coord_t(layer_thickness * std::tan(lightning_infill_straightening_angle));
generateInitialInternalOverhangs(print_object); generateInitialInternalOverhangs(print_object);
generateTrees(print_object); generateTrees(print_object);
@ -54,19 +54,20 @@ Generator::Generator(const PrintObject &print_object)
void Generator::generateInitialInternalOverhangs(const PrintObject &print_object) void Generator::generateInitialInternalOverhangs(const PrintObject &print_object)
{ {
m_overhang_per_layer.resize(print_object.layers().size()); m_overhang_per_layer.resize(print_object.layers().size());
const float infill_wall_offset = - m_infill_extrusion_width; // FIXME: It can be adjusted to improve bonding between infill and perimeters.
const float infill_wall_offset = 0;// m_infill_extrusion_width;
Polygons infill_area_above; Polygons infill_area_above;
//Iterate from top to bottom, to subtract the overhang areas above from the overhang areas on the layer below, to get only overhang in the top layer where it is overhanging. //Iterate from top to bottom, to subtract the overhang areas above from the overhang areas on the layer below, to get only overhang in the top layer where it is overhanging.
for (int layer_nr = print_object.layers().size() - 1; layer_nr >= 0; layer_nr--) { for (int layer_nr = int(print_object.layers().size()) - 1; layer_nr >= 0; layer_nr--) {
Polygons infill_area_here; Polygons infill_area_here;
for (const LayerRegion* layerm : print_object.get_layer(layer_nr)->regions()) for (const LayerRegion* layerm : print_object.get_layer(layer_nr)->regions())
for (const Surface& surface : layerm->fill_surfaces.surfaces) for (const Surface& surface : layerm->fill_surfaces.surfaces)
if (surface.surface_type == stInternal) if (surface.surface_type == stInternal)
append(infill_area_here, offset(surface.expolygon, infill_wall_offset)); append(infill_area_here, infill_wall_offset == 0 ? surface.expolygon : offset(surface.expolygon, infill_wall_offset));
//Remove the part of the infill area that is already supported by the walls. //Remove the part of the infill area that is already supported by the walls.
Polygons overhang = diff(offset(infill_area_here, -m_wall_supporting_radius), infill_area_above); Polygons overhang = diff(offset(infill_area_here, -float(m_wall_supporting_radius)), infill_area_above);
m_overhang_per_layer[layer_nr] = overhang; m_overhang_per_layer[layer_nr] = overhang;
infill_area_above = std::move(infill_area_here); infill_area_above = std::move(infill_area_here);
@ -82,16 +83,17 @@ const Layer& Generator::getTreesForLayer(const size_t& layer_id) const
void Generator::generateTrees(const PrintObject &print_object) void Generator::generateTrees(const PrintObject &print_object)
{ {
m_lightning_layers.resize(print_object.layers().size()); m_lightning_layers.resize(print_object.layers().size());
const coord_t infill_wall_offset = - m_infill_extrusion_width; // FIXME: It can be adjusted to improve bonding between infill and perimeters.
const coord_t infill_wall_offset = 0;// m_infill_extrusion_width;
std::vector<Polygons> infill_outlines(print_object.layers().size(), Polygons()); std::vector<Polygons> infill_outlines(print_object.layers().size(), Polygons());
// For-each layer from top to bottom: // For-each layer from top to bottom:
for (int layer_id = print_object.layers().size() - 1; layer_id >= 0; layer_id--) for (int layer_id = int(print_object.layers().size()) - 1; layer_id >= 0; layer_id--)
for (const LayerRegion *layerm : print_object.get_layer(layer_id)->regions()) for (const LayerRegion *layerm : print_object.get_layer(layer_id)->regions())
for (const Surface &surface : layerm->fill_surfaces.surfaces) for (const Surface &surface : layerm->fill_surfaces.surfaces)
if (surface.surface_type == stInternal) if (surface.surface_type == stInternal)
append(infill_outlines[layer_id], offset(surface.expolygon, infill_wall_offset)); append(infill_outlines[layer_id], infill_wall_offset == 0 ? surface.expolygon : offset(surface.expolygon, infill_wall_offset));
// For various operations its beneficial to quickly locate nearby features on the polygon: // For various operations its beneficial to quickly locate nearby features on the polygon:
const size_t top_layer_id = print_object.layers().size() - 1; const size_t top_layer_id = print_object.layers().size() - 1;
@ -99,16 +101,16 @@ void Generator::generateTrees(const PrintObject &print_object)
outlines_locator.create(infill_outlines[top_layer_id], locator_cell_size); outlines_locator.create(infill_outlines[top_layer_id], locator_cell_size);
// For-each layer from top to bottom: // For-each layer from top to bottom:
for (int layer_id = top_layer_id; layer_id >= 0; layer_id--) for (int layer_id = int(top_layer_id); layer_id >= 0; layer_id--) {
{ Layer &current_lightning_layer = m_lightning_layers[layer_id];
Layer& current_lightning_layer = m_lightning_layers[layer_id]; const Polygons &current_outlines = infill_outlines[layer_id];
Polygons& current_outlines = infill_outlines[layer_id]; const BoundingBox &current_outlines_bbox = get_extents(current_outlines);
// register all trees propagated from the previous layer as to-be-reconnected // register all trees propagated from the previous layer as to-be-reconnected
std::vector<NodeSPtr> to_be_reconnected_tree_roots = current_lightning_layer.tree_roots; std::vector<NodeSPtr> to_be_reconnected_tree_roots = current_lightning_layer.tree_roots;
current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, outlines_locator, m_supporting_radius, m_wall_supporting_radius); current_lightning_layer.generateNewTrees(m_overhang_per_layer[layer_id], current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius);
current_lightning_layer.reconnectRoots(to_be_reconnected_tree_roots, current_outlines, outlines_locator, m_supporting_radius, m_wall_supporting_radius); current_lightning_layer.reconnectRoots(to_be_reconnected_tree_roots, current_outlines, current_outlines_bbox, outlines_locator, m_supporting_radius, m_wall_supporting_radius);
// Initialize trees for next lower layer from the current one. // Initialize trees for next lower layer from the current one.
if (layer_id == 0) if (layer_id == 0)

View file

@ -45,7 +45,7 @@ public:
* already be calculated at this point. * already be calculated at this point.
* \param mesh The mesh to generate infill for. * \param mesh The mesh to generate infill for.
*/ */
Generator(const PrintObject &print_object); explicit Generator(const PrintObject &print_object);
/*! /*!
* Get a tree of paths generated for a certain layer of the mesh. * Get a tree of paths generated for a certain layer of the mesh.

View file

@ -3,12 +3,11 @@
#include "Layer.hpp" //The class we're implementing. #include "Layer.hpp" //The class we're implementing.
#include <iterator> // advance
#include "DistanceField.hpp" #include "DistanceField.hpp"
#include "TreeNode.hpp" #include "TreeNode.hpp"
#include "../../Geometry.hpp" #include "../../Geometry.hpp"
#include "Utils.hpp"
namespace Slic3r::FillLightning { namespace Slic3r::FillLightning {
@ -23,10 +22,15 @@ Point GroundingLocation::p() const
return tree_node ? tree_node->getLocation() : *boundary_location; return tree_node ? tree_node->getLocation() : *boundary_location;
} }
void Layer::fillLocator(SparseNodeGrid &tree_node_locator) inline static Point to_grid_point(const Point &point, const BoundingBox &bbox)
{ {
std::function<void(NodeSPtr)> add_node_to_locator_func = [&tree_node_locator](NodeSPtr node) { return (point - bbox.min) / locator_cell_size;
tree_node_locator.insert(std::make_pair(Point(node->getLocation().x() / locator_cell_size, node->getLocation().y() / locator_cell_size), node)); }
void Layer::fillLocator(SparseNodeGrid &tree_node_locator, const BoundingBox& current_outlines_bbox)
{
std::function<void(NodeSPtr)> add_node_to_locator_func = [&tree_node_locator, &current_outlines_bbox](const NodeSPtr &node) {
tree_node_locator.insert(std::make_pair(to_grid_point(node->getLocation(), current_outlines_bbox), node));
}; };
for (auto& tree : tree_roots) for (auto& tree : tree_roots)
tree->visitNodes(add_node_to_locator_func); tree->visitNodes(add_node_to_locator_func);
@ -36,38 +40,46 @@ void Layer::generateNewTrees
( (
const Polygons& current_overhang, const Polygons& current_overhang,
const Polygons& current_outlines, const Polygons& current_outlines,
const BoundingBox& current_outlines_bbox,
const EdgeGrid::Grid& outlines_locator, const EdgeGrid::Grid& outlines_locator,
const coord_t supporting_radius, const coord_t supporting_radius,
const coord_t wall_supporting_radius const coord_t wall_supporting_radius
) )
{ {
DistanceField distance_field(supporting_radius, current_outlines, current_overhang); DistanceField distance_field(supporting_radius, current_outlines, current_outlines_bbox, current_overhang);
SparseNodeGrid tree_node_locator; SparseNodeGrid tree_node_locator;
fillLocator(tree_node_locator); fillLocator(tree_node_locator, current_outlines_bbox);
// Until no more points need to be added to support all: // Until no more points need to be added to support all:
// Determine next point from tree/outline areas via distance-field // Determine next point from tree/outline areas via distance-field
Point unsupported_location; Point unsupported_location;
while (distance_field.tryGetNextPoint(&unsupported_location)) { while (distance_field.tryGetNextPoint(&unsupported_location)) {
GroundingLocation grounding_loc = getBestGroundingLocation( GroundingLocation grounding_loc = getBestGroundingLocation(
unsupported_location, current_outlines, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator); unsupported_location, current_outlines, current_outlines_bbox, outlines_locator, supporting_radius, wall_supporting_radius, tree_node_locator);
NodeSPtr new_parent; NodeSPtr new_parent;
NodeSPtr new_child; NodeSPtr new_child;
this->attach(unsupported_location, grounding_loc, new_child, new_parent); this->attach(unsupported_location, grounding_loc, new_child, new_parent);
tree_node_locator.insert(std::make_pair(Point(new_child->getLocation().x() / locator_cell_size, new_child->getLocation().y() / locator_cell_size), new_child)); tree_node_locator.insert(std::make_pair(to_grid_point(new_child->getLocation(), current_outlines_bbox), new_child));
if (new_parent) if (new_parent)
tree_node_locator.insert(std::make_pair(Point(new_parent->getLocation().x() / locator_cell_size, new_parent->getLocation().y() / locator_cell_size), new_parent)); tree_node_locator.insert(std::make_pair(to_grid_point(new_parent->getLocation(), current_outlines_bbox), new_parent));
// update distance field // update distance field
distance_field.update(grounding_loc.p(), unsupported_location); distance_field.update(grounding_loc.p(), unsupported_location);
} }
#ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT
{
static int iRun = 0;
export_to_svg(debug_out_path("FillLightning-TreeNodes-%d.svg", iRun++), current_outlines, this->tree_roots);
}
#endif /* LIGHTNING_TREE_NODE_DEBUG_OUTPUT */
} }
static bool polygonCollidesWithLineSegment(const Point from, const Point to, const EdgeGrid::Grid &loc_to_line) static bool polygonCollidesWithLineSegment(const Point &from, const Point &to, const EdgeGrid::Grid &loc_to_line)
{ {
struct Visitor { struct Visitor {
explicit Visitor(const EdgeGrid::Grid &grid) : grid(grid) {} explicit Visitor(const EdgeGrid::Grid &grid, const Line &line) : grid(grid), line(line) {}
bool operator()(coord_t iy, coord_t ix) { bool operator()(coord_t iy, coord_t ix) {
// Called with a row and colum of the grid cell, which is intersected by a line. // Called with a row and colum of the grid cell, which is intersected by a line.
@ -87,7 +99,7 @@ static bool polygonCollidesWithLineSegment(const Point from, const Point to, con
const EdgeGrid::Grid& grid; const EdgeGrid::Grid& grid;
Line line; Line line;
bool intersect = false; bool intersect = false;
} visitor(loc_to_line); } visitor(loc_to_line, {from, to});
loc_to_line.visit_cells_intersecting_line(from, to, visitor); loc_to_line.visit_cells_intersecting_line(from, to, visitor);
return visitor.intersect; return visitor.intersect;
@ -97,6 +109,7 @@ GroundingLocation Layer::getBestGroundingLocation
( (
const Point& unsupported_location, const Point& unsupported_location,
const Polygons& current_outlines, const Polygons& current_outlines,
const BoundingBox& current_outlines_bbox,
const EdgeGrid::Grid& outline_locator, const EdgeGrid::Grid& outline_locator,
const coord_t supporting_radius, const coord_t supporting_radius,
const coord_t wall_supporting_radius, const coord_t wall_supporting_radius,
@ -128,8 +141,8 @@ GroundingLocation Layer::getBestGroundingLocation
if (current_dist >= wall_supporting_radius) { // Only reconnect tree roots to other trees if they are not already close to the outlines. if (current_dist >= wall_supporting_radius) { // Only reconnect tree roots to other trees if they are not already close to the outlines.
const coord_t search_radius = std::min(current_dist, within_dist); const coord_t search_radius = std::min(current_dist, within_dist);
BoundingBox region(unsupported_location - Point(search_radius, search_radius), unsupported_location + Point(search_radius + locator_cell_size, search_radius + locator_cell_size)); BoundingBox region(unsupported_location - Point(search_radius, search_radius), unsupported_location + Point(search_radius + locator_cell_size, search_radius + locator_cell_size));
region.min /= locator_cell_size; region.min = to_grid_point(region.min, current_outlines_bbox);
region.max /= locator_cell_size; region.max = to_grid_point(region.max, current_outlines_bbox);
Point grid_addr; Point grid_addr;
for (grid_addr.y() = region.min.y(); grid_addr.y() < region.max.y(); ++ grid_addr.y()) for (grid_addr.y() = region.min.y(); grid_addr.y() < region.max.y(); ++ grid_addr.y())
for (grid_addr.x() = region.min.x(); grid_addr.x() < region.max.x(); ++ grid_addr.x()) { for (grid_addr.x() = region.min.x(); grid_addr.x() < region.max.x(); ++ grid_addr.x()) {
@ -176,6 +189,7 @@ void Layer::reconnectRoots
( (
std::vector<NodeSPtr>& to_be_reconnected_tree_roots, std::vector<NodeSPtr>& to_be_reconnected_tree_roots,
const Polygons& current_outlines, const Polygons& current_outlines,
const BoundingBox& current_outlines_bbox,
const EdgeGrid::Grid& outline_locator, const EdgeGrid::Grid& outline_locator,
const coord_t supporting_radius, const coord_t supporting_radius,
const coord_t wall_supporting_radius const coord_t wall_supporting_radius
@ -184,10 +198,10 @@ void Layer::reconnectRoots
constexpr coord_t tree_connecting_ignore_offset = 100; constexpr coord_t tree_connecting_ignore_offset = 100;
SparseNodeGrid tree_node_locator; SparseNodeGrid tree_node_locator;
fillLocator(tree_node_locator); fillLocator(tree_node_locator, current_outlines_bbox);
const coord_t within_max_dist = outline_locator.resolution() * 2; const coord_t within_max_dist = outline_locator.resolution() * 2;
for (auto root_ptr : to_be_reconnected_tree_roots) for (const auto &root_ptr : to_be_reconnected_tree_roots)
{ {
auto old_root_it = std::find(tree_roots.begin(), tree_roots.end(), root_ptr); auto old_root_it = std::find(tree_roots.begin(), tree_roots.end(), root_ptr);
@ -203,7 +217,7 @@ void Layer::reconnectRoots
root_ptr->addChild(new_root); root_ptr->addChild(new_root);
new_root->reroot(); new_root->reroot();
tree_node_locator.insert(std::make_pair(Point(new_root->getLocation().x() / locator_cell_size, new_root->getLocation().y() / locator_cell_size), new_root)); tree_node_locator.insert(std::make_pair(to_grid_point(new_root->getLocation(), current_outlines_bbox), new_root));
*old_root_it = std::move(new_root); // replace old root with new root *old_root_it = std::move(new_root); // replace old root with new root
continue; continue;
@ -217,6 +231,7 @@ void Layer::reconnectRoots
( (
root_ptr->getLocation(), root_ptr->getLocation(),
current_outlines, current_outlines,
current_outlines_bbox,
outline_locator, outline_locator,
supporting_radius, supporting_radius,
tree_connecting_ignore_width, tree_connecting_ignore_width,
@ -233,7 +248,7 @@ void Layer::reconnectRoots
attach_ptr->reroot(); attach_ptr->reroot();
new_root->addChild(attach_ptr); new_root->addChild(attach_ptr);
tree_node_locator.insert(std::make_pair(new_root->getLocation(), new_root)); tree_node_locator.insert(std::make_pair(to_grid_point(new_root->getLocation(), current_outlines_bbox), new_root));
*old_root_it = std::move(new_root); // replace old root with new root *old_root_it = std::move(new_root); // replace old root with new root
} }
@ -256,15 +271,25 @@ void Layer::reconnectRoots
} }
} }
/* /*!
* Implementation assumes moving inside, but moving outside should just as well be possible. * Moves the point \p from onto the nearest polygon or leaves the point as-is, when the comb boundary is not within the root of \p max_dist2 distance.
* Given a \p distance more than zero, the point will end up inside, and conversely outside.
* When the point is already in/outside by more than \p distance, \p from is unaltered, but the polygon is returned.
* When the point is in/outside by less than \p distance, \p from is moved to the correct place.
* Implementation assumes moving inside, but moving outside should just as well be possible.
*
* \param polygons The polygons onto which to move the point
* \param from[in,out] The point to move.
* \param distance The distance by which to move the point.
* \param max_dist2 The squared maximal allowed distance from the point to the nearest polygon.
* \return The index to the polygon onto which we have moved the point.
*/ */
static unsigned int moveInside(const Polygons& polygons, Point& from, int distance, int64_t maxDist2) static unsigned int moveInside(const Polygons& polygons, Point& from, int distance, int64_t maxDist2)
{ {
Point ret = from; Point ret = from;
int64_t bestDist2 = std::numeric_limits<int64_t>::max(); int64_t bestDist2 = std::numeric_limits<int64_t>::max();
unsigned int bestPoly = static_cast<unsigned int>(-1); auto bestPoly = static_cast<unsigned int>(-1);
bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary bool is_already_on_correct_side_of_boundary = false; // whether [from] is already on the right side of the boundary
for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++) for (unsigned int poly_idx = 0; poly_idx < polygons.size(); poly_idx++)
{ {
const Polygon &poly = polygons[poly_idx]; const Polygon &poly = polygons[poly_idx];
@ -333,7 +358,7 @@ static unsigned int moveInside(const Polygons& polygons, Point& from, int distan
else else
{ // x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | . { // x is projected to a point properly on the line segment (not onto a vertex). The case which looks like | .
projected_p_beyond_prev_segment = false; projected_p_beyond_prev_segment = false;
Point x = a + ab * dot_prod / ab_length2; Point x = (a.cast<int64_t>() + ab.cast<int64_t>() * dot_prod / ab_length2).cast<coord_t>();
int64_t dist2 = (p - x).cast<int64_t>().squaredNorm(); int64_t dist2 = (p - x).cast<int64_t>().squaredNorm();
if (dist2 < bestDist2) if (dist2 < bestDist2)

View file

@ -41,9 +41,10 @@ public:
( (
const Polygons& current_overhang, const Polygons& current_overhang,
const Polygons& current_outlines, const Polygons& current_outlines,
const BoundingBox& current_outlines_bbox,
const EdgeGrid::Grid& outline_locator, const EdgeGrid::Grid& outline_locator,
const coord_t supporting_radius, coord_t supporting_radius,
const coord_t wall_supporting_radius coord_t wall_supporting_radius
); );
/*! Determine & connect to connection point in tree/outline. /*! Determine & connect to connection point in tree/outline.
@ -53,9 +54,10 @@ public:
( (
const Point& unsupported_location, const Point& unsupported_location,
const Polygons& current_outlines, const Polygons& current_outlines,
const BoundingBox& current_outlines_bbox,
const EdgeGrid::Grid& outline_locator, const EdgeGrid::Grid& outline_locator,
const coord_t supporting_radius, coord_t supporting_radius,
const coord_t wall_supporting_radius, coord_t wall_supporting_radius,
const SparseNodeGrid& tree_node_locator, const SparseNodeGrid& tree_node_locator,
const NodeSPtr& exclude_tree = nullptr const NodeSPtr& exclude_tree = nullptr
); );
@ -71,16 +73,17 @@ public:
( (
std::vector<NodeSPtr>& to_be_reconnected_tree_roots, std::vector<NodeSPtr>& to_be_reconnected_tree_roots,
const Polygons& current_outlines, const Polygons& current_outlines,
const BoundingBox& current_outlines_bbox,
const EdgeGrid::Grid& outline_locator, const EdgeGrid::Grid& outline_locator,
const coord_t supporting_radius, coord_t supporting_radius,
const coord_t wall_supporting_radius coord_t wall_supporting_radius
); );
Polylines convertToLines(const Polygons& limit_to_outline, const coord_t line_width) const; Polylines convertToLines(const Polygons& limit_to_outline, coord_t line_width) const;
coord_t getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location); coord_t getWeightedDistance(const Point& boundary_loc, const Point& unsupported_location);
void fillLocator(SparseNodeGrid& tree_node_locator); void fillLocator(SparseNodeGrid& tree_node_locator, const BoundingBox& current_outlines_bbox);
}; };
} // namespace Slic3r::FillLightning } // namespace Slic3r::FillLightning

View file

@ -4,7 +4,6 @@
#include "TreeNode.hpp" #include "TreeNode.hpp"
#include "../../Geometry.hpp" #include "../../Geometry.hpp"
#include "../../ClipperUtils.hpp"
namespace Slic3r::FillLightning { namespace Slic3r::FillLightning {
@ -107,7 +106,7 @@ NodeSPtr Node::deepCopy() const
return local_root; return local_root;
} }
void Node::reroot(NodeSPtr new_parent /*= nullptr*/) void Node::reroot(const NodeSPtr &new_parent)
{ {
if (! m_is_root) { if (! m_is_root) {
auto old_parent = m_parent.lock(); auto old_parent = m_parent.lock();
@ -142,7 +141,7 @@ NodeSPtr Node::closestNode(const Point& loc)
return result; return result;
} }
bool inside(const Polygons &polygons, const Point p) bool inside(const Polygons &polygons, const Point &p)
{ {
int poly_count_inside = 0; int poly_count_inside = 0;
for (const Polygon &poly : polygons) { for (const Polygon &poly : polygons) {
@ -181,7 +180,7 @@ bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeG
} visitor { outline_locator, a.cast<double>(), b.cast<double>() }; } visitor { outline_locator, a.cast<double>(), b.cast<double>() };
outline_locator.visit_cells_intersecting_line(a, b, visitor); outline_locator.visit_cells_intersecting_line(a, b, visitor);
return visitor.d2min < within_max_dist * within_max_dist; return visitor.d2min < double(within_max_dist) * double(within_max_dist);
} }
bool Node::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector<NodeSPtr>& rerooted_parts) bool Node::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locator, std::vector<NodeSPtr>& rerooted_parts)
@ -226,14 +225,14 @@ bool Node::realign(const Polygons& outlines, const EdgeGrid::Grid& outline_locat
void Node::straighten(const coord_t magnitude, const coord_t max_remove_colinear_dist) void Node::straighten(const coord_t magnitude, const coord_t max_remove_colinear_dist)
{ {
straighten(magnitude, m_p, 0, max_remove_colinear_dist * max_remove_colinear_dist); straighten(magnitude, m_p, 0, int64_t(max_remove_colinear_dist) * int64_t(max_remove_colinear_dist));
} }
Node::RectilinearJunction Node::straighten( Node::RectilinearJunction Node::straighten(
const coord_t magnitude, const coord_t magnitude,
const Point& junction_above, const Point& junction_above,
const coord_t accumulated_dist, const coord_t accumulated_dist,
const coord_t max_remove_colinear_dist2) const int64_t max_remove_colinear_dist2)
{ {
constexpr coord_t junction_magnitude_factor_numerator = 3; constexpr coord_t junction_magnitude_factor_numerator = 3;
constexpr coord_t junction_magnitude_factor_denominator = 4; constexpr coord_t junction_magnitude_factor_denominator = 4;
@ -245,13 +244,13 @@ Node::RectilinearJunction Node::straighten(
auto child_dist = coord_t((m_p - child_p->m_p).cast<double>().norm()); auto child_dist = coord_t((m_p - child_p->m_p).cast<double>().norm());
RectilinearJunction junction_below = child_p->straighten(magnitude, junction_above, accumulated_dist + child_dist, max_remove_colinear_dist2); RectilinearJunction junction_below = child_p->straighten(magnitude, junction_above, accumulated_dist + child_dist, max_remove_colinear_dist2);
coord_t total_dist_to_junction_below = junction_below.total_recti_dist; coord_t total_dist_to_junction_below = junction_below.total_recti_dist;
Point a = junction_above; const Point& a = junction_above;
Point b = junction_below.junction_loc; Point b = junction_below.junction_loc;
if (a != b) // should always be true! if (a != b) // should always be true!
{ {
Point ab = b - a; Point ab = b - a;
Point destination = a + ab * accumulated_dist / std::max(coord_t(1), total_dist_to_junction_below); Point destination = (a.cast<int64_t>() + ab.cast<int64_t>() * int64_t(accumulated_dist) / std::max(int64_t(1), int64_t(total_dist_to_junction_below))).cast<coord_t>();
if ((destination - m_p).cast<double>().squaredNorm() <= magnitude * magnitude) if ((destination - m_p).cast<int64_t>().squaredNorm() <= int64_t(magnitude) * int64_t(magnitude))
m_p = destination; m_p = destination;
else else
m_p += ((destination - m_p).cast<double>().normalized() * magnitude).cast<coord_t>(); m_p += ((destination - m_p).cast<double>().normalized() * magnitude).cast<coord_t>();
@ -262,7 +261,7 @@ Node::RectilinearJunction Node::straighten(
child_p = m_children.front(); //recursive call to straighten might have removed the child child_p = m_children.front(); //recursive call to straighten might have removed the child
const NodeSPtr& parent_node = m_parent.lock(); const NodeSPtr& parent_node = m_parent.lock();
if (parent_node && if (parent_node &&
(child_p->m_p - parent_node->m_p).cast<double>().squaredNorm() < max_remove_colinear_dist2 && (child_p->m_p - parent_node->m_p).cast<int64_t>().squaredNorm() < max_remove_colinear_dist2 &&
Line::distance_to_squared(m_p, parent_node->m_p, child_p->m_p) < close_enough * close_enough) { Line::distance_to_squared(m_p, parent_node->m_p, child_p->m_p) < close_enough * close_enough) {
child_p->m_parent = m_parent; child_p->m_parent = m_parent;
for (auto& sibling : parent_node->m_children) for (auto& sibling : parent_node->m_children)
@ -347,7 +346,7 @@ coord_t Node::prune(const coord_t& pruning_distance)
void Node::convertToPolylines(Polygons& output, const coord_t line_width) const void Node::convertToPolylines(Polygons& output, const coord_t line_width) const
{ {
Polygons result; Polygons result;
output.emplace_back(); result.emplace_back();
convertToPolylines(0, result); convertToPolylines(0, result);
removeJunctionOverlap(result, line_width); removeJunctionOverlap(result, line_width);
append(output, std::move(result)); append(output, std::move(result));
@ -386,7 +385,7 @@ void Node::removeJunctionOverlap(Polygons& result_lines, const coord_t line_widt
coord_t to_be_reduced = reduction; coord_t to_be_reduced = reduction;
Point a = polyline.back(); Point a = polyline.back();
for (int point_idx = polyline.size() - 2; point_idx >= 0; point_idx--) { for (int point_idx = int(polyline.size()) - 2; point_idx >= 0; point_idx--) {
const Point b = polyline[point_idx]; const Point b = polyline[point_idx];
const Point ab = b - a; const Point ab = b - a;
const auto ab_len = coord_t(ab.cast<double>().norm()); const auto ab_len = coord_t(ab.cast<double>().norm());
@ -408,4 +407,29 @@ void Node::removeJunctionOverlap(Polygons& result_lines, const coord_t line_widt
} }
} }
#ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT
void export_to_svg(const NodeSPtr &root_node, SVG &svg)
{
for (const NodeSPtr &children : root_node->m_children) {
svg.draw(Line(root_node->getLocation(), children->getLocation()), "red");
export_to_svg(children, svg);
}
}
void export_to_svg(const std::string &path, const Polygons &contour, const std::vector<NodeSPtr> &root_nodes) {
BoundingBox bbox = get_extents(contour);
bbox.offset(SCALED_EPSILON);
SVG svg(path, bbox);
svg.draw_outline(contour, "blue");
for (const NodeSPtr &root_node: root_nodes) {
for (const NodeSPtr &children: root_node->m_children) {
svg.draw(Line(root_node->getLocation(), children->getLocation()), "red");
export_to_svg(children, svg);
}
}
}
#endif /* LIGHTNING_TREE_NODE_DEBUG_OUTPUT */
} // namespace Slic3r::FillLightning } // namespace Slic3r::FillLightning

View file

@ -11,6 +11,9 @@
#include "../../EdgeGrid.hpp" #include "../../EdgeGrid.hpp"
#include "../../Polygon.hpp" #include "../../Polygon.hpp"
#include "SVG.hpp"
//#define LIGHTNING_TREE_NODE_DEBUG_OUTPUT
namespace Slic3r::FillLightning namespace Slic3r::FillLightning
{ {
@ -99,9 +102,9 @@ public:
std::vector<NodeSPtr>& next_trees, std::vector<NodeSPtr>& next_trees,
const Polygons& next_outlines, const Polygons& next_outlines,
const EdgeGrid::Grid& outline_locator, const EdgeGrid::Grid& outline_locator,
const coord_t prune_distance, coord_t prune_distance,
const coord_t smooth_magnitude, coord_t smooth_magnitude,
const coord_t max_remove_colinear_dist coord_t max_remove_colinear_dist
) const; ) const;
/*! /*!
@ -156,7 +159,7 @@ public:
* This is then recursively bubbled up until it reaches the (former) root, which then will become a leaf. * This is then recursively bubbled up until it reaches the (former) root, which then will become a leaf.
* \param new_parent The (new) parent-node of the root, useful for recursing or immediately attaching the node to another tree. * \param new_parent The (new) parent-node of the root, useful for recursing or immediately attaching the node to another tree.
*/ */
void reroot(NodeSPtr new_parent = nullptr); void reroot(const NodeSPtr &new_parent = nullptr);
/*! /*!
* Retrieves the closest node to the specified location. * Retrieves the closest node to the specified location.
@ -211,7 +214,7 @@ protected:
* \param magnitude The maximum allowed distance to move the node. * \param magnitude The maximum allowed distance to move the node.
* \param max_remove_colinear_dist Maximum distance of the (compound) line-segment from which a co-linear point may be removed. * \param max_remove_colinear_dist Maximum distance of the (compound) line-segment from which a co-linear point may be removed.
*/ */
void straighten(const coord_t magnitude, const coord_t max_remove_colinear_dist); void straighten(coord_t magnitude, coord_t max_remove_colinear_dist);
/*! Recursive part of \ref straighten(.) /*! Recursive part of \ref straighten(.)
* \param junction_above The last seen junction with multiple children above * \param junction_above The last seen junction with multiple children above
@ -219,7 +222,7 @@ protected:
* \param max_remove_colinear_dist2 Maximum distance _squared_ of the (compound) line-segment from which a co-linear point may be removed. * \param max_remove_colinear_dist2 Maximum distance _squared_ of the (compound) line-segment from which a co-linear point may be removed.
* \return the total distance along the tree from the last junction above to the first next junction below and the location of the next junction below * \return the total distance along the tree from the last junction above to the first next junction below and the location of the next junction below
*/ */
RectilinearJunction straighten(const coord_t magnitude, const Point& junction_above, const coord_t accumulated_dist, const coord_t max_remove_colinear_dist2); RectilinearJunction straighten(coord_t magnitude, const Point& junction_above, coord_t accumulated_dist, int64_t max_remove_colinear_dist2);
/*! Prune the tree from the extremeties (leaf-nodes) until the pruning distance is reached. /*! Prune the tree from the extremeties (leaf-nodes) until the pruning distance is reached.
* \return The distance that has been pruned. If less than \p distance, then the whole tree was puned away. * \return The distance that has been pruned. If less than \p distance, then the whole tree was puned away.
@ -236,7 +239,7 @@ public:
* *
* \param output all branches in this tree connected into polylines * \param output all branches in this tree connected into polylines
*/ */
void convertToPolylines(Polygons& output, const coord_t line_width) const; void convertToPolylines(Polygons& output, coord_t line_width) const;
/*! If this was ever a direct child of the root, it'll have a previous grounding location. /*! If this was ever a direct child of the root, it'll have a previous grounding location.
* *
@ -257,7 +260,7 @@ protected:
*/ */
void convertToPolylines(size_t long_line_idx, Polygons& output) const; void convertToPolylines(size_t long_line_idx, Polygons& output) const;
void removeJunctionOverlap(Polygons& polylines, const coord_t line_width) const; void removeJunctionOverlap(Polygons& polylines, coord_t line_width) const;
bool m_is_root; bool m_is_root;
Point m_p; Point m_p;
@ -265,10 +268,20 @@ protected:
std::vector<NodeSPtr> m_children; std::vector<NodeSPtr> m_children;
std::optional<Point> m_last_grounding_location; //<! The last known grounding location, see 'getLastGroundingLocation()'. std::optional<Point> m_last_grounding_location; //<! The last known grounding location, see 'getLastGroundingLocation()'.
#ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT
friend void export_to_svg(const NodeSPtr &root_node, Slic3r::SVG &svg);
friend void export_to_svg(const std::string &path, const Polygons &contour, const std::vector<NodeSPtr> &root_nodes);
#endif /* LIGHTNING_TREE_NODE_DEBUG_OUTPUT */
}; };
bool inside(const Polygons &polygons, const Point p); bool inside(const Polygons &polygons, const Point &p);
bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeGrid::Grid& outline_locator, Point& result, const coord_t within_max_dist); bool lineSegmentPolygonsIntersection(const Point& a, const Point& b, const EdgeGrid::Grid& outline_locator, Point& result, coord_t within_max_dist);
#ifdef LIGHTNING_TREE_NODE_DEBUG_OUTPUT
void export_to_svg(const NodeSPtr &root_node, SVG &svg);
void export_to_svg(const std::string &path, const Polygons &contour, const std::vector<NodeSPtr> &root_nodes);
#endif /* LIGHTNING_TREE_NODE_DEBUG_OUTPUT */
} // namespace Slic3r::FillLightning } // namespace Slic3r::FillLightning

View file

@ -737,9 +737,7 @@ void GCodeProcessorResult::reset() {
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER); filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY); filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
custom_gcode_per_print_z = std::vector<CustomGCode::Item>(); custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
#if ENABLE_SPIRAL_VASE_LAYERS
spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>(); spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>();
#endif // ENABLE_SPIRAL_VASE_LAYERS
time = 0; time = 0;
} }
#else #else
@ -755,9 +753,7 @@ void GCodeProcessorResult::reset() {
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER); filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY); filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
custom_gcode_per_print_z = std::vector<CustomGCode::Item>(); custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
#if ENABLE_SPIRAL_VASE_LAYERS
spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>(); spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>();
#endif // ENABLE_SPIRAL_VASE_LAYERS
} }
#endif // ENABLE_GCODE_VIEWER_STATISTICS #endif // ENABLE_GCODE_VIEWER_STATISTICS
@ -905,17 +901,13 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_result.max_print_height = config.max_print_height; m_result.max_print_height = config.max_print_height;
#if ENABLE_SPIRAL_VASE_LAYERS
const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_vase"); const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_vase");
if (spiral_vase != nullptr) if (spiral_vase != nullptr)
m_spiral_vase_active = spiral_vase->value; m_spiral_vase_active = spiral_vase->value;
#endif // ENABLE_SPIRAL_VASE_LAYERS
#if ENABLE_Z_OFFSET_CORRECTION
const ConfigOptionFloat* z_offset = config.option<ConfigOptionFloat>("z_offset"); const ConfigOptionFloat* z_offset = config.option<ConfigOptionFloat>("z_offset");
if (z_offset != nullptr) if (z_offset != nullptr)
m_z_offset = z_offset->value; m_z_offset = z_offset->value;
#endif // ENABLE_Z_OFFSET_CORRECTION
} }
void GCodeProcessor::apply_config(const DynamicPrintConfig& config) void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
@ -1160,17 +1152,13 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
if (max_print_height != nullptr) if (max_print_height != nullptr)
m_result.max_print_height = max_print_height->value; m_result.max_print_height = max_print_height->value;
#if ENABLE_SPIRAL_VASE_LAYERS
const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_vase"); const ConfigOptionBool* spiral_vase = config.option<ConfigOptionBool>("spiral_vase");
if (spiral_vase != nullptr) if (spiral_vase != nullptr)
m_spiral_vase_active = spiral_vase->value; m_spiral_vase_active = spiral_vase->value;
#endif // ENABLE_SPIRAL_VASE_LAYERS
#if ENABLE_Z_OFFSET_CORRECTION
const ConfigOptionFloat* z_offset = config.option<ConfigOptionFloat>("z_offset"); const ConfigOptionFloat* z_offset = config.option<ConfigOptionFloat>("z_offset");
if (z_offset != nullptr) if (z_offset != nullptr)
m_z_offset = z_offset->value; m_z_offset = z_offset->value;
#endif // ENABLE_Z_OFFSET_CORRECTION
} }
void GCodeProcessor::enable_stealth_time_estimator(bool enabled) void GCodeProcessor::enable_stealth_time_estimator(bool enabled)
@ -1203,9 +1191,7 @@ void GCodeProcessor::reset()
m_forced_height = 0.0f; m_forced_height = 0.0f;
m_mm3_per_mm = 0.0f; m_mm3_per_mm = 0.0f;
m_fan_speed = 0.0f; m_fan_speed = 0.0f;
#if ENABLE_Z_OFFSET_CORRECTION
m_z_offset = 0.0f; m_z_offset = 0.0f;
#endif // ENABLE_Z_OFFSET_CORRECTION
m_extrusion_role = erNone; m_extrusion_role = erNone;
m_extruder_id = 0; m_extruder_id = 0;
@ -1238,9 +1224,7 @@ void GCodeProcessor::reset()
m_options_z_corrector.reset(); m_options_z_corrector.reset();
#if ENABLE_SPIRAL_VASE_LAYERS
m_spiral_vase_active = false; m_spiral_vase_active = false;
#endif // ENABLE_SPIRAL_VASE_LAYERS
#if ENABLE_GCODE_VIEWER_DATA_CHECKING #if ENABLE_GCODE_VIEWER_DATA_CHECKING
m_mm3_per_mm_compare.reset(); m_mm3_per_mm_compare.reset();
@ -1950,16 +1934,17 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
// layer change tag // layer change tag
if (comment == reserved_tag(ETags::Layer_Change)) { if (comment == reserved_tag(ETags::Layer_Change)) {
++m_layer_id; ++m_layer_id;
#if ENABLE_SPIRAL_VASE_LAYERS
if (m_spiral_vase_active) { if (m_spiral_vase_active) {
assert(!m_result.moves.empty()); if (m_result.moves.empty())
size_t move_id = m_result.moves.size() - 1; m_result.spiral_vase_layers.push_back({ m_first_layer_height, { 0, 0 } });
if (!m_result.spiral_vase_layers.empty() && m_end_position[Z] == m_result.spiral_vase_layers.back().first) else {
m_result.spiral_vase_layers.back().second.second = move_id; const size_t move_id = m_result.moves.size() - 1;
else if (!m_result.spiral_vase_layers.empty() && m_end_position[Z] == m_result.spiral_vase_layers.back().first)
m_result.spiral_vase_layers.push_back({ static_cast<float>(m_end_position[Z]), { move_id, move_id } }); m_result.spiral_vase_layers.back().second.second = move_id;
else
m_result.spiral_vase_layers.push_back({ static_cast<float>(m_end_position[Z]), { move_id, move_id } });
}
} }
#endif // ENABLE_SPIRAL_VASE_LAYERS
return; return;
} }
@ -2752,11 +2737,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
// the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later // the threshold value = 0.0625f == 0.25 * 0.25 is arbitrary, we may find some smarter condition later
if ((new_pos - *first_vertex).squaredNorm() < 0.0625f) { if ((new_pos - *first_vertex).squaredNorm() < 0.0625f) {
#if ENABLE_Z_OFFSET_CORRECTION
set_end_position(0.5f * (new_pos + *first_vertex) + m_z_offset * Vec3f::UnitZ()); set_end_position(0.5f * (new_pos + *first_vertex) + m_z_offset * Vec3f::UnitZ());
#else
set_end_position(0.5f * (new_pos + *first_vertex));
#endif // ENABLE_Z_OFFSET_CORRECTION
store_move_vertex(EMoveType::Seam); store_move_vertex(EMoveType::Seam);
set_end_position(curr_pos); set_end_position(curr_pos);
} }
@ -2769,10 +2750,8 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id]); m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id]);
} }
#if ENABLE_SPIRAL_VASE_LAYERS
if (m_spiral_vase_active && !m_result.spiral_vase_layers.empty() && !m_result.moves.empty()) if (m_spiral_vase_active && !m_result.spiral_vase_layers.empty() && !m_result.moves.empty())
m_result.spiral_vase_layers.back().second.second = m_result.moves.size() - 1; m_result.spiral_vase_layers.back().second.second = m_result.moves.size() - 1;
#endif // ENABLE_SPIRAL_VASE_LAYERS
// store move // store move
store_move_vertex(type); store_move_vertex(type);
@ -3290,11 +3269,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
m_extrusion_role, m_extrusion_role,
m_extruder_id, m_extruder_id,
m_cp_color.current, m_cp_color.current,
#if ENABLE_Z_OFFSET_CORRECTION
Vec3f(m_end_position[X], m_end_position[Y], m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z] - m_z_offset) + m_extruder_offsets[m_extruder_id], Vec3f(m_end_position[X], m_end_position[Y], m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z] - m_z_offset) + m_extruder_offsets[m_extruder_id],
#else
Vec3f(m_end_position[X], m_end_position[Y], m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z]) + m_extruder_offsets[m_extruder_id],
#endif // ENABLE_Z_OFFSET_CORRECTION
static_cast<float>(m_end_position[E] - m_start_position[E]), static_cast<float>(m_end_position[E] - m_start_position[E]),
m_feedrate, m_feedrate,
m_width, m_width,

View file

@ -131,9 +131,7 @@ namespace Slic3r {
std::vector<float> filament_densities; std::vector<float> filament_densities;
PrintEstimatedStatistics print_statistics; PrintEstimatedStatistics print_statistics;
std::vector<CustomGCode::Item> custom_gcode_per_print_z; std::vector<CustomGCode::Item> custom_gcode_per_print_z;
#if ENABLE_SPIRAL_VASE_LAYERS
std::vector<std::pair<float, std::pair<size_t, size_t>>> spiral_vase_layers; std::vector<std::pair<float, std::pair<size_t, size_t>>> spiral_vase_layers;
#endif // ENABLE_SPIRAL_VASE_LAYERS
#if ENABLE_GCODE_VIEWER_STATISTICS #if ENABLE_GCODE_VIEWER_STATISTICS
int64_t time{ 0 }; int64_t time{ 0 };
@ -542,9 +540,7 @@ namespace Slic3r {
float m_forced_height; // mm float m_forced_height; // mm
float m_mm3_per_mm; float m_mm3_per_mm;
float m_fan_speed; // percentage float m_fan_speed; // percentage
#if ENABLE_Z_OFFSET_CORRECTION
float m_z_offset; // mm float m_z_offset; // mm
#endif // ENABLE_Z_OFFSET_CORRECTION
ExtrusionRole m_extrusion_role; ExtrusionRole m_extrusion_role;
unsigned char m_extruder_id; unsigned char m_extruder_id;
ExtruderColors m_extruder_colors; ExtruderColors m_extruder_colors;
@ -559,9 +555,7 @@ namespace Slic3r {
SeamsDetector m_seams_detector; SeamsDetector m_seams_detector;
OptionsZCorrector m_options_z_corrector; OptionsZCorrector m_options_z_corrector;
size_t m_last_default_color_id; size_t m_last_default_color_id;
#if ENABLE_SPIRAL_VASE_LAYERS
bool m_spiral_vase_active; bool m_spiral_vase_active;
#endif // ENABLE_SPIRAL_VASE_LAYERS
#if ENABLE_GCODE_VIEWER_STATISTICS #if ENABLE_GCODE_VIEWER_STATISTICS
std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time; std::chrono::time_point<std::chrono::high_resolution_clock> m_start_time;
#endif // ENABLE_GCODE_VIEWER_STATISTICS #endif // ENABLE_GCODE_VIEWER_STATISTICS

View file

@ -20,6 +20,10 @@ namespace FillAdaptive {
struct Octree; struct Octree;
}; };
namespace FillLightning {
class Generator;
};
class LayerRegion class LayerRegion
{ {
public: public:
@ -151,8 +155,8 @@ public:
} }
void make_perimeters(); void make_perimeters();
// Phony version of make_fills() without parameters for Perl integration only. // Phony version of make_fills() without parameters for Perl integration only.
void make_fills() { this->make_fills(nullptr, nullptr); } void make_fills() { this->make_fills(nullptr, nullptr, nullptr); }
void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree); void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator);
void make_ironing(); void make_ironing();
void export_region_slices_to_svg(const char *path) const; void export_region_slices_to_svg(const char *path) const;

View file

@ -177,6 +177,11 @@ inline bool operator<(const Point &l, const Point &r)
return l.x() < r.x() || (l.x() == r.x() && l.y() < r.y()); return l.x() < r.x() || (l.x() == r.x() && l.y() < r.y());
} }
inline Point operator* (const Point& l, const double &r)
{
return {coord_t(l.x() * r), coord_t(l.y() * r)};
}
inline bool is_approx(const Point &p1, const Point &p2, coord_t epsilon = coord_t(SCALED_EPSILON)) inline bool is_approx(const Point &p1, const Point &p2, coord_t epsilon = coord_t(SCALED_EPSILON))
{ {
Point d = (p2 - p1).cwiseAbs(); Point d = (p2 - p1).cwiseAbs();

View file

@ -35,7 +35,13 @@ namespace FillAdaptive {
struct Octree; struct Octree;
struct OctreeDeleter; struct OctreeDeleter;
using OctreePtr = std::unique_ptr<Octree, OctreeDeleter>; using OctreePtr = std::unique_ptr<Octree, OctreeDeleter>;
}; }; // namespace FillAdaptive
namespace FillLightning {
class Generator;
struct GeneratorDeleter;
using GeneratorPtr = std::unique_ptr<Generator, GeneratorDeleter>;
}; // namespace FillLightning
// Print step IDs for keeping track of the print state. // Print step IDs for keeping track of the print state.
// The Print steps are applied in this order. // The Print steps are applied in this order.
@ -387,6 +393,7 @@ private:
void combine_infill(); void combine_infill();
void _generate_support_material(); void _generate_support_material();
std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> prepare_adaptive_infill_data(); std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> prepare_adaptive_infill_data();
FillLightning::GeneratorPtr prepare_lightning_infill_data();
// XYZ in scaled coordinates // XYZ in scaled coordinates
Vec3crd m_size; Vec3crd m_size;

View file

@ -108,9 +108,7 @@ static const t_config_enum_values s_keys_map_InfillPattern {
{ "octagramspiral", ipOctagramSpiral }, { "octagramspiral", ipOctagramSpiral },
{ "adaptivecubic", ipAdaptiveCubic }, { "adaptivecubic", ipAdaptiveCubic },
{ "supportcubic", ipSupportCubic }, { "supportcubic", ipSupportCubic },
#if HAS_LIGHTNING_INFILL
{ "lightning", ipLightning } { "lightning", ipLightning }
#endif // HAS_LIGHTNING_INFILL
}; };
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InfillPattern) CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(InfillPattern)
@ -1155,9 +1153,7 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("octagramspiral"); def->enum_values.push_back("octagramspiral");
def->enum_values.push_back("adaptivecubic"); def->enum_values.push_back("adaptivecubic");
def->enum_values.push_back("supportcubic"); def->enum_values.push_back("supportcubic");
#if HAS_LIGHTNING_INFILL
def->enum_values.push_back("lightning"); def->enum_values.push_back("lightning");
#endif // HAS_LIGHTNING_INFILL
def->enum_labels.push_back(L("Rectilinear")); def->enum_labels.push_back(L("Rectilinear"));
def->enum_labels.push_back(L("Aligned Rectilinear")); def->enum_labels.push_back(L("Aligned Rectilinear"));
def->enum_labels.push_back(L("Grid")); def->enum_labels.push_back(L("Grid"));
@ -1174,9 +1170,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back(L("Octagram Spiral")); def->enum_labels.push_back(L("Octagram Spiral"));
def->enum_labels.push_back(L("Adaptive Cubic")); def->enum_labels.push_back(L("Adaptive Cubic"));
def->enum_labels.push_back(L("Support Cubic")); def->enum_labels.push_back(L("Support Cubic"));
#if HAS_LIGHTNING_INFILL
def->enum_labels.push_back(L("Lightning")); def->enum_labels.push_back(L("Lightning"));
#endif // HAS_LIGHTNING_INFILL
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipStars)); def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipStars));
def = this->add("first_layer_acceleration", coFloat); def = this->add("first_layer_acceleration", coFloat);

View file

@ -57,14 +57,10 @@ enum class FuzzySkinType {
All, All,
}; };
#define HAS_LIGHTNING_INFILL 0
enum InfillPattern : int { enum InfillPattern : int {
ipRectilinear, ipMonotonic, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, ipRectilinear, ipMonotonic, ipAlignedRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase, ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipAdaptiveCubic, ipSupportCubic, ipSupportBase,
#if HAS_LIGHTNING_INFILL ipLightning,
ipLightning,
#endif // HAS_LIGHTNING_INFILL
ipCount, ipCount,
}; };

View file

@ -14,6 +14,7 @@
#include "TriangleMeshSlicer.hpp" #include "TriangleMeshSlicer.hpp"
#include "Utils.hpp" #include "Utils.hpp"
#include "Fill/FillAdaptive.hpp" #include "Fill/FillAdaptive.hpp"
#include "Fill/FillLightning.hpp"
#include "Format/STL.hpp" #include "Format/STL.hpp"
#include <float.h> #include <float.h>
@ -353,14 +354,15 @@ void PrintObject::infill()
if (this->set_started(posInfill)) { if (this->set_started(posInfill)) {
auto [adaptive_fill_octree, support_fill_octree] = this->prepare_adaptive_infill_data(); auto [adaptive_fill_octree, support_fill_octree] = this->prepare_adaptive_infill_data();
auto lightning_generator = this->prepare_lightning_infill_data();
BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start"; BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start";
tbb::parallel_for( tbb::parallel_for(
tbb::blocked_range<size_t>(0, m_layers.size()), tbb::blocked_range<size_t>(0, m_layers.size()),
[this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree](const tbb::blocked_range<size_t>& range) { [this, &adaptive_fill_octree = adaptive_fill_octree, &support_fill_octree = support_fill_octree, &lightning_generator](const tbb::blocked_range<size_t>& range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
m_print->throw_if_canceled(); m_print->throw_if_canceled();
m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get()); m_layers[layer_idx]->make_fills(adaptive_fill_octree.get(), support_fill_octree.get(), lightning_generator.get());
} }
} }
); );
@ -453,6 +455,18 @@ std::pair<FillAdaptive::OctreePtr, FillAdaptive::OctreePtr> PrintObject::prepare
support_line_spacing ? build_octree(mesh, overhangs.front(), support_line_spacing, true) : OctreePtr()); support_line_spacing ? build_octree(mesh, overhangs.front(), support_line_spacing, true) : OctreePtr());
} }
FillLightning::GeneratorPtr PrintObject::prepare_lightning_infill_data()
{
bool has_lightning_infill = false;
for (size_t region_id = 0; region_id < this->num_printing_regions(); ++region_id)
if (const PrintRegionConfig &config = this->printing_region(region_id).config(); config.fill_density > 0 && config.fill_pattern == ipLightning) {
has_lightning_infill = true;
break;
}
return has_lightning_infill ? FillLightning::build_generator(std::as_const(*this)) : FillLightning::GeneratorPtr();
}
void PrintObject::clear_layers() void PrintObject::clear_layers()
{ {
for (Layer *l : m_layers) for (Layer *l : m_layers)

View file

@ -1174,7 +1174,8 @@ sla::SupportPoints SLAPrintObject::transformed_support_points() const
{ {
assert(m_model_object != nullptr); assert(m_model_object != nullptr);
auto spts = m_model_object->sla_support_points; auto spts = m_model_object->sla_support_points;
auto tr = trafo().cast<float>(); const Transform3d& vol_trafo = m_model_object->volumes.front()->get_transformation().get_matrix();
const Transform3f& tr = (trafo() * vol_trafo).cast<float>();
for (sla::SupportPoint& suppt : spts) { for (sla::SupportPoint& suppt : spts) {
suppt.pos = tr * suppt.pos; suppt.pos = tr * suppt.pos;
} }
@ -1186,8 +1187,10 @@ sla::DrainHoles SLAPrintObject::transformed_drainhole_points() const
{ {
assert(m_model_object != nullptr); assert(m_model_object != nullptr);
auto pts = m_model_object->sla_drain_holes; auto pts = m_model_object->sla_drain_holes;
auto tr = trafo().cast<float>(); const Transform3d& vol_trafo = m_model_object->volumes.front()->get_transformation().get_matrix();
auto sc = m_model_object->instances.front()->get_scaling_factor().cast<float>(); const Geometry::Transformation trans(trafo() * vol_trafo);
const Transform3f& tr = trans.get_matrix().cast<float>();
const Vec3f sc = trans.get_scaling_factor().cast<float>();
for (sla::DrainHole &hl : pts) { for (sla::DrainHole &hl : pts) {
hl.pos = tr * hl.pos; hl.pos = tr * hl.pos;
hl.normal = tr * hl.normal - tr.translation(); hl.normal = tr * hl.normal - tr.translation();

View file

@ -34,19 +34,6 @@
#define ENABLE_ENVIRONMENT_MAP 0 #define ENABLE_ENVIRONMENT_MAP 0
// Enable smoothing of objects normals // Enable smoothing of objects normals
#define ENABLE_SMOOTH_NORMALS 0 #define ENABLE_SMOOTH_NORMALS 0
// Enable rendering markers for options in preview as fixed screen size points
#define ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS 1
//================
// 2.4.1.rc techs
//================
#define ENABLE_2_4_1_RC 1
// Enable detection of layers for spiral vase prints
#define ENABLE_SPIRAL_VASE_LAYERS (1 && ENABLE_2_4_1_RC)
// Enable correction of toolpaths when z offset is set
#define ENABLE_Z_OFFSET_CORRECTION (1 && ENABLE_2_4_1_RC)
//==================== //====================
@ -82,6 +69,12 @@
#define ENABLE_SHOW_TOOLPATHS_COG (1 && ENABLE_2_5_0_ALPHA1) #define ENABLE_SHOW_TOOLPATHS_COG (1 && ENABLE_2_5_0_ALPHA1)
// Enable recalculating toolpaths when switching to/from volumetric rate visualization // Enable recalculating toolpaths when switching to/from volumetric rate visualization
#define ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC (1 && ENABLE_2_5_0_ALPHA1) #define ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC (1 && ENABLE_2_5_0_ALPHA1)
// Enable modified camera control using mouse
#define ENABLE_NEW_CAMERA_MOVEMENTS (1 && ENABLE_2_5_0_ALPHA1)
// Enable modified rectangle selection
#define ENABLE_NEW_RECTANGLE_SELECTION (1 && ENABLE_2_5_0_ALPHA1)
// Enable alternative version of file_wildcards()
#define ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR (1 && ENABLE_2_5_0_ALPHA1)
#endif // _prusaslicer_technologies_h_ #endif // _prusaslicer_technologies_h_

View file

@ -515,7 +515,7 @@ void Bed3D::render_system(GLCanvas3D& canvas, const Transform3d& view_matrix, co
render_model(view_matrix, projection_matrix); render_model(view_matrix, projection_matrix);
if (show_texture) if (show_texture)
render_texture(bottom, canvas); render_texture(bottom, canvas, view_matrix, projection_matrix);
} }
#else #else
void Bed3D::render_system(GLCanvas3D& canvas, bool bottom, bool show_texture) void Bed3D::render_system(GLCanvas3D& canvas, bool bottom, bool show_texture)
@ -528,11 +528,19 @@ void Bed3D::render_system(GLCanvas3D& canvas, bool bottom, bool show_texture)
} }
#endif // ENABLE_GL_SHADERS_ATTRIBUTES #endif // ENABLE_GL_SHADERS_ATTRIBUTES
#if ENABLE_GL_SHADERS_ATTRIBUTES
void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix)
#else
void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
#endif // ENABLE_GL_SHADERS_ATTRIBUTES
{ {
if (m_texture_filename.empty()) { if (m_texture_filename.empty()) {
m_texture.reset(); m_texture.reset();
#if ENABLE_GL_SHADERS_ATTRIBUTES
render_default(bottom, false, view_matrix, projection_matrix);
#else
render_default(bottom, false); render_default(bottom, false);
#endif // ENABLE_GL_SHADERS_ATTRIBUTES
return; return;
} }
@ -545,7 +553,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
if (m_temp_texture.get_id() == 0 || m_temp_texture.get_source() != m_texture_filename) { if (m_temp_texture.get_id() == 0 || m_temp_texture.get_source() != m_texture_filename) {
// generate a temporary lower resolution texture to show while no main texture levels have been compressed // generate a temporary lower resolution texture to show while no main texture levels have been compressed
if (!m_temp_texture.load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8)) { if (!m_temp_texture.load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8)) {
#if ENABLE_GL_SHADERS_ATTRIBUTES
render_default(bottom, false, view_matrix, projection_matrix);
#else
render_default(bottom, false); render_default(bottom, false);
#endif // ENABLE_GL_SHADERS_ATTRIBUTES
return; return;
} }
canvas.request_extra_frame(); canvas.request_extra_frame();
@ -553,7 +565,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
// starts generating the main texture, compression will run asynchronously // starts generating the main texture, compression will run asynchronously
if (!m_texture.load_from_svg_file(m_texture_filename, true, true, true, max_tex_size)) { if (!m_texture.load_from_svg_file(m_texture_filename, true, true, true, max_tex_size)) {
#if ENABLE_GL_SHADERS_ATTRIBUTES
render_default(bottom, false, view_matrix, projection_matrix);
#else
render_default(bottom, false); render_default(bottom, false);
#endif // ENABLE_GL_SHADERS_ATTRIBUTES
return; return;
} }
} }
@ -561,7 +577,11 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
// generate a temporary lower resolution texture to show while no main texture levels have been compressed // generate a temporary lower resolution texture to show while no main texture levels have been compressed
if (m_temp_texture.get_id() == 0 || m_temp_texture.get_source() != m_texture_filename) { if (m_temp_texture.get_id() == 0 || m_temp_texture.get_source() != m_texture_filename) {
if (!m_temp_texture.load_from_file(m_texture_filename, false, GLTexture::None, false)) { if (!m_temp_texture.load_from_file(m_texture_filename, false, GLTexture::None, false)) {
#if ENABLE_GL_SHADERS_ATTRIBUTES
render_default(bottom, false, view_matrix, projection_matrix);
#else
render_default(bottom, false); render_default(bottom, false);
#endif // ENABLE_GL_SHADERS_ATTRIBUTES
return; return;
} }
canvas.request_extra_frame(); canvas.request_extra_frame();
@ -569,12 +589,20 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
// starts generating the main texture, compression will run asynchronously // starts generating the main texture, compression will run asynchronously
if (!m_texture.load_from_file(m_texture_filename, true, GLTexture::MultiThreaded, true)) { if (!m_texture.load_from_file(m_texture_filename, true, GLTexture::MultiThreaded, true)) {
#if ENABLE_GL_SHADERS_ATTRIBUTES
render_default(bottom, false, view_matrix, projection_matrix);
#else
render_default(bottom, false); render_default(bottom, false);
#endif // ENABLE_GL_SHADERS_ATTRIBUTES
return; return;
} }
} }
else { else {
#if ENABLE_GL_SHADERS_ATTRIBUTES
render_default(bottom, false, view_matrix, projection_matrix);
#else
render_default(bottom, false); render_default(bottom, false);
#endif // ENABLE_GL_SHADERS_ATTRIBUTES
return; return;
} }
} }
@ -600,9 +628,8 @@ void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas)
if (shader != nullptr) { if (shader != nullptr) {
shader->start_using(); shader->start_using();
#if ENABLE_GL_SHADERS_ATTRIBUTES #if ENABLE_GL_SHADERS_ATTRIBUTES
const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("view_model_matrix", view_matrix);
shader->set_uniform("view_model_matrix", camera.get_view_matrix()); shader->set_uniform("projection_matrix", projection_matrix);
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
#endif // ENABLE_GL_SHADERS_ATTRIBUTES #endif // ENABLE_GL_SHADERS_ATTRIBUTES
shader->set_uniform("transparent_background", bottom); shader->set_uniform("transparent_background", bottom);
shader->set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg")); shader->set_uniform("svg_source", boost::algorithm::iends_with(m_texture.get_source(), ".svg"));
@ -754,7 +781,11 @@ void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture, bo
#endif // ENABLE_GL_SHADERS_ATTRIBUTES #endif // ENABLE_GL_SHADERS_ATTRIBUTES
{ {
if (m_texture_filename.empty() && m_model_filename.empty()) { if (m_texture_filename.empty() && m_model_filename.empty()) {
#if ENABLE_GL_SHADERS_ATTRIBUTES
render_default(bottom, picking, view_matrix, projection_matrix);
#else
render_default(bottom, picking); render_default(bottom, picking);
#endif // ENABLE_GL_SHADERS_ATTRIBUTES
return; return;
} }
@ -766,10 +797,18 @@ void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture, bo
#endif // ENABLE_GL_SHADERS_ATTRIBUTES #endif // ENABLE_GL_SHADERS_ATTRIBUTES
if (show_texture) if (show_texture)
#if ENABLE_GL_SHADERS_ATTRIBUTES
render_texture(bottom, canvas, view_matrix, projection_matrix);
#else
render_texture(bottom, canvas); render_texture(bottom, canvas);
#endif // ENABLE_GL_SHADERS_ATTRIBUTES
} }
#if ENABLE_GL_SHADERS_ATTRIBUTES
void Bed3D::render_default(bool bottom, bool picking, const Transform3d& view_matrix, const Transform3d& projection_matrix)
#else
void Bed3D::render_default(bool bottom, bool picking) void Bed3D::render_default(bool bottom, bool picking)
#endif // ENABLE_GL_SHADERS_ATTRIBUTES
{ {
m_texture.reset(); m_texture.reset();
@ -786,9 +825,8 @@ void Bed3D::render_default(bool bottom, bool picking)
shader->start_using(); shader->start_using();
#if ENABLE_GL_SHADERS_ATTRIBUTES #if ENABLE_GL_SHADERS_ATTRIBUTES
const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("view_model_matrix", view_matrix);
shader->set_uniform("view_model_matrix", camera.get_view_matrix()); shader->set_uniform("projection_matrix", projection_matrix);
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
#endif // ENABLE_GL_SHADERS_ATTRIBUTES #endif // ENABLE_GL_SHADERS_ATTRIBUTES
glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_DEPTH_TEST));

View file

@ -168,18 +168,18 @@ private:
void render_axes(); void render_axes();
#if ENABLE_GL_SHADERS_ATTRIBUTES #if ENABLE_GL_SHADERS_ATTRIBUTES
void render_system(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture); void render_system(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture);
#else void render_texture(bool bottom, GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix);
void render_system(GLCanvas3D& canvas, bool bottom, bool show_texture);
#endif // ENABLE_GL_SHADERS_ATTRIBUTES
void render_texture(bool bottom, GLCanvas3D& canvas);
#if ENABLE_GL_SHADERS_ATTRIBUTES
void render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix); void render_model(const Transform3d& view_matrix, const Transform3d& projection_matrix);
void render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture, bool picking); void render_custom(GLCanvas3D& canvas, const Transform3d& view_matrix, const Transform3d& projection_matrix, bool bottom, bool show_texture, bool picking);
void render_default(bool bottom, bool picking, const Transform3d& view_matrix, const Transform3d& projection_matrix);
#else #else
void render_system(GLCanvas3D& canvas, bool bottom, bool show_texture);
void render_texture(bool bottom, GLCanvas3D& canvas);
void render_model(); void render_model();
void render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture, bool picking); void render_custom(GLCanvas3D& canvas, bool bottom, bool show_texture, bool picking);
#endif // ENABLE_GL_SHADERS_ATTRIBUTES
void render_default(bool bottom, bool picking); void render_default(bool bottom, bool picking);
#endif // ENABLE_GL_SHADERS_ATTRIBUTES
#if !ENABLE_LEGACY_OPENGL_REMOVAL #if !ENABLE_LEGACY_OPENGL_REMOVAL
void release_VBOs(); void release_VBOs();
#endif // !ENABLE_LEGACY_OPENGL_REMOVAL #endif // !ENABLE_LEGACY_OPENGL_REMOVAL

View file

@ -459,6 +459,7 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u
static const ImVec4 COMMAND_COLOR = { 0.8f, 0.8f, 0.0f, 1.0f }; static const ImVec4 COMMAND_COLOR = { 0.8f, 0.8f, 0.0f, 1.0f };
static const ImVec4 PARAMETERS_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f }; static const ImVec4 PARAMETERS_COLOR = { 1.0f, 1.0f, 1.0f, 1.0f };
static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f }; static const ImVec4 COMMENT_COLOR = { 0.7f, 0.7f, 0.7f, 1.0f };
static const ImVec4 ELLIPSIS_COLOR = { 0.0f, 0.7f, 0.0f, 1.0f };
if (!m_visible || m_filename.empty() || m_lines_ends.empty() || curr_line_id == 0) if (!m_visible || m_filename.empty() || m_lines_ends.empty() || curr_line_id == 0)
return; return;
@ -503,6 +504,35 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u
ImGuiWrapper& imgui = *wxGetApp().imgui(); ImGuiWrapper& imgui = *wxGetApp().imgui();
auto add_item_to_line = [&imgui](const std::string& txt, const ImVec4& color, float spacing, size_t& current_length) {
static const size_t LENGTH_THRESHOLD = 60;
if (txt.empty())
return false;
std::string out_text = txt;
bool reduced = false;
if (current_length + out_text.length() > LENGTH_THRESHOLD) {
out_text = out_text.substr(0, LENGTH_THRESHOLD - current_length);
reduced = true;
}
current_length += out_text.length();
ImGui::SameLine(0.0f, spacing);
ImGui::PushStyleColor(ImGuiCol_Text, color);
imgui.text(out_text);
ImGui::PopStyleColor();
if (reduced) {
ImGui::SameLine(0.0f, 0.0f);
ImGui::PushStyleColor(ImGuiCol_Text, ELLIPSIS_COLOR);
imgui.text("...");
ImGui::PopStyleColor();
}
return reduced;
};
imgui.set_next_window_pos(0.0f, top, ImGuiCond_Always, 0.0f, 0.0f); imgui.set_next_window_pos(0.0f, top, ImGuiCond_Always, 0.0f, 0.0f);
imgui.set_next_window_size(0.0f, wnd_height, ImGuiCond_Always); imgui.set_next_window_size(0.0f, wnd_height, ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
@ -527,41 +557,22 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, u
ImGui::GetColorU32(SELECTION_RECT_COLOR)); ImGui::GetColorU32(SELECTION_RECT_COLOR));
} }
// render line number
const std::string id_str = std::to_string(id); const std::string id_str = std::to_string(id);
// spacer to right align text // spacer to right align text
ImGui::Dummy({ id_width - ImGui::CalcTextSize(id_str.c_str()).x, text_height }); ImGui::Dummy({ id_width - ImGui::CalcTextSize(id_str.c_str()).x, text_height });
ImGui::SameLine(0.0f, 0.0f);
ImGui::PushStyleColor(ImGuiCol_Text, LINE_NUMBER_COLOR);
imgui.text(id_str);
ImGui::PopStyleColor();
if (!line.command.empty() || !line.comment.empty()) size_t line_length = 0;
ImGui::SameLine(); // render line number
bool stop_adding = add_item_to_line(id_str, LINE_NUMBER_COLOR, 0.0f, line_length);
// render command if (!stop_adding && !line.command.empty())
if (!line.command.empty()) { // render command
ImGui::PushStyleColor(ImGuiCol_Text, COMMAND_COLOR); stop_adding = add_item_to_line(line.command, COMMAND_COLOR, -1.0f, line_length);
imgui.text(line.command); if (!stop_adding && !line.parameters.empty())
ImGui::PopStyleColor(); // render parameters
} stop_adding = add_item_to_line(line.parameters, PARAMETERS_COLOR, 0.0f, line_length);
if (!stop_adding && !line.comment.empty())
// render parameters // render comment
if (!line.parameters.empty()) { stop_adding = add_item_to_line(line.comment, COMMENT_COLOR, line.command.empty() ? -1.0f : 0.0f, line_length);
ImGui::SameLine(0.0f, 0.0f);
ImGui::PushStyleColor(ImGuiCol_Text, PARAMETERS_COLOR);
imgui.text(line.parameters);
ImGui::PopStyleColor();
}
// render comment
if (!line.comment.empty()) {
if (!line.command.empty())
ImGui::SameLine(0.0f, 0.0f);
ImGui::PushStyleColor(ImGuiCol_Text, COMMENT_COLOR);
imgui.text(line.comment);
ImGui::PopStyleColor();
}
} }
imgui.end(); imgui.end();
@ -1310,18 +1321,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
log_memory_used(label, vertices_size + indices_size); log_memory_used(label, vertices_size + indices_size);
}; };
// format data into the buffers to be rendered as points
auto add_vertices_as_point = [](const GCodeProcessorResult::MoveVertex& curr, VertexBuffer& vertices) {
vertices.push_back(curr.position.x());
vertices.push_back(curr.position.y());
vertices.push_back(curr.position.z());
};
auto add_indices_as_point = [](const GCodeProcessorResult::MoveVertex& curr, TBuffer& buffer,
unsigned int ibuffer_id, IndexBuffer& indices, size_t move_id) {
buffer.add_path(curr, ibuffer_id, indices.size(), move_id);
indices.push_back(static_cast<IBufferType>(indices.size()));
};
// format data into the buffers to be rendered as lines // format data into the buffers to be rendered as lines
auto add_vertices_as_line = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, VertexBuffer& vertices) { auto add_vertices_as_line = [](const GCodeProcessorResult::MoveVertex& prev, const GCodeProcessorResult::MoveVertex& curr, VertexBuffer& vertices) {
#if !ENABLE_GL_SHADERS_ATTRIBUTES #if !ENABLE_GL_SHADERS_ATTRIBUTES
@ -1816,7 +1815,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
switch (t_buffer.render_primitive_type) switch (t_buffer.render_primitive_type)
{ {
case TBuffer::ERenderPrimitiveType::Point: { add_vertices_as_point(curr, v_buffer); break; }
case TBuffer::ERenderPrimitiveType::Line: { add_vertices_as_line(prev, curr, v_buffer); break; } case TBuffer::ERenderPrimitiveType::Line: { add_vertices_as_line(prev, curr, v_buffer); break; }
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC #if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast<unsigned int>(v_multibuffer.size()) - 1, v_buffer, move_id, account_for_volumetric_rate); break; } case TBuffer::ERenderPrimitiveType::Triangle: { add_vertices_as_solid(prev, curr, t_buffer, static_cast<unsigned int>(v_multibuffer.size()) - 1, v_buffer, move_id, account_for_volumetric_rate); break; }
@ -2152,8 +2150,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
if (i_multibuffer.back().size() * sizeof(IBufferType) >= IBUFFER_THRESHOLD_BYTES - indiced_size_to_add) { if (i_multibuffer.back().size() * sizeof(IBufferType) >= IBUFFER_THRESHOLD_BYTES - indiced_size_to_add) {
i_multibuffer.push_back(IndexBuffer()); i_multibuffer.push_back(IndexBuffer());
vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]); vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]);
if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point && if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::BatchedModel) {
t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::BatchedModel) {
Path& last_path = t_buffer.paths.back(); Path& last_path = t_buffer.paths.back();
last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, move_id - 1); last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, move_id - 1);
} }
@ -2169,8 +2166,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
curr_vertex_buffer.second = 0; curr_vertex_buffer.second = 0;
vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]); vbo_index_list.push_back(t_buffer.vertices.vbos[curr_vertex_buffer.first]);
if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::Point && if (t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::BatchedModel) {
t_buffer.render_primitive_type != TBuffer::ERenderPrimitiveType::BatchedModel) {
Path& last_path = t_buffer.paths.back(); Path& last_path = t_buffer.paths.back();
last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, move_id - 1); last_path.add_sub_path(prev, static_cast<unsigned int>(i_multibuffer.size()) - 1, 0, move_id - 1);
} }
@ -2180,11 +2176,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
switch (t_buffer.render_primitive_type) switch (t_buffer.render_primitive_type)
{ {
case TBuffer::ERenderPrimitiveType::Point: {
add_indices_as_point(curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id);
curr_vertex_buffer.second += t_buffer.max_vertices_per_segment();
break;
}
case TBuffer::ERenderPrimitiveType::Line: { case TBuffer::ERenderPrimitiveType::Line: {
#if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC #if ENABLE_VOLUMETRIC_RATE_TOOLPATHS_RECALC
add_indices_as_line(prev, curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id, account_for_volumetric_rate); add_indices_as_line(prev, curr, t_buffer, static_cast<unsigned int>(i_multibuffer.size()) - 1, i_buffer, move_id, account_for_volumetric_rate);
@ -2322,7 +2313,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
sort_remove_duplicates(m_extruder_ids); sort_remove_duplicates(m_extruder_ids);
m_extruder_ids.shrink_to_fit(); m_extruder_ids.shrink_to_fit();
#if ENABLE_SPIRAL_VASE_LAYERS
// replace layers for spiral vase mode // replace layers for spiral vase mode
if (!gcode_result.spiral_vase_layers.empty()) { if (!gcode_result.spiral_vase_layers.empty()) {
m_layers.reset(); m_layers.reset();
@ -2330,7 +2320,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result)
m_layers.append(layer.first, { layer.second.first, layer.second.second }); m_layers.append(layer.first, { layer.second.first, layer.second.second });
} }
} }
#endif // ENABLE_SPIRAL_VASE_LAYERS
// set layers z range // set layers z range
if (!m_layers.empty()) if (!m_layers.empty())
@ -2732,10 +2721,6 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
unsigned int size_in_indices = 0; unsigned int size_in_indices = 0;
switch (buffer.render_primitive_type) switch (buffer.render_primitive_type)
{ {
case TBuffer::ERenderPrimitiveType::Point: {
size_in_indices = buffer.indices_per_segment();
break;
}
case TBuffer::ERenderPrimitiveType::Line: case TBuffer::ERenderPrimitiveType::Line:
case TBuffer::ERenderPrimitiveType::Triangle: { case TBuffer::ERenderPrimitiveType::Triangle: {
unsigned int segments_count = std::min(m_sequential_view.current.last, sub_path.last.s_id) - std::max(m_sequential_view.current.first, sub_path.first.s_id); unsigned int segments_count = std::min(m_sequential_view.current.last, sub_path.last.s_id) - std::max(m_sequential_view.current.first, sub_path.first.s_id);
@ -2968,56 +2953,11 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool
void GCodeViewer::render_toolpaths() void GCodeViewer::render_toolpaths()
{ {
#if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS
const float point_size = 20.0f;
#else
const float point_size = 0.8f;
#endif // ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS
#if !ENABLE_GL_SHADERS_ATTRIBUTES #if !ENABLE_GL_SHADERS_ATTRIBUTES
const std::array<float, 4> light_intensity = { 0.25f, 0.70f, 0.75f, 0.75f }; const std::array<float, 4> light_intensity = { 0.25f, 0.70f, 0.75f, 0.75f };
#endif // !ENABLE_GL_SHADERS_ATTRIBUTES #endif // !ENABLE_GL_SHADERS_ATTRIBUTES
const Camera& camera = wxGetApp().plater()->get_camera(); const Camera& camera = wxGetApp().plater()->get_camera();
const double zoom = camera.get_zoom(); const double zoom = camera.get_zoom();
const std::array<int, 4>& viewport = camera.get_viewport();
const float near_plane_height = camera.get_type() == Camera::EType::Perspective ? static_cast<float>(viewport[3]) / (2.0f * static_cast<float>(2.0 * std::tan(0.5 * Geometry::deg2rad(camera.get_fov())))) :
static_cast<float>(viewport[3]) * 0.0005;
auto shader_init_as_points = [zoom, point_size, near_plane_height](GLShaderProgram& shader) {
#if ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS
shader.set_uniform("use_fixed_screen_size", 1);
#else
shader.set_uniform("use_fixed_screen_size", 0);
#endif // ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS
shader.set_uniform("zoom", zoom);
shader.set_uniform("percent_outline_radius", 0.0f);
shader.set_uniform("percent_center_radius", 0.33f);
shader.set_uniform("point_size", point_size);
shader.set_uniform("near_plane_height", near_plane_height);
};
auto render_as_points = [
#if ENABLE_GCODE_VIEWER_STATISTICS
this
#endif // ENABLE_GCODE_VIEWER_STATISTICS
](std::vector<RenderPath>::iterator it_path, std::vector<RenderPath>::iterator it_end, GLShaderProgram& shader, int uniform_color) {
glsafe(::glEnable(GL_VERTEX_PROGRAM_POINT_SIZE));
glsafe(::glEnable(GL_POINT_SPRITE));
for (auto it = it_path; it != it_end && it_path->ibuffer_id == it->ibuffer_id; ++it) {
const RenderPath& path = *it;
// Some OpenGL drivers crash on empty glMultiDrawElements, see GH #7415.
assert(! path.sizes.empty());
assert(! path.offsets.empty());
shader.set_uniform(uniform_color, path.color);
glsafe(::glMultiDrawElements(GL_POINTS, (const GLsizei*)path.sizes.data(), GL_UNSIGNED_SHORT, (const void* const*)path.offsets.data(), (GLsizei)path.sizes.size()));
#if ENABLE_GCODE_VIEWER_STATISTICS
++m_statistics.gl_multi_points_calls_count;
#endif // ENABLE_GCODE_VIEWER_STATISTICS
}
glsafe(::glDisable(GL_POINT_SPRITE));
glsafe(::glDisable(GL_VERTEX_PROGRAM_POINT_SIZE));
};
#if !ENABLE_GL_SHADERS_ATTRIBUTES #if !ENABLE_GL_SHADERS_ATTRIBUTES
auto shader_init_as_lines = [light_intensity](GLShaderProgram &shader) { auto shader_init_as_lines = [light_intensity](GLShaderProgram &shader) {
@ -3198,9 +3138,6 @@ void GCodeViewer::render_toolpaths()
shader->set_uniform("view_model_matrix", view_matrix); shader->set_uniform("view_model_matrix", view_matrix);
shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("projection_matrix", camera.get_projection_matrix());
shader->set_uniform("normal_matrix", (Matrix3d)view_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); shader->set_uniform("normal_matrix", (Matrix3d)view_matrix.matrix().block(0, 0, 3, 3).inverse().transpose());
const int position_id = shader->get_attrib_location("v_position");
const int normal_id = shader->get_attrib_location("v_normal");
#endif // ENABLE_GL_SHADERS_ATTRIBUTES #endif // ENABLE_GL_SHADERS_ATTRIBUTES
if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::InstancedModel) { if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::InstancedModel) {
@ -3211,6 +3148,8 @@ void GCodeViewer::render_toolpaths()
else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) { else if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::BatchedModel) {
shader->set_uniform("emission_factor", 0.25f); shader->set_uniform("emission_factor", 0.25f);
#if ENABLE_GL_SHADERS_ATTRIBUTES #if ENABLE_GL_SHADERS_ATTRIBUTES
const int position_id = shader->get_attrib_location("v_position");
const int normal_id = shader->get_attrib_location("v_normal");
render_as_batched_model(buffer, *shader, position_id, normal_id); render_as_batched_model(buffer, *shader, position_id, normal_id);
#else #else
render_as_batched_model(buffer, *shader); render_as_batched_model(buffer, *shader);
@ -3219,8 +3158,8 @@ void GCodeViewer::render_toolpaths()
} }
else { else {
#if ENABLE_GL_SHADERS_ATTRIBUTES #if ENABLE_GL_SHADERS_ATTRIBUTES
if (buffer.render_primitive_type == TBuffer::ERenderPrimitiveType::Point) const int position_id = shader->get_attrib_location("v_position");
shader_init_as_points(*shader); const int normal_id = shader->get_attrib_location("v_normal");
#else #else
switch (buffer.render_primitive_type) { switch (buffer.render_primitive_type) {
case TBuffer::ERenderPrimitiveType::Point: shader_init_as_points(*shader); break; case TBuffer::ERenderPrimitiveType::Point: shader_init_as_points(*shader); break;
@ -3267,10 +3206,6 @@ void GCodeViewer::render_toolpaths()
// Render all elements with it_path->ibuffer_id == ibuffer_id, possible with varying colors. // Render all elements with it_path->ibuffer_id == ibuffer_id, possible with varying colors.
switch (buffer.render_primitive_type) switch (buffer.render_primitive_type)
{ {
case TBuffer::ERenderPrimitiveType::Point: {
render_as_points(it_path, buffer.render_paths.end(), *shader, uniform_color);
break;
}
case TBuffer::ERenderPrimitiveType::Line: { case TBuffer::ERenderPrimitiveType::Line: {
glsafe(::glLineWidth(static_cast<GLfloat>(line_width(zoom)))); glsafe(::glLineWidth(static_cast<GLfloat>(line_width(zoom))));
render_as_lines(it_path, buffer.render_paths.end(), *shader, uniform_color); render_as_lines(it_path, buffer.render_paths.end(), *shader, uniform_color);
@ -3304,9 +3239,9 @@ void GCodeViewer::render_toolpaths()
} }
#if ENABLE_GCODE_VIEWER_STATISTICS #if ENABLE_GCODE_VIEWER_STATISTICS
auto render_sequential_range_cap = [this] auto render_sequential_range_cap = [this, &camera]
#else #else
auto render_sequential_range_cap = [] auto render_sequential_range_cap = [&camera]
#endif // ENABLE_GCODE_VIEWER_STATISTICS #endif // ENABLE_GCODE_VIEWER_STATISTICS
(const SequentialRangeCap& cap) { (const SequentialRangeCap& cap) {
const TBuffer* buffer = cap.buffer; const TBuffer* buffer = cap.buffer;
@ -3317,7 +3252,6 @@ void GCodeViewer::render_toolpaths()
shader->start_using(); shader->start_using();
#if ENABLE_GL_SHADERS_ATTRIBUTES #if ENABLE_GL_SHADERS_ATTRIBUTES
const Camera& camera = wxGetApp().plater()->get_camera();
const Transform3d& view_matrix = camera.get_view_matrix(); const Transform3d& view_matrix = camera.get_view_matrix();
shader->set_uniform("view_model_matrix", view_matrix); shader->set_uniform("view_model_matrix", view_matrix);
shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("projection_matrix", camera.get_projection_matrix());
@ -4678,7 +4612,6 @@ void GCodeViewer::render_statistics()
} }
if (ImGui::CollapsingHeader("OpenGL calls")) { if (ImGui::CollapsingHeader("OpenGL calls")) {
add_counter(std::string("Multi GL_POINTS:"), m_statistics.gl_multi_points_calls_count);
add_counter(std::string("Multi GL_LINES:"), m_statistics.gl_multi_lines_calls_count); add_counter(std::string("Multi GL_LINES:"), m_statistics.gl_multi_lines_calls_count);
add_counter(std::string("Multi GL_TRIANGLES:"), m_statistics.gl_multi_triangles_calls_count); add_counter(std::string("Multi GL_TRIANGLES:"), m_statistics.gl_multi_triangles_calls_count);
add_counter(std::string("GL_TRIANGLES:"), m_statistics.gl_triangles_calls_count); add_counter(std::string("GL_TRIANGLES:"), m_statistics.gl_triangles_calls_count);

View file

@ -284,7 +284,6 @@ class GCodeViewer
{ {
enum class ERenderPrimitiveType : unsigned char enum class ERenderPrimitiveType : unsigned char
{ {
Point,
Line, Line,
Triangle, Triangle,
InstancedModel, InstancedModel,
@ -325,7 +324,6 @@ class GCodeViewer
unsigned int max_vertices_per_segment() const { unsigned int max_vertices_per_segment() const {
switch (render_primitive_type) switch (render_primitive_type)
{ {
case ERenderPrimitiveType::Point: { return 1; }
case ERenderPrimitiveType::Line: { return 2; } case ERenderPrimitiveType::Line: { return 2; }
case ERenderPrimitiveType::Triangle: { return 8; } case ERenderPrimitiveType::Triangle: { return 8; }
default: { return 0; } default: { return 0; }
@ -337,7 +335,6 @@ class GCodeViewer
unsigned int indices_per_segment() const { unsigned int indices_per_segment() const {
switch (render_primitive_type) switch (render_primitive_type)
{ {
case ERenderPrimitiveType::Point: { return 1; }
case ERenderPrimitiveType::Line: { return 2; } case ERenderPrimitiveType::Line: { return 2; }
case ERenderPrimitiveType::Triangle: { return 30; } // 3 indices x 10 triangles case ERenderPrimitiveType::Triangle: { return 30; } // 3 indices x 10 triangles
default: { return 0; } default: { return 0; }
@ -347,7 +344,6 @@ class GCodeViewer
unsigned int max_indices_per_segment() const { unsigned int max_indices_per_segment() const {
switch (render_primitive_type) switch (render_primitive_type)
{ {
case ERenderPrimitiveType::Point: { return 1; }
case ERenderPrimitiveType::Line: { return 2; } case ERenderPrimitiveType::Line: { return 2; }
case ERenderPrimitiveType::Triangle: { return 36; } // 3 indices x 12 triangles case ERenderPrimitiveType::Triangle: { return 36; } // 3 indices x 12 triangles
default: { return 0; } default: { return 0; }
@ -358,7 +354,6 @@ class GCodeViewer
bool has_data() const { bool has_data() const {
switch (render_primitive_type) switch (render_primitive_type)
{ {
case ERenderPrimitiveType::Point:
case ERenderPrimitiveType::Line: case ERenderPrimitiveType::Line:
case ERenderPrimitiveType::Triangle: { case ERenderPrimitiveType::Triangle: {
return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0; return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
@ -585,7 +580,6 @@ class GCodeViewer
int64_t refresh_time{ 0 }; int64_t refresh_time{ 0 };
int64_t refresh_paths_time{ 0 }; int64_t refresh_paths_time{ 0 };
// opengl calls // opengl calls
int64_t gl_multi_points_calls_count{ 0 };
int64_t gl_multi_lines_calls_count{ 0 }; int64_t gl_multi_lines_calls_count{ 0 };
int64_t gl_multi_triangles_calls_count{ 0 }; int64_t gl_multi_triangles_calls_count{ 0 };
int64_t gl_triangles_calls_count{ 0 }; int64_t gl_triangles_calls_count{ 0 };
@ -628,7 +622,6 @@ class GCodeViewer
} }
void reset_opengl() { void reset_opengl() {
gl_multi_points_calls_count = 0;
gl_multi_lines_calls_count = 0; gl_multi_lines_calls_count = 0;
gl_multi_triangles_calls_count = 0; gl_multi_triangles_calls_count = 0;
gl_triangles_calls_count = 0; gl_triangles_calls_count = 0;

View file

@ -1696,13 +1696,17 @@ void GLCanvas3D::render()
wxGetApp().imgui()->new_frame(); wxGetApp().imgui()->new_frame();
if (m_picking_enabled) { if (m_picking_enabled) {
if (m_rectangle_selection.is_dragging()) #if ENABLE_NEW_RECTANGLE_SELECTION
// picking pass using rectangle selection if (m_rectangle_selection.is_dragging() && !m_rectangle_selection.is_empty())
_rectangular_selection_picking_pass(); #else
else if (!m_volumes.empty()) if (m_rectangle_selection.is_dragging())
// regular picking pass #endif // ENABLE_NEW_RECTANGLE_SELECTION
_picking_pass(); // picking pass using rectangle selection
} _rectangular_selection_picking_pass();
else if (!m_volumes.empty())
// regular picking pass
_picking_pass();
}
#if ENABLE_RENDER_PICKING_PASS #if ENABLE_RENDER_PICKING_PASS
if (!m_picking_enabled || !m_show_picking_texture) { if (!m_picking_enabled || !m_show_picking_texture) {
@ -2228,7 +2232,13 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
if (state.step[istep].state == PrintStateBase::DONE) { if (state.step[istep].state == PrintStateBase::DONE) {
TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles); TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles);
assert(! mesh.empty()); assert(! mesh.empty());
mesh.transform(sla_print->sla_trafo(*m_model->objects[volume.object_idx()]).inverse());
// sla_trafo does not contain volume trafo. To get a mesh to create
// a new volume from, we have to apply vol trafo inverse separately.
const ModelObject& mo = *m_model->objects[volume.object_idx()];
Transform3d trafo = sla_print->sla_trafo(mo)
* mo.volumes.front()->get_transformation().get_matrix();
mesh.transform(trafo.inverse());
#if ENABLE_SMOOTH_NORMALS #if ENABLE_SMOOTH_NORMALS
#if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_LEGACY_OPENGL_REMOVAL
volume.model.init_from(mesh, true); volume.model.init_from(mesh, true);
@ -2924,8 +2934,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
if (imgui->update_key_data(evt)) { if (imgui->update_key_data(evt)) {
render(); render();
} }
else else {
{
if (!m_gizmos.on_key(evt)) { if (!m_gizmos.on_key(evt)) {
if (evt.GetEventType() == wxEVT_KEY_UP) { if (evt.GetEventType() == wxEVT_KEY_UP) {
if (evt.ShiftDown() && evt.ControlDown() && keyCode == WXK_SPACE) { if (evt.ShiftDown() && evt.ControlDown() && keyCode == WXK_SPACE) {
@ -2948,8 +2957,13 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
_update_selection_from_hover(); _update_selection_from_hover();
m_rectangle_selection.stop_dragging(); m_rectangle_selection.stop_dragging();
m_mouse.ignore_left_up = true; m_mouse.ignore_left_up = true;
#if !ENABLE_NEW_RECTANGLE_SELECTION
m_dirty = true; m_dirty = true;
#endif // !ENABLE_NEW_RECTANGLE_SELECTION
} }
#if ENABLE_NEW_RECTANGLE_SELECTION
m_dirty = true;
#endif // ENABLE_NEW_RECTANGLE_SELECTION
// set_cursor(Standard); // set_cursor(Standard);
} }
else if (keyCode == WXK_ALT) { else if (keyCode == WXK_ALT) {
@ -2961,8 +2975,17 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
} }
// set_cursor(Standard); // set_cursor(Standard);
} }
else if (keyCode == WXK_CONTROL) else if (keyCode == WXK_CONTROL) {
#if ENABLE_NEW_CAMERA_MOVEMENTS
if (m_mouse.dragging) {
// if the user releases CTRL while rotating the 3D scene
// prevent from moving the selected volume
m_mouse.drag.move_volume_idx = -1;
m_mouse.set_start_position_3D_as_invalid();
}
#endif // ENABLE_NEW_CAMERA_MOVEMENTS
m_dirty = true; m_dirty = true;
}
else if (m_gizmos.is_enabled() && !m_selection.is_empty()) { else if (m_gizmos.is_enabled() && !m_selection.is_empty()) {
translationProcessor.process(evt); translationProcessor.process(evt);
@ -2993,15 +3016,16 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
if (keyCode == WXK_SHIFT) { if (keyCode == WXK_SHIFT) {
translationProcessor.process(evt); translationProcessor.process(evt);
if (m_picking_enabled && (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)) if (m_picking_enabled && (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)) {
{
m_mouse.ignore_left_up = false; m_mouse.ignore_left_up = false;
// set_cursor(Cross); // set_cursor(Cross);
} }
#if ENABLE_NEW_RECTANGLE_SELECTION
m_dirty = true;
#endif // ENABLE_NEW_RECTANGLE_SELECTION
} }
else if (keyCode == WXK_ALT) { else if (keyCode == WXK_ALT) {
if (m_picking_enabled && (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)) if (m_picking_enabled && (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)) {
{
m_mouse.ignore_left_up = false; m_mouse.ignore_left_up = false;
// set_cursor(Cross); // set_cursor(Cross);
} }
@ -3420,6 +3444,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_layers_editing.state = LayersEditing::Editing; m_layers_editing.state = LayersEditing::Editing;
_perform_layer_editing_action(&evt); _perform_layer_editing_action(&evt);
} }
#if !ENABLE_NEW_RECTANGLE_SELECTION
else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled) { else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled) {
if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports
&& m_gizmos.get_current_type() != GLGizmosManager::FdmSupports && m_gizmos.get_current_type() != GLGizmosManager::FdmSupports
@ -3429,23 +3454,52 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_dirty = true; m_dirty = true;
} }
} }
#endif // !ENABLE_NEW_RECTANGLE_SELECTION
else { else {
#if ENABLE_NEW_RECTANGLE_SELECTION
if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled) {
if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports &&
m_gizmos.get_current_type() != GLGizmosManager::FdmSupports &&
m_gizmos.get_current_type() != GLGizmosManager::Seam &&
m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation) {
m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::EState::Select : GLSelectionRectangle::EState::Deselect);
m_dirty = true;
}
}
#endif // ENABLE_NEW_RECTANGLE_SELECTION
// Select volume in this 3D canvas. // Select volume in this 3D canvas.
// Don't deselect a volume if layer editing is enabled or any gizmo is active. We want the object to stay selected // Don't deselect a volume if layer editing is enabled or any gizmo is active. We want the object to stay selected
// during the scene manipulation. // during the scene manipulation.
#if ENABLE_NEW_RECTANGLE_SELECTION
if (m_picking_enabled && (!any_gizmo_active || !evt.ShiftDown()) && (!m_hover_volume_idxs.empty() || !is_layers_editing_enabled()) &&
!m_rectangle_selection.is_dragging()) {
#else
if (m_picking_enabled && (!any_gizmo_active || !evt.CmdDown()) && (!m_hover_volume_idxs.empty() || !is_layers_editing_enabled())) { if (m_picking_enabled && (!any_gizmo_active || !evt.CmdDown()) && (!m_hover_volume_idxs.empty() || !is_layers_editing_enabled())) {
#endif // ENABLE_NEW_RECTANGLE_SELECTION
if (evt.LeftDown() && !m_hover_volume_idxs.empty()) { if (evt.LeftDown() && !m_hover_volume_idxs.empty()) {
int volume_idx = get_first_hover_volume_idx(); int volume_idx = get_first_hover_volume_idx();
bool already_selected = m_selection.contains_volume(volume_idx); bool already_selected = m_selection.contains_volume(volume_idx);
#if ENABLE_NEW_RECTANGLE_SELECTION
bool shift_down = evt.ShiftDown();
#else
bool ctrl_down = evt.CmdDown(); bool ctrl_down = evt.CmdDown();
#endif // ENABLE_NEW_RECTANGLE_SELECTION
Selection::IndicesList curr_idxs = m_selection.get_volume_idxs(); Selection::IndicesList curr_idxs = m_selection.get_volume_idxs();
#if ENABLE_NEW_RECTANGLE_SELECTION
if (already_selected && shift_down)
m_selection.remove(volume_idx);
else {
m_selection.add(volume_idx, !shift_down, true);
#else
if (already_selected && ctrl_down) if (already_selected && ctrl_down)
m_selection.remove(volume_idx); m_selection.remove(volume_idx);
else { else {
m_selection.add(volume_idx, !ctrl_down, true); m_selection.add(volume_idx, !ctrl_down, true);
#endif // ENABLE_NEW_RECTANGLE_SELECTION
m_mouse.drag.move_requires_threshold = !already_selected; m_mouse.drag.move_requires_threshold = !already_selected;
if (already_selected) if (already_selected)
m_mouse.set_move_start_threshold_position_2D_as_invalid(); m_mouse.set_move_start_threshold_position_2D_as_invalid();
@ -3467,18 +3521,25 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
} }
} }
#if ENABLE_NEW_RECTANGLE_SELECTION
if (!m_hover_volume_idxs.empty() && !m_rectangle_selection.is_dragging()) {
#else
if (!m_hover_volume_idxs.empty()) { if (!m_hover_volume_idxs.empty()) {
#endif // ENABLE_NEW_RECTANGLE_SELECTION
if (evt.LeftDown() && m_moving_enabled && m_mouse.drag.move_volume_idx == -1) { if (evt.LeftDown() && m_moving_enabled && m_mouse.drag.move_volume_idx == -1) {
// Only accept the initial position, if it is inside the volume bounding box. // Only accept the initial position, if it is inside the volume bounding box.
int volume_idx = get_first_hover_volume_idx(); const int volume_idx = get_first_hover_volume_idx();
BoundingBoxf3 volume_bbox = m_volumes.volumes[volume_idx]->transformed_bounding_box(); BoundingBoxf3 volume_bbox = m_volumes.volumes[volume_idx]->transformed_bounding_box();
volume_bbox.offset(1.0); volume_bbox.offset(1.0);
if ((!any_gizmo_active || !evt.CmdDown()) && volume_bbox.contains(m_mouse.scene_position)) { if ((!any_gizmo_active || !evt.CmdDown()) && volume_bbox.contains(m_mouse.scene_position)) {
m_volumes.volumes[volume_idx]->hover = GLVolume::HS_None; m_volumes.volumes[volume_idx]->hover = GLVolume::HS_None;
// The dragging operation is initiated. // The dragging operation is initiated.
m_mouse.drag.move_volume_idx = volume_idx; m_mouse.drag.move_volume_idx = volume_idx;
#if ENABLE_NEW_CAMERA_MOVEMENTS
m_selection.setup_cache(); m_selection.setup_cache();
m_mouse.drag.start_position_3D = m_mouse.scene_position; if (!evt.CmdDown())
#endif // ENABLE_NEW_CAMERA_MOVEMENTS
m_mouse.drag.start_position_3D = m_mouse.scene_position;
m_sequential_print_clearance_first_displacement = true; m_sequential_print_clearance_first_displacement = true;
m_moving = true; m_moving = true;
} }
@ -3486,31 +3547,36 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
} }
} }
} }
#if ENABLE_NEW_CAMERA_MOVEMENTS
else if (evt.Dragging() && evt.LeftIsDown() && !evt.CmdDown() && m_layers_editing.state == LayersEditing::Unknown &&
m_mouse.drag.move_volume_idx != -1 && m_mouse.is_start_position_3D_defined()) {
#else
else if (evt.Dragging() && evt.LeftIsDown() && m_layers_editing.state == LayersEditing::Unknown && m_mouse.drag.move_volume_idx != -1) { else if (evt.Dragging() && evt.LeftIsDown() && m_layers_editing.state == LayersEditing::Unknown && m_mouse.drag.move_volume_idx != -1) {
if (!m_mouse.drag.move_requires_threshold) { #endif // ENABLE_NEW_CAMERA_MOVEMENTS
if (!m_mouse.drag.move_requires_threshold) {
m_mouse.dragging = true; m_mouse.dragging = true;
Vec3d cur_pos = m_mouse.drag.start_position_3D; Vec3d cur_pos = m_mouse.drag.start_position_3D;
// we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag // we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag
if (m_selection.contains_volume(get_first_hover_volume_idx())) { if (m_selection.contains_volume(get_first_hover_volume_idx())) {
const Camera& camera = wxGetApp().plater()->get_camera(); const Camera& camera = wxGetApp().plater()->get_camera();
if (std::abs(camera.get_dir_forward()(2)) < EPSILON) { if (std::abs(camera.get_dir_forward().z()) < EPSILON) {
// side view -> move selected volumes orthogonally to camera view direction // side view -> move selected volumes orthogonally to camera view direction
Linef3 ray = mouse_ray(pos); const Linef3 ray = mouse_ray(pos);
Vec3d dir = ray.unit_vector(); const Vec3d dir = ray.unit_vector();
// finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position
// use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form
// in our case plane normal and ray direction are the same (orthogonal view) // in our case plane normal and ray direction are the same (orthogonal view)
// when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal
Vec3d inters = ray.a + (m_mouse.drag.start_position_3D - ray.a).dot(dir) / dir.squaredNorm() * dir; const Vec3d inters = ray.a + (m_mouse.drag.start_position_3D - ray.a).dot(dir) / dir.squaredNorm() * dir;
// vector from the starting position to the found intersection // vector from the starting position to the found intersection
Vec3d inters_vec = inters - m_mouse.drag.start_position_3D; const Vec3d inters_vec = inters - m_mouse.drag.start_position_3D;
Vec3d camera_right = camera.get_dir_right(); const Vec3d camera_right = camera.get_dir_right();
Vec3d camera_up = camera.get_dir_up(); const Vec3d camera_up = camera.get_dir_up();
// finds projection of the vector along the camera axes // finds projection of the vector along the camera axes
double projection_x = inters_vec.dot(camera_right); const double projection_x = inters_vec.dot(camera_right);
double projection_z = inters_vec.dot(camera_up); const double projection_z = inters_vec.dot(camera_up);
// apply offset // apply offset
cur_pos = m_mouse.drag.start_position_3D + projection_x * camera_right + projection_z * camera_up; cur_pos = m_mouse.drag.start_position_3D + projection_x * camera_right + projection_z * camera_up;
@ -3520,7 +3586,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// Get new position at the same Z of the initial click point. // Get new position at the same Z of the initial click point.
float z0 = 0.0f; float z0 = 0.0f;
float z1 = 1.0f; float z1 = 1.0f;
cur_pos = Linef3(_mouse_to_3d(pos, &z0), _mouse_to_3d(pos, &z1)).intersect_plane(m_mouse.drag.start_position_3D(2)); cur_pos = Linef3(_mouse_to_3d(pos, &z0), _mouse_to_3d(pos, &z1)).intersect_plane(m_mouse.drag.start_position_3D.z());
} }
} }
@ -3532,7 +3598,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
} }
} }
else if (evt.Dragging() && evt.LeftIsDown() && m_picking_enabled && m_rectangle_selection.is_dragging()) { else if (evt.Dragging() && evt.LeftIsDown() && m_picking_enabled && m_rectangle_selection.is_dragging()) {
#if ENABLE_NEW_RECTANGLE_SELECTION
// keeps the mouse position updated while dragging the selection rectangle
m_mouse.position = pos.cast<double>();
m_rectangle_selection.dragging(m_mouse.position);
#else
m_rectangle_selection.dragging(pos.cast<double>()); m_rectangle_selection.dragging(pos.cast<double>());
#endif // ENABLE_NEW_RECTANGLE_SELECTION
m_dirty = true; m_dirty = true;
} }
else if (evt.Dragging()) { else if (evt.Dragging()) {
@ -3545,13 +3617,19 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
} }
} }
// do not process the dragging if the left mouse was set down in another canvas // do not process the dragging if the left mouse was set down in another canvas
else if (evt.LeftIsDown()) { #if ENABLE_NEW_CAMERA_MOVEMENTS
else if (evt.LeftIsDown() || evt.MiddleIsDown()) {
// if dragging over blank area with left button, rotate // if dragging over blank area with left button, rotate
if ((any_gizmo_active || evt.CmdDown() || evt.MiddleIsDown() || m_hover_volume_idxs.empty()) && m_mouse.is_start_position_3D_defined()) {
#else
// if dragging over blank area with left button, rotate
else if (evt.LeftIsDown()) {
if ((any_gizmo_active || m_hover_volume_idxs.empty()) && m_mouse.is_start_position_3D_defined()) { if ((any_gizmo_active || m_hover_volume_idxs.empty()) && m_mouse.is_start_position_3D_defined()) {
const Vec3d rot = (Vec3d(pos.x(), pos.y(), 0.) - m_mouse.drag.start_position_3D) * (PI * TRACKBALLSIZE / 180.); #endif // ENABLE_NEW_CAMERA_MOVEMENTS
const Vec3d rot = (Vec3d(pos.x(), pos.y(), 0.0) - m_mouse.drag.start_position_3D) * (PI * TRACKBALLSIZE / 180.0);
if (wxGetApp().app_config->get("use_free_camera") == "1") if (wxGetApp().app_config->get("use_free_camera") == "1")
// Virtual track ball (similar to the 3DConnexion mouse). // Virtual track ball (similar to the 3DConnexion mouse).
wxGetApp().plater()->get_camera().rotate_local_around_target(Vec3d(rot.y(), rot.x(), 0.)); wxGetApp().plater()->get_camera().rotate_local_around_target(Vec3d(rot.y(), rot.x(), 0.0));
else { else {
// Forces camera right vector to be parallel to XY plane in case it has been misaligned using the 3D mouse free rotation. // Forces camera right vector to be parallel to XY plane in case it has been misaligned using the 3D mouse free rotation.
// It is cheaper to call this function right away instead of testing wxGetApp().plater()->get_mouse3d_controller().connected(), // It is cheaper to call this function right away instead of testing wxGetApp().plater()->get_mouse3d_controller().connected(),
@ -3564,15 +3642,20 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_dirty = true; m_dirty = true;
} }
m_mouse.drag.start_position_3D = Vec3d((double)pos(0), (double)pos(1), 0.0); m_mouse.drag.start_position_3D = Vec3d((double)pos.x(), (double)pos.y(), 0.0);
} }
#if ENABLE_NEW_CAMERA_MOVEMENTS
else if (evt.RightIsDown()) {
// If dragging with right button, pan.
#else
else if (evt.MiddleIsDown() || evt.RightIsDown()) { else if (evt.MiddleIsDown() || evt.RightIsDown()) {
// If dragging over blank area with right button, pan. // If dragging over blank area with right button, pan.
#endif // ENABLE_NEW_CAMERA_MOVEMENTS
if (m_mouse.is_start_position_2D_defined()) { if (m_mouse.is_start_position_2D_defined()) {
// get point in model space at Z = 0 // get point in model space at Z = 0
float z = 0.0f; float z = 0.0f;
const Vec3d& cur_pos = _mouse_to_3d(pos, &z); const Vec3d& cur_pos = _mouse_to_3d(pos, &z);
Vec3d orig = _mouse_to_3d(m_mouse.drag.start_position_2D, &z); const Vec3d orig = _mouse_to_3d(m_mouse.drag.start_position_2D, &z);
Camera& camera = wxGetApp().plater()->get_camera(); Camera& camera = wxGetApp().plater()->get_camera();
if (wxGetApp().app_config->get("use_free_camera") != "1") if (wxGetApp().app_config->get("use_free_camera") != "1")
// Forces camera right vector to be parallel to XY plane in case it has been misaligned using the 3D mouse free rotation. // Forces camera right vector to be parallel to XY plane in case it has been misaligned using the 3D mouse free rotation.
@ -3645,7 +3728,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (!m_mouse.dragging) { if (!m_mouse.dragging) {
// do not post the event if the user is panning the scene // do not post the event if the user is panning the scene
// or if right click was done over the wipe tower // or if right click was done over the wipe tower
bool post_right_click_event = m_hover_volume_idxs.empty() || !m_volumes.volumes[get_first_hover_volume_idx()]->is_wipe_tower; const bool post_right_click_event = m_hover_volume_idxs.empty() || !m_volumes.volumes[get_first_hover_volume_idx()]->is_wipe_tower;
if (post_right_click_event) if (post_right_click_event)
post_event(RBtnEvent(EVT_GLCANVAS_RIGHT_CLICK, { logical_pos, m_hover_volume_idxs.empty() })); post_event(RBtnEvent(EVT_GLCANVAS_RIGHT_CLICK, { logical_pos, m_hover_volume_idxs.empty() }));
} }
@ -6338,7 +6421,9 @@ void GLCanvas3D::_update_volumes_hover_state()
return; return;
} }
#if !ENABLE_NEW_RECTANGLE_SELECTION
bool selection_modifiers_only = m_selection.is_empty() || m_selection.is_any_modifier(); bool selection_modifiers_only = m_selection.is_empty() || m_selection.is_any_modifier();
#endif // !ENABLE_NEW_RECTANGLE_SELECTION
bool hover_modifiers_only = true; bool hover_modifiers_only = true;
for (int i : m_hover_volume_idxs) { for (int i : m_hover_volume_idxs) {
@ -6367,9 +6452,14 @@ void GLCanvas3D::_update_volumes_hover_state()
if (volume.hover != GLVolume::HS_None) if (volume.hover != GLVolume::HS_None)
continue; continue;
#if ENABLE_NEW_RECTANGLE_SELECTION
bool deselect = volume.selected && ((shift_pressed && m_rectangle_selection.is_empty()) || (alt_pressed && !m_rectangle_selection.is_empty()));
bool select = !volume.selected && (m_rectangle_selection.is_empty() || (shift_pressed && !m_rectangle_selection.is_empty()));
#else
bool deselect = volume.selected && ((ctrl_pressed && !shift_pressed) || alt_pressed); bool deselect = volume.selected && ((ctrl_pressed && !shift_pressed) || alt_pressed);
// (volume->is_modifier && !selection_modifiers_only && !is_ctrl_pressed) -> allows hovering on selected modifiers belonging to selection of type Instance // (volume->is_modifier && !selection_modifiers_only && !is_ctrl_pressed) -> allows hovering on selected modifiers belonging to selection of type Instance
bool select = (!volume.selected || (volume.is_modifier && !selection_modifiers_only && !ctrl_pressed)) && !alt_pressed; bool select = (!volume.selected || (volume.is_modifier && !selection_modifiers_only && !ctrl_pressed)) && !alt_pressed;
#endif // ENABLE_NEW_RECTANGLE_SELECTION
if (select || deselect) { if (select || deselect) {
bool as_volume = bool as_volume =
@ -7287,7 +7377,7 @@ void GLCanvas3D::_update_selection_from_hover()
bool ctrl_pressed = wxGetKeyState(WXK_CONTROL); bool ctrl_pressed = wxGetKeyState(WXK_CONTROL);
if (m_hover_volume_idxs.empty()) { if (m_hover_volume_idxs.empty()) {
if (!ctrl_pressed && (m_rectangle_selection.get_state() == GLSelectionRectangle::Select)) if (!ctrl_pressed && m_rectangle_selection.get_state() == GLSelectionRectangle::EState::Select)
m_selection.remove_all(); m_selection.remove_all();
return; return;
@ -7304,7 +7394,10 @@ void GLCanvas3D::_update_selection_from_hover()
} }
bool selection_changed = false; bool selection_changed = false;
if (state == GLSelectionRectangle::Select) { #if ENABLE_NEW_RECTANGLE_SELECTION
if (!m_rectangle_selection.is_empty()) {
#endif // ENABLE_NEW_RECTANGLE_SELECTION
if (state == GLSelectionRectangle::EState::Select) {
bool contains_all = true; bool contains_all = true;
for (int i : m_hover_volume_idxs) { for (int i : m_hover_volume_idxs) {
if (!m_selection.contains_volume((unsigned int)i)) { if (!m_selection.contains_volume((unsigned int)i)) {
@ -7315,7 +7408,7 @@ void GLCanvas3D::_update_selection_from_hover()
// the selection is going to be modified (Add) // the selection is going to be modified (Add)
if (!contains_all) { if (!contains_all) {
wxGetApp().plater()->take_snapshot(_(L("Selection-Add from rectangle")), UndoRedo::SnapshotType::Selection); wxGetApp().plater()->take_snapshot(_L("Selection-Add from rectangle"), UndoRedo::SnapshotType::Selection);
selection_changed = true; selection_changed = true;
} }
} }
@ -7330,21 +7423,24 @@ void GLCanvas3D::_update_selection_from_hover()
// the selection is going to be modified (Remove) // the selection is going to be modified (Remove)
if (contains_any) { if (contains_any) {
wxGetApp().plater()->take_snapshot(_(L("Selection-Remove from rectangle")), UndoRedo::SnapshotType::Selection); wxGetApp().plater()->take_snapshot(_L("Selection-Remove from rectangle"), UndoRedo::SnapshotType::Selection);
selection_changed = true; selection_changed = true;
} }
} }
#if ENABLE_NEW_RECTANGLE_SELECTION
}
#endif // ENABLE_NEW_RECTANGLE_SELECTION
if (!selection_changed) if (!selection_changed)
return; return;
Plater::SuppressSnapshots suppress(wxGetApp().plater()); Plater::SuppressSnapshots suppress(wxGetApp().plater());
if ((state == GLSelectionRectangle::Select) && !ctrl_pressed) if (state == GLSelectionRectangle::EState::Select && !ctrl_pressed)
m_selection.clear(); m_selection.clear();
for (int i : m_hover_volume_idxs) { for (int i : m_hover_volume_idxs) {
if (state == GLSelectionRectangle::Select) { if (state == GLSelectionRectangle::EState::Select) {
if (hover_modifiers_only) { if (hover_modifiers_only) {
const GLVolume& v = *m_volumes.volumes[i]; const GLVolume& v = *m_volumes.volumes[i];
m_selection.add_volume(v.object_idx(), v.volume_idx(), v.instance_idx(), false); m_selection.add_volume(v.object_idx(), v.volume_idx(), v.instance_idx(), false);

View file

@ -13,12 +13,12 @@ namespace GUI {
void GLSelectionRectangle::start_dragging(const Vec2d& mouse_position, EState state) void GLSelectionRectangle::start_dragging(const Vec2d& mouse_position, EState state)
{ {
if (is_dragging() || (state == Off)) if (is_dragging() || state == EState::Off)
return; return;
m_state = state; m_state = state;
m_start_corner = mouse_position; m_start_corner = mouse_position;
m_end_corner = mouse_position; m_end_corner = mouse_position;
} }
void GLSelectionRectangle::dragging(const Vec2d& mouse_position) void GLSelectionRectangle::dragging(const Vec2d& mouse_position)
@ -36,7 +36,7 @@ namespace GUI {
if (!is_dragging()) if (!is_dragging())
return out; return out;
m_state = Off; m_state = EState::Off;
const Camera& camera = wxGetApp().plater()->get_camera(); const Camera& camera = wxGetApp().plater()->get_camera();
Matrix4d modelview = camera.get_view_matrix().matrix(); Matrix4d modelview = camera.get_view_matrix().matrix();
@ -66,7 +66,7 @@ namespace GUI {
void GLSelectionRectangle::stop_dragging() void GLSelectionRectangle::stop_dragging()
{ {
if (is_dragging()) if (is_dragging())
m_state = Off; m_state = EState::Off;
} }
void GLSelectionRectangle::render(const GLCanvas3D& canvas) void GLSelectionRectangle::render(const GLCanvas3D& canvas)
@ -108,8 +108,8 @@ namespace GUI {
glsafe(::glLineWidth(1.5f)); glsafe(::glLineWidth(1.5f));
#if !ENABLE_LEGACY_OPENGL_REMOVAL #if !ENABLE_LEGACY_OPENGL_REMOVAL
float color[3]; float color[3];
color[0] = (m_state == Select) ? 0.3f : 1.0f; color[0] = (m_state == EState::Select) ? 0.3f : 1.0f;
color[1] = (m_state == Select) ? 1.0f : 0.3f; color[1] = (m_state == EState::Select) ? 1.0f : 0.3f;
color[2] = 0.3f; color[2] = 0.3f;
glsafe(::glColor3fv(color)); glsafe(::glColor3fv(color));
#endif // !ENABLE_LEGACY_OPENGL_REMOVAL #endif // !ENABLE_LEGACY_OPENGL_REMOVAL
@ -169,7 +169,7 @@ namespace GUI {
shader->set_uniform("projection_matrix", Transform3d::Identity()); shader->set_uniform("projection_matrix", Transform3d::Identity());
#endif // ENABLE_GL_SHADERS_ATTRIBUTES #endif // ENABLE_GL_SHADERS_ATTRIBUTES
m_rectangle.set_color(ColorRGBA((m_state == Select) ? 0.3f : 1.0f, (m_state == Select) ? 1.0f : 0.3f, 0.3f, 1.0f)); m_rectangle.set_color(ColorRGBA((m_state == EState::Select) ? 0.3f : 1.0f, (m_state == EState::Select) ? 1.0f : 0.3f, 0.3f, 1.0f));
m_rectangle.render(); m_rectangle.render();
shader->stop_using(); shader->stop_using();
} }

View file

@ -14,7 +14,7 @@ class GLCanvas3D;
class GLSelectionRectangle { class GLSelectionRectangle {
public: public:
enum EState { enum class EState {
Off, Off,
Select, Select,
Deselect Deselect
@ -35,7 +35,11 @@ public:
void render(const GLCanvas3D& canvas); void render(const GLCanvas3D& canvas);
bool is_dragging() const { return m_state != Off; } bool is_dragging() const { return m_state != EState::Off; }
#if ENABLE_NEW_RECTANGLE_SELECTION
bool is_empty() const { return m_state == EState::Off || m_start_corner.isApprox(m_end_corner); }
#endif // ENABLE_NEW_RECTANGLE_SELECTION
EState get_state() const { return m_state; } EState get_state() const { return m_state; }
float get_width() const { return std::abs(m_start_corner.x() - m_end_corner.x()); } float get_width() const { return std::abs(m_start_corner.x() - m_end_corner.x()); }
@ -46,7 +50,7 @@ public:
float get_bottom() const { return std::min(m_start_corner.y(), m_end_corner.y()); } float get_bottom() const { return std::min(m_start_corner.y(), m_end_corner.y()); }
private: private:
EState m_state{ Off }; EState m_state{ EState::Off };
Vec2d m_start_corner{ Vec2d::Zero() }; Vec2d m_start_corner{ Vec2d::Zero() };
Vec2d m_end_corner{ Vec2d::Zero() }; Vec2d m_end_corner{ Vec2d::Zero() };
#if ENABLE_LEGACY_OPENGL_REMOVAL #if ENABLE_LEGACY_OPENGL_REMOVAL

View file

@ -499,6 +499,44 @@ static const FileWildcards file_wildcards_by_type[FT_SIZE] = {
/* FT_SL1 */ { "Masked SLA files"sv, { ".sl1"sv, ".sl1s"sv, ".pwmx"sv } }, /* FT_SL1 */ { "Masked SLA files"sv, { ".sl1"sv, ".sl1s"sv, ".pwmx"sv } },
}; };
#if ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR
wxString file_wildcards(FileType file_type)
{
const FileWildcards& data = file_wildcards_by_type[file_type];
std::string title;
std::string mask;
// Generate cumulative first item
for (const std::string_view& ext : data.file_extensions) {
if (title.empty()) {
title = "*";
title += ext;
mask = title;
}
else {
title += ", *";
title += ext;
mask += ";*";
mask += ext;
}
mask += ";*";
mask += boost::to_upper_copy(std::string(ext));
}
wxString ret = GUI::format_wxstr("%s (%s)|%s", data.title, title, mask);
// Adds an item for each of the extensions
if (data.file_extensions.size() > 1) {
for (const std::string_view& ext : data.file_extensions) {
title = "*";
title += ext;
ret += GUI::format_wxstr("|%s (%s)|%s", data.title, title, title);
}
}
return ret;
}
#else
// This function produces a Win32 file dialog file template mask to be consumed by wxWidgets on all platforms. // This function produces a Win32 file dialog file template mask to be consumed by wxWidgets on all platforms.
// The function accepts a custom extension parameter. If the parameter is provided, the custom extension // The function accepts a custom extension parameter. If the parameter is provided, the custom extension
// will be added as a fist to the list. This is important for a "file save" dialog on OSX, which strips // will be added as a fist to the list. This is important for a "file save" dialog on OSX, which strips
@ -551,8 +589,10 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
mask += ";*"; mask += ";*";
mask += boost::to_upper_copy(std::string(ext)); mask += boost::to_upper_copy(std::string(ext));
} }
return GUI::format_wxstr("%s (%s)|%s", data.title, title, mask); return GUI::format_wxstr("%s (%s)|%s", data.title, title, mask);
} }
#endif // ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR
static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); } static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); }

View file

@ -71,7 +71,11 @@ enum FileType
FT_SIZE, FT_SIZE,
}; };
#if ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR
extern wxString file_wildcards(FileType file_type);
#else
extern wxString file_wildcards(FileType file_type, const std::string &custom_extension = std::string{}); extern wxString file_wildcards(FileType file_type, const std::string &custom_extension = std::string{});
#endif // ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR
enum ConfigMenuIDs { enum ConfigMenuIDs {
ConfigMenuWizard, ConfigMenuWizard,

View file

@ -966,6 +966,11 @@ void Preview::load_print_as_fff(bool keep_z_range)
Refresh(); Refresh();
zs = m_canvas->get_volumes_print_zs(true); zs = m_canvas->get_volumes_print_zs(true);
} }
else {
m_left_sizer->Hide(m_bottom_toolbar_panel);
m_left_sizer->Layout();
Refresh();
}
if (!zs.empty() && !m_keep_current_preview_type) { if (!zs.empty() && !m_keep_current_preview_type) {
unsigned int number_extruders = wxGetApp().is_editor() ? unsigned int number_extruders = wxGetApp().is_editor() ?
@ -1040,7 +1045,6 @@ void Preview::load_print_as_sla()
if (IsShown()) { if (IsShown()) {
m_canvas->load_sla_preview(); m_canvas->load_sla_preview();
m_left_sizer->Hide(m_bottom_toolbar_panel); m_left_sizer->Hide(m_bottom_toolbar_panel);
m_left_sizer->Hide(m_bottom_toolbar_panel);
m_left_sizer->Layout(); m_left_sizer->Layout();
Refresh(); Refresh();

View file

@ -122,9 +122,11 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking)
#endif // ENABLE_LEGACY_OPENGL_REMOVAL #endif // ENABLE_LEGACY_OPENGL_REMOVAL
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
Geometry::Transformation trafo = vol->get_instance_transformation() * vol->get_volume_transformation();
#if ENABLE_GL_SHADERS_ATTRIBUTES #if ENABLE_GL_SHADERS_ATTRIBUTES
const Transform3d instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); const Transform3d instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse();
const Transform3d instance_matrix = Geometry::assemble_transform(m_c->selection_info()->get_sla_shift() * Vec3d::UnitZ()) * vol->get_instance_transformation().get_matrix(); const Transform3d instance_matrix = Geometry::assemble_transform(m_c->selection_info()->get_sla_shift() * Vec3d::UnitZ()) * trafo.get_matrix();
const Camera& camera = wxGetApp().plater()->get_camera(); const Camera& camera = wxGetApp().plater()->get_camera();
const Transform3d& view_matrix = camera.get_view_matrix(); const Transform3d& view_matrix = camera.get_view_matrix();
@ -132,8 +134,8 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking)
shader->set_uniform("projection_matrix", projection_matrix); shader->set_uniform("projection_matrix", projection_matrix);
#else #else
const Transform3d& instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); const Transform3d& instance_scaling_matrix_inverse = trafo.get_matrix(true, true, false, true).inverse();
const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); const Transform3d& instance_matrix = trafo.get_matrix();
glsafe(::glPushMatrix()); glsafe(::glPushMatrix());
glsafe(::glTranslated(0.0, 0.0, m_c->selection_info()->get_sla_shift())); glsafe(::glTranslated(0.0, 0.0, m_c->selection_info()->get_sla_shift()));
@ -222,7 +224,7 @@ bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const
auto sel_info = m_c->selection_info(); auto sel_info = m_c->selection_info();
int active_inst = m_c->selection_info()->get_active_instance(); int active_inst = m_c->selection_info()->get_active_instance();
const ModelInstance* mi = sel_info->model_object()->instances[active_inst]; const ModelInstance* mi = sel_info->model_object()->instances[active_inst];
const Transform3d& trafo = mi->get_transformation().get_matrix(); const Transform3d& trafo = mi->get_transformation().get_matrix() * sel_info->model_object()->volumes.front()->get_matrix();
Vec3d transformed_point = trafo * point; Vec3d transformed_point = trafo * point;
transformed_point(2) += sel_info->get_sla_shift(); transformed_point(2) += sel_info->get_sla_shift();
@ -241,7 +243,7 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, V
const Camera& camera = wxGetApp().plater()->get_camera(); const Camera& camera = wxGetApp().plater()->get_camera();
const Selection& selection = m_parent.get_selection(); const Selection& selection = m_parent.get_selection();
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
Geometry::Transformation trafo = volume->get_instance_transformation(); Geometry::Transformation trafo = volume->get_instance_transformation() * volume->get_volume_transformation();
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift())); trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift()));
double clp_dist = m_c->object_clipper()->get_position(); double clp_dist = m_c->object_clipper()->get_position();
@ -293,7 +295,7 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos
if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) { if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) {
if (m_hover_id == -1) { if (m_hover_id == -1) {
if (shift_down || alt_down) { if (shift_down || alt_down) {
m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect); m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::EState::Select : GLSelectionRectangle::EState::Deselect);
} }
} }
else { else {
@ -359,7 +361,7 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos
trafo, wxGetApp().plater()->get_camera(), points_inside, trafo, wxGetApp().plater()->get_camera(), points_inside,
m_c->object_clipper()->get_clipping_plane())) m_c->object_clipper()->get_clipping_plane()))
{ {
if (rectangle_status == GLSelectionRectangle::Deselect) if (rectangle_status == GLSelectionRectangle::EState::Deselect)
unselect_point(points_idxs[idx]); unselect_point(points_idxs[idx]);
else else
select_point(points_idxs[idx]); select_point(points_idxs[idx]);

View file

@ -151,18 +151,17 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
#endif // ENABLE_LEGACY_OPENGL_REMOVAL #endif // ENABLE_LEGACY_OPENGL_REMOVAL
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
const Transform3d instance_scaling_matrix_inverse = vol->get_instance_transformation().get_matrix(true, true, false, true).inverse(); Geometry::Transformation transformation(vol->get_instance_transformation().get_matrix() * vol->get_volume_transformation().get_matrix());
const Transform3d& instance_scaling_matrix_inverse = transformation.get_matrix(true, true, false, true).inverse();
#if ENABLE_GL_SHADERS_ATTRIBUTES #if ENABLE_GL_SHADERS_ATTRIBUTES
const Transform3d instance_matrix = Geometry::assemble_transform(m_c->selection_info()->get_sla_shift() * Vec3d::UnitZ()) * vol->get_instance_transformation().get_matrix(); const Transform3d instance_matrix = Geometry::assemble_transform(m_c->selection_info()->get_sla_shift() * Vec3d::UnitZ()) * transformation.get_matrix();
const Camera& camera = wxGetApp().plater()->get_camera(); const Camera& camera = wxGetApp().plater()->get_camera();
const Transform3d& view_matrix = camera.get_view_matrix(); const Transform3d& view_matrix = camera.get_view_matrix();
const Transform3d& projection_matrix = camera.get_projection_matrix(); const Transform3d& projection_matrix = camera.get_projection_matrix();
shader->set_uniform("projection_matrix", projection_matrix); shader->set_uniform("projection_matrix", projection_matrix);
#else #else
const Transform3d& instance_matrix = vol->get_instance_transformation().get_matrix(); const Transform3d& instance_matrix = transformation.get_matrix();
const float z_shift = m_c->selection_info()->get_sla_shift(); const float z_shift = m_c->selection_info()->get_sla_shift();
glsafe(::glPushMatrix()); glsafe(::glPushMatrix());
glsafe(::glTranslated(0.0, 0.0, z_shift)); glsafe(::glTranslated(0.0, 0.0, z_shift));
@ -344,7 +343,7 @@ bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const
auto sel_info = m_c->selection_info(); auto sel_info = m_c->selection_info();
int active_inst = m_c->selection_info()->get_active_instance(); int active_inst = m_c->selection_info()->get_active_instance();
const ModelInstance* mi = sel_info->model_object()->instances[active_inst]; const ModelInstance* mi = sel_info->model_object()->instances[active_inst];
const Transform3d& trafo = mi->get_transformation().get_matrix(); const Transform3d& trafo = mi->get_transformation().get_matrix() * sel_info->model_object()->volumes.front()->get_matrix();
Vec3d transformed_point = trafo * point; Vec3d transformed_point = trafo * point;
transformed_point(2) += sel_info->get_sla_shift(); transformed_point(2) += sel_info->get_sla_shift();
@ -363,7 +362,7 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec
const Camera& camera = wxGetApp().plater()->get_camera(); const Camera& camera = wxGetApp().plater()->get_camera();
const Selection& selection = m_parent.get_selection(); const Selection& selection = m_parent.get_selection();
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
Geometry::Transformation trafo = volume->get_instance_transformation(); Geometry::Transformation trafo = volume->get_instance_transformation() * volume->get_volume_transformation();
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift())); trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_c->selection_info()->get_sla_shift()));
double clp_dist = m_c->object_clipper()->get_position(); double clp_dist = m_c->object_clipper()->get_position();
@ -419,7 +418,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) { if (action == SLAGizmoEventType::LeftDown && (shift_down || alt_down || control_down)) {
if (m_hover_id == -1) { if (m_hover_id == -1) {
if (shift_down || alt_down) { if (shift_down || alt_down) {
m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect); m_selection_rectangle.start_dragging(mouse_position, shift_down ? GLSelectionRectangle::EState::Select : GLSelectionRectangle::EState::Deselect);
} }
} }
else { else {
@ -490,7 +489,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
{ {
if (idx >= orig_pts_num) // this is a cone-base, get index of point it belongs to if (idx >= orig_pts_num) // this is a cone-base, get index of point it belongs to
idx -= orig_pts_num; idx -= orig_pts_num;
if (rectangle_status == GLSelectionRectangle::Deselect) if (rectangle_status == GLSelectionRectangle::EState::Deselect)
unselect_point(points_idxs[idx]); unselect_point(points_idxs[idx]);
else else
select_point(points_idxs[idx]); select_point(points_idxs[idx]);
@ -1238,7 +1237,7 @@ void GLGizmoSlaSupports::get_data_from_backend()
if (po->model_object()->id() == mo->id()) { if (po->model_object()->id() == mo->id()) {
m_normal_cache.clear(); m_normal_cache.clear();
const std::vector<sla::SupportPoint>& points = po->get_support_points(); const std::vector<sla::SupportPoint>& points = po->get_support_points();
auto mat = po->trafo().inverse().cast<float>(); auto mat = (po->trafo() * po->model_object()->volumes.front()->get_transformation().get_matrix()).inverse().cast<float>();
for (unsigned int i=0; i<points.size();++i) for (unsigned int i=0; i<points.size();++i)
m_normal_cache.emplace_back(sla::SupportPoint(mat * points[i].pos, points[i].head_front_radius, points[i].is_new_island)); m_normal_cache.emplace_back(sla::SupportPoint(mat * points[i].pos, points[i].head_front_radius, points[i].is_new_island));

View file

@ -251,7 +251,7 @@ void HollowedMesh::on_update()
const GLCanvas3D* canvas = get_pool()->get_canvas(); const GLCanvas3D* canvas = get_pool()->get_canvas();
const PrintObjects& print_objects = canvas->sla_print()->objects(); const PrintObjects& print_objects = canvas->sla_print()->objects();
const SLAPrintObject* print_object = m_print_object_idx != -1 const SLAPrintObject* print_object = (m_print_object_idx >= 0 && m_print_object_idx < int(print_objects.size()))
? print_objects[m_print_object_idx] ? print_objects[m_print_object_idx]
: nullptr; : nullptr;
@ -276,7 +276,7 @@ void HollowedMesh::on_update()
const TriangleMesh& backend_mesh = print_object->get_mesh_to_slice(); const TriangleMesh& backend_mesh = print_object->get_mesh_to_slice();
if (! backend_mesh.empty()) { if (! backend_mesh.empty()) {
m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh)); m_hollowed_mesh_transformed.reset(new TriangleMesh(backend_mesh));
Transform3d trafo_inv = canvas->sla_print()->sla_trafo(*mo).inverse(); Transform3d trafo_inv = (canvas->sla_print()->sla_trafo(*mo) * print_object->model_object()->volumes.front()->get_transformation().get_matrix()).inverse();
m_hollowed_mesh_transformed->transform(trafo_inv); m_hollowed_mesh_transformed->transform(trafo_inv);
m_drainholes = print_object->model_object()->sla_drain_holes; m_drainholes = print_object->model_object()->sla_drain_holes;
m_old_hollowing_timestamp = timestamp; m_old_hollowing_timestamp = timestamp;
@ -499,7 +499,7 @@ void SupportsClipper::on_update()
const GLCanvas3D* canvas = get_pool()->get_canvas(); const GLCanvas3D* canvas = get_pool()->get_canvas();
const PrintObjects& print_objects = canvas->sla_print()->objects(); const PrintObjects& print_objects = canvas->sla_print()->objects();
const SLAPrintObject* print_object = m_print_object_idx != -1 const SLAPrintObject* print_object = (m_print_object_idx >= 0 && m_print_object_idx < int(print_objects.size()))
? print_objects[m_print_object_idx] ? print_objects[m_print_object_idx]
: nullptr; : nullptr;

View file

@ -5566,7 +5566,7 @@ bool Plater::load_files(const wxArrayString& filenames)
if (!model().objects.empty()) { if (!model().objects.empty()) {
if ((boost::algorithm::iends_with(filename, ".3mf") && !is_project_3mf(it->string())) || if ((boost::algorithm::iends_with(filename, ".3mf") && !is_project_3mf(it->string())) ||
(boost::algorithm::iends_with(filename, ".amf") && !boost::algorithm::iends_with(filename, ".zip.amf"))) (boost::algorithm::iends_with(filename, ".amf") && !boost::algorithm::iends_with(filename, ".zip.amf")))
load_type = LoadType::OpenProject; load_type = LoadType::LoadGeometry;
else { else {
if (wxGetApp().app_config->get("show_drop_project_dialog") == "1") { if (wxGetApp().app_config->get("show_drop_project_dialog") == "1") {
ProjectDropDialog dlg(filename); ProjectDropDialog dlg(filename);
@ -5943,11 +5943,17 @@ void Plater::export_gcode(bool prefer_removable)
fs::path output_path; fs::path output_path;
{ {
std::string ext = default_output_file.extension().string(); #if !ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR
std::string ext = default_output_file.extension().string();
#endif // !ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR
wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _L("Save G-code file as:") : _L("Save SL1 / SL1S file as:"), wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _L("Save G-code file as:") : _L("Save SL1 / SL1S file as:"),
start_dir, start_dir,
from_path(default_output_file.filename()), from_path(default_output_file.filename()),
#if ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR
GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : FT_SL1),
#else
GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : FT_SL1, ext), GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : FT_SL1, ext),
#endif // ENABLE_ALTERNATIVE_FILE_WILDCARDS_GENERATOR
wxFD_SAVE | wxFD_OVERWRITE_PROMPT wxFD_SAVE | wxFD_OVERWRITE_PROMPT
); );
if (dlg.ShowModal() == wxID_OK) { if (dlg.ShowModal() == wxID_OK) {