mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-09 18:27:00 +00:00
Place the Wipe Tower in the Profile Validator
The validator forces a two-filament print with the prime tower on and slices it at the config default position (x 15, y 220), which lies off any bed shallower than the tower. It calls validate() but slices regardless, so the off-plate tower was exported silently; with the generation-time footprint check it is rejected instead, and 522 of the 1013 printer presets failed the slice check. The validator now positions the tower the way the GUI and CLI do before slicing: beside the centred cube, clear of the edge exclusion strips some beds carry, then pulled inside the printable outline by the tower's own estimated footprint.
This commit is contained in:
@@ -8,7 +8,11 @@
|
|||||||
#define NANOSVGRAST_IMPLEMENTATION
|
#define NANOSVGRAST_IMPLEMENTATION
|
||||||
#include "nanosvg/nanosvgrast.h"
|
#include "nanosvg/nanosvgrast.h"
|
||||||
|
|
||||||
|
#include "libslic3r/BoundingBox.hpp"
|
||||||
#include "libslic3r/GCode.hpp"
|
#include "libslic3r/GCode.hpp"
|
||||||
|
#include "libslic3r/GCode/WipeTower.hpp"
|
||||||
|
#include "libslic3r/GCode/WipeTowerEstimate.hpp"
|
||||||
|
#include "libslic3r/Geometry.hpp"
|
||||||
#include "libslic3r/Preset.hpp"
|
#include "libslic3r/Preset.hpp"
|
||||||
#include "libslic3r/Config.hpp"
|
#include "libslic3r/Config.hpp"
|
||||||
#include "libslic3r/PresetBundle.hpp"
|
#include "libslic3r/PresetBundle.hpp"
|
||||||
@@ -116,15 +120,45 @@ Vec2d printable_area_center(const DynamicPrintConfig &cfg)
|
|||||||
return 0.5 * (lo + hi);
|
return 0.5 * (lo + hi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Put the prime tower where the GUI and CLI would before slicing. The config default (x 15, y 220)
|
||||||
|
// lies off any bed shallower than the tower, and generation rejects an off-plate tower instead of
|
||||||
|
// exporting it. Beside the centred cube, clear of the edge exclusion strips some beds carry, then
|
||||||
|
// pulled inside the printable outline by the tower's own estimated footprint, with a few mm of
|
||||||
|
// clearance so the conflict checker never sees the two touch.
|
||||||
|
void place_wipe_tower(DynamicPrintConfig &cfg, const Vec2d ¢er)
|
||||||
|
{
|
||||||
|
const auto *area = cfg.option<ConfigOptionPoints>("printable_area");
|
||||||
|
if (area == nullptr || area->values.size() < 3)
|
||||||
|
return;
|
||||||
|
const WipeTowerFootprint footprint = estimate_wipe_tower_footprint(cfg, resolve_wipe_tower_type(cfg), {0, 1}, cfg.opt_float("layer_height"), 10.);
|
||||||
|
if (footprint.depth < EPSILON)
|
||||||
|
return;
|
||||||
|
const double margin = WIPE_TOWER_MARGIN + footprint.brim_width;
|
||||||
|
// The position is the tower's own origin; a rotated tower extends from it in another
|
||||||
|
// direction, so place the rotated box's extents rather than the origin.
|
||||||
|
Slic3r::Polygon box({Point::new_scale(0., 0.), Point::new_scale(footprint.width, 0.), Point::new_scale(footprint.width, footprint.depth), Point::new_scale(0., footprint.depth)});
|
||||||
|
box.rotate(Geometry::deg2rad(cfg.opt_float("wipe_tower_rotation_angle")));
|
||||||
|
const BoundingBox local = get_extents(box);
|
||||||
|
const Vec2d lo = unscale(local.min);
|
||||||
|
const Vec2d size = unscale(local.max) - lo;
|
||||||
|
Vec2d pos(center.x() + 5. + margin + 5. - lo.x(), center.y() - size.y() / 2. - lo.y());
|
||||||
|
box.translate(Point::new_scale(pos.x(), pos.y()));
|
||||||
|
const Vec2f move = WipeTower::move_box_inside_polygon(get_extents(box), Polygons{Polygon::new_scale(area->values)}, scaled<coord_t>(margin));
|
||||||
|
pos += move.cast<double>();
|
||||||
|
cfg.option<ConfigOptionFloats>("wipe_tower_x", true)->values = {pos.x()};
|
||||||
|
cfg.option<ConfigOptionFloats>("wipe_tower_y", true)->values = {pos.y()};
|
||||||
|
}
|
||||||
|
|
||||||
// Slice one centered cube that switches from filament 1 to filament 2 partway up, so exactly one
|
// Slice one centered cube that switches from filament 1 to filament 2 partway up, so exactly one
|
||||||
// filament change fires, then export. The change drives the printer's own change_filament_gcode: on a
|
// filament change fires, then export. The change drives the printer's own change_filament_gcode: on a
|
||||||
// single-nozzle machine it rides the AMS prime tower (append_tcr), on a multi-nozzle machine it routes
|
// single-nozzle machine it rides the AMS prime tower (append_tcr), on a multi-nozzle machine it routes
|
||||||
// through the nozzle swap (set_extruder / append_tcr2) - the engine picks the path from the printer's
|
// through the nozzle swap (set_extruder / append_tcr2) - the engine picks the path from the printer's
|
||||||
// topology, so one model covers both. An undefined placeholder in any shipped custom g-code throws
|
// topology, so one model covers both. An undefined placeholder in any shipped custom g-code throws
|
||||||
// Slic3r::PlaceholderParserError from export.
|
// Slic3r::PlaceholderParserError from export.
|
||||||
std::string slice_two_color_cube_and_export(const DynamicPrintConfig &cfg, bool is_bbl)
|
std::string slice_two_color_cube_and_export(DynamicPrintConfig cfg, bool is_bbl)
|
||||||
{
|
{
|
||||||
const Vec2d center = printable_area_center(cfg);
|
const Vec2d center = printable_area_center(cfg);
|
||||||
|
place_wipe_tower(cfg, center);
|
||||||
TriangleMesh m = make_cube(10, 10, 10);
|
TriangleMesh m = make_cube(10, 10, 10);
|
||||||
m.translate(float(center.x() - 5.), float(center.y() - 5.), 0.f);
|
m.translate(float(center.x() - 5.), float(center.y() - 5.), 0.f);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user