diff --git a/deps/wxWidgets/wxWidgets.cmake b/deps/wxWidgets/wxWidgets.cmake index 293942568..bf5fd6289 100644 --- a/deps/wxWidgets/wxWidgets.cmake +++ b/deps/wxWidgets/wxWidgets.cmake @@ -10,7 +10,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") endif() set(_unicode_utf8 OFF) -if (UNIX) # wxWidgets will not use char as the underlying type for wxString unless its forced to. +if (UNIX AND NOT APPLE) # wxWidgets will not use char as the underlying type for wxString unless its forced to. set (_unicode_utf8 ON) endif() diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 7f7b589d0..a4146f169 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -15,20 +15,4 @@ sub config { return $self->object->config; } -sub region { - my $self = shift; - my ($region_id) = @_; - - while ($self->region_count <= $region_id) { - $self->add_region($self->object->print->get_region($self->region_count)); - } - - return $self->get_region($region_id); -} - -sub regions { - my ($self) = @_; - return [ map $self->get_region($_), 0..($self->region_count-1) ]; -} - 1; diff --git a/resources/localization/list.txt b/resources/localization/list.txt index d68f99bff..bc8c9f272 100644 --- a/resources/localization/list.txt +++ b/resources/localization/list.txt @@ -17,15 +17,20 @@ src/slic3r/GUI/GalleryDialog.cpp src/slic3r/GUI/GCodeViewer.cpp src/slic3r/GUI/GLCanvas3D.cpp src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +src/slic3r/GUI/Gizmos/GLGizmoCut.hpp src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp src/slic3r/GUI/Gizmos/GLGizmoMove.cpp src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp src/slic3r/GUI/Gizmos/GLGizmoScale.cpp src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp src/slic3r/GUI/Gizmos/GLGizmosManager.cpp src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp diff --git a/resources/profiles/Voron.idx b/resources/profiles/Voron.idx index a593d2fb4..a169eb751 100644 --- a/resources/profiles/Voron.idx +++ b/resources/profiles/Voron.idx @@ -1,2 +1,4 @@ +min_slic3r_version = 2.4.2 +1.0.1 Added 350mm Voron v1 variant. Updated max print heights. Removed redundant v1 volcano nozzle variants. min_slic3r_version = 2.4.0-beta0 1.0.0 Initial version diff --git a/resources/profiles/Voron.ini b/resources/profiles/Voron.ini index cd0fbd2d9..e34da5010 100644 --- a/resources/profiles/Voron.ini +++ b/resources/profiles/Voron.ini @@ -7,7 +7,7 @@ name = Voron # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the PrusaSlicer configuration to be downgraded. -config_version = 1.0.0 +config_version = 1.0.1 # Where to get the updates from? config_update_url = https://files.prusa3d.com/wp-content/uploads/repository/PrusaSlicer-settings-master/live/Voron/ @@ -72,21 +72,30 @@ default_materials = Basic PLA @VORON; Basic PLA VOLCANO @VORON; Basic PET @VORON [printer_model:Voron_v1_250_afterburner] name = Voron v1 250mm3 -variants = 0.4; 0.25; 0.3; 0.5; 0.6; 0.8; volcano 0.6; volcano 0.8; volcano 1.0; volcano 1.2 +variants = 0.4; 0.25; 0.3; 0.5; 0.6; 0.8 technology = FFF family = Voron v1 Afterburner bed_model = printbed-v1-250.stl bed_texture = bedtexture-v1-250.png -default_materials = Basic PLA @VORON; Basic PLA VOLCANO @VORON; Basic PET @VORON; Basic PET VOLCANO @VORON; Basic ABS @VORON; Basic ABS VOLCANO @VORON +default_materials = Basic PLA @VORON; Basic PET @VORON; Basic ABS @VORON [printer_model:Voron_v1_300_afterburner] name = Voron v1 300mm3 -variants = 0.4; 0.25; 0.3; 0.5; 0.6; 0.8; volcano 0.6; volcano 0.8; volcano 1.0; volcano 1.2 +variants = 0.4; 0.25; 0.3; 0.5; 0.6; 0.8 technology = FFF family = Voron v1 Afterburner bed_model = printbed-v1-300.stl bed_texture = bedtexture-v1-300.png -default_materials = Basic PLA @VORON; Basic PLA VOLCANO @VORON; Basic PET @VORON; Basic PET VOLCANO @VORON; Basic ABS @VORON; Basic ABS VOLCANO @VORON +default_materials = Basic PLA @VORON; Basic PET @VORON; Basic ABS @VORON + +[printer_model:Voron_v1_350_afterburner] +name = Voron v1 350mm3 +variants = 0.4; 0.25; 0.3; 0.5; 0.6; 0.8 +technology = FFF +family = Voron v1 Afterburner +bed_model = printbed-v1-350.stl +bed_texture = bedtexture-v2-350.png +default_materials = Basic PLA @VORON; Basic PET @VORON; Basic ABS @VORON [printer_model:Voron_v0_120] name = Voron Zero 120mm3 @@ -239,21 +248,21 @@ retract_speed = 50 [printer:*Voron_v2_250*] inherits = *common* bed_shape = 0x0,250x0,250x250,0x250 -max_print_height = 250 +max_print_height = 230 printer_model = Voron_v2_250 printer_notes = Unoffical profile.\nPRINTER_HAS_BOWDEN\nE3DV6 [printer:*Voron_v2_300*] inherits = *common* bed_shape = 0x0,300x0,300x300,0x300 -max_print_height = 300 +max_print_height = 280 printer_model = Voron_v2_300 printer_notes = Unoffical profile.\nPRINTER_HAS_BOWDEN\nE3DV6 [printer:*Voron_v2_350*] inherits = *common* bed_shape = 0x0,350x0,350x350,0x350 -max_print_height = 350 +max_print_height = 330 printer_model = Voron_v2_350 printer_notes = Unoffical profile.\nPRINTER_HAS_BOWDEN\nE3DV6 @@ -282,10 +291,17 @@ printer_notes = Unoffical profile.\nE3DV6 [printer:*Voron_v1_300_afterburner*] inherits = *common*; *afterburner* bed_shape = 0x0,300x0,300x300,0x300 -max_print_height = 230 +max_print_height = 280 printer_model = Voron_v1_300_afterburner printer_notes = Unoffical profile.\nE3DV6 +[printer:*Voron_v1_350_afterburner*] +inherits = *common*; *afterburner* +bed_shape = 0x0,350x0,350x350,0x350 +max_print_height = 330 +printer_model = Voron_v1_350_afterburner +printer_notes = Unoffical profile.\nE3DV6 + [printer:*Voron_v0_120*] inherits = *common* bed_shape = 0x0,120x0,120x120,0x120 @@ -455,6 +471,24 @@ inherits = *Voron_v1_300_afterburner*; *0.6nozzle* [printer:Voron_v1_300_afterburner 0.8 nozzle] inherits = *Voron_v1_300_afterburner*; *0.8nozzle* +[printer:Voron_v1_350_afterburner 0.25 nozzle] +inherits = *Voron_v1_350_afterburner*; *0.25nozzle* + +[printer:Voron_v1_350_afterburner 0.3 nozzle] +inherits = *Voron_v1_350_afterburner*; *0.3nozzle* + +[printer:Voron_v1_350_afterburner 0.4 nozzle] +inherits = *Voron_v1_350_afterburner*; *0.4nozzle* + +[printer:Voron_v1_350_afterburner 0.5 nozzle] +inherits = *Voron_v1_350_afterburner*; *0.5nozzle* + +[printer:Voron_v1_350_afterburner 0.6 nozzle] +inherits = *Voron_v1_350_afterburner*; *0.6nozzle* + +[printer:Voron_v1_350_afterburner 0.8 nozzle] +inherits = *Voron_v1_350_afterburner*; *0.8nozzle* + [printer:Voron_v2_250_afterburner 0.25 nozzle] inherits = *Voron_v2_250_afterburner*; *0.25nozzle* @@ -652,7 +686,7 @@ fill_angle = 45 fill_density = 15% fill_pattern = gyroid first_layer_acceleration = 1000 -first_layer_height = 75% +first_layer_height = 0.2 first_layer_speed = 30 gap_fill_speed = 40 gcode_comments = 0 diff --git a/resources/profiles/Voron/Voron_v1_250_afterburner_thumbnail.png b/resources/profiles/Voron/Voron_v1_250_afterburner_thumbnail.png index 07c3202ca..2b9339097 100644 Binary files a/resources/profiles/Voron/Voron_v1_250_afterburner_thumbnail.png and b/resources/profiles/Voron/Voron_v1_250_afterburner_thumbnail.png differ diff --git a/resources/profiles/Voron/Voron_v1_300_afterburner_thumbnail.png b/resources/profiles/Voron/Voron_v1_300_afterburner_thumbnail.png index 07c3202ca..2b9339097 100644 Binary files a/resources/profiles/Voron/Voron_v1_300_afterburner_thumbnail.png and b/resources/profiles/Voron/Voron_v1_300_afterburner_thumbnail.png differ diff --git a/resources/profiles/Voron/Voron_v1_350_afterburner_thumbnail.png b/resources/profiles/Voron/Voron_v1_350_afterburner_thumbnail.png index 07c3202ca..2b9339097 100644 Binary files a/resources/profiles/Voron/Voron_v1_350_afterburner_thumbnail.png and b/resources/profiles/Voron/Voron_v1_350_afterburner_thumbnail.png differ diff --git a/src/libslic3r/AABBTreeIndirect.hpp b/src/libslic3r/AABBTreeIndirect.hpp index 3ea18de8d..014b49af1 100644 --- a/src/libslic3r/AABBTreeIndirect.hpp +++ b/src/libslic3r/AABBTreeIndirect.hpp @@ -771,8 +771,8 @@ inline bool is_any_triangle_in_radius( auto distancer = detail::IndexedTriangleSetDistancer { vertices, faces, tree, point }; - size_t hit_idx; - VectorType hit_point = VectorType::Ones() * (std::nan("")); + size_t hit_idx; + VectorType hit_point = VectorType::Ones() * (NaN); if(tree.empty()) { @@ -828,22 +828,22 @@ struct Intersecting> { template auto intersecting(const G &g) { return Intersecting{g}; } -template struct Containing {}; +template struct Within {}; // Intersection predicate specialization for box-box intersections template -struct Containing> { +struct Within> { Eigen::AlignedBox box; - Containing(const Eigen::AlignedBox &bb): box{bb} {} + Within(const Eigen::AlignedBox &bb): box{bb} {} bool operator() (const typename Tree::Node &node) const { - return box.contains(node.bbox); + return node.is_leaf() ? box.contains(node.bbox) : box.intersects(node.bbox); } }; -template auto containing(const G &g) { return Containing{g}; } +template auto within(const G &g) { return Within{g}; } namespace detail { @@ -858,7 +858,7 @@ void traverse_recurse(const Tree &tree, if (!pred(tree.node(idx))) return; if (tree.node(idx).is_leaf()) { - callback(tree.node(idx).idx); + callback(tree.node(idx)); } else { // call this with left and right node idx: diff --git a/src/libslic3r/AStar.hpp b/src/libslic3r/AStar.hpp new file mode 100644 index 000000000..052d0e814 --- /dev/null +++ b/src/libslic3r/AStar.hpp @@ -0,0 +1,182 @@ +#ifndef ASTAR_HPP +#define ASTAR_HPP + +#include "libslic3r/Point.hpp" +#include "libslic3r/MutablePriorityQueue.hpp" + +#include + +namespace Slic3r { namespace astar { + +// Input interface for the Astar algorithm. Specialize this struct for a +// particular type and implement all the 4 methods and specify the Node type +// to register the new type for the astar implementation. +template struct TracerTraits_ +{ + // The type of a node used by this tracer. Usually a point in space. + using Node = typename T::Node; + + // Call fn for every new node reachable from node 'src'. fn should have the + // candidate node as its only argument. + template + static void foreach_reachable(const T &tracer, const Node &src, Fn &&fn) + { + tracer.foreach_reachable(src, fn); + } + + // Get the distance from node 'a' to node 'b'. This is sometimes referred + // to as the g value of a node in AStar context. + static float distance(const T &tracer, const Node &a, const Node &b) + { + return tracer.distance(a, b); + } + + // Get the estimated distance heuristic from node 'n' to the destination. + // This is referred to as the h value in AStar context. + // If node 'n' is the goal, this function should return a negative value. + static float goal_heuristic(const T &tracer, const Node &n) + { + return tracer.goal_heuristic(n); + } + + // Return a unique identifier (hash) for node 'n'. + static size_t unique_id(const T &tracer, const Node &n) + { + return tracer.unique_id(n); + } +}; + +// Helper definition to get the node type of a tracer +template +using TracerNodeT = typename TracerTraits_>::Node; + +namespace detail { +// Helper functions dispatching calls through the TracerTraits_ interface + +template using TracerTraits = TracerTraits_>; + +template +void foreach_reachable(const T &tracer, const TracerNodeT &from, Fn &&fn) +{ + TracerTraits::foreach_reachable(tracer, from, fn); +} + +template +float trace_distance(const T &tracer, const TracerNodeT &a, const TracerNodeT &b) +{ + return TracerTraits::distance(tracer, a, b); +} + +template +float goal_heuristic(const T &tracer, const TracerNodeT &n) +{ + return TracerTraits::goal_heuristic(tracer, n); +} + +template +size_t unique_id(const T &tracer, const TracerNodeT &n) +{ + return TracerTraits::unique_id(tracer, n); +} + +} // namespace astar_detail + +// Run the AStar algorithm on a tracer implementation. +// The 'tracer' argument encapsulates the domain (grid, point cloud, etc...) +// The 'source' argument is the starting node. +// The 'out' argument is the output iterator into which the output nodes are +// written. +// Note that no destination node is given. The tracer's goal_heuristic() method +// should return a negative value if a node is a destination node. +template +bool search_route(const Tracer &tracer, const TracerNodeT &source, It out) +{ + using namespace detail; + + using Node = TracerNodeT; + enum class QueueType { Open, Closed, None }; + + struct QNode // Queue node. Keeps track of scores g, and h + { + Node node; // The actual node itself + QueueType qtype = QueueType::None; // Which queue holds this node + + float g = 0.f, h = 0.f; + float f() const { return g + h; } + }; + + // TODO: apply a linear memory allocator + using QMap = std::unordered_map; + + // The traversed nodes are stored here encapsulated in QNodes + QMap cached_nodes; + + struct LessPred { // Comparison functor needed by MutablePriorityQueue + QMap &m; + bool operator ()(size_t node_a, size_t node_b) { + auto ait = m.find(node_a); + auto bit = m.find(node_b); + assert (ait != m.end() && bit != m.end()); + + return ait->second.f() < bit->second.f(); + } + }; + + auto qopen = + make_mutable_priority_queue([](size_t, size_t){}, + LessPred{cached_nodes}); + + auto qclosed = + make_mutable_priority_queue([](size_t, size_t){}, + LessPred{cached_nodes}); + + QNode initial{source, QueueType::Open}; + cached_nodes.insert({unique_id(tracer, source), initial}); + qopen.push(unique_id(tracer, source)); + + bool goal_reached = false; + + while (!goal_reached && !qopen.empty()) { + size_t q_id = qopen.top(); + qopen.pop(); + QNode q = cached_nodes.at(q_id); + + foreach_reachable(tracer, q.node, [&](const Node &nd) { + if (goal_reached) return goal_reached; + + float h = goal_heuristic(tracer, nd); + if (h < 0.f) { + goal_reached = true; + } else { + float dst = trace_distance(tracer, q.node, nd); + QNode qnd{nd, QueueType::None, q.g + dst, h}; + size_t qnd_id = unique_id(tracer, nd); + + auto it = cached_nodes.find(qnd_id); + + if (it == cached_nodes.end() || + (it->second.qtype != QueueType::None && qnd.f() < it->second.f())) { + qnd.qtype = QueueType::Open; + cached_nodes.insert_or_assign(qnd_id, qnd); + qopen.push(qnd_id); + } + } + + return goal_reached; + }); + + q.qtype = QueueType::Closed; + cached_nodes.insert_or_assign(q_id, q); + qclosed.push(q_id); + + // write the output + *out = q.node; + ++out; + } + + return goal_reached; +} + +}} // namespace Slic3r::astar + +#endif // ASTAR_HPP diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp index 0ff87c88d..47171007a 100644 --- a/src/libslic3r/Arrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -15,7 +15,7 @@ class CircleBed { double radius_; public: - inline CircleBed(): center_(0, 0), radius_(std::nan("")) {} + inline CircleBed(): center_(0, 0), radius_(NaNd) {} explicit inline CircleBed(const Point& c, double r): center_(c), radius_(r) {} inline double radius() const { return radius_; } diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 589be0e0d..059227d8c 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -17,6 +17,7 @@ endif() set(SLIC3R_SOURCES pchheader.cpp pchheader.hpp + AStar.hpp BoundingBox.cpp BoundingBox.hpp BridgeDetector.cpp @@ -219,6 +220,7 @@ set(SLIC3R_SOURCES PrintObject.cpp PrintObjectSlice.cpp PrintRegion.cpp + PointGrid.hpp PNGReadWrite.hpp PNGReadWrite.cpp QuadricEdgeCollapse.cpp diff --git a/src/libslic3r/Execution/Execution.hpp b/src/libslic3r/Execution/Execution.hpp index dcfd86bde..57ad4b41b 100644 --- a/src/libslic3r/Execution/Execution.hpp +++ b/src/libslic3r/Execution/Execution.hpp @@ -30,8 +30,8 @@ template using AsTraits = Traits>; // Each execution policy should declare two types of mutexes. A a spin lock and // a blocking mutex. These types should satisfy the BasicLockable concept. -template using SpinningMutex = typename Traits::SpinningMutex; -template using BlockingMutex = typename Traits::BlockingMutex; +template using SpinningMutex = typename AsTraits::SpinningMutex; +template using BlockingMutex = typename AsTraits::BlockingMutex; // Query the available threads for concurrency. template > diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 52a8a563c..e72a7b914 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -85,7 +85,7 @@ public: virtual ExtrusionEntity* clone() const = 0; // Create a new object, initialize it with this object using the move semantics. virtual ExtrusionEntity* clone_move() = 0; - virtual ~ExtrusionEntity() {} + virtual ~ExtrusionEntity() = default; virtual void reverse() = 0; virtual const Point& first_point() const = 0; virtual const Point& last_point() const = 0; diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index 130bdc83c..16211ca42 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -36,9 +36,13 @@ public: ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : entities(std::move(other.entities)), no_sort(other.no_sort) {} explicit ExtrusionEntityCollection(const ExtrusionPaths &paths); ExtrusionEntityCollection& operator=(const ExtrusionEntityCollection &other); - ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other) - { this->entities = std::move(other.entities); this->no_sort = other.no_sort; return *this; } - ~ExtrusionEntityCollection() { clear(); } + ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other) { + this->clear(); + this->entities = std::move(other.entities); + this->no_sort = other.no_sort; + return *this; + } + ~ExtrusionEntityCollection() override { clear(); } explicit operator ExtrusionPaths() const; bool is_collection() const override { return true; } diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 92e0a698e..1611b2325 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1991,14 +1991,15 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers if (comment == reserved_tag(ETags::Layer_Change)) { ++m_layer_id; if (m_spiral_vase_active) { - if (m_result.moves.empty()) - m_result.spiral_vase_layers.push_back({ m_first_layer_height, { 0, 0 } }); + if (m_result.moves.empty() || m_result.spiral_vase_layers.empty()) + // add a placeholder for layer height. the actual value will be set inside process_G1() method + m_result.spiral_vase_layers.push_back({ FLT_MAX, { 0, 0 } }); else { const size_t move_id = m_result.moves.size() - 1; - if (!m_result.spiral_vase_layers.empty() && m_end_position[Z] == m_result.spiral_vase_layers.back().first) + if (!m_result.spiral_vase_layers.empty()) m_result.spiral_vase_layers.back().second.second = move_id; - else - m_result.spiral_vase_layers.push_back({ static_cast(m_end_position[Z]), { move_id, move_id } }); + // add a placeholder for layer height. the actual value will be set inside process_G1() method + m_result.spiral_vase_layers.push_back({ FLT_MAX, { move_id, move_id } }); } } return; @@ -2828,8 +2829,13 @@ 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]); } - 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; + if (m_spiral_vase_active && !m_result.spiral_vase_layers.empty()) { + if (m_result.spiral_vase_layers.back().first == FLT_MAX && delta_pos[Z] > 0.0) + // replace layer height placeholder with correct value + m_result.spiral_vase_layers.back().first = static_cast(m_end_position[Z]); + if (!m_result.moves.empty()) + m_result.spiral_vase_layers.back().second.second = m_result.moves.size() - 1; + } // store move #if ENABLE_PROCESS_G2_G3_LINES diff --git a/src/libslic3r/KDTreeIndirect.hpp b/src/libslic3r/KDTreeIndirect.hpp index 36a157456..37c10827b 100644 --- a/src/libslic3r/KDTreeIndirect.hpp +++ b/src/libslic3r/KDTreeIndirect.hpp @@ -11,231 +11,276 @@ namespace Slic3r { +enum class VisitorReturnMask : unsigned int { + CONTINUE_LEFT = 1, + CONTINUE_RIGHT = 2, + STOP = 4, +}; + // KD tree for N-dimensional closest point search. template class KDTreeIndirect { public: - static constexpr size_t NumDimensions = ANumDimensions; - using CoordinateFn = ACoordinateFn; - using CoordType = ACoordType; + static constexpr size_t NumDimensions = ANumDimensions; + using CoordinateFn = ACoordinateFn; + using CoordType = ACoordType; // Following could be static constexpr size_t, but that would not link in C++11 enum : size_t { npos = size_t(-1) }; - KDTreeIndirect(CoordinateFn coordinate) : coordinate(coordinate) {} - KDTreeIndirect(CoordinateFn coordinate, std::vector indices) : coordinate(coordinate) { this->build(std::move(indices)); } - KDTreeIndirect(CoordinateFn coordinate, std::vector &&indices) : coordinate(coordinate) { this->build(std::move(indices)); } - KDTreeIndirect(CoordinateFn coordinate, size_t num_indices) : coordinate(coordinate) { this->build(num_indices); } - KDTreeIndirect(KDTreeIndirect &&rhs) : m_nodes(std::move(rhs.m_nodes)), coordinate(std::move(rhs.coordinate)) {} - KDTreeIndirect& operator=(KDTreeIndirect &&rhs) { m_nodes = std::move(rhs.m_nodes); coordinate = std::move(rhs.coordinate); return *this; } - void clear() { m_nodes.clear(); } + KDTreeIndirect(CoordinateFn coordinate) : coordinate(coordinate) {} + KDTreeIndirect(CoordinateFn coordinate, std::vector indices) : coordinate(coordinate) { this->build(indices); } + KDTreeIndirect(CoordinateFn coordinate, size_t num_indices) : coordinate(coordinate) { this->build(num_indices); } + KDTreeIndirect(KDTreeIndirect &&rhs) : m_nodes(std::move(rhs.m_nodes)), coordinate(std::move(rhs.coordinate)) {} + KDTreeIndirect& operator=(KDTreeIndirect &&rhs) { m_nodes = std::move(rhs.m_nodes); coordinate = std::move(rhs.coordinate); return *this; } + void clear() { m_nodes.clear(); } - void build(size_t num_indices) - { - std::vector indices; - indices.reserve(num_indices); - for (size_t i = 0; i < num_indices; ++ i) - indices.emplace_back(i); - this->build(std::move(indices)); - } + void build(size_t num_indices) + { + std::vector indices; + indices.reserve(num_indices); + for (size_t i = 0; i < num_indices; ++ i) + indices.emplace_back(i); + this->build(indices); + } - void build(std::vector &&indices) - { - if (indices.empty()) - clear(); - else { - // Allocate enough memory for a full binary tree. - m_nodes.assign(next_highest_power_of_2(indices.size() + 1), npos); - build_recursive(indices, 0, 0, 0, indices.size() - 1); - } - indices.clear(); - } + void build(std::vector &indices) + { + if (indices.empty()) + clear(); + else { + // Allocate enough memory for a full binary tree. + m_nodes.assign(next_highest_power_of_2(indices.size() + 1), npos); + build_recursive(indices, 0, 0, 0, indices.size() - 1); + } + indices.clear(); + } - enum class VisitorReturnMask : unsigned int - { - CONTINUE_LEFT = 1, - CONTINUE_RIGHT = 2, - STOP = 4, - }; - template - unsigned int descent_mask(const CoordType &point_coord, const CoordType &search_radius, size_t idx, size_t dimension) const - { - CoordType dist = point_coord - this->coordinate(idx, dimension); - return (dist * dist < search_radius + CoordType(EPSILON)) ? - // The plane intersects a hypersphere centered at point_coord of search_radius. - ((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) : - // The plane does not intersect the hypersphere. - (dist > CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT); - } + template + unsigned int descent_mask(const CoordType &point_coord, const CoordType &search_radius, size_t idx, size_t dimension) const + { + CoordType dist = point_coord - this->coordinate(idx, dimension); + return (dist * dist < search_radius + CoordType(EPSILON)) ? + // The plane intersects a hypersphere centered at point_coord of search_radius. + ((unsigned int)(VisitorReturnMask::CONTINUE_LEFT) | (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT)) : + // The plane does not intersect the hypersphere. + (dist > CoordType(0)) ? (unsigned int)(VisitorReturnMask::CONTINUE_RIGHT) : (unsigned int)(VisitorReturnMask::CONTINUE_LEFT); + } - // Visitor is supposed to return a bit mask of VisitorReturnMask. - template - void visit(Visitor &visitor) const - { + // Visitor is supposed to return a bit mask of VisitorReturnMask. + template + void visit(Visitor &visitor) const + { visit_recursive(0, 0, visitor); - } + } - CoordinateFn coordinate; + CoordinateFn coordinate; private: - // Build a balanced tree by splitting the input sequence by an axis aligned plane at a dimension. - void build_recursive(std::vector &input, size_t node, const size_t dimension, const size_t left, const size_t right) - { - if (left > right) - return; + // Build a balanced tree by splitting the input sequence by an axis aligned plane at a dimension. + void build_recursive(std::vector &input, size_t node, const size_t dimension, const size_t left, const size_t right) + { + if (left > right) + return; - assert(node < m_nodes.size()); + assert(node < m_nodes.size()); - if (left == right) { - // Insert a node into the balanced tree. - m_nodes[node] = input[left]; - return; - } + if (left == right) { + // Insert a node into the balanced tree. + m_nodes[node] = input[left]; + return; + } - // Partition the input to left / right pieces of the same length to produce a balanced tree. - size_t center = (left + right) / 2; - partition_input(input, dimension, left, right, center); - // Insert a node into the tree. - m_nodes[node] = input[center]; - // Build up the left / right subtrees. - size_t next_dimension = dimension; - if (++ next_dimension == NumDimensions) - next_dimension = 0; - if (center > left) - build_recursive(input, node * 2 + 1, next_dimension, left, center - 1); - build_recursive(input, node * 2 + 2, next_dimension, center + 1, right); - } + // Partition the input to left / right pieces of the same length to produce a balanced tree. + size_t center = (left + right) / 2; + partition_input(input, dimension, left, right, center); + // Insert a node into the tree. + m_nodes[node] = input[center]; + // Build up the left / right subtrees. + size_t next_dimension = dimension; + if (++ next_dimension == NumDimensions) + next_dimension = 0; + if (center > left) + build_recursive(input, node * 2 + 1, next_dimension, left, center - 1); + build_recursive(input, node * 2 + 2, next_dimension, center + 1, right); + } - // Partition the input m_nodes at "k" and "dimension" using the QuickSelect method: - // https://en.wikipedia.org/wiki/Quickselect - // Items left of the k'th item are lower than the k'th item in the "dimension", - // items right of the k'th item are higher than the k'th item in the "dimension", - void partition_input(std::vector &input, const size_t dimension, size_t left, size_t right, const size_t k) const - { - while (left < right) { - size_t center = (left + right) / 2; - CoordType pivot; - { - // Bubble sort the input[left], input[center], input[right], so that a median of the three values - // will end up in input[center]. - CoordType left_value = this->coordinate(input[left], dimension); - CoordType center_value = this->coordinate(input[center], dimension); - CoordType right_value = this->coordinate(input[right], dimension); - if (left_value > center_value) { - std::swap(input[left], input[center]); - std::swap(left_value, center_value); - } - if (left_value > right_value) { - std::swap(input[left], input[right]); - right_value = left_value; - } - if (center_value > right_value) { - std::swap(input[center], input[right]); - center_value = right_value; - } - pivot = center_value; - } - if (right <= left + 2) - // The interval is already sorted. - break; - size_t i = left; - size_t j = right - 1; - std::swap(input[center], input[j]); - // Partition the set based on the pivot. - for (;;) { - // Skip left points that are already at correct positions. - // Search will certainly stop at position (right - 1), which stores the pivot. - while (this->coordinate(input[++ i], dimension) < pivot) ; - // Skip right points that are already at correct positions. - while (this->coordinate(input[-- j], dimension) > pivot && i < j) ; - if (i >= j) - break; - std::swap(input[i], input[j]); - } - // Restore pivot to the center of the sequence. - std::swap(input[i], input[right - 1]); - // Which side the kth element is in? - if (k < i) - right = i - 1; - else if (k == i) - // Sequence is partitioned, kth element is at its place. - break; - else - left = i + 1; - } - } + // Partition the input m_nodes at "k" and "dimension" using the QuickSelect method: + // https://en.wikipedia.org/wiki/Quickselect + // Items left of the k'th item are lower than the k'th item in the "dimension", + // items right of the k'th item are higher than the k'th item in the "dimension", + void partition_input(std::vector &input, const size_t dimension, size_t left, size_t right, const size_t k) const + { + while (left < right) { + size_t center = (left + right) / 2; + CoordType pivot; + { + // Bubble sort the input[left], input[center], input[right], so that a median of the three values + // will end up in input[center]. + CoordType left_value = this->coordinate(input[left], dimension); + CoordType center_value = this->coordinate(input[center], dimension); + CoordType right_value = this->coordinate(input[right], dimension); + if (left_value > center_value) { + std::swap(input[left], input[center]); + std::swap(left_value, center_value); + } + if (left_value > right_value) { + std::swap(input[left], input[right]); + right_value = left_value; + } + if (center_value > right_value) { + std::swap(input[center], input[right]); + center_value = right_value; + } + pivot = center_value; + } + if (right <= left + 2) + // The interval is already sorted. + break; + size_t i = left; + size_t j = right - 1; + std::swap(input[center], input[j]); + // Partition the set based on the pivot. + for (;;) { + // Skip left points that are already at correct positions. + // Search will certainly stop at position (right - 1), which stores the pivot. + while (this->coordinate(input[++ i], dimension) < pivot) ; + // Skip right points that are already at correct positions. + while (this->coordinate(input[-- j], dimension) > pivot && i < j) ; + if (i >= j) + break; + std::swap(input[i], input[j]); + } + // Restore pivot to the center of the sequence. + std::swap(input[i], input[right - 1]); + // Which side the kth element is in? + if (k < i) + right = i - 1; + else if (k == i) + // Sequence is partitioned, kth element is at its place. + break; + else + left = i + 1; + } + } - template - void visit_recursive(size_t node, size_t dimension, Visitor &visitor) const - { - assert(! m_nodes.empty()); - if (node >= m_nodes.size() || m_nodes[node] == npos) - return; + template + void visit_recursive(size_t node, size_t dimension, Visitor &visitor) const + { + assert(! m_nodes.empty()); + if (node >= m_nodes.size() || m_nodes[node] == npos) + return; - // Left / right child node index. - size_t left = node * 2 + 1; - size_t right = left + 1; - unsigned int mask = visitor(m_nodes[node], dimension); - if ((mask & (unsigned int)VisitorReturnMask::STOP) == 0) { - size_t next_dimension = (++ dimension == NumDimensions) ? 0 : dimension; - if (mask & (unsigned int)VisitorReturnMask::CONTINUE_LEFT) - visit_recursive(left, next_dimension, visitor); - if (mask & (unsigned int)VisitorReturnMask::CONTINUE_RIGHT) - visit_recursive(right, next_dimension, visitor); - } - } + // Left / right child node index. + size_t left = node * 2 + 1; + size_t right = left + 1; + unsigned int mask = visitor(m_nodes[node], dimension); + if ((mask & (unsigned int)VisitorReturnMask::STOP) == 0) { + size_t next_dimension = (++ dimension == NumDimensions) ? 0 : dimension; + if (mask & (unsigned int)VisitorReturnMask::CONTINUE_LEFT) + visit_recursive(left, next_dimension, visitor); + if (mask & (unsigned int)VisitorReturnMask::CONTINUE_RIGHT) + visit_recursive(right, next_dimension, visitor); + } + } - std::vector m_nodes; + std::vector m_nodes; }; // Find a closest point using Euclidian metrics. // Returns npos if not found. -template -size_t find_closest_point(const KDTreeIndirectType &kdtree, const PointType &point, FilterFn filter) +template +std::array find_closest_points( + const KDTreeIndirect &kdtree, + const PointType &point, + FilterFn filter) { - using CoordType = typename KDTreeIndirectType::CoordType; + using Tree = KDTreeIndirect; - struct Visitor { - const KDTreeIndirectType &kdtree; - const PointType &point; - const FilterFn filter; - size_t min_idx = KDTreeIndirectType::npos; - CoordType min_dist = std::numeric_limits::max(); + struct Visitor + { + const Tree &kdtree; + const PointType &point; + const FilterFn filter; - Visitor(const KDTreeIndirectType &kdtree, const PointType &point, FilterFn filter) : kdtree(kdtree), point(point), filter(filter) {} - unsigned int operator()(size_t idx, size_t dimension) { - if (this->filter(idx)) { - auto dist = CoordType(0); - for (size_t i = 0; i < KDTreeIndirectType::NumDimensions; ++ i) { - CoordType d = point[i] - kdtree.coordinate(idx, i); - dist += d * d; - } - if (dist < min_dist) { - min_dist = dist; - min_idx = idx; - } - } - return kdtree.descent_mask(point[dimension], min_dist, idx, dimension); - } - } visitor(kdtree, point, filter); + std::array, K> results; - kdtree.visit(visitor); - return visitor.min_idx; + Visitor(const Tree &kdtree, const PointType &point, FilterFn filter) + : kdtree(kdtree), point(point), filter(filter) + { + results.fill(std::make_pair(Tree::npos, + std::numeric_limits::max())); + } + unsigned int operator()(size_t idx, size_t dimension) + { + if (this->filter(idx)) { + auto dist = CoordT(0); + for (size_t i = 0; i < D; ++i) { + CoordT d = point[i] - kdtree.coordinate(idx, i); + dist += d * d; + } + + auto res = std::make_pair(idx, dist); + auto it = std::lower_bound(results.begin(), results.end(), + res, [](auto &r1, auto &r2) { + return r1.second < r2.second; + }); + + if (it != results.end()) { + std::rotate(it, std::prev(results.end()), results.end()); + *it = res; + } + } + return kdtree.descent_mask(point[dimension], + results.front().second, idx, + dimension); + } + } visitor(kdtree, point, filter); + + kdtree.visit(visitor); + std::array ret; + for (size_t i = 0; i < K; i++) ret[i] = visitor.results[i].first; + + return ret; +} + +template +std::array find_closest_points( + const KDTreeIndirect &kdtree, const PointType &point) +{ + return find_closest_points(kdtree, point, [](size_t) { return true; }); +} + +template +size_t find_closest_point(const KDTreeIndirect &kdtree, + const PointType &point, + FilterFn filter) +{ + return find_closest_points<1>(kdtree, point, filter)[0]; } template size_t find_closest_point(const KDTreeIndirectType& kdtree, const PointType& point) { - return find_closest_point(kdtree, point, [](size_t) { return true; }); + return find_closest_point(kdtree, point, [](size_t) { return true; }); } // Find nearby points (spherical neighbourhood) using Euclidian metrics. template std::vector find_nearby_points(const KDTreeIndirectType &kdtree, const PointType ¢er, - const typename KDTreeIndirectType::CoordType& max_distance, FilterFn filter) - { + const typename KDTreeIndirectType::CoordType& max_distance, FilterFn filter) +{ using CoordType = typename KDTreeIndirectType::CoordType; struct Visitor { @@ -247,7 +292,7 @@ std::vector find_nearby_points(const KDTreeIndirectType &kdtree, const P Visitor(const KDTreeIndirectType &kdtree, const PointType& center, const CoordType &max_distance, FilterFn filter) : - kdtree(kdtree), center(center), max_distance_squared(max_distance*max_distance), filter(filter) { + kdtree(kdtree), center(center), max_distance_squared(max_distance*max_distance), filter(filter) { } unsigned int operator()(size_t idx, size_t dimension) { if (this->filter(idx)) { @@ -260,7 +305,7 @@ std::vector find_nearby_points(const KDTreeIndirectType &kdtree, const P result.push_back(idx); } } - return kdtree.descent_mask(center[dimension], max_distance_squared, idx, dimension); + return kdtree.descent_mask(center[dimension], max_distance_squared, idx, dimension); } } visitor(kdtree, center, max_distance, filter); @@ -270,13 +315,59 @@ std::vector find_nearby_points(const KDTreeIndirectType &kdtree, const P template std::vector find_nearby_points(const KDTreeIndirectType &kdtree, const PointType ¢er, - const typename KDTreeIndirectType::CoordType& max_distance) - { + const typename KDTreeIndirectType::CoordType& max_distance) +{ return find_nearby_points(kdtree, center, max_distance, [](size_t) { return true; }); } +// Find nearby points (spherical neighbourhood) using Euclidian metrics. +template +std::vector find_nearby_points(const KDTreeIndirectType &kdtree, + const PointType &bb_min, + const PointType &bb_max, + FilterFn filter) +{ + struct Visitor { + const KDTreeIndirectType &kdtree; + const PointType &bb_min, &bb_max; + const FilterFn filter; + std::vector result; + + Visitor(const KDTreeIndirectType &kdtree, const PointType& bbmin, const PointType& bbmax, + FilterFn filter) : + kdtree(kdtree), bb_min{bbmin}, bb_max{bbmax}, filter(filter) { + } + unsigned int operator()(size_t idx, size_t dimension) { + unsigned int ret = + static_cast(VisitorReturnMask::CONTINUE_LEFT) | + static_cast(VisitorReturnMask::CONTINUE_RIGHT); + + if (this->filter(idx)) { + PointType p; + bool contains = true; + for (size_t i = 0; i < KDTreeIndirectType::NumDimensions; ++i) { + p(i) = kdtree.coordinate(idx, i); + contains = contains && bb_min(i) <= p(i) && p(i) <= bb_max(i); + } + + if (p(dimension) < bb_min(dimension)) + ret = static_cast(VisitorReturnMask::CONTINUE_RIGHT); + if (p(dimension) > bb_max(dimension)) + ret = static_cast(VisitorReturnMask::CONTINUE_LEFT); + + if (contains) + result.emplace_back(idx); + } + + return ret; + } + } visitor(kdtree, bb_min, bb_max, filter); + + kdtree.visit(visitor); + return visitor.result; +} } // namespace Slic3r diff --git a/src/libslic3r/Optimize/Optimizer.hpp b/src/libslic3r/Optimize/Optimizer.hpp index 8ae55c61c..bf95d9ee0 100644 --- a/src/libslic3r/Optimize/Optimizer.hpp +++ b/src/libslic3r/Optimize/Optimizer.hpp @@ -41,13 +41,13 @@ template using Bounds = std::array; class StopCriteria { // If the absolute value difference between two scores. - double m_abs_score_diff = std::nan(""); + double m_abs_score_diff = NaNd; // If the relative value difference between two scores. - double m_rel_score_diff = std::nan(""); + double m_rel_score_diff = NaNd; // Stop if this value or better is found. - double m_stop_score = std::nan(""); + double m_stop_score = NaNd; // A predicate that if evaluates to true, the optimization should terminate // and the best result found prior to termination should be returned. diff --git a/src/libslic3r/PointGrid.hpp b/src/libslic3r/PointGrid.hpp new file mode 100644 index 000000000..259a2950e --- /dev/null +++ b/src/libslic3r/PointGrid.hpp @@ -0,0 +1,74 @@ +#ifndef POINTGRID_HPP +#define POINTGRID_HPP + +#include +#include +#include + +namespace Slic3r { + +template +class PointGrid { + Vec3i m_size; + std::vector> m_data; + const int XY; + +public: + explicit PointGrid(std::vector> data, const Vec3i &size) + : m_data(std::move(data)), m_size{size}, XY{m_size.x() * m_size.y()} + {} + + const Vec<3, T> & get(size_t idx) const { return m_data[idx]; } + const Vec<3, T> & get(const Vec3i &coord) const + { + return m_data[get_idx(coord)]; + } + + size_t get_idx(const Vec3i &coord) const + { + size_t ret = coord.z() * XY + coord.y() * m_size.x() + coord.x(); + + return ret; + } + + Vec3i get_coord(size_t idx) const { + int iz = idx / XY; + int iy = (idx / m_size.x()) % m_size.y(); + int ix = idx % m_size.x(); + + return {ix, iy, iz}; + } + + const std::vector> & data() const { return m_data; } + size_t point_count() const { return m_data.size(); } + bool empty() const { return m_data.empty(); } +}; + +template +PointGrid point_grid(Ex policy, + const BoundingBox3Base> &bounds, + const Vec<3, CoordT> &stride) +{ + Vec3i numpts = Vec3i::Zero(); + + for (int n = 0; n < 3; ++n) + numpts(n) = (bounds.max(n) - bounds.min(n)) / stride(n); + + std::vector> out(numpts.x() * numpts.y() * numpts.z()); + + size_t XY = numpts[X] * numpts[Y]; + + execution::for_each(policy, size_t(0), out.size(), [&](size_t i) { + int iz = i / XY; + int iy = (i / numpts[X]) % numpts[Y]; + int ix = i % numpts[X]; + + out[i] = Vec<3, CoordT>(ix * stride.x(), iy * stride.y(), iz * stride.z()); + }); + + return PointGrid{std::move(out), numpts}; +} + +} // namespace Slic3r + +#endif // POINTGRID_HPP diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index ab12bc8ce..0c1060b7d 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -220,6 +220,16 @@ public: config.set_deserialize_strict(items); return config; } + static DynamicPrintConfig new_with(const t_config_option_key &opt_key, const std::string &str, bool append = false) { + DynamicPrintConfig config; + config.set_deserialize_strict(opt_key, str, append); + return config; + } + static DynamicPrintConfig new_with(std::initializer_list items) { + DynamicPrintConfig config; + config.set_deserialize_strict(items); + return config; + } static DynamicPrintConfig* new_from_defaults_keys(const std::vector &keys); // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. @@ -1131,6 +1141,8 @@ public: void set(const std::string &opt_key, T value) { m_data.set(opt_key, value, true); this->touch(); } void set_deserialize(const t_config_option_key &opt_key, const std::string &str, ConfigSubstitutionContext &substitution_context, bool append = false) { m_data.set_deserialize(opt_key, str, substitution_context, append); this->touch(); } + void set_deserialize_strict(const t_config_option_key &opt_key, const std::string &str, bool append = false) + { m_data.set_deserialize_strict(opt_key, str, append); this->touch(); } bool erase(const t_config_option_key &opt_key) { bool out = m_data.erase(opt_key); if (out) this->touch(); return out; } // Getters are thread safe. diff --git a/src/libslic3r/SLA/Rotfinder.cpp b/src/libslic3r/SLA/Rotfinder.cpp index 196646dc9..847e638e6 100644 --- a/src/libslic3r/SLA/Rotfinder.cpp +++ b/src/libslic3r/SLA/Rotfinder.cpp @@ -76,7 +76,7 @@ struct Facestats { // Try to guess the number of support points needed to support a mesh double get_misalginment_score(const TriangleMesh &mesh, const Transform3f &tr) { - if (mesh.its.vertices.empty()) return std::nan(""); + if (mesh.its.vertices.empty()) return NaNd; auto accessfn = [&mesh, &tr](size_t fi) { Facestats fc{get_transformed_triangle(mesh, tr, fi)}; @@ -117,7 +117,7 @@ inline double get_supportedness_score(const Facestats &fc) // Try to guess the number of support points needed to support a mesh double get_supportedness_score(const TriangleMesh &mesh, const Transform3f &tr) { - if (mesh.its.vertices.empty()) return std::nan(""); + if (mesh.its.vertices.empty()) return NaNd; auto accessfn = [&mesh, &tr](size_t fi) { Facestats fc{get_transformed_triangle(mesh, tr, fi)}; @@ -149,10 +149,10 @@ float find_ground_level(const TriangleMesh &mesh, return execution::reduce(ex_tbb, size_t(0), vsize, zmin, minfn, accessfn, granularity); } -float get_supportedness_onfloor_score(const TriangleMesh &mesh, - const Transform3f & tr) +double get_supportedness_onfloor_score(const TriangleMesh &mesh, + const Transform3f &tr) { - if (mesh.its.vertices.empty()) return std::nan(""); + if (mesh.its.vertices.empty()) return NaNd; size_t Nthreads = std::thread::hardware_concurrency(); diff --git a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp index aa69fdc77..e4ca3f59c 100644 --- a/src/libslic3r/SLA/SupportTreeBuildsteps.cpp +++ b/src/libslic3r/SLA/SupportTreeBuildsteps.cpp @@ -654,7 +654,7 @@ void SupportTreeBuildsteps::filter() for (const SupportPoint &sp : m_support_pts) { m_thr(); heads.emplace_back( - std::nan(""), + NaNd, sp.head_front_radius, 0., m_cfg.head_penetration_mm, diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index de3e3ae96..c3ec3e399 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -408,9 +408,9 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) AABBTreeIndirect::traverse( tree, AABBTreeIndirect::intersecting(ebb), - [&part_to_drill, &hollowed_mesh](size_t faceid) + [&part_to_drill, &hollowed_mesh](const auto& node) { - part_to_drill.indices.emplace_back(hollowed_mesh.its.indices[faceid]); + part_to_drill.indices.emplace_back(hollowed_mesh.its.indices[node.idx]); }); auto cgal_meshpart = MeshBoolean::cgal::triangle_mesh_to_cgal( @@ -1036,7 +1036,7 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() { // Estimated printing time // A layers count o the highest object if (printer_input.size() == 0) - print_statistics.estimated_print_time = std::nan(""); + print_statistics.estimated_print_time = NaNd; else { print_statistics.estimated_print_time = estim_time; print_statistics.layers_times = layers_times; diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 39a43b752..03c4eaef6 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -347,6 +347,12 @@ public: inline bool empty() const { return size() == 0; } }; +template> +constexpr T NaN = std::numeric_limits::quiet_NaN(); + +constexpr float NaNf = NaN; +constexpr double NaNd = NaN; + } // namespace Slic3r #endif diff --git a/src/slic3r/GUI/DesktopIntegrationDialog.cpp b/src/slic3r/GUI/DesktopIntegrationDialog.cpp index 81c681bc3..7f99a505c 100644 --- a/src/slic3r/GUI/DesktopIntegrationDialog.cpp +++ b/src/slic3r/GUI/DesktopIntegrationDialog.cpp @@ -10,6 +10,7 @@ #include "libslic3r/Platform.hpp" #include "libslic3r/Config.hpp" +#include #include #include #include @@ -503,4 +504,4 @@ DesktopIntegrationDialog::~DesktopIntegrationDialog() } // namespace GUI } // namespace Slic3r -#endif // __linux__ \ No newline at end of file +#endif // __linux__ diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 20a3d8396..dda50ec05 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -1163,7 +1163,7 @@ void Control::draw_ruler(wxDC& dc) } }; - double short_tick = std::nan(""); + double short_tick = NaNd; int tick = 0; double value = 0.0; size_t sequence = 0; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 49bcbce08..b2fa825ac 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -828,7 +828,7 @@ public: class WipeTowerInfo { protected: - Vec2d m_pos = {std::nan(""), std::nan("")}; + Vec2d m_pos = {NaNd, NaNd}; double m_rotation = 0.; BoundingBoxf m_bb; friend class GLCanvas3D; diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index e5778c555..53fa9b0ec 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/src/slic3r/Utils/AppUpdater.cpp b/src/slic3r/Utils/AppUpdater.cpp index 60739ccb3..27f2e34bc 100644 --- a/src/slic3r/Utils/AppUpdater.cpp +++ b/src/slic3r/Utils/AppUpdater.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -242,7 +243,7 @@ boost::filesystem::path AppUpdater::priv::download_file(const DownloadAppData& d tmp_path += format(".%1%%2%", get_current_pid(), ".download"); try { - boost::filesystem::fstream file(tmp_path, std::ios::out | std::ios::binary | std::ios::trunc); + boost::nowide::fstream file(tmp_path.string(), std::ios::out | std::ios::binary | std::ios::trunc); file.write(body.c_str(), body.size()); file.close(); boost::filesystem::rename(tmp_path, dest_path); diff --git a/t/custom_gcode.t b/t/custom_gcode.t deleted file mode 100644 index 1bb52b618..000000000 --- a/t/custom_gcode.t +++ /dev/null @@ -1,210 +0,0 @@ -use Test::More tests => 38; -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use List::Util qw(first); -use Slic3r; -use Slic3r::Test; - -{ - my $config = Slic3r::Config::new_from_defaults; - - my $test = sub { - my ($conf) = @_; - $conf ||= $config; - - my $print = Slic3r::Test::init_print('2x20x10', config => $conf); - - my $last_move_was_z_change = 0; - Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { - my ($self, $cmd, $args, $info) = @_; - - if ($last_move_was_z_change && $cmd ne $config->layer_gcode) { - fail 'custom layer G-code was not applied after Z change'; - } - if (!$last_move_was_z_change && $cmd eq $config->layer_gcode) { - fail 'custom layer G-code was not applied after Z change'; - } - - $last_move_was_z_change = (defined $info->{dist_Z} && $info->{dist_Z} > 0); - }); - - 1; - }; - - $config->set('start_gcode', '_MY_CUSTOM_START_GCODE_'); # to avoid dealing with the nozzle lift in start G-code - $config->set('layer_gcode', '_MY_CUSTOM_LAYER_GCODE_'); - ok $test->(), "custom layer G-code is applied after Z move and before other moves"; -} - -#========================================================== - -{ - my $config = Slic3r::Config::new_from_defaults; - $config->set('output_filename_format', 'ts_[travel_speed]_lh_[layer_height].gcode'); - $config->set('start_gcode', "TRAVEL:[travel_speed] HEIGHT:[layer_height]\n"); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - - my $output_file = $print->print->output_filepath; - my ($t, $h) = map $config->$_, qw(travel_speed layer_height); - ok $output_file =~ /ts_${t}_/, 'print config options are replaced in output filename'; - ok $output_file =~ /lh_$h\./, 'region config options are replaced in output filename'; - - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /TRAVEL:$t/, 'print config options are replaced in custom G-code'; - ok $gcode =~ /HEIGHT:$h/, 'region config options are replaced in custom G-code'; -} - -{ - my $config = Slic3r::Config->new; - $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]); - $config->set('extruder', 2); - $config->set('first_layer_temperature', [200,205]); - - { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /M104 S205 T1/, 'temperature set correctly for non-zero yet single extruder'; - ok $gcode !~ /M104 S\d+ T0/, 'unused extruder correctly ignored'; - } - - $config->set('infill_extruder', 1); - { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /M104 S200 T0/, 'temperature set correctly for first extruder'; - ok $gcode =~ /M104 S205 T1/, 'temperature set correctly for second extruder'; - } - - my @start_gcode = (qq! -;__temp0:[first_layer_temperature_0]__ -;__temp1:[first_layer_temperature_1]__ -;__temp2:[first_layer_temperature_2]__ - !, qq! -;__temp0:{first_layer_temperature[0]}__ -;__temp1:{first_layer_temperature[1]}__ -;__temp2:{first_layer_temperature[2]}__ - !); - my @syntax_description = (' (legacy syntax)', ' (new syntax)'); - for my $i (0, 1) { - $config->set('start_gcode', $start_gcode[$i]); - { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - # we use the [infill_extruder] placeholder to make sure this test doesn't - # catch a false positive caused by the unparsed start G-code option itself - # being embedded in the G-code - ok $gcode =~ /temp0:200/, 'temperature placeholder for first extruder correctly populated' . $syntax_description[$i]; - ok $gcode =~ /temp1:205/, 'temperature placeholder for second extruder correctly populated' . $syntax_description[$i]; - ok $gcode =~ /temp2:200/, 'temperature placeholder for unused extruder populated with first value' . $syntax_description[$i]; - } - } - - $config->set('start_gcode', qq! -;substitution:{if infill_extruder==1}extruder1 - {elsif infill_extruder==2}extruder2 - {else}extruder3{endif} - !); - { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /substitution:extruder1/, 'if / else / endif - first block returned'; - } -} - -{ - my $config = Slic3r::Config::new_from_defaults; - $config->set('before_layer_gcode', ';BEFORE [layer_num]'); - $config->set('layer_gcode', ';CHANGE [layer_num]'); - $config->set('support_material', 1); - $config->set('layer_height', 0.2); - my $print = Slic3r::Test::init_print('overhang', config => $config); - my $gcode = Slic3r::Test::gcode($print); - - my @before = (); - my @change = (); - foreach my $line (split /\R+/, $gcode) { - if ($line =~ /;BEFORE (\d+)/) { - push @before, $1; - } elsif ($line =~ /;CHANGE (\d+)/) { - push @change, $1; - fail 'inconsistent layer_num before and after layer change' - if $1 != $before[-1]; - } - } - is_deeply \@before, \@change, 'layer_num is consistent before and after layer changes'; - ok !defined(first { $change[$_] != $change[$_-1]+1 } 1..$#change), - 'layer_num grows continously'; # i.e. no duplicates or regressions -} - -{ - my $config = Slic3r::Config->new; - $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6,0.6]); - $config->set('start_gcode', qq! -;substitution:{if infill_extruder==1}if block - {elsif infill_extruder==2}elsif block 1 - {elsif infill_extruder==3}elsif block 2 - {elsif infill_extruder==4}elsif block 3 - {else}endif block{endif} - !); - my @returned = ('', 'if block', 'elsif block 1', 'elsif block 2', 'elsif block 3', 'endif block'); - for my $i (1,2,3,4,5) { - $config->set('infill_extruder', $i); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - my $found_other = 0; - for my $j (1,2,3,4,5) { - next if $i == $j; - $found_other = 1 if $gcode =~ /substitution:$returned[$j]/; - } - ok $gcode =~ /substitution:$returned[$i]/, 'if / else / endif - ' . $returned[$i] . ' returned'; - ok !$found_other, 'if / else / endif - only ' . $returned[$i] . ' returned'; - } -} - -{ - my $config = Slic3r::Config->new; - $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]); - $config->set('start_gcode', - ';substitution:{if infill_extruder==1}{if perimeter_extruder==1}block11{else}block12{endif}' . - '{elsif infill_extruder==2}{if perimeter_extruder==1}block21{else}block22{endif}' . - '{else}{if perimeter_extruder==1}block31{else}block32{endif}{endif}:end'); - for my $i (1,2,3) { - $config->set('infill_extruder', $i); - for my $j (1,2) { - $config->set('perimeter_extruder', $j); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /substitution:block$i$j:end/, "two level if / else / endif - block$i$j returned"; - } - } -} - -{ - my $config = Slic3r::Config->new; - $config->set('start_gcode', - ';substitution:{if notes=="MK2"}MK2{elsif notes=="MK3"}MK3{else}MK1{endif}:end'); - for my $printer_name ("MK2", "MK3", "MK1") { - $config->set('notes', $printer_name); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /substitution:$printer_name:end/, "printer name $printer_name matched"; - } -} - -{ - my $config = Slic3r::Config::new_from_defaults; - $config->set('complete_objects', 1); - $config->set('between_objects_gcode', '_MY_CUSTOM_GCODE_'); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 3); - my $gcode = Slic3r::Test::gcode($print); - is scalar(() = $gcode =~ /^_MY_CUSTOM_GCODE_/gm), 2, 'between_objects_gcode is applied correctly'; -} - -__END__ diff --git a/t/print.t b/t/print.t deleted file mode 100644 index 2144e80c1..000000000 --- a/t/print.t +++ /dev/null @@ -1,65 +0,0 @@ -use Test::More tests => 6; -use strict; -use warnings; - -BEGIN { - use FindBin; - use lib "$FindBin::Bin/../lib"; - use local::lib "$FindBin::Bin/../local-lib"; -} - -use List::Util qw(first); -use Slic3r; -use Slic3r::Geometry qw(unscale X Y); -use Slic3r::Test; - -{ - my $config = Slic3r::Config::new_from_defaults; - my $print_center = [100,100]; - my $print = Slic3r::Test::init_print('20mm_cube', config => $config, print_center => $print_center); - my @extrusion_points = (); - Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { - my ($self, $cmd, $args, $info) = @_; - - if ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) { - push @extrusion_points, my $point = Slic3r::Point->new_scale($args->{X}, $args->{Y}); - } - }); - my $bb = Slic3r::Geometry::BoundingBox->new_from_points(\@extrusion_points); - my $center = $bb->center; - ok abs(unscale($center->[X]) - $print_center->[X]) < 0.005, 'print is centered around print_center (X)'; - ok abs(unscale($center->[Y]) - $print_center->[Y]) < 0.005, 'print is centered around print_center (Y)'; -} - -{ - # this represents the aggregate config from presets - my $config = Slic3r::Config::new_from_defaults; - # Define 4 extruders. - $config->set('nozzle_diameter', [0.4, 0.4, 0.4, 0.4]); - - # user adds one object to the plater - my $print = Slic3r::Test::init_print(my $model = Slic3r::Test::model('20mm_cube'), config => $config); - - # user sets a per-region option - my $model2 = $model->clone; - $model2->get_object(0)->config->set('fill_density', 100); - $print->apply($model2, $config); - - is $print->print->regions->[0]->config->fill_density, 100, 'region config inherits model object config'; - - # user exports G-code, thus the default config is reapplied - $model2->get_object(0)->config->erase('fill_density'); - $print->apply($model2, $config); - - is $print->print->regions->[0]->config->fill_density, 20, 'region config is resetted'; - - # user assigns object extruders - $model2->get_object(0)->config->set('extruder', 3); - $model2->get_object(0)->config->set('perimeter_extruder', 2); - $print->apply($model2, $config); - - is $print->print->regions->[0]->config->infill_extruder, 3, 'extruder setting is correctly expanded'; - is $print->print->regions->[0]->config->perimeter_extruder, 2, 'extruder setting does not override explicitely specified extruders'; -} - -__END__ diff --git a/tests/fff_print/test_custom_gcode.cpp b/tests/fff_print/test_custom_gcode.cpp index 0368b9604..37103b316 100644 --- a/tests/fff_print/test_custom_gcode.cpp +++ b/tests/fff_print/test_custom_gcode.cpp @@ -1,8 +1,11 @@ #include +#include #include #include +#include + #include "libslic3r/Config.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/PrintConfig.hpp" @@ -12,11 +15,12 @@ using namespace Slic3r; -#if 0 SCENARIO("Output file format", "[CustomGCode]") { WHEN("output_file_format set") { auto config = Slic3r::DynamicPrintConfig::full_print_config_with({ + { "travel_speed", "130"}, + { "layer_height", "0.4"}, { "output_filename_format", "ts_[travel_speed]_lh_[layer_height].gcode" }, { "start_gcode", "TRAVEL:[travel_speed] HEIGHT:[layer_height]\n" } }); @@ -25,20 +29,9 @@ SCENARIO("Output file format", "[CustomGCode]") Model model; Test::init_print({ Test::TestMesh::cube_2x20x10 }, print, model, config); - std::string output_file = print.output_filepath(); + std::string output_file = print.output_filepath({}, {}); THEN("print config options are replaced in output filename") { - output_file.find(std::string("ts_") + ) - } - my ($t, $h) = map $config->$_, qw(travel_speed layer_height); - ok $output_file =~ /ts_${t}_/, ''; - ok $output_file =~ /lh_$h\./, 'region config options are replaced in output filename'; - - std::string gcode = print.gcode(print); - THEN("print config options are replaced in custom G-code") { - ok $gcode =~ /TRAVEL:$t/, ''; - } - THEN("region config options are replaced in custom G-code") { - ok $gcode =~ /HEIGHT:$h/, ''; + REQUIRE(output_file == "ts_130_lh_0.4.gcode"); } } } @@ -56,165 +49,213 @@ SCENARIO("Custom G-code", "[CustomGCode]") parser.parse_buffer(Slic3r::Test::slice({ Test::TestMesh::cube_2x20x10 }, config), [&last_move_was_z_change, &num_layer_changes_not_applied](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) { - if (line.extruding(self)) { - if (! was_extruding) - seam_points.emplace_back(self.xy_scaled()); - was_extruding = true; - } else if (! line.cmd_is("M73")) { - // skips remaining time lines (M73) - was_extruding = false; - } if (last_move_was_z_change != line.cmd_is("_MY_CUSTOM_LAYER_GCODE_")) ++ num_layer_changes_not_applied; last_move_was_z_change = line.dist_Z(self) > 0; }); - THEN("custom layer G-code is applied after Z move and before other moves"); + THEN("custom layer G-code is applied after Z move and before other moves") { + REQUIRE(num_layer_changes_not_applied == 0); + } }; -{ - my $config = Slic3r::Config->new; - $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]); - $config->set('extruder', 2); - $config->set('first_layer_temperature', [200,205]); + auto config = Slic3r::DynamicPrintConfig::new_with({ + { "nozzle_diameter", { 0.6,0.6,0.6,0.6 } }, + { "extruder", 2 }, + { "first_layer_temperature", { 200, 205 } } + }); + config.normalize_fdm(); + WHEN("Printing with single but non-zero extruder") { + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config); + THEN("temperature set correctly for non-zero yet single extruder") { + REQUIRE(Slic3r::Test::contains(gcode, "\nM104 S205 T1 ;")); + } + THEN("unused extruder correctly ignored") { + REQUIRE(! Slic3r::Test::contains_regex(gcode, "M104 S\\d+ T0")); + } + } + WHEN("Printing with two extruders") { + config.opt_int("infill_extruder") = 1; + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config); + THEN("temperature set correctly for first extruder") { + REQUIRE(Slic3r::Test::contains(gcode, "\nM104 S200 T0 ;")); + }; + THEN("temperature set correctly for second extruder") { + REQUIRE(Slic3r::Test::contains(gcode, "\nM104 S205 T1 ;")); + }; + } + auto test = [](DynamicPrintConfig &config) { + // we use the [infill_extruder] placeholder to make sure this test doesn't + // catch a false positive caused by the unparsed start G-code option itself + // being embedded in the G-code + config.opt_int("infill_extruder") = 1; + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config); + THEN("temperature placeholder for first extruder correctly populated") { + REQUIRE(Slic3r::Test::contains(gcode, "temp0:200")); + } + THEN("temperature placeholder for second extruder correctly populated") { + REQUIRE(Slic3r::Test::contains(gcode, "temp1:205")); + } + THEN("temperature placeholder for unused extruder populated with first value") { + REQUIRE(Slic3r::Test::contains(gcode, "temp2:200")); + } + }; + WHEN("legacy syntax") { + config.set_deserialize_strict("start_gcode", + ";__temp0:[first_layer_temperature_0]__\n" + ";__temp1:[first_layer_temperature_1]__\n" + ";__temp2:[first_layer_temperature_2]__\n"); + test(config); + } + WHEN("new syntax") { + config.set_deserialize_strict("start_gcode", + ";__temp0:{first_layer_temperature[0]}__\n" + ";__temp1:{first_layer_temperature[1]}__\n" + ";__temp2:{first_layer_temperature[2]}__\n"); + test(config); + } + WHEN("Vojtech's syntax") { + config.set_deserialize_strict({ + { "infill_extruder", 1 }, + { "start_gcode", + ";substitution:{if infill_extruder==1}extruder1" + "{elsif infill_extruder==2}extruder2" + "{else}extruder3{endif}" + } + }); + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config); + THEN("if / else / endif - first block returned") { + REQUIRE(Test::contains(gcode, "\n;substitution:extruder1\n")); + } + } + GIVEN("Layer change G-codes") { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /M104 S205 T1/, 'temperature set correctly for non-zero yet single extruder'; - ok $gcode !~ /M104 S\d+ T0/, 'unused extruder correctly ignored'; + auto config = Slic3r::DynamicPrintConfig::full_print_config_with({ + { "before_layer_gcode", ";BEFORE [layer_num]" }, + { "layer_gcode", ";CHANGE [layer_num]" }, + { "support_material", 1 }, + { "layer_height", 0.2 } + }); + WHEN("before and after layer change G-codes set") { + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::overhang }, config); + GCodeReader parser; + std::vector before; + std::vector change; + parser.parse_buffer(gcode, [&before, &change](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line){ + int d; + if (sscanf(line.raw().c_str(), ";BEFORE %d", &d) == 1) + before.emplace_back(d); + else if (sscanf(line.raw().c_str(), ";CHANGE %d", &d) == 1) { + change.emplace_back(d); + if (d != before.back()) + throw std::runtime_error("inconsistent layer_num before and after layer change"); + } + }); + THEN("layer_num is consistent before and after layer changes") { + REQUIRE(before == change); + } + THEN("layer_num grows continously") { + // i.e. no duplicates or regressions + bool successive = true; + for (size_t i = 1; i < change.size(); ++ i) + if (change[i - 1] + 1 != change[i]) + successive = false; + REQUIRE(successive); + } + } } - - $config->set('infill_extruder', 1); + GIVEN("if / elsif / elsif / elsif / else / endif") { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /M104 S200 T0/, 'temperature set correctly for first extruder'; - ok $gcode =~ /M104 S205 T1/, 'temperature set correctly for second extruder'; + auto config = Slic3r::DynamicPrintConfig::new_with({ + { "nozzle_diameter", { 0.6,0.6,0.6,0.6,0.6 } }, + { "start_gcode", + ";substitution:{if infill_extruder==1}if block" + "{elsif infill_extruder==2}elsif block 1" + "{elsif infill_extruder==3}elsif block 2" + "{elsif infill_extruder==4}elsif block 3" + "{else}endif block{endif}" + ":end" + } + }); + std::string returned[] = { "" /* indexed by one based extruder ID */, "if block", "elsif block 1", "elsif block 2", "elsif block 3", "endif block" }; + auto test = [&config, &returned](int i) { + config.set_deserialize_strict({ { "infill_extruder", i } }); + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config); + int found_error = 0; + for (int j = 1; j <= 5; ++ j) + if (i != j && Slic3r::Test::contains(gcode, std::string("substitution:") + returned[j] + ":end")) + // failure + ++ found_error; + THEN(std::string("if / else / endif returned ") + returned[i]) { + REQUIRE(Slic3r::Test::contains(gcode, std::string("substitution:") + returned[i] + ":end")); + } + THEN(std::string("if / else / endif - only ") + returned[i] + "returned") { + REQUIRE(found_error == 0); + } + }; + WHEN("infill_extruder == 1") { test(1); } + WHEN("infill_extruder == 2") { test(2); } + WHEN("infill_extruder == 3") { test(3); } + WHEN("infill_extruder == 4") { test(4); } + WHEN("infill_extruder == 5") { test(5); } } - - my @start_gcode = (qq! -;__temp0:[first_layer_temperature_0]__ -;__temp1:[first_layer_temperature_1]__ -;__temp2:[first_layer_temperature_2]__ - !, qq! -;__temp0:{first_layer_temperature[0]}__ -;__temp1:{first_layer_temperature[1]}__ -;__temp2:{first_layer_temperature[2]}__ - !); - my @syntax_description = (' (legacy syntax)', ' (new syntax)'); - for my $i (0, 1) { - $config->set('start_gcode', $start_gcode[$i]); - { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - # we use the [infill_extruder] placeholder to make sure this test doesn't - # catch a false positive caused by the unparsed start G-code option itself - # being embedded in the G-code - ok $gcode =~ /temp0:200/, 'temperature placeholder for first extruder correctly populated' . $syntax_description[$i]; - ok $gcode =~ /temp1:205/, 'temperature placeholder for second extruder correctly populated' . $syntax_description[$i]; - ok $gcode =~ /temp2:200/, 'temperature placeholder for unused extruder populated with first value' . $syntax_description[$i]; - } + GIVEN("nested if / if / else / endif") { + auto config = Slic3r::DynamicPrintConfig::full_print_config_with({ + { "nozzle_diameter", { 0.6,0.6,0.6,0.6,0.6 } }, + { "start_gcode", + ";substitution:{if infill_extruder==1}{if perimeter_extruder==1}block11{else}block12{endif}" + "{elsif infill_extruder==2}{if perimeter_extruder==1}block21{else}block22{endif}" + "{else}{if perimeter_extruder==1}block31{else}block32{endif}{endif}:end" + } + }); + auto test = [&config](int i) { + config.opt_int("infill_extruder") = i; + int failed = 0; + for (int j = 1; j <= 2; ++ j) { + config.opt_int("perimeter_extruder") = j; + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config); + if (! Slic3r::Test::contains(gcode, std::string("substitution:block") + std::to_string(i) + std::to_string(j) + ":end")) + ++ failed; + } + THEN(std::string("two level if / else / endif - block for infill_extruder ") + std::to_string(i) + "succeeded") { + REQUIRE(failed == 0); + } + }; + WHEN("infill_extruder == 1") { test(1); } + WHEN("infill_extruder == 2") { test(2); } + WHEN("infill_extruder == 3") { test(3); } } - - $config->set('start_gcode', qq! -;substitution:{if infill_extruder==1}extruder1 - {elsif infill_extruder==2}extruder2 - {else}extruder3{endif} - !); - { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /substitution:extruder1/, 'if / else / endif - first block returned'; + GIVEN("printer type in notes") { + auto config = Slic3r::DynamicPrintConfig::new_with({ + { "start_gcode", + ";substitution:{if notes==\"MK2\"}MK2{elsif notes==\"MK3\"}MK3{else}MK1{endif}:end" + } + }); + auto test = [&config](const std::string &printer_name) { + config.set_deserialize_strict("notes", printer_name); + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config); + THEN(std::string("printer name ") + printer_name + " matched") { + REQUIRE(Slic3r::Test::contains(gcode, std::string("substitution:") + printer_name + ":end")); + } + }; + WHEN("printer MK2") { test("MK2"); } + WHEN("printer MK3") { test("MK3"); } + WHEN("printer MK1") { test("MK1"); } } -} - -{ - my $config = Slic3r::Config::new_from_defaults; - $config->set('before_layer_gcode', ';BEFORE [layer_num]'); - $config->set('layer_gcode', ';CHANGE [layer_num]'); - $config->set('support_material', 1); - $config->set('layer_height', 0.2); - my $print = Slic3r::Test::init_print('overhang', config => $config); - my $gcode = Slic3r::Test::gcode($print); - - my @before = (); - my @change = (); - foreach my $line (split /\R+/, $gcode) { - if ($line =~ /;BEFORE (\d+)/) { - push @before, $1; - } elsif ($line =~ /;CHANGE (\d+)/) { - push @change, $1; - fail 'inconsistent layer_num before and after layer change' - if $1 != $before[-1]; - } - } - is_deeply \@before, \@change, 'layer_num is consistent before and after layer changes'; - ok !defined(first { $change[$_] != $change[$_-1]+1 } 1..$#change), - 'layer_num grows continously'; # i.e. no duplicates or regressions -} - -{ - my $config = Slic3r::Config->new; - $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6,0.6]); - $config->set('start_gcode', qq! -;substitution:{if infill_extruder==1}if block - {elsif infill_extruder==2}elsif block 1 - {elsif infill_extruder==3}elsif block 2 - {elsif infill_extruder==4}elsif block 3 - {else}endif block{endif} - !); - my @returned = ('', 'if block', 'elsif block 1', 'elsif block 2', 'elsif block 3', 'endif block'); - for my $i (1,2,3,4,5) { - $config->set('infill_extruder', $i); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - my $found_other = 0; - for my $j (1,2,3,4,5) { - next if $i == $j; - $found_other = 1 if $gcode =~ /substitution:$returned[$j]/; - } - ok $gcode =~ /substitution:$returned[$i]/, 'if / else / endif - ' . $returned[$i] . ' returned'; - ok !$found_other, 'if / else / endif - only ' . $returned[$i] . ' returned'; - } -} - -{ - my $config = Slic3r::Config->new; - $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]); - $config->set('start_gcode', - ';substitution:{if infill_extruder==1}{if perimeter_extruder==1}block11{else}block12{endif}' . - '{elsif infill_extruder==2}{if perimeter_extruder==1}block21{else}block22{endif}' . - '{else}{if perimeter_extruder==1}block31{else}block32{endif}{endif}:end'); - for my $i (1,2,3) { - $config->set('infill_extruder', $i); - for my $j (1,2) { - $config->set('perimeter_extruder', $j); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /substitution:block$i$j:end/, "two level if / else / endif - block$i$j returned"; + GIVEN("sequential print with between_objects_gcode") { + auto config = Slic3r::DynamicPrintConfig::full_print_config_with({ + { "complete_objects", 1 }, + { "between_objects_gcode", "_MY_CUSTOM_GCODE_" } + }); + std::string gcode = Slic3r::Test::slice( + // 3x 20mm box + { Slic3r::Test::TestMesh::cube_20x20x20, Slic3r::Test::TestMesh::cube_20x20x20, Slic3r::Test::TestMesh::cube_20x20x20 }, + config); + THEN("between_objects_gcode is applied correctly") { + const boost::regex expression("^_MY_CUSTOM_GCODE_"); + const std::ptrdiff_t match_count = + std::distance(boost::sregex_iterator(gcode.begin(), gcode.end(), expression), boost::sregex_iterator()); + REQUIRE(match_count == 2); } } } - -{ - my $config = Slic3r::Config->new; - $config->set('start_gcode', - ';substitution:{if notes=="MK2"}MK2{elsif notes=="MK3"}MK3{else}MK1{endif}:end'); - for my $printer_name ("MK2", "MK3", "MK1") { - $config->set('notes', $printer_name); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /substitution:$printer_name:end/, "printer name $printer_name matched"; - } -} - -{ - my $config = Slic3r::Config::new_from_defaults; - $config->set('complete_objects', 1); - $config->set('between_objects_gcode', '_MY_CUSTOM_GCODE_'); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 3); - my $gcode = Slic3r::Test::gcode($print); - is scalar(() = $gcode =~ /^_MY_CUSTOM_GCODE_/gm), 2, 'between_objects_gcode is applied correctly'; -} - -#endif \ No newline at end of file diff --git a/tests/fff_print/test_data.cpp b/tests/fff_print/test_data.cpp index a52583cfc..6d8756057 100644 --- a/tests/fff_print/test_data.cpp +++ b/tests/fff_print/test_data.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include using namespace std; @@ -228,6 +229,7 @@ void init_print(std::vector &&meshes, Slic3r::Print &print, Slic3r object->add_instance(); } arrange_objects(model, InfiniteBed{}, ArrangeParams{ scaled(min_object_distance(config))}); + model.center_instances_around_point({100, 100}); for (ModelObject *mo : model.objects) { mo->ensure_on_bed(); print.auto_assign_extruders(mo); @@ -352,6 +354,17 @@ std::string slice(std::initializer_list meshes, std::initializer_l return gcode(print); } +bool contains(const std::string &data, const std::string &pattern) +{ + return data.find(pattern) != data.npos; +} + +bool contains_regex(const std::string &data, const std::string &pattern) +{ + boost::regex re(pattern); + return boost::regex_match(data, re); +} + } } // namespace Slic3r::Test #include diff --git a/tests/fff_print/test_data.hpp b/tests/fff_print/test_data.hpp index b699e5e4e..fd110caf1 100644 --- a/tests/fff_print/test_data.hpp +++ b/tests/fff_print/test_data.hpp @@ -81,6 +81,9 @@ std::string slice(std::initializer_list meshes, const DynamicPrint std::string slice(std::initializer_list meshes, std::initializer_list config_items, bool comments = false); std::string slice(std::initializer_list meshes, std::initializer_list config_items, bool comments = false); +bool contains(const std::string &data, const std::string &pattern); +bool contains_regex(const std::string &data, const std::string &pattern); + } } // namespace Slic3r::Test diff --git a/tests/fff_print/test_print.cpp b/tests/fff_print/test_print.cpp index a139e4c2b..395f75841 100644 --- a/tests/fff_print/test_print.cpp +++ b/tests/fff_print/test_print.cpp @@ -126,3 +126,62 @@ SCENARIO("Print: Brim generation", "[Print]") { } } } + +SCENARIO("Ported from Perl", "[Print]") { + GIVEN("20mm cube") { + WHEN("Print center is set to 100x100 (test framework default)") { + auto config = Slic3r::DynamicPrintConfig::full_print_config(); + std::string gcode = Slic3r::Test::slice({ TestMesh::cube_20x20x20 }, config); + GCodeReader parser; + Points extrusion_points; + parser.parse_buffer(gcode, [&extrusion_points](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) + { + if (line.cmd_is("G1") && line.extruding(self) && line.dist_XY(self) > 0) + extrusion_points.emplace_back(line.new_XY_scaled(self)); + }); + Vec2d center = unscaled(BoundingBox(extrusion_points).center()); + THEN("print is centered around print_center") { + REQUIRE(is_approx(center.x(), 100.)); + REQUIRE(is_approx(center.y(), 100.)); + } + } + } + GIVEN("Model with multiple objects") { + auto config = Slic3r::DynamicPrintConfig::full_print_config_with({ + { "nozzle_diameter", { 0.4, 0.4, 0.4, 0.4 } } + }); + Print print; + Model model; + Slic3r::Test::init_print({ TestMesh::cube_20x20x20 }, print, model, config); + + // User sets a per-region option, also testing a deep copy of Model. + Model model2(model); + model2.objects.front()->config.set_deserialize_strict("fill_density", "100%"); + WHEN("fill_density overridden") { + print.apply(model2, config); + THEN("region config inherits model object config") { + REQUIRE(print.get_print_region(0).config().fill_density == 100); + } + } + + model2.objects.front()->config.erase("fill_density"); + WHEN("fill_density resetted") { + print.apply(model2, config); + THEN("region config is resetted") { + REQUIRE(print.get_print_region(0).config().fill_density == 20); + } + } + + WHEN("extruder is assigned") { + model2.objects.front()->config.set("extruder", 3); + model2.objects.front()->config.set("perimeter_extruder", 2); + print.apply(model2, config); + THEN("extruder setting is correctly expanded") { + REQUIRE(print.get_print_region(0).config().infill_extruder == 3); + } + THEN("extruder setting does not override explicitely specified extruders") { + REQUIRE(print.get_print_region(0).config().perimeter_extruder == 2); + } + } + } +} diff --git a/tests/fff_print/test_skirt_brim.cpp b/tests/fff_print/test_skirt_brim.cpp index 8f508f323..655517220 100644 --- a/tests/fff_print/test_skirt_brim.cpp +++ b/tests/fff_print/test_skirt_brim.cpp @@ -38,6 +38,7 @@ TEST_CASE("Skirt height is honored", "[Skirt]") { { "support_material_speed", 99 }, // avoid altering speeds unexpectedly { "cooling", false }, + // avoid altering speeds unexpectedly { "first_layer_speed", "100%" } }); diff --git a/tests/libslic3r/CMakeLists.txt b/tests/libslic3r/CMakeLists.txt index c68ba897c..ee9044350 100644 --- a/tests/libslic3r/CMakeLists.txt +++ b/tests/libslic3r/CMakeLists.txt @@ -4,6 +4,7 @@ add_executable(${_TEST_NAME}_tests ${_TEST_NAME}_tests.cpp test_3mf.cpp test_aabbindirect.cpp + test_kdtreeindirect.cpp test_clipper_offset.cpp test_clipper_utils.cpp test_color.cpp @@ -28,6 +29,7 @@ add_executable(${_TEST_NAME}_tests test_triangulation.cpp test_emboss.cpp test_indexed_triangle_set.cpp + test_astar.cpp ../libnest2d/printer_parts.cpp ) diff --git a/tests/libslic3r/test_astar.cpp b/tests/libslic3r/test_astar.cpp new file mode 100644 index 000000000..176d7694a --- /dev/null +++ b/tests/libslic3r/test_astar.cpp @@ -0,0 +1,71 @@ +#include + +#include "libslic3r/BoundingBox.hpp" +#include "libslic3r/AStar.hpp" +#include "libslic3r/Execution/ExecutionSeq.hpp" +#include "libslic3r/PointGrid.hpp" + +using namespace Slic3r; + +struct PointGridTracer { + using Node = size_t; + const PointGrid &grid; + size_t final; + + PointGridTracer(const PointGrid &g, size_t goal) : + grid{g}, final{goal} {} + + template + void foreach_reachable(size_t from, Fn &&fn) const + { + Vec3i from_crd = grid.get_coord(from); + REQUIRE(grid.get_idx(from_crd) == from); + + if (size_t i = grid.get_idx(from_crd + Vec3i{ 1, 0, 0}); i < grid.point_count()) fn(i); + if (size_t i = grid.get_idx(from_crd + Vec3i{ 0, 1, 0}); i < grid.point_count()) fn(i); + if (size_t i = grid.get_idx(from_crd + Vec3i{ 0, 0, 1}); i < grid.point_count()) fn(i); + if (size_t i = grid.get_idx(from_crd + Vec3i{ 1, 1, 0}); i < grid.point_count()) fn(i); + if (size_t i = grid.get_idx(from_crd + Vec3i{ 0, 1, 1}); i < grid.point_count()) fn(i); + if (size_t i = grid.get_idx(from_crd + Vec3i{ 1, 1, 1}); i < grid.point_count()) fn(i); + if (size_t i = grid.get_idx(from_crd + Vec3i{-1, 0, 0}); from_crd.x() > 0 && i < grid.point_count()) fn(i); + if (size_t i = grid.get_idx(from_crd + Vec3i{ 0, -1, 0}); from_crd.y() > 0 && i < grid.point_count()) fn(i); + if (size_t i = grid.get_idx(from_crd + Vec3i{ 0, 0, -1}); from_crd.z() > 0 && i < grid.point_count()) fn(i); + if (size_t i = grid.get_idx(from_crd + Vec3i{-1, -1, 0}); from_crd.x() > 0 && from_crd.y() > 0 && i < grid.point_count()) fn(i); + if (size_t i = grid.get_idx(from_crd + Vec3i{ 0, -1, -1}); from_crd.y() > 0 && from_crd.z() && i < grid.point_count()) fn(i); + if (size_t i = grid.get_idx(from_crd + Vec3i{-1, -1, -1}); from_crd.x() > 0 && from_crd.y() > 0 && from_crd.z() && i < grid.point_count()) fn(i); + + } + + float distance(size_t a, size_t b) const + { + return (grid.get(a) - grid.get(b)).squaredNorm(); + } + + float goal_heuristic(size_t n) const + { + return n == final ? -1.f : (grid.get(n) - grid.get(final)).squaredNorm(); + } + + size_t unique_id(size_t n) const { return n; } +}; + +TEST_CASE("astar algorithm test over 3D point grid", "[AStar]") { + auto vol = BoundingBox3Base{{0.f, 0.f, 0.f}, {1.f, 1.f, 1.f}}; + + auto pgrid = point_grid(ex_seq, vol, {0.1f, 0.1f, 0.1f}); + + size_t target = pgrid.point_count() - 1; + + std::cout << "Tracing route to " << pgrid.get_coord(target).transpose() << std::endl; + PointGridTracer pgt{pgrid, pgrid.point_count() - 1}; + std::vector out; + bool found = astar::search_route(pgt, size_t(0), std::back_inserter(out)); + + std::cout << "Route taken: "; + for (size_t i : out) { + std::cout << "(" << pgrid.get_coord(i).transpose() << ") "; + } + std::cout << std::endl; + + REQUIRE(found); +} diff --git a/tests/libslic3r/test_kdtreeindirect.cpp b/tests/libslic3r/test_kdtreeindirect.cpp new file mode 100644 index 000000000..28d409bbb --- /dev/null +++ b/tests/libslic3r/test_kdtreeindirect.cpp @@ -0,0 +1,142 @@ +#include + +#include "libslic3r/KDTreeIndirect.hpp" +#include "libslic3r/Execution/ExecutionSeq.hpp" +#include "libslic3r/BoundingBox.hpp" +#include "libslic3r/PointGrid.hpp" + +using namespace Slic3r; + +//template +//struct Within { // Wrapper for the `within` predicate that counts calls. + +// kdtree::Within pred; + +// Within(G box): pred{box} {} + +// // Number of times the predicate was called +// mutable size_t call_count = 0; + +// std::pair operator() (const Vec3f &p, size_t dim) +// { +// ++call_count; + +// return pred(p, dim); +// } +//}; + +static double volume(const BoundingBox3Base &box) +{ + auto sz = box.size(); + return sz.x() * sz.y() * sz.z(); +} + +static double volume(const Eigen::AlignedBox &box) +{ + return box.volume(); +} + +TEST_CASE("Test kdtree query for a Box", "[KDTreeIndirect]") +{ + auto vol = BoundingBox3Base{{0.f, 0.f, 0.f}, {10.f, 10.f, 10.f}}; + + auto pgrid = point_grid(ex_seq, vol, Vec3f{0.1f, 0.1f, 0.1f}); + + REQUIRE(!pgrid.empty()); + + auto coordfn = [&pgrid] (size_t i, size_t D) { return pgrid.get(i)(int(D)); }; + KDTreeIndirect<3, float, decltype(coordfn)> tree{coordfn, pgrid.point_count()}; + + std::vector out; + + auto qbox = BoundingBox3Base{Vec3f{0.f, 0.f, 0.f}, Vec3f{.5f, .5f, .5f}}; + + size_t call_count = 0; + out = find_nearby_points(tree, qbox.min, qbox.max, [&call_count](size_t) { + call_count++; + return true; + }); + + // Output shall be non-empty + REQUIRE(!out.empty()); + + std::sort(out.begin(), out.end()); + + // No duplicates allowed in the output + auto it = std::unique(out.begin(), out.end()); + REQUIRE(it == out.end()); + + // Test if inside points are in the output and outside points are not. + bool succ = true; + for (size_t i = 0; i < pgrid.point_count(); ++i) { + auto foundit = std::find(out.begin(), out.end(), i); + bool contains = qbox.contains(pgrid.get(i)); + succ = succ && contains ? foundit != out.end() : foundit == out.end(); + + if (!succ) { + std::cout << "invalid point: " << i << " " << pgrid.get(i).transpose() + << std::endl; + break; + } + } + + REQUIRE(succ); + + // Test for the expected cost of the query. + double gridvolume = volume(vol); + double queryvolume = volume(qbox); + double volratio = (queryvolume / gridvolume); + REQUIRE(call_count < 3 * volratio * pgrid.point_count()); + REQUIRE(call_count < pgrid.point_count()); +} + +//TEST_CASE("Test kdtree query for a Sphere", "[KDTreeIndirect]") { +// auto vol = BoundingBox3Base{{0.f, 0.f, 0.f}, {10.f, 10.f, 10.f}}; + +// auto pgrid = point_grid(ex_seq, vol, Vec3f{0.1f, 0.1f, 0.1f}); + +// REQUIRE(!pgrid.empty()); + +// auto coordfn = [&pgrid] (size_t i, size_t D) { return pgrid.get(i)(int(D)); }; +// kdtree::KDTreeIndirect<3, float, decltype(coordfn)> tree{coordfn, pgrid.point_count()}; + +// std::vector out; + +// auto querysphere = kdtree::Sphere{Vec3f{5.f, 5.f, 5.f}, 2.f}; + +// auto pred = Within(querysphere); + +// kdtree::query(tree, pred, std::back_inserter(out)); + +// // Output shall be non-empty +// REQUIRE(!out.empty()); + +// std::sort(out.begin(), out.end()); + +// // No duplicates allowed in the output +// auto it = std::unique(out.begin(), out.end()); +// REQUIRE(it == out.end()); + +// // Test if inside points are in the output and outside points are not. +// bool succ = true; +// for (size_t i = 0; i < pgrid.point_count(); ++i) { +// auto foundit = std::find(out.begin(), out.end(), i); +// bool contains = (querysphere.center - pgrid.get(i)).squaredNorm() < pred.pred.r2; +// succ = succ && contains ? foundit != out.end() : foundit == out.end(); + +// if (!succ) { +// std::cout << "invalid point: " << i << " " << pgrid.get(i).transpose() +// << std::endl; +// break; +// } +// } + +// REQUIRE(succ); + +// // Test for the expected cost of the query. +// double gridvolume = volume(vol); +// double queryvolume = volume(querysphere); +// double volratio = (queryvolume / gridvolume); +// REQUIRE(pred.call_count < 3 * volratio * pgrid.point_count()); +// REQUIRE(pred.call_count < pgrid.point_count()); +//} diff --git a/tests/libslic3r/test_mutable_priority_queue.cpp b/tests/libslic3r/test_mutable_priority_queue.cpp index 40ed5a158..78c97bc7e 100644 --- a/tests/libslic3r/test_mutable_priority_queue.cpp +++ b/tests/libslic3r/test_mutable_priority_queue.cpp @@ -343,7 +343,7 @@ TEST_CASE("Mutable priority queue - reshedule first", "[MutableSkipHeapPriorityQ TEST_CASE("Mutable priority queue - first pop", "[MutableSkipHeapPriorityQueue]") { struct MyValue{ - int id; + size_t id; float val; }; size_t count = 50000; @@ -356,15 +356,15 @@ TEST_CASE("Mutable priority queue - first pop", "[MutableSkipHeapPriorityQueue]" [](MyValue &l, MyValue &r) { return l.val < r.val; }); q.reserve(count); for (size_t id = 0; id < count; id++) { - MyValue mv; - mv.id = id; - mv.val = rand(); + MyValue mv{ id, rand() / 100.f }; q.push(mv); } MyValue it = q.top(); // copy q.pop(); - bool valid = (it.id != 0) && (idxs[0] < 3 * count); - CHECK(valid); + // is valid id (no initial value default value) + CHECK(it.id != 0); + // is first item in queue valid value + CHECK(idxs[0] != std::numeric_limits::max()); } TEST_CASE("Mutable priority queue complex", "[MutableSkipHeapPriorityQueue]") diff --git a/tests/sla_print/sla_test_utils.cpp b/tests/sla_print/sla_test_utils.cpp index d98a92037..a58b33ca0 100644 --- a/tests/sla_print/sla_test_utils.cpp +++ b/tests/sla_print/sla_test_utils.cpp @@ -386,7 +386,7 @@ long raster_pxsum(const sla::RasterGrayscaleAA &raster) double raster_white_area(const sla::RasterGrayscaleAA &raster) { - if (raster.resolution().pixels() == 0) return std::nan(""); + if (raster.resolution().pixels() == 0) return NaNd; auto res = raster.resolution(); double a = 0; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 97cbdafe3..6b6f6e9be 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -60,8 +60,6 @@ size_t object_count() %code%{ RETVAL = THIS->objects().size(); %}; - PrintRegionPtrs* regions() - %code%{ RETVAL = const_cast(&THIS->print_regions_mutable()); %}; void auto_assign_extruders(ModelObject* model_object); std::string output_filepath(std::string path = "")