diff --git a/src/libslic3r/BeltBrim.cpp b/src/libslic3r/BeltBrim.cpp index 66a271384f..a25659b30e 100644 --- a/src/libslic3r/BeltBrim.cpp +++ b/src/libslic3r/BeltBrim.cpp @@ -376,12 +376,14 @@ static void belt_brim_band_paths(const BeltBrimContext &bc, // overhang outside the belt footprint and land in the brim ring, which the flattened // brim_object_gap - a belt-plane separation - does not cover. // -// SEQUENCING: this reads the layers and support layers of every object on the plate, so -// it must not overlap with another object's support step, which rebuilds them. -// Print::process() therefore generates the belt brims one object after the other once the -// parallel support step is over (PrintObject::generate_belt_brim()), and an object that -// arrives on or leaves the plate invalidates every other object's support step -// (PrintApply.cpp) so the brims are clipped against what is there now. +// SEQUENCING: this reads every object's layers and this object's own support layers. +// Another object's support step shifts that object's layer Z into the object frame for +// the duration of the run (PrintObject::_generate_support_material()), so the brims must +// not overlap with the parallel support step: Print::process() generates them one object +// after the other once that step is over (PrintObject::generate_belt_brim()), and an +// object that arrives on or leaves the plate invalidates the other brim owners' support +// step (PrintApply.cpp) so their brims are clipped against what is there now. Only this +// object's supports are dodged; another object's support at the same Z is not. // `region_bbox` bounds the brim; anything outside it cannot clip a brim line, so whole // objects are skipped without materialising their polygons. On a typical plate the // objects do not overlap and every foreign object drops out here, which matters because diff --git a/src/libslic3r/BeltGCode.cpp b/src/libslic3r/BeltGCode.cpp index 54d9abe30f..e76e76b84f 100644 --- a/src/libslic3r/BeltGCode.cpp +++ b/src/libslic3r/BeltGCode.cpp @@ -25,7 +25,7 @@ void BeltGCode::write_belt_header(GCodeOutputStream &file, const Print &print) // for the physical tilt the G-code viewer uses to enable belt view. file.write_format("; belt_slice_rotation = %s\n", full_cfg.opt_serialize("belt_slice_rotation").c_str()); file.write_format("; belt_slice_rotation_angle = %.1f\n", print.config().belt_slice_rotation_angle.value); - // Machine-frame transform: shear (cot) + scale (1/sin) derived from the belt + // Machine-frame transform: shear (cot) + scale (1/|sin|) derived from the belt // tilt angle (or belt_frame_tilt_angle when decoupled). file.write_format("; belt_frame_tilt_decouple = %d\n", print.config().belt_frame_tilt_decouple.value ? 1 : 0); file.write_format("; belt_frame_tilt_angle = %.1f\n", print.config().belt_frame_tilt_angle.value); diff --git a/src/libslic3r/BeltSliceStrategy.cpp b/src/libslic3r/BeltSliceStrategy.cpp index 8aac729f1b..7b25ac53ec 100644 --- a/src/libslic3r/BeltSliceStrategy.cpp +++ b/src/libslic3r/BeltSliceStrategy.cpp @@ -73,8 +73,7 @@ void BeltSliceStrategy::apply_preslice_transforms(Transform3d &trafo, z_shift.matrix()(2, 3) = z_shift_val; trafo = z_shift * trafo; } - // out_belt_min_z is only meaningful in belt mode; the standalone-remap path - // never reported it. + // out_belt_min_z is only meaningful in belt mode. if (out_belt_min_z && config.belt_printer.value) { *out_belt_min_z = (min_z != std::numeric_limits::max()) ? min_z : 0.; } diff --git a/src/libslic3r/BeltSliceStrategy.hpp b/src/libslic3r/BeltSliceStrategy.hpp index 33a7ed4979..1385818b5c 100644 --- a/src/libslic3r/BeltSliceStrategy.hpp +++ b/src/libslic3r/BeltSliceStrategy.hpp @@ -8,25 +8,23 @@ namespace Slic3r { -// Belt printer / pre-slice transform strategy. +// Belt printer pre-slice transform strategy. // -// Composes, in order, the pre-slice mesh transforms applied before slicing: -// 1. Pre-slice axis remap (standalone — works without belt mode) -// 2. Belt rotation (the sole mesh-side belt transform; shear & scale are a +// Composes, in order, the mesh transforms applied before slicing on a belt printer: +// 1. Belt rotation (the sole mesh-side belt transform; shear & scale are a // g-code-side stage, see MachineFrameTransform) -// 3. Per-object Z-shift that lifts the mesh above the build plate +// 2. Per-object Z-shift that lifts the mesh so its slicing frame starts at the +// belt below its footprint // -// Isolates this belt/remap-specific logic from the generic slicing pipeline in +// Isolates this belt-specific logic from the generic slicing pipeline in // PrintObjectSlice.cpp. class BeltSliceStrategy { public: - // Apply the pre-slice remap + belt rotation + Z-shift to `trafo` in place. - // No-op when neither a remap nor a belt rotation is configured. + // Apply the belt rotation + Z-shift to `trafo` in place. No-op when no belt + // rotation is configured. // - // out_belt_min_z (if non-null) receives the minimum mesh Z after the - // transforms, but only in belt-printer mode — the standalone-remap path - // never reported it. + // out_belt_min_z (if non-null) receives the minimum mesh Z after the transforms. static void apply_preslice_transforms(Transform3d &trafo, const PrintConfig &config, const ModelVolumePtrs &model_volumes, diff --git a/src/libslic3r/BeltTransform.cpp b/src/libslic3r/BeltTransform.cpp index d11e2fccdc..9863e80831 100644 --- a/src/libslic3r/BeltTransform.cpp +++ b/src/libslic3r/BeltTransform.cpp @@ -46,8 +46,6 @@ Transform3d BeltTransformPipeline::build_forward_transform(const PrintConfig &co // ---- Belt floor parameters ------------------------------------------------ -// Shared implementation for both PrintConfig and DynamicPrintConfig. -// Template avoids duplicating the math for the two config types. namespace { // Belt floor in the rotated slicer frame: the image of z_machine = 0 under R. @@ -81,33 +79,15 @@ inline double belt_floor_z(const BeltTransformPipeline::BeltFloorParams &fp, con } -template BeltTransformPipeline::BeltHeightResult compute_belt_height_and_floor_impl( - const Config &config, const BoundingBoxf3 &bb, double original_height) + const PrintConfig &config, const BoundingBoxf3 &bb, double original_height) { BeltTransformPipeline::BeltHeightResult result; result.object_height = original_height; - // Extract the mesh rotation from config (the sole mesh-side belt transform). - BeltRotationAxis rot_axis; - double rot_angle; - - if constexpr (std::is_same_v) { - rot_axis = config.belt_slice_rotation.value; - rot_angle = config.belt_slice_rotation_angle.value; - } else { - // DynamicPrintConfig path - auto get_float = [&](const char *key) { - auto *opt = config.template option(key); - return opt ? opt->value : 0.0; - }; - auto get_rot_axis = [&](const char *key) { - auto *opt = config.template option>(key); - return opt ? opt->value : BeltRotationAxis::None; - }; - rot_axis = get_rot_axis("belt_slice_rotation"); - rot_angle = get_float("belt_slice_rotation_angle"); - } + // The mesh rotation (the sole mesh-side belt transform). + const BeltRotationAxis rot_axis = config.belt_slice_rotation.value; + const double rot_angle = config.belt_slice_rotation_angle.value; bool has_rotation = rot_axis != BeltRotationAxis::None && std::abs(rot_angle) > EPSILON; if (!has_rotation) @@ -159,12 +139,6 @@ BeltTransformPipeline::BeltHeightResult BeltTransformPipeline::compute_belt_heig return compute_belt_height_and_floor_impl(config, bbox, original_height); } -BeltTransformPipeline::BeltHeightResult BeltTransformPipeline::compute_belt_height_and_floor( - const DynamicPrintConfig &config, const BoundingBoxf3 &bbox, double original_height) -{ - return compute_belt_height_and_floor_impl(config, bbox, original_height); -} - bool BeltTransformPipeline::floor_shear(const PrintConfig &config, BeltFloorParams &out) { out = BeltFloorParams{}; diff --git a/src/libslic3r/BeltTransform.hpp b/src/libslic3r/BeltTransform.hpp index 7409fb2c7f..1aae8ba39b 100644 --- a/src/libslic3r/BeltTransform.hpp +++ b/src/libslic3r/BeltTransform.hpp @@ -82,12 +82,6 @@ public: return t; } - static PhysicalTilt physical_tilt(const PrintConfig &config) - { - return physical_tilt(config.belt_slice_rotation.value, - config.belt_slice_rotation_angle.value); - } - // ---- Matrix builders -------------------------------------------------- // Build the 3x3 rotation matrix from belt_slice_rotation* config. @@ -133,11 +127,6 @@ public: static BeltHeightResult compute_belt_height_and_floor( const PrintConfig &config, const BoundingBoxf3 &bbox, double original_height); - - // Overload for DynamicPrintConfig (used by static slicing_parameters). - static BeltHeightResult compute_belt_height_and_floor( - const DynamicPrintConfig &config, const BoundingBoxf3 &bbox, - double original_height); }; } // namespace Slic3r diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 6797f34821..c92ebc8e1c 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2431,6 +2431,9 @@ std::vector>> GCode::collec errors.push_back(e); continue; } + // On a belt an object may be left without a layer to print at all. + if (per_object[i].empty()) + continue; OrderingItem ordering_item; ordering_item.object_idx = i; ordering.reserve(ordering.size() + per_object[i].size()); @@ -3244,6 +3247,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Belt brim apron bands each get their own change_layer() call. for (const BeltBrimBand &band : object->belt_brim_prologue()) zs.push_back(band.print_z); + if (zs.empty()) + continue; std::sort(zs.begin(), zs.end()); //BBS: merge numerically very close Z values. auto end_it = std::unique(zs.begin(), zs.end()); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 4aa96f9a4c..da78aa0a35 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -393,13 +393,6 @@ public: } }; - // Public accessor for the first-layer plane evaluator. Used by - // CoolingBuffer (which is constructed with a GCode reference and needs - // to read the plane for per-segment fan re-evaluation). All other - // first-layer-plane access points (on_first_layer overload, effective - // index helper) are in the protected section since they're called from - // GCode internals only. - protected: class GCodeOutputStream { public: @@ -839,12 +832,6 @@ protected: std::unique_ptr m_cooling_buffer; std::unique_ptr m_spiral_vase; - // First-layer plane evaluator. Constructed once per print from the - // PrintConfig. is_active() == false on non-belt printers and on belt - // printers without a Z-axis shear; in that case all per-path plane - // checks short-circuit to the legacy Layer::id() == 0 path. - // Plate origin, kept so a writer replaced during export can be given it again. - std::unique_ptr m_pressure_equalizer; std::unique_ptr m_pa_processor; diff --git a/src/libslic3r/GCode/BeltBackTransform.hpp b/src/libslic3r/GCode/BeltBackTransform.hpp index 3956450a9f..236ad84f51 100644 --- a/src/libslic3r/GCode/BeltBackTransform.hpp +++ b/src/libslic3r/GCode/BeltBackTransform.hpp @@ -28,9 +28,6 @@ public: // no back-transform is active. Vec3d apply(const Vec3d &pos) const; - // True if a non-identity back-transform is active. - bool is_active() const { return m_active; } - private: bool m_active = false; Transform3d m_inverse = Transform3d::Identity(); diff --git a/src/libslic3r/GCode/BeltKinematics.cpp b/src/libslic3r/GCode/BeltKinematics.cpp index c36c088658..27d5dad105 100644 --- a/src/libslic3r/GCode/BeltKinematics.cpp +++ b/src/libslic3r/GCode/BeltKinematics.cpp @@ -10,12 +10,8 @@ namespace Slic3r { BeltKinematics::BeltKinematics(const PrintConfig &config, bool world_coordinates) : m_world_coordinates(world_coordinates) { - m_back_active = m_back_transform.init_from_config(config); + m_back_transform.init_from_config(config); m_machine_frame.init_from_config(config); - if (m_back_active) - // BeltBackTransform stores the inverse of this; keep the forward so - // to_logical() can reverse the whole chain. - m_back_forward = BeltTransformPipeline::build_forward_transform(config); } Vec3d BeltKinematics::to_machine(const Vec3d &p) const @@ -25,15 +21,6 @@ Vec3d BeltKinematics::to_machine(const Vec3d &p) const return m_machine_frame.apply(after_remap); } -Vec3d BeltKinematics::to_logical(const Vec3d &machine) const -{ - const Vec3d before_frame = m_machine_frame.apply_inverse(machine); - const Vec3d before_remap = this->apply_axis_remap_inverse(before_frame); - if (m_world_coordinates || ! m_back_active) - return before_remap; - return m_back_forward * before_remap; -} - void install_belt_kinematics(GCodeWriter &writer, const PrintConfig &config, bool world_coordinates) { writer.set_kinematics(std::make_unique(config, world_coordinates)); diff --git a/src/libslic3r/GCode/BeltKinematics.hpp b/src/libslic3r/GCode/BeltKinematics.hpp index 8f10bd1074..7dc08635ab 100644 --- a/src/libslic3r/GCode/BeltKinematics.hpp +++ b/src/libslic3r/GCode/BeltKinematics.hpp @@ -31,12 +31,6 @@ public: explicit BeltKinematics(const PrintConfig &config, bool world_coordinates = false); Vec3d to_machine(const Vec3d &p) const override; - Vec3d to_logical(const Vec3d &machine) const override; - // Machine -> build-volume frame. Only the machine-frame shear/scale is undone, - // matching what GCodeProcessor's bounds validation wants. This is deliberately - // NOT to_logical(). - Vec3d to_build_volume(const Vec3d &machine) const override - { return m_machine_frame.apply_inverse(machine); } // A belt writer has always emitted full XYZ on every move, whether or not any // individual stage reports itself active. Making this conditional would change @@ -47,14 +41,9 @@ public: // coordinates and G2/G3 cannot describe it. bool supports_arc_moves() const override { return false; } - bool world_coordinates() const { return m_world_coordinates; } - private: BeltBackTransform m_back_transform; MachineFrameTransform m_machine_frame; - // Forward of what m_back_transform inverts, kept so to_logical() can undo it. - Transform3d m_back_forward { Transform3d::Identity() }; - bool m_back_active { false }; bool m_world_coordinates { false }; }; diff --git a/src/libslic3r/GCode/MachineKinematics.cpp b/src/libslic3r/GCode/MachineKinematics.cpp index 063020fe8a..d9b4b65513 100644 --- a/src/libslic3r/GCode/MachineKinematics.cpp +++ b/src/libslic3r/GCode/MachineKinematics.cpp @@ -17,32 +17,9 @@ Vec3d CartesianKinematics::apply_axis_remap(const Vec3d &pos) const return { remap(m_remap_x), remap(m_remap_y), remap(m_remap_z) }; } -// Inverse of the above. Output axis i is fed by source axis (r_i % 3); walking -// the three outputs therefore fills every source component exactly once, so long -// as the remap is a permutation (which set_axis_remap callers guarantee). -Vec3d CartesianKinematics::apply_axis_remap_inverse(const Vec3d &machine) const -{ - if (!has_axis_remap()) - return machine; - Vec3d out = Vec3d::Zero(); - const int r[3] = { m_remap_x, m_remap_y, m_remap_z }; - for (int i = 0; i < 3; ++i) { - const int axis = r[i] % 3; - if (r[i] < 3) out[axis] = machine[i]; - else if (r[i] < 6) out[axis] = -machine[i]; - else out[axis] = m_build_vol_max[axis] - machine[i]; - } - return out; -} - Vec3d CartesianKinematics::to_machine(const Vec3d &p) const { return this->apply_axis_remap(p); } -Vec3d CartesianKinematics::to_logical(const Vec3d &machine) const -{ - return this->apply_axis_remap_inverse(machine); -} - } // namespace Slic3r diff --git a/src/libslic3r/GCode/MachineKinematics.hpp b/src/libslic3r/GCode/MachineKinematics.hpp index 5c53bd9f58..fc13282998 100644 --- a/src/libslic3r/GCode/MachineKinematics.hpp +++ b/src/libslic3r/GCode/MachineKinematics.hpp @@ -24,17 +24,6 @@ public: // Logical placed point -> emitted machine point. virtual Vec3d to_machine(const Vec3d &p) const = 0; - // Inverse of to_machine(), back to the logical placed frame. Intended for - // consumers that must reconstruct model coordinates from emitted G-code - // (the G-code viewer's upright preview). - virtual Vec3d to_logical(const Vec3d &machine) const = 0; - - // Machine point -> build-volume frame, for bounds validation only. This is - // deliberately NOT to_logical(): the build-volume check wants the physical - // frame the printable area is expressed in, not the model frame. Keeping - // them separate stops the two contracts from being confused. - virtual Vec3d to_build_volume(const Vec3d &machine) const = 0; - // True when a move must emit X, Y and Z because omitting a word would be // wrong under this mapping. Deliberately not called "couples_axes": a pure // axis permutation forces full emission without physically coupling axes. @@ -72,8 +61,6 @@ class CartesianKinematics : public MachineKinematics { public: Vec3d to_machine(const Vec3d &p) const override; - Vec3d to_logical(const Vec3d &machine) const override; - Vec3d to_build_volume(const Vec3d &machine) const override { return machine; } bool must_emit_all_axes() const override { return this->has_axis_remap(); } bool suppress_lift_at_unknown_position() const override { return this->has_axis_remap(); } @@ -93,7 +80,6 @@ public: protected: Vec3d apply_axis_remap(const Vec3d &pos) const; - Vec3d apply_axis_remap_inverse(const Vec3d &pos) const; int m_remap_x { 0 }; int m_remap_y { 1 }; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index c726f7b79f..fae1fcdb0b 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -431,10 +431,7 @@ public: // the whole list to 0-based afterwards). Lowest positive outer_wall_filament_id // over the printing regions, 1 if none is explicitly set. unsigned int belt_brim_filament() const; - // False when this object's instances sit at different points ALONG the belt, which - // would need a separate set of bands each. Public so validate() can explain it. const std::vector& belt_brim_by_layer() const { return m_belt_brim_by_layer; } - const std::vector& belt_brim_areas_by_layer() const { return m_belt_brim_areas_by_layer; } const std::vector& belt_brim_prologue() const { return m_belt_brim_prologue; } void clear_belt_brim(); void set_belt_brim(std::vector &&by_layer, diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 53f93f5b69..096f505542 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -1918,10 +1918,11 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ update_apply_status(this->invalidate_steps({ psSkirtBrim, psWipeTower, psGCodeExport })); // A belt brim is clipped against the other objects on the plate (BeltBrim.cpp, // belt_brim_obstacles), and it is rebuilt with its object's support step: an - // object that arrived or left changes every other object's brim. + // object that arrived or left changes every other brim owner's brim. if ((new_objects || deleted_objects) && m_config.belt_printer.value) for (PrintObject *object : m_objects) - update_apply_status(object->invalidate_step(posSupportMaterial)); + if (object->has_belt_brim()) + update_apply_status(object->invalidate_step(posSupportMaterial)); if (new_objects) update_apply_status(false); print_regions_reshuffled = true; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 6723b903fd..c95ee6f5ce 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -7415,7 +7415,7 @@ void PrintConfigDef::init_fff_params() def->label = L("Machine-frame tilt angle"); def->category = L("Printable space"); def->tooltip = L("Tilt angle (degrees) used to derive the machine-frame shear (cot) and " - "scale (1/sin) applied to G-code. Only used when 'Decouple machine-frame " + "scale (1/|sin|) applied to G-code. Only used when 'Decouple machine-frame " "tilt' is enabled; otherwise the belt tilt angle is used."); def->sidetext = L("°"); def->min = -89.9; @@ -7477,7 +7477,7 @@ void PrintConfigDef::init_fff_params() def->label = L("Belt purge tower width"); def->category = L("Printable space"); def->tooltip = L("Width (machine X, across the belt) of the purge prism that is automatically " - "generated on belt printers when the prime tower is enabled and multiple " + "generated on belt printers when the belt purge tower is enabled and multiple " "filaments are used. Filament-change purging is routed into this prism's " "extrusions instead of a classic wipe tower. Its height is computed " "automatically from the worst-case purge volume per layer: a wider prism " diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 06e71a60b3..facce01787 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -909,19 +909,10 @@ void PrintObject::slice() // Belt floor Z-shift: where is the belt surface in final slicer space? // - // The belt surface is at model_Y=0 (XZ belt plane). After the full - // pipeline (trafo_centered → pre_remap → shear → z_shift), the belt - // surface equation in slicer space is: - // Z_belt = sf * from_axis + belt_surface_z_centered + z_shift_val - // - // belt_surface_z_centered = remapped_bbox.min.z() (the Z position of - // the belt surface in centered-pre-shear slicer space, which is 0 - // without pre-remap but nonzero when e.g. Y↔Z swap shifts the belt - // surface away from Z=0 by the centering offset). - // - // z_shift_val = max(0, -m_belt_min_z) (lifts mesh above Z=0). - // - // So: belt_floor_z_shift = remapped_bb.min.z() + z_shift_val + // The belt surface is the model's Z=0 plane. After the belt rotation and the + // Z-shift it is the plane Z_belt = shear_factor * from_axis + z_shift_val in + // slicer space, with z_shift_val = max(0, -m_belt_min_z), the lift that starts + // the slicing frame at the belt below the footprint. if (std::abs(m_slicing_params.belt_floor_shear_factor) > EPSILON) { double z_shift_val = (m_belt_min_z < 0.) ? -m_belt_min_z : 0.; // The belt surface is at Z=0 in centered slicer space and bb.min.z() is @@ -967,10 +958,10 @@ void PrintObject::slice() if (m_layers.empty()) throw Slic3r::SlicingError(L("No layers were detected. You might want to repair your STL file(s) or check their size or thickness and retry.\n")); - // Belt printer global mode: offset all layer Z values so objects at - // different bed positions print at different heights on the tilted belt. - // This is a post-slicing adjustment — the sliced geometry is identical - // regardless of global mode, only the output Z coordinates change. + // Belt printer: offset all layer Z values so objects at different positions + // along the belt print at different heights on the tilted belt. This is a + // post-slicing adjustment: the sliced geometry is the same, only the output Z + // coordinates change. { const auto &pcfg = this->print()->config(); BOOST_LOG_TRIVIAL(trace) << "Belt global check: belt_printer=" << pcfg.belt_printer.value diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6542abb8f6..fadcd11f56 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -17017,11 +17017,38 @@ void Plater::calib_flowrate(bool is_linear, int pass, InfillPattern pattern) { } +// The belt provini tower (Calib_Params::test_model 1) is one embossed model per +// temperature range. +static std::string belt_temp_tower_asset(const Calib_Params ¶ms) +{ + const int t_start = (int) lround(params.start); + const int t_end = (int) lround(params.end); + return Slic3r::resources_dir() + "/calib/temperature_tower/belt_temp_tower_" + + std::to_string(t_start) + "_" + std::to_string(t_end) + ".stl"; +} + void Plater::calib_temp(const Calib_Params& params) { constexpr double base_temp_tower_nozzle_diameter = 0.4; constexpr double base_temp_tower_block_height = 10.0; constexpr int base_temp_tower_temp_step = 5; + // A belt provini tower exists only for the ranges it was embossed for, and another + // range's model would print numbers that do not match its temperatures. Refuse + // before the current project is replaced. + if (params.mode == CalibMode::Calib_Temp_Tower && params.test_model >= 1) { + const auto &printer_config = wxGetApp().preset_bundle->printers.get_edited_preset().config; + if (printer_config.has("belt_printer") && printer_config.opt_bool("belt_printer") && + ! boost::filesystem::exists(belt_temp_tower_asset(params))) { + MessageDialog dlg(static_cast(wxGetApp().mainframe), + format_wxstr(_L("No belt temperature tower is available for the range %1% to %2% °C. " + "Use a range the tower models cover, for example 230 to 190."), + (int) lround(params.start), (int) lround(params.end)), + _L("Temperature tower"), wxICON_ERROR | wxOK); + dlg.ShowModal(); + return; + } + } + const auto calib_temp_name = _L("Nozzle temperature test"); new_project(false, false, calib_temp_name); wxGetApp().mainframe->select_tab(TAB_ID_PREPARE); @@ -17077,19 +17104,9 @@ void Plater::calib_temp(const Calib_Params& params) { temps.push_back(t); if (temps.empty()) temps.push_back(t_start); - const std::string calib_dir = Slic3r::resources_dir() + "/calib/temperature_tower/"; - std::string asset = calib_dir + "belt_temp_tower_" + std::to_string(t_start) + "_" + std::to_string(t_end) + ".stl"; - if (!boost::filesystem::exists(asset)) { - // The embossed numbers are part of the model, so another model's tower would - // print numbers that do not match its temperatures. - MessageDialog dlg(static_cast(wxGetApp().mainframe), - format_wxstr(_L("No belt temperature tower is available for the range %1% to %2% °C. " - "Use a range the tower models cover, for example 230 to 190."), - t_start, t_end), - _L("Temperature tower"), wxICON_ERROR | wxOK); - dlg.ShowModal(); + const std::string asset = belt_temp_tower_asset(params); + if (!boost::filesystem::exists(asset)) // refused above, before new_project() return; - } if (!add_model(false, asset) || model().objects.empty()) return; diff --git a/tests/fff_print/test_print.cpp b/tests/fff_print/test_print.cpp index a84bc3b451..58f8d12e39 100644 --- a/tests/fff_print/test_print.cpp +++ b/tests/fff_print/test_print.cpp @@ -1381,26 +1381,27 @@ TEST_CASE("Belt G-code has no layer that prints nothing", "[Print][belt][GCode][ size_t layers = 0, empty = 0, total_header = 0, total_count = 0; bool extruded = true; // before the first layer change - std::istringstream in(gc); - std::string line; auto close_layer = [&]() { if (! extruded) ++ empty; }; - while (std::getline(in, line)) { - if (line.rfind(";LAYER_CHANGE", 0) == 0) { + GCodeReader reader; + reader.apply_config(config); + reader.parse_buffer(gc, [&](GCodeReader &self, const GCodeReader::GCodeLine &line) { + const std::string &raw = line.raw(); + if (raw.rfind(";LAYER_CHANGE", 0) == 0) { close_layer(); ++ layers; extruded = false; - } else if (line.rfind("; total layer number: ", 0) == 0) { + } else if (raw.rfind("; total layer number: ", 0) == 0) { // Counted by the G-code processor from the layer changes it saw. - total_header = size_t(std::atoi(line.c_str() + 22)); - } else if (line.rfind("; total layers count = ", 0) == 0) { + total_header = size_t(std::atoi(raw.c_str() + 22)); + } else if (raw.rfind("; total layers count = ", 0) == 0) { // GCode::m_layer_count, counted up front from the objects' layers; it also // drives the M73 progress and the total_layer_count placeholder. - total_count = size_t(std::atoi(line.c_str() + 23)); - } else if (! extruded && line.rfind("G1 ", 0) == 0 && line.find('E') != std::string::npos - && (line.find('X') != std::string::npos || line.find('Y') != std::string::npos)) { + total_count = size_t(std::atoi(raw.c_str() + 23)); + } else if (! extruded && line.extruding(self) && line.dist_XY(self) > EPSILON) { + // Material laid down along a move: a wipe or an unretraction does not count. extruded = true; } - } + }); close_layer(); INFO("layers " << layers << ", header " << total_header << ", count " << total_count << ", layers without extrusion " << empty);