mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-02 15:52:14 +00:00
Belt/Standard calibrations (#54)
Enables supported printing of standard Orcaslicer calibration profiles. * Build 2 Checkpoint * fix support generation wedge, ghost layers * flip cornering tests 180 deg to waste less supports * fix row spacing on the flow ratio calibrations * more testing, this didn't fix anything * switched rotation tools, same issue * fixed Z-offset issues * add rest of PA features, may look a bit weird on a belt * make temp towers work * re-enable spiral on calibrations that want it * Final cleanup pre-PR and community testing
This commit is contained in:
@@ -38,7 +38,11 @@ void BeltGCodeWriter::set_machine_frame_transform(const PrintConfig &config)
|
|||||||
Vec3d BeltGCodeWriter::to_machine_coords(const Vec3d &pos) const
|
Vec3d BeltGCodeWriter::to_machine_coords(const Vec3d &pos) const
|
||||||
{
|
{
|
||||||
// Step 1+2: To Cartesian (back_transform + axis_remap).
|
// Step 1+2: To Cartesian (back_transform + axis_remap).
|
||||||
Vec3d after_back = m_belt_back_transform.apply(pos);
|
// In world-coordinates mode (PA line / PA pattern calibration) the input
|
||||||
|
// already describes a point relative to the belt surface, so the
|
||||||
|
// slicer->world back-transform is skipped and only the machine kinematics
|
||||||
|
// (axis remap + frame shear/scale) are applied.
|
||||||
|
Vec3d after_back = m_world_coordinates ? pos : m_belt_back_transform.apply(pos);
|
||||||
Vec3d result = apply_axis_remap(after_back);
|
Vec3d result = apply_axis_remap(after_back);
|
||||||
Vec3d after_remap = result;
|
Vec3d after_remap = result;
|
||||||
// Step 3: Machine-frame transform (belt frame tilt) applied LAST so it acts
|
// Step 3: Machine-frame transform (belt frame tilt) applied LAST so it acts
|
||||||
|
|||||||
@@ -23,6 +23,14 @@ public:
|
|||||||
void set_machine_frame_transform(const PrintConfig &config);
|
void set_machine_frame_transform(const PrintConfig &config);
|
||||||
Vec3d to_machine_coords(const Vec3d &pos) const;
|
Vec3d to_machine_coords(const Vec3d &pos) const;
|
||||||
|
|
||||||
|
// World-coordinates mode: incoming coordinates are treated as points
|
||||||
|
// relative to the physical belt surface (X across, Y along the belt,
|
||||||
|
// Z height above it) instead of slicing-frame coordinates — the
|
||||||
|
// slicer->world back-transform is skipped. Used by the PA line / PA
|
||||||
|
// pattern calibration generators, whose logical bed coordinates describe
|
||||||
|
// first-layer drawings on the build surface.
|
||||||
|
void set_world_coordinates(bool enable) { m_world_coordinates = enable; }
|
||||||
|
|
||||||
// First-layer plane: when set to a non-null active evaluator, travel
|
// First-layer plane: when set to a non-null active evaluator, travel
|
||||||
// speed selection consults the plane per-move and uses
|
// speed selection consults the plane per-move and uses
|
||||||
// initial_layer_travel_speed for points within first_layer_height_mm
|
// initial_layer_travel_speed for points within first_layer_height_mm
|
||||||
@@ -47,6 +55,7 @@ protected:
|
|||||||
private:
|
private:
|
||||||
BeltBackTransform m_belt_back_transform;
|
BeltBackTransform m_belt_back_transform;
|
||||||
MachineFrameTransform m_machine_frame_transform;
|
MachineFrameTransform m_machine_frame_transform;
|
||||||
|
bool m_world_coordinates = false;
|
||||||
// Borrowed pointer; lifetime owned by GCode. null = inactive.
|
// Borrowed pointer; lifetime owned by GCode. null = inactive.
|
||||||
const FirstLayerPlane *m_first_layer_plane = nullptr;
|
const FirstLayerPlane *m_first_layer_plane = nullptr;
|
||||||
double m_first_layer_thickness_mm = 0.;
|
double m_first_layer_thickness_mm = 0.;
|
||||||
|
|||||||
@@ -1834,6 +1834,12 @@ std::vector<GCode::LayerToPrint> GCode::collect_layers_to_print(const PrintObjec
|
|||||||
last_extrusion_layer = &layers_to_print.back();
|
last_extrusion_layer = &layers_to_print.back();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ORCA-Belt: objects print at their position along the belt, so the first
|
||||||
|
// extrusions legitimately start far above Z=0. Drop the spurious
|
||||||
|
// "empty layers from the bed" range while keeping genuine mid-print gaps.
|
||||||
|
if (skip_empty_first_layer && !warning_ranges.empty() && warning_ranges.front().first == 0.)
|
||||||
|
warning_ranges.erase(warning_ranges.begin());
|
||||||
|
|
||||||
if (! warning_ranges.empty()) {
|
if (! warning_ranges.empty()) {
|
||||||
std::string warning;
|
std::string warning;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
@@ -3307,7 +3313,16 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
|||||||
|
|
||||||
pa_test.set_speed(fast_speed, slow_speed);
|
pa_test.set_speed(fast_speed, slow_speed);
|
||||||
pa_test.draw_numbers() = print.calib_params().print_numbers;
|
pa_test.draw_numbers() = print.calib_params().print_numbers;
|
||||||
|
|
||||||
|
// ORCA-Belt: the PA line test draws directly on the build surface in
|
||||||
|
// logical bed coordinates — on a belt printer that surface is the
|
||||||
|
// belt plane, not the slicing plane.
|
||||||
|
BeltGCodeWriter* belt_writer = dynamic_cast<BeltGCodeWriter*>(m_writer.get());
|
||||||
|
if (belt_writer != nullptr)
|
||||||
|
belt_writer->set_world_coordinates(true);
|
||||||
gcode += pa_test.generate_test(params.start, params.step, std::llround(std::ceil((params.end - params.start) / params.step)) + 1);
|
gcode += pa_test.generate_test(params.start, params.step, std::llround(std::ceil((params.end - params.start) / params.step)) + 1);
|
||||||
|
if (belt_writer != nullptr)
|
||||||
|
belt_writer->set_world_coordinates(false);
|
||||||
|
|
||||||
file.write(gcode);
|
file.write(gcode);
|
||||||
} else {
|
} else {
|
||||||
@@ -4653,31 +4668,57 @@ LayerResult GCode::process_layer(
|
|||||||
gcode += ";_SET_FAN_SPEED_CHANGING_LAYER\n";
|
gcode += ";_SET_FAN_SPEED_CHANGING_LAYER\n";
|
||||||
|
|
||||||
//Calibration Layer-specific GCode
|
//Calibration Layer-specific GCode
|
||||||
|
// ORCA-Belt: on belt printers the calibration object is counter-rotated to
|
||||||
|
// stand upright in slicing space on top of a support wedge, so its first
|
||||||
|
// layer starts above Z=0 (at its position along the belt) with support-only
|
||||||
|
// layers below it. Reference the per-height calibration bands to the bottom
|
||||||
|
// of the object so they keep their designed meaning; on regular printers
|
||||||
|
// the object base is at Z=0 and calib_z == print_z.
|
||||||
|
double calib_z = print_z;
|
||||||
|
if (m_config.belt_printer.value && print.calib_mode() != CalibMode::Calib_None) {
|
||||||
|
// Skip empty ghost layers the grid may produce below the object.
|
||||||
|
for (const Layer* l : layer.object()->layers())
|
||||||
|
if (!l->lslices.empty()) {
|
||||||
|
calib_z = print_z - (l->print_z - l->height);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
switch (print.calib_mode()) {
|
switch (print.calib_mode()) {
|
||||||
case CalibMode::Calib_PA_Tower: {
|
case CalibMode::Calib_PA_Tower: {
|
||||||
gcode += writer().set_pressure_advance(print.calib_params().start + static_cast<int>(print_z) * print.calib_params().step);
|
gcode += writer().set_pressure_advance(print.calib_params().start + static_cast<int>(std::max(0.0, calib_z)) * print.calib_params().step);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CalibMode::Calib_Temp_Tower: {
|
case CalibMode::Calib_Temp_Tower: {
|
||||||
gcode += writer().set_temperature(this->interpolate_value_across_layers(static_cast<float>(print.calib_params().start), static_cast<float>(print.calib_params().end), 5.0f));
|
// ORCA-Belt: the sectioned variant prints each temperature as its
|
||||||
|
// own object in native belt orientation, with the temperature
|
||||||
|
// encoded in the object name ("temp_230") — step per object
|
||||||
|
// instead of ramping per layer band.
|
||||||
|
int sectioned_temp = 0;
|
||||||
|
if (m_config.belt_printer.value &&
|
||||||
|
sscanf(layer.object()->model_object()->name.c_str(), "temp_%d", §ioned_temp) == 1 &&
|
||||||
|
sectioned_temp > 0) {
|
||||||
|
gcode += writer().set_temperature(static_cast<unsigned int>(sectioned_temp));
|
||||||
|
} else {
|
||||||
|
gcode += writer().set_temperature(this->interpolate_value_across_layers(static_cast<float>(print.calib_params().start), static_cast<float>(print.calib_params().end), 5.0f));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CalibMode::Calib_VFA_Tower: {
|
case CalibMode::Calib_VFA_Tower: {
|
||||||
auto _speed = print.calib_params().start + std::floor(print_z / 5.0) * print.calib_params().step;
|
auto _speed = print.calib_params().start + std::floor(std::max(0.0, calib_z) / 5.0) * print.calib_params().step;
|
||||||
m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloat(std::round(_speed)));
|
m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloat(std::round(_speed)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CalibMode::Calib_Vol_speed_Tower: {
|
case CalibMode::Calib_Vol_speed_Tower: {
|
||||||
auto _speed = print.calib_params().start + print_z * print.calib_params().step;
|
auto _speed = print.calib_params().start + std::max(0.0, calib_z) * print.calib_params().step;
|
||||||
m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloat(std::round(_speed)));
|
m_calib_config.set_key_value("outer_wall_speed", new ConfigOptionFloat(std::round(_speed)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CalibMode::Calib_Retraction_tower: {
|
case CalibMode::Calib_Retraction_tower: {
|
||||||
auto _length = print.calib_params().start + std::floor(std::max(0.0,print_z-0.4)) * print.calib_params().step;
|
auto _length = print.calib_params().start + std::floor(std::max(0.0,calib_z-0.4)) * print.calib_params().step;
|
||||||
DynamicConfig _cfg;
|
DynamicConfig _cfg;
|
||||||
_cfg.set_key_value("retraction_length", new ConfigOptionFloats{_length});
|
_cfg.set_key_value("retraction_length", new ConfigOptionFloats{_length});
|
||||||
writer().config.apply(_cfg);
|
writer().config.apply(_cfg);
|
||||||
sprintf(buf, "; Calib_Retraction_tower: Z_HEIGHT: %g, length:%g\n", print_z, _length);
|
sprintf(buf, "; Calib_Retraction_tower: Z_HEIGHT: %g, length:%g\n", calib_z, _length);
|
||||||
gcode += buf;
|
gcode += buf;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -7415,25 +7456,39 @@ std::string GCode::extrusion_role_to_string_for_parser(const ExtrusionRole & rol
|
|||||||
// Step = 0 means gradual interpolation finishing at last value.
|
// Step = 0 means gradual interpolation finishing at last value.
|
||||||
float GCode::interpolate_value_across_layers(float start_value, float end_value, float step) const
|
float GCode::interpolate_value_across_layers(float start_value, float end_value, float step) const
|
||||||
{
|
{
|
||||||
if (m_layer_index <= 1) {
|
float ratio;
|
||||||
|
// ORCA-Belt: counter-rotated calibration objects stand on a support wedge,
|
||||||
|
// so support-only layers below the object would stretch a layer-index
|
||||||
|
// interpolation. Use the object's own Z span instead, so the value ramps
|
||||||
|
// across the test geometry only.
|
||||||
|
if (m_config.belt_printer.value && m_layer != nullptr && !m_layer->object()->layers().empty()) {
|
||||||
|
const auto& layers = m_layer->object()->layers();
|
||||||
|
// Skip empty ghost layers the grid may produce below the object.
|
||||||
|
double z_min = layers.front()->print_z;
|
||||||
|
for (const Layer* l : layers)
|
||||||
|
if (!l->lslices.empty()) { z_min = l->print_z; break; }
|
||||||
|
const double z_max = layers.back()->print_z;
|
||||||
|
if (m_layer->print_z <= z_min + EPSILON || z_max - z_min <= EPSILON)
|
||||||
|
return start_value;
|
||||||
|
ratio = float(std::min(1.0, (m_layer->print_z - z_min) / (z_max - z_min)));
|
||||||
|
} else if (m_layer_index <= 1) {
|
||||||
return start_value;
|
return start_value;
|
||||||
|
} else {
|
||||||
|
ratio = m_layer_index / (m_layer_count - 1.f);
|
||||||
}
|
}
|
||||||
else {
|
bool use_steps = step > 0.f;
|
||||||
bool use_steps = step > 0.f;
|
if (use_steps) {
|
||||||
if (use_steps) {
|
if (start_value > end_value) {
|
||||||
if (start_value > end_value) {
|
start_value += step;
|
||||||
start_value += step;
|
} else {
|
||||||
} else {
|
end_value += step;
|
||||||
end_value += step;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
float ratio = m_layer_index / (m_layer_count - 1.f);
|
|
||||||
float value = start_value + ratio * (end_value - start_value);
|
|
||||||
if (use_steps) {
|
|
||||||
value = trunc(value / step) * step;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
float value = start_value + ratio * (end_value - start_value);
|
||||||
|
if (use_steps) {
|
||||||
|
value = trunc(value / step) * step;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string encodeBase64(uint64_t value)
|
std::string encodeBase64(uint64_t value)
|
||||||
|
|||||||
@@ -966,7 +966,14 @@ void PrintObject::slice()
|
|||||||
// correct machine-frame coordinates whether or not a global mode is active.
|
// correct machine-frame coordinates whether or not a global mode is active.
|
||||||
double belt_surface_z = BeltTransformPipeline::has_preslice_remap(pcfg)
|
double belt_surface_z = BeltTransformPipeline::has_preslice_remap(pcfg)
|
||||||
? BeltTransformPipeline::remap_bbox(*this->model_object(), pcfg).min.z() : 0.;
|
? BeltTransformPipeline::remap_bbox(*this->model_object(), pcfg).min.z() : 0.;
|
||||||
double belt_z_shift = m_belt_min_z - belt_surface_z;
|
// The compensation must mirror the Z-shift actually applied, which
|
||||||
|
// is max(0, -m_belt_min_z): when the transformed mesh starts ABOVE
|
||||||
|
// slicer Z=0 (m_belt_min_z > 0 — possible for counter-rotated or
|
||||||
|
// asymmetric geometry whose centered-frame minimum lands positive)
|
||||||
|
// no lift was applied, and an unclamped m_belt_min_z here would
|
||||||
|
// leak straight into the layer Z values, floating the whole object
|
||||||
|
// off the belt by exactly that amount.
|
||||||
|
double belt_z_shift = std::min(m_belt_min_z, 0.) - belt_surface_z;
|
||||||
double global_z_offset = belt_z_shift;
|
double global_z_offset = belt_z_shift;
|
||||||
|
|
||||||
// Centering correction: trafo_centered pretranslates by
|
// Centering correction: trafo_centered pretranslates by
|
||||||
|
|||||||
@@ -1801,8 +1801,7 @@ void TreeSupport::generate()
|
|||||||
{
|
{
|
||||||
BeltFloorContext ctx;
|
BeltFloorContext ctx;
|
||||||
if (ctx.init(m_slicing_params, *m_print_config)
|
if (ctx.init(m_slicing_params, *m_print_config)
|
||||||
&& m_print_config->belt_support_floor_mode.value == BeltSupportFloorMode::GeneratorOnly
|
&& m_print_config->belt_support_floor_mode.value == BeltSupportFloorMode::GeneratorOnly) {
|
||||||
&& m_object->support_layer_count() > 0) {
|
|
||||||
const auto &sp = m_slicing_params;
|
const auto &sp = m_slicing_params;
|
||||||
// Find the lowest non-empty, non-brim support layer.
|
// Find the lowest non-empty, non-brim support layer.
|
||||||
ExPolygons source_areas;
|
ExPolygons source_areas;
|
||||||
@@ -1830,16 +1829,61 @@ void TreeSupport::generate()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ORCA-Belt calibration: a counter-rotated calibration object
|
||||||
|
// stands on a support wedge that lies entirely below the object's
|
||||||
|
// first layer, where the tree pipeline has no layers at all — so
|
||||||
|
// no support content can exist yet. Seed the extension directly
|
||||||
|
// from the floating portion of the first layer (anything more
|
||||||
|
// than one layer height above the belt floor). For objects whose
|
||||||
|
// first layer rests on the belt the floating region is empty and
|
||||||
|
// behavior is unchanged.
|
||||||
|
double first_z = m_object->support_layer_count() > 0 ? m_object->get_support_layer(0)->print_z : 0.;
|
||||||
|
bool seeded = false;
|
||||||
|
if (source_areas.empty() && m_object_config->enable_support.value && !m_object->layers().empty()) {
|
||||||
|
// The layer grid may start with an empty ghost layer just below
|
||||||
|
// the object (grid rounding against the belt global Z offset) —
|
||||||
|
// anchor the seed to the first layer that has geometry. Object
|
||||||
|
// layer print_z and the floor plane are both in the globally
|
||||||
|
// offset frame here (belt_floor_z_shift was adjusted alongside
|
||||||
|
// the layer Z values in PrintObject::slice()).
|
||||||
|
const Layer *first_layer = nullptr;
|
||||||
|
for (const Layer *l : m_object->layers())
|
||||||
|
if (!l->lslices_extrudable.empty()) { first_layer = l; break; }
|
||||||
|
if (first_layer != nullptr) {
|
||||||
|
ExPolygons floating = diff_ex(first_layer->lslices_extrudable,
|
||||||
|
ctx.surface_polygon(first_layer->bottom_z() - first_layer->height));
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "[BELT-CALIB] wedge seed: obj=" << m_object->model_object()->name
|
||||||
|
<< " bottom_z=" << first_layer->bottom_z() << " floating=" << floating.size();
|
||||||
|
if (!floating.empty()) {
|
||||||
|
source_areas = std::move(floating);
|
||||||
|
first_z = first_layer->bottom_z();
|
||||||
|
seeded = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!source_areas.empty()) {
|
if (!source_areas.empty()) {
|
||||||
BoundingBoxf3 bb = belt_remapped_bbox(*m_object->model_object(), m_object->print()->config());
|
BoundingBoxf3 bb = belt_remapped_bbox(*m_object->model_object(), m_object->print()->config());
|
||||||
double from_extent = std::abs(bb.min(ctx.from_axis()));
|
double from_extent = std::abs(bb.min(ctx.from_axis()));
|
||||||
double bb_min_z = std::abs(bb.min.z());
|
double bb_min_z = std::abs(bb.min.z());
|
||||||
double first_z = m_object->get_support_layer(0)->print_z;
|
|
||||||
// Depth = from-axis extent + pre-shear bbox Z offset (ensure_on_bed
|
// Depth = from-axis extent + pre-shear bbox Z offset (ensure_on_bed
|
||||||
// distance) + 10mm safety margin. The 10mm is a bodge to avoid
|
// distance) + 10mm safety margin. The 10mm is a bodge to avoid
|
||||||
// small cutoff artifacts — ideally computed exactly from belt geometry.
|
// small cutoff artifacts — ideally computed exactly from belt geometry.
|
||||||
double extra_depth = std::min(from_extent + bb_min_z + 10., std::max(0., first_z));
|
double extra_depth = std::min(from_extent + bb_min_z + 10., std::max(0., first_z));
|
||||||
|
if (seeded) {
|
||||||
|
// Seeded wedge: the depth is known exactly — down to the lowest
|
||||||
|
// belt-floor point under the floating footprint. The bbox
|
||||||
|
// heuristic above under-estimates it for meshes centered
|
||||||
|
// around their origin (every object loaded through the GUI).
|
||||||
|
double min_floor = first_z;
|
||||||
|
for (const ExPolygon &ep : source_areas)
|
||||||
|
for (const Point &pt : ep.contour.points)
|
||||||
|
min_floor = std::min(min_floor, ctx.floor_print_z(pt));
|
||||||
|
extra_depth = std::min(std::max(0., first_z), first_z - min_floor + 2.);
|
||||||
|
}
|
||||||
int num_extra = std::max(0, (int)std::ceil(extra_depth / sp.layer_height));
|
int num_extra = std::max(0, (int)std::ceil(extra_depth / sp.layer_height));
|
||||||
|
// Seeded wedge: top layers become a dense support interface so the
|
||||||
|
// object's floating first layer bridges a roof, not sparse infill.
|
||||||
|
const int interface_layers = seeded ? std::max(0, m_object_config->support_interface_top_layers.value) : 0;
|
||||||
ExPolygons prev_areas = source_areas;
|
ExPolygons prev_areas = source_areas;
|
||||||
// Build belt extension layers (lowest Z first).
|
// Build belt extension layers (lowest Z first).
|
||||||
SupportLayerPtrs belt_ext_layers;
|
SupportLayerPtrs belt_ext_layers;
|
||||||
@@ -1853,8 +1897,15 @@ void TreeSupport::generate()
|
|||||||
sl->base_areas = clipped;
|
sl->base_areas = clipped;
|
||||||
// Populate area_groups — generate_toolpaths() iterates these,
|
// Populate area_groups — generate_toolpaths() iterates these,
|
||||||
// not base_areas directly.
|
// not base_areas directly.
|
||||||
for (auto &expoly : sl->base_areas)
|
// Note: base areas only get infill when support_base_pattern
|
||||||
sl->area_groups.emplace_back(&expoly, SupportLayer::BaseType, 0);
|
// is explicitly set (with the default pattern tree bases are
|
||||||
|
// walls-only) — the calibration flow sets rectilinear.
|
||||||
|
const bool roof = i <= interface_layers;
|
||||||
|
for (auto &expoly : sl->base_areas) {
|
||||||
|
sl->area_groups.emplace_back(&expoly, roof ? SupportLayer::RoofType : SupportLayer::BaseType, 0);
|
||||||
|
if (roof)
|
||||||
|
sl->area_groups.back().interface_id = i & 1;
|
||||||
|
}
|
||||||
sl->lslices = clipped;
|
sl->lslices = clipped;
|
||||||
sl->lslices_bboxes.reserve(clipped.size());
|
sl->lslices_bboxes.reserve(clipped.size());
|
||||||
for (const ExPolygon &ep : clipped)
|
for (const ExPolygon &ep : clipped)
|
||||||
@@ -1865,6 +1916,9 @@ void TreeSupport::generate()
|
|||||||
if (!belt_ext_layers.empty()) {
|
if (!belt_ext_layers.empty()) {
|
||||||
auto &sl_vec = m_object->support_layers();
|
auto &sl_vec = m_object->support_layers();
|
||||||
sl_vec.insert(sl_vec.begin(), belt_ext_layers.begin(), belt_ext_layers.end());
|
sl_vec.insert(sl_vec.begin(), belt_ext_layers.begin(), belt_ext_layers.end());
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "[BELT-CALIB] wedge ext layers=" << belt_ext_layers.size()
|
||||||
|
<< " z=" << belt_ext_layers.front()->print_z << ".." << belt_ext_layers.back()->print_z
|
||||||
|
<< " seeded=" << seeded;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "calib.hpp"
|
#include "calib.hpp"
|
||||||
|
#include "BeltGCodeWriter.hpp"
|
||||||
#include "BoundingBox.hpp"
|
#include "BoundingBox.hpp"
|
||||||
#include "Config.hpp"
|
#include "Config.hpp"
|
||||||
#include "Model.hpp"
|
#include "Model.hpp"
|
||||||
@@ -587,21 +588,21 @@ CustomGCode::Info CalibPressureAdvancePattern::generate_custom_gcodes(const Dyna
|
|||||||
|
|
||||||
refresh_setup(config, is_bbl_machine, object, origin);
|
refresh_setup(config, is_bbl_machine, object, origin);
|
||||||
|
|
||||||
gcode << move_to(Vec2d(m_starting_point.x(), m_starting_point.y()), m_writer, "Move to start XY position");
|
gcode << move_to(Vec2d(m_starting_point.x(), m_starting_point.y()), *m_writer, "Move to start XY position");
|
||||||
gcode << m_writer.travel_to_z(height_first_layer() + height_z_offset(), "Move to start Z position");
|
gcode << m_writer->travel_to_z(height_first_layer() + height_z_offset(), "Move to start Z position");
|
||||||
gcode << m_writer.set_pressure_advance(m_params.start);
|
gcode << m_writer->set_pressure_advance(m_params.start);
|
||||||
|
|
||||||
const DrawBoxOptArgs default_box_opt_args(wall_count(), height_first_layer(), line_width_first_layer(),
|
const DrawBoxOptArgs default_box_opt_args(wall_count(), height_first_layer(), line_width_first_layer(),
|
||||||
speed_adjust(speed_first_layer()));
|
speed_adjust(speed_first_layer()));
|
||||||
|
|
||||||
// create anchoring frame
|
// create anchoring frame
|
||||||
gcode << draw_box(m_writer, m_starting_point.x(), m_starting_point.y(), print_size_x(), frame_size_y(), default_box_opt_args);
|
gcode << draw_box(*m_writer, m_starting_point.x(), m_starting_point.y(), print_size_x(), frame_size_y(), default_box_opt_args);
|
||||||
|
|
||||||
// create tab for numbers
|
// create tab for numbers
|
||||||
DrawBoxOptArgs draw_box_opt_args = default_box_opt_args;
|
DrawBoxOptArgs draw_box_opt_args = default_box_opt_args;
|
||||||
draw_box_opt_args.is_filled = true;
|
draw_box_opt_args.is_filled = true;
|
||||||
draw_box_opt_args.num_perimeters = wall_count();
|
draw_box_opt_args.num_perimeters = wall_count();
|
||||||
gcode << draw_box(m_writer, m_starting_point.x(), m_starting_point.y() + frame_size_y() + line_spacing_first_layer(),
|
gcode << draw_box(*m_writer, m_starting_point.x(), m_starting_point.y() + frame_size_y() + line_spacing_first_layer(),
|
||||||
print_size_x(),
|
print_size_x(),
|
||||||
max_numbering_height() + line_spacing_first_layer() + m_glyph_padding_vertical * 2, draw_box_opt_args);
|
max_numbering_height() + line_spacing_first_layer() + m_glyph_padding_vertical * 2, draw_box_opt_args);
|
||||||
|
|
||||||
@@ -627,15 +628,15 @@ CustomGCode::Info CalibPressureAdvancePattern::generate_custom_gcodes(const Dyna
|
|||||||
gcode = std::stringstream(); // reset for next layer contents
|
gcode = std::stringstream(); // reset for next layer contents
|
||||||
gcode << "; start pressure advance pattern for layer\n";
|
gcode << "; start pressure advance pattern for layer\n";
|
||||||
|
|
||||||
gcode << m_writer.travel_to_z(layer_height, "Move to layer height");
|
gcode << m_writer->travel_to_z(layer_height, "Move to layer height");
|
||||||
gcode << m_writer.reset_e();
|
gcode << m_writer->reset_e();
|
||||||
}
|
}
|
||||||
|
|
||||||
// line numbering
|
// line numbering
|
||||||
if (i == 1) {
|
if (i == 1) {
|
||||||
m_number_len = max_numbering_length();
|
m_number_len = max_numbering_length();
|
||||||
|
|
||||||
gcode << m_writer.set_pressure_advance(m_params.start);
|
gcode << m_writer->set_pressure_advance(m_params.start);
|
||||||
|
|
||||||
double number_e_per_mm = e_per_mm(line_width(), height_layer(),
|
double number_e_per_mm = e_per_mm(line_width(), height_layer(),
|
||||||
m_config.option<ConfigOptionFloats>("nozzle_diameter")->get_at(0),
|
m_config.option<ConfigOptionFloats>("nozzle_diameter")->get_at(0),
|
||||||
@@ -646,20 +647,20 @@ CustomGCode::Info CalibPressureAdvancePattern::generate_custom_gcodes(const Dyna
|
|||||||
for (int j = 0; j < num_patterns; j += 2) {
|
for (int j = 0; j < num_patterns; j += 2) {
|
||||||
gcode << draw_number(glyph_start_x(j), m_starting_point.y() + frame_size_y() + m_glyph_padding_vertical + line_width(),
|
gcode << draw_number(glyph_start_x(j), m_starting_point.y() + frame_size_y() + m_glyph_padding_vertical + line_width(),
|
||||||
m_params.start + (j * m_params.step), m_draw_digit_mode, line_width(), number_e_per_mm,
|
m_params.start + (j * m_params.step), m_draw_digit_mode, line_width(), number_e_per_mm,
|
||||||
speed_first_layer(), m_writer);
|
speed_first_layer(), *m_writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// flow value
|
// flow value
|
||||||
int line_num = num_patterns + 2;
|
int line_num = num_patterns + 2;
|
||||||
gcode << draw_number(glyph_start_x(line_num), m_starting_point.y() + frame_size_y() + m_glyph_padding_vertical + line_width(),
|
gcode << draw_number(glyph_start_x(line_num), m_starting_point.y() + frame_size_y() + m_glyph_padding_vertical + line_width(),
|
||||||
flow_val(), m_draw_digit_mode, line_width(), number_e_per_mm,
|
flow_val(), m_draw_digit_mode, line_width(), number_e_per_mm,
|
||||||
speed_first_layer(), m_writer);
|
speed_first_layer(), *m_writer);
|
||||||
|
|
||||||
// acceleration
|
// acceleration
|
||||||
line_num = num_patterns + 4;
|
line_num = num_patterns + 4;
|
||||||
gcode << draw_number(glyph_start_x(line_num), m_starting_point.y() + frame_size_y() + m_glyph_padding_vertical + line_width(),
|
gcode << draw_number(glyph_start_x(line_num), m_starting_point.y() + frame_size_y() + m_glyph_padding_vertical + line_width(),
|
||||||
accel, m_draw_digit_mode, line_width(), number_e_per_mm,
|
accel, m_draw_digit_mode, line_width(), number_e_per_mm,
|
||||||
speed_first_layer(), m_writer);
|
speed_first_layer(), *m_writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -678,20 +679,20 @@ CustomGCode::Info CalibPressureAdvancePattern::generate_custom_gcodes(const Dyna
|
|||||||
/* Draw a line at slightly slower accel and speed in order to trick gcode writer to force update acceleration and speed.
|
/* Draw a line at slightly slower accel and speed in order to trick gcode writer to force update acceleration and speed.
|
||||||
* We do this since several tests may be generated by their own gcode writers which are
|
* We do this since several tests may be generated by their own gcode writers which are
|
||||||
* not aware about their neighbours updating acceleration/speed */
|
* not aware about their neighbours updating acceleration/speed */
|
||||||
gcode << m_writer.set_print_acceleration(std::max<int>(1, accel - 1));
|
gcode << m_writer->set_print_acceleration(std::max<int>(1, accel - 1));
|
||||||
gcode << move_to(Vec2d(m_starting_point.x(), m_starting_point.y()), m_writer, "Move to starting point", zhop_height, layer_height);
|
gcode << move_to(Vec2d(m_starting_point.x(), m_starting_point.y()), *m_writer, "Move to starting point", zhop_height, layer_height);
|
||||||
gcode << draw_line(m_writer, Vec2d(m_starting_point.x(), m_starting_point.y() + frame_size_y()), line_width(), height_layer(), speed_adjust(std::max<int>(1, speed_perimeter() - 1)), "Accel/flow trick line");
|
gcode << draw_line(*m_writer, Vec2d(m_starting_point.x(), m_starting_point.y() + frame_size_y()), line_width(), height_layer(), speed_adjust(std::max<int>(1, speed_perimeter() - 1)), "Accel/flow trick line");
|
||||||
gcode << m_writer.set_print_acceleration(accel);
|
gcode << m_writer->set_print_acceleration(accel);
|
||||||
}
|
}
|
||||||
|
|
||||||
double initial_x = to_x;
|
double initial_x = to_x;
|
||||||
double initial_y = to_y;
|
double initial_y = to_y;
|
||||||
|
|
||||||
gcode << move_to(Vec2d(to_x, to_y), m_writer, "Move to pattern start",zhop_height,layer_height);
|
gcode << move_to(Vec2d(to_x, to_y), *m_writer, "Move to pattern start",zhop_height,layer_height);
|
||||||
|
|
||||||
for (int j = 0; j < num_patterns; ++j) {
|
for (int j = 0; j < num_patterns; ++j) {
|
||||||
// increment pressure advance
|
// increment pressure advance
|
||||||
gcode << m_writer.set_pressure_advance(m_params.start + (j * m_params.step));
|
gcode << m_writer->set_pressure_advance(m_params.start + (j * m_params.step));
|
||||||
|
|
||||||
for (int k = 0; k < wall_count(); ++k) {
|
for (int k = 0; k < wall_count(); ++k) {
|
||||||
to_x += std::cos(to_radians(m_corner_angle) / 2) * side_length;
|
to_x += std::cos(to_radians(m_corner_angle) / 2) * side_length;
|
||||||
@@ -701,27 +702,27 @@ CustomGCode::Info CalibPressureAdvancePattern::generate_custom_gcodes(const Dyna
|
|||||||
auto draw_line_arg_line_width = line_width(); // don't use line_width_first_layer so results are consistent across all layers
|
auto draw_line_arg_line_width = line_width(); // don't use line_width_first_layer so results are consistent across all layers
|
||||||
auto draw_line_arg_speed = i == 0 ? speed_adjust(speed_first_layer()) : speed_adjust(speed_perimeter());
|
auto draw_line_arg_speed = i == 0 ? speed_adjust(speed_first_layer()) : speed_adjust(speed_perimeter());
|
||||||
auto draw_line_arg_comment = "Print pattern wall";
|
auto draw_line_arg_comment = "Print pattern wall";
|
||||||
gcode << draw_line(m_writer, Vec2d(to_x, to_y), draw_line_arg_line_width, draw_line_arg_height, draw_line_arg_speed, draw_line_arg_comment);
|
gcode << draw_line(*m_writer, Vec2d(to_x, to_y), draw_line_arg_line_width, draw_line_arg_height, draw_line_arg_speed, draw_line_arg_comment);
|
||||||
|
|
||||||
to_x -= std::cos(to_radians(m_corner_angle) / 2) * side_length;
|
to_x -= std::cos(to_radians(m_corner_angle) / 2) * side_length;
|
||||||
to_y += std::sin(to_radians(m_corner_angle) / 2) * side_length;
|
to_y += std::sin(to_radians(m_corner_angle) / 2) * side_length;
|
||||||
|
|
||||||
gcode << draw_line(m_writer, Vec2d(to_x, to_y), draw_line_arg_line_width, draw_line_arg_height, draw_line_arg_speed, draw_line_arg_comment);
|
gcode << draw_line(*m_writer, Vec2d(to_x, to_y), draw_line_arg_line_width, draw_line_arg_height, draw_line_arg_speed, draw_line_arg_comment);
|
||||||
|
|
||||||
to_y = initial_y;
|
to_y = initial_y;
|
||||||
if (k != wall_count() - 1) {
|
if (k != wall_count() - 1) {
|
||||||
// perimeters not done yet. move to next perimeter
|
// perimeters not done yet. move to next perimeter
|
||||||
to_x += line_spacing_angle();
|
to_x += line_spacing_angle();
|
||||||
gcode << move_to(Vec2d(to_x, to_y), m_writer, "Move to start next pattern wall", zhop_height, layer_height); // Call move to command with XY as well as z hop and layer height to invoke and undo z lift
|
gcode << move_to(Vec2d(to_x, to_y), *m_writer, "Move to start next pattern wall", zhop_height, layer_height); // Call move to command with XY as well as z hop and layer height to invoke and undo z lift
|
||||||
} else if (j != num_patterns - 1) {
|
} else if (j != num_patterns - 1) {
|
||||||
// patterns not done yet. move to next pattern
|
// patterns not done yet. move to next pattern
|
||||||
to_x += m_pattern_spacing + line_width();
|
to_x += m_pattern_spacing + line_width();
|
||||||
gcode << move_to(Vec2d(to_x, to_y), m_writer, "Move to next pattern", zhop_height, layer_height); // Call move to command with XY as well as z hop and layer height to invoke and undo z lift
|
gcode << move_to(Vec2d(to_x, to_y), *m_writer, "Move to next pattern", zhop_height, layer_height); // Call move to command with XY as well as z hop and layer height to invoke and undo z lift
|
||||||
} else if (i != m_num_layers - 1) {
|
} else if (i != m_num_layers - 1) {
|
||||||
// layers not done yet. move back to start
|
// layers not done yet. move back to start
|
||||||
to_x = initial_x;
|
to_x = initial_x;
|
||||||
gcode << move_to(Vec2d(to_x, to_y), m_writer, "Move back to start position", zhop_height, layer_height); // Call move to command with XY as well as z hop and layer height to invoke and undo z lift
|
gcode << move_to(Vec2d(to_x, to_y), *m_writer, "Move back to start position", zhop_height, layer_height); // Call move to command with XY as well as z hop and layer height to invoke and undo z lift
|
||||||
gcode << m_writer.reset_e(); // reset extruder before printing placeholder cube to avoid over extrusion
|
gcode << m_writer->reset_e(); // reset extruder before printing placeholder cube to avoid over extrusion
|
||||||
} else {
|
} else {
|
||||||
// everything done
|
// everything done
|
||||||
}
|
}
|
||||||
@@ -729,7 +730,7 @@ CustomGCode::Info CalibPressureAdvancePattern::generate_custom_gcodes(const Dyna
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gcode << m_writer.set_pressure_advance(m_params.start);
|
gcode << m_writer->set_pressure_advance(m_params.start);
|
||||||
gcode << "; end pressure advance pattern for layer\n";
|
gcode << "; end pressure advance pattern for layer\n";
|
||||||
|
|
||||||
CustomGCode::Item item;
|
CustomGCode::Item item;
|
||||||
@@ -796,13 +797,36 @@ void CalibPressureAdvancePattern::_refresh_writer(bool is_bbl_machine, const Mod
|
|||||||
PrintConfig print_config;
|
PrintConfig print_config;
|
||||||
print_config.apply(m_config, true);
|
print_config.apply(m_config, true);
|
||||||
|
|
||||||
m_writer.apply_print_config(print_config);
|
// ORCA-Belt: the pattern is drawn in logical bed coordinates directly on
|
||||||
m_writer.set_xy_offset(origin(0), origin(1));
|
// the build surface — on a belt printer that means the belt plane, which
|
||||||
m_writer.set_is_bbl_machine(is_bbl_machine);
|
// needs the machine kinematics (axis remap + frame shear/scale) with the
|
||||||
|
// coordinates interpreted as world points (see set_world_coordinates).
|
||||||
|
if (print_config.belt_printer.value) {
|
||||||
|
auto belt_writer = std::make_shared<BeltGCodeWriter>();
|
||||||
|
belt_writer->set_belt_back_transform(print_config);
|
||||||
|
belt_writer->set_machine_frame_transform(print_config);
|
||||||
|
belt_writer->set_world_coordinates(true);
|
||||||
|
const int rx = int(print_config.gcode_remap_x.value);
|
||||||
|
const int ry = int(print_config.gcode_remap_y.value);
|
||||||
|
const int rz = int(print_config.gcode_remap_z.value);
|
||||||
|
if (rx != 0 || ry != 1 || rz != 2) {
|
||||||
|
belt_writer->set_axis_remap(rx, ry, rz);
|
||||||
|
BoundingBoxf bbox_bed(print_config.printable_area.values);
|
||||||
|
belt_writer->set_build_volume_max(Vec3d(bbox_bed.max.x(), bbox_bed.max.y(),
|
||||||
|
print_config.printable_height.value));
|
||||||
|
}
|
||||||
|
m_writer = std::move(belt_writer);
|
||||||
|
} else if (dynamic_cast<BeltGCodeWriter*>(m_writer.get()) != nullptr) {
|
||||||
|
m_writer = std::make_shared<GCodeWriter>();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_writer->apply_print_config(print_config);
|
||||||
|
m_writer->set_xy_offset(origin(0), origin(1));
|
||||||
|
m_writer->set_is_bbl_machine(is_bbl_machine);
|
||||||
|
|
||||||
const unsigned int extruder_id = object.volumes.front()->extruder_id();
|
const unsigned int extruder_id = object.volumes.front()->extruder_id();
|
||||||
m_writer.set_extruders({extruder_id});
|
m_writer->set_extruders({extruder_id});
|
||||||
m_writer.set_extruder(extruder_id);
|
m_writer->set_extruder(extruder_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
double CalibPressureAdvancePattern::object_size_x() const
|
double CalibPressureAdvancePattern::object_size_x() const
|
||||||
|
|||||||
@@ -358,7 +358,10 @@ private:
|
|||||||
|
|
||||||
const Calib_Params &m_params;
|
const Calib_Params &m_params;
|
||||||
|
|
||||||
GCodeWriter m_writer;
|
// Polymorphic so belt printers get a BeltGCodeWriter in world-coordinates
|
||||||
|
// mode (_refresh_writer); shared_ptr keeps the class copyable — the writer
|
||||||
|
// is rebuilt by refresh_setup() before every use anyway.
|
||||||
|
std::shared_ptr<GCodeWriter> m_writer{std::make_shared<GCodeWriter>()};
|
||||||
Vec3d m_starting_point;
|
Vec3d m_starting_point;
|
||||||
bool m_is_start_point_fixed = false;
|
bool m_is_start_point_fixed = false;
|
||||||
|
|
||||||
|
|||||||
@@ -12594,8 +12594,181 @@ void Plater::add_model(bool imperial_units, std::string fname)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ORCA-Belt: belt-printer handling for the desktop calibration tests.
|
||||||
|
//
|
||||||
|
// Belt slicing applies a global pre-slice rotation R(angle, axis) to every
|
||||||
|
// mesh (see BeltTransform.hpp) so the slicing planes match the tilted gantry.
|
||||||
|
// Calibration models are designed for upright slicing: their per-height test
|
||||||
|
// bands and XY-plane quality features assume slicer Z is the model's own Z
|
||||||
|
// axis. Counter-rotating each calibration object by the inverse rotation in
|
||||||
|
// world space cancels the global rotation, so in slicing space the object
|
||||||
|
// stands upright exactly as on a flat-bed printer and every test keeps its
|
||||||
|
// designed meaning. Physically the object then leans over the belt with its
|
||||||
|
// bottom face overhanging, so per-object supports fill the wedge between the
|
||||||
|
// bottom face and the belt. The wedge prints entirely below the object's base
|
||||||
|
// plane and leaves the test geometry untouched. Manual tree support is used
|
||||||
|
// so the deliberate bridge/overhang features of the test models stay
|
||||||
|
// unsupported; the wedge under the floating bottom face is built by the
|
||||||
|
// belt-floor extension in TreeSupport::generate(), which stacks the floating
|
||||||
|
// first-layer footprint down to the belt surface.
|
||||||
|
static bool belt_calib_rotation_params(double& angle_rad, Vec3d& axis)
|
||||||
|
{
|
||||||
|
const auto& printer_config = wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||||
|
const auto* belt_opt = printer_config.option<ConfigOptionBool>("belt_printer");
|
||||||
|
if (belt_opt == nullptr || !belt_opt->value)
|
||||||
|
return false;
|
||||||
|
const auto* axis_opt = printer_config.option<ConfigOptionEnum<BeltRotationAxis>>("belt_slice_rotation");
|
||||||
|
const auto* angle_opt = printer_config.option<ConfigOptionFloat>("belt_slice_rotation_angle");
|
||||||
|
if (axis_opt == nullptr || angle_opt == nullptr)
|
||||||
|
return false;
|
||||||
|
switch (axis_opt->value) {
|
||||||
|
case BeltRotationAxis::X: axis = Vec3d::UnitX(); break;
|
||||||
|
case BeltRotationAxis::Y: axis = Vec3d::UnitY(); break;
|
||||||
|
// Z rotation is an in-plane spin and None means no tilt; objects already
|
||||||
|
// slice upright in those cases and need no special handling.
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
angle_rad = -Geometry::deg2rad(angle_opt->value);
|
||||||
|
return std::abs(angle_rad) > EPSILON;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ORCA-Belt: flip the ringing tower 180° about Z before the belt
|
||||||
|
// counter-rotation — its sloped face then leans over the belt and the
|
||||||
|
// support wedge gets much smaller.
|
||||||
|
static void belt_calib_flip_ringing_tower(Model &model)
|
||||||
|
{
|
||||||
|
double angle_rad = 0.;
|
||||||
|
Vec3d axis = Vec3d::UnitX();
|
||||||
|
if (belt_calib_rotation_params(angle_rad, axis) && !model.objects.empty())
|
||||||
|
model.objects.front()->rotate(M_PI, Vec3d::UnitZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Plater::_calib_apply_belt_mode()
|
||||||
|
{
|
||||||
|
double angle_rad = 0.;
|
||||||
|
Vec3d axis = Vec3d::UnitX();
|
||||||
|
if (!belt_calib_rotation_params(angle_rad, axis))
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||||
|
// Spiral vase stays enabled where the tests request it: the support wedge
|
||||||
|
// lies strictly below the object, support layers never spiralize
|
||||||
|
// (spiral_vase_enable requires an object layer), and the spiral/support
|
||||||
|
// exclusivity check only applies to globally enabled supports — the wedge
|
||||||
|
// uses per-object supports.
|
||||||
|
// A skirt would be drawn in the first slicing plane, which lies mostly
|
||||||
|
// above the belt surface.
|
||||||
|
print_config->set_key_value("skirt_loops", new ConfigOptionInt(0));
|
||||||
|
|
||||||
|
const Matrix3d cancel_rotation = Eigen::AngleAxisd(angle_rad, axis).toRotationMatrix();
|
||||||
|
std::vector<size_t> obj_idxs;
|
||||||
|
for (size_t i = 0; i < model().objects.size(); ++i) {
|
||||||
|
ModelObject* obj = model().objects[i];
|
||||||
|
if (obj->instances.size() != 1)
|
||||||
|
continue;
|
||||||
|
obj_idxs.emplace_back(i);
|
||||||
|
|
||||||
|
// Manual tree support: only the floating bottom face gets a support
|
||||||
|
// wedge (via the belt-floor extension in TreeSupport::generate()),
|
||||||
|
// leaving the test features untouched. The style is pinned to hybrid
|
||||||
|
// because the default style resolves to organic, which bypasses the
|
||||||
|
// non-organic generator that hosts the belt-floor extension.
|
||||||
|
obj->config.set_key_value("enable_support", new ConfigOptionBool(true));
|
||||||
|
obj->config.set_key_value("support_type", new ConfigOptionEnum<SupportType>(stTree));
|
||||||
|
obj->config.set_key_value("support_style", new ConfigOptionEnum<SupportMaterialStyle>(smsTreeHybrid));
|
||||||
|
obj->config.set_key_value("support_on_build_plate_only", new ConfigOptionBool(false));
|
||||||
|
// With the default base pattern, tree support base areas print as
|
||||||
|
// hollow outlines (no infill) — the wedge needs a real pattern.
|
||||||
|
obj->config.set_key_value("support_base_pattern", new ConfigOptionEnum<SupportMaterialPattern>(smpRectilinear));
|
||||||
|
|
||||||
|
// Counter-rotate exactly the way the rotate gizmo would: rotation on
|
||||||
|
// the instance, then a plain drop to the bed. This leaves the object
|
||||||
|
// in the same state shape as any manually rotated object, which the
|
||||||
|
// belt pipeline is known to handle.
|
||||||
|
ModelInstance* inst = obj->instances.front();
|
||||||
|
inst->rotate(cancel_rotation);
|
||||||
|
obj->invalidate_bounding_box();
|
||||||
|
obj->ensure_on_bed();
|
||||||
|
|
||||||
|
{
|
||||||
|
const BoundingBoxf3 rb = obj->raw_bounding_box();
|
||||||
|
const Vec3d io = inst->get_offset();
|
||||||
|
const Vec3d ir = inst->get_rotation();
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "[BELT-CALIB] helper exit: obj=" << obj->name
|
||||||
|
<< " inst_offset=(" << io.x() << "," << io.y() << "," << io.z() << ")"
|
||||||
|
<< " inst_rot=(" << ir.x() << "," << ir.y() << "," << ir.z() << ")"
|
||||||
|
<< " vol0_offset=(" << obj->volumes.front()->get_offset().x() << ","
|
||||||
|
<< obj->volumes.front()->get_offset().y() << "," << obj->volumes.front()->get_offset().z() << ")"
|
||||||
|
<< " raw_bbox=(" << rb.min.x() << "," << rb.min.y() << "," << rb.min.z()
|
||||||
|
<< ")..(" << rb.max.x() << "," << rb.max.y() << "," << rb.max.z() << ")"
|
||||||
|
<< " min_z=" << obj->min_z();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each object's support wedge extends upstream of it by roughly its own
|
||||||
|
// depth (at 45°), so the tight flat-bed layouts of the multi-part tests
|
||||||
|
// leave wedges intersecting the neighbouring parts. Keep the grid rows of
|
||||||
|
// the test layouts together and open up the space between rows just
|
||||||
|
// enough for the wedge shadow.
|
||||||
|
if (obj_idxs.size() > 1) {
|
||||||
|
std::vector<ModelObject*> sorted_objs;
|
||||||
|
sorted_objs.reserve(obj_idxs.size());
|
||||||
|
for (size_t i : obj_idxs)
|
||||||
|
sorted_objs.emplace_back(model().objects[i]);
|
||||||
|
std::sort(sorted_objs.begin(), sorted_objs.end(), [](const ModelObject* a, const ModelObject* b) {
|
||||||
|
return a->instances.front()->get_offset(Y) < b->instances.front()->get_offset(Y);
|
||||||
|
});
|
||||||
|
std::vector<std::vector<ModelObject*>> rows;
|
||||||
|
double row_y = std::numeric_limits<double>::quiet_NaN();
|
||||||
|
for (ModelObject* o : sorted_objs) {
|
||||||
|
const double oy = o->instances.front()->get_offset(Y);
|
||||||
|
if (rows.empty() || oy - row_y > 1.)
|
||||||
|
rows.emplace_back();
|
||||||
|
rows.back().emplace_back(o);
|
||||||
|
row_y = oy;
|
||||||
|
}
|
||||||
|
const double wedge_factor = std::abs(std::tan(angle_rad));
|
||||||
|
double cursor = std::numeric_limits<double>::quiet_NaN();
|
||||||
|
for (std::vector<ModelObject*>& row : rows) {
|
||||||
|
double rmin = std::numeric_limits<double>::max();
|
||||||
|
double rmax = std::numeric_limits<double>::lowest();
|
||||||
|
for (ModelObject* o : row) {
|
||||||
|
const BoundingBoxf3 bb = o->instance_bounding_box(0);
|
||||||
|
rmin = std::min(rmin, bb.min.y());
|
||||||
|
rmax = std::max(rmax, bb.max.y());
|
||||||
|
}
|
||||||
|
if (std::isnan(cursor))
|
||||||
|
cursor = rmin; // the first row anchors the layout
|
||||||
|
const double shift = cursor - rmin;
|
||||||
|
for (ModelObject* o : row) {
|
||||||
|
ModelInstance* inst = o->instances.front();
|
||||||
|
inst->set_offset(Y, inst->get_offset(Y) + shift);
|
||||||
|
o->invalidate_bounding_box();
|
||||||
|
}
|
||||||
|
cursor += (rmax - rmin) * (1. + wedge_factor) + 5.;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wxGetApp().get_tab(Preset::TYPE_PRINT)->update_dirty();
|
||||||
|
wxGetApp().get_tab(Preset::TYPE_PRINT)->reload_config();
|
||||||
|
changed_objects(obj_idxs);
|
||||||
|
}
|
||||||
|
|
||||||
void Plater::calib_pa(const Calib_Params& params)
|
void Plater::calib_pa(const Calib_Params& params)
|
||||||
{
|
{
|
||||||
|
// ORCA-Belt: PA Line / PA Pattern have the belt plumbing in place
|
||||||
|
// (BeltGCodeWriter::set_world_coordinates draws them on the belt surface)
|
||||||
|
// but are not validated yet — keep them gated to the PA Tower for now.
|
||||||
|
{
|
||||||
|
double angle_rad = 0.;
|
||||||
|
Vec3d axis = Vec3d::UnitX();
|
||||||
|
if (belt_calib_rotation_params(angle_rad, axis) && params.mode != CalibMode::Calib_PA_Tower) {
|
||||||
|
MessageDialog msg_dlg(nullptr, _L("PA Line and PA Pattern tests are not enabled yet on belt printers.\nPlease use the PA Tower method instead."),
|
||||||
|
wxEmptyString, wxICON_WARNING | wxOK);
|
||||||
|
msg_dlg.ShowModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
const auto calib_pa_name = wxString::Format(L"Pressure Advance Test");
|
const auto calib_pa_name = wxString::Format(L"Pressure Advance Test");
|
||||||
new_project(false, false, calib_pa_name);
|
new_project(false, false, calib_pa_name);
|
||||||
wxGetApp().mainframe->select_tab(size_t(MainFrame::tp3DEditor));
|
wxGetApp().mainframe->select_tab(size_t(MainFrame::tp3DEditor));
|
||||||
@@ -12927,6 +13100,7 @@ void Plater::_calib_pa_tower(const Calib_Params& params) {
|
|||||||
cut_horizontal(0, 0, new_height, ModelObjectCutAttribute::KeepLower);
|
cut_horizontal(0, 0, new_height, ModelObjectCutAttribute::KeepLower);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_calib_apply_belt_mode();
|
||||||
_calib_pa_select_added_objects();
|
_calib_pa_select_added_objects();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13104,6 +13278,8 @@ void Plater::calib_flowrate(bool is_linear, int pass, InfillPattern pattern) {
|
|||||||
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||||
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
|
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
|
||||||
|
|
||||||
|
_calib_apply_belt_mode();
|
||||||
|
|
||||||
// Refresh object after scaling
|
// Refresh object after scaling
|
||||||
const std::vector<size_t> object_idx(boost::counting_iterator<size_t>(0), boost::counting_iterator<size_t>(model().objects.size()));
|
const std::vector<size_t> object_idx(boost::counting_iterator<size_t>(0), boost::counting_iterator<size_t>(model().objects.size()));
|
||||||
changed_objects(object_idx);
|
changed_objects(object_idx);
|
||||||
@@ -13121,6 +13297,18 @@ void Plater::calib_temp(const Calib_Params& params) {
|
|||||||
if (params.mode != CalibMode::Calib_Temp_Tower)
|
if (params.mode != CalibMode::Calib_Temp_Tower)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// ORCA-Belt: a counter-rotated tower would cantilever every block off a
|
||||||
|
// support wedge — print the temperature blocks as separate objects in
|
||||||
|
// native belt orientation instead.
|
||||||
|
{
|
||||||
|
double belt_angle_rad = 0.;
|
||||||
|
Vec3d belt_axis = Vec3d::UnitX();
|
||||||
|
if (belt_calib_rotation_params(belt_angle_rad, belt_axis)) {
|
||||||
|
_calib_temp_belt_sectioned(params, std::abs(belt_angle_rad));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
add_model(false, Slic3r::resources_dir() + "/calib/temperature_tower/temperature_tower.drc");
|
add_model(false, Slic3r::resources_dir() + "/calib/temperature_tower/temperature_tower.drc");
|
||||||
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||||
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
||||||
@@ -13190,6 +13378,107 @@ void Plater::calib_temp(const Calib_Params& params) {
|
|||||||
p->background_process.fff_print()->set_calib_params(params);
|
p->background_process.fff_print()->set_calib_params(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ORCA-Belt: sectioned temperature test. Each temperature gets its own block
|
||||||
|
// cut out of the temperature tower model, printed in native belt orientation
|
||||||
|
// (no counter-rotation, no support wedge) and spaced along the belt so the
|
||||||
|
// blocks' layer ranges are disjoint — they print strictly one after another,
|
||||||
|
// starting with the start temperature closest to the gantry. The temperature
|
||||||
|
// is encoded in the object name ("temp_230") and applied per object by the
|
||||||
|
// Calib_Temp_Tower handler at G-code time, replacing the per-layer-band ramp
|
||||||
|
// that only makes sense for a monolithic upright tower.
|
||||||
|
void Plater::_calib_temp_belt_sectioned(const Calib_Params& params, double belt_angle_rad)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||||
|
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
||||||
|
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||||
|
|
||||||
|
const long start_temp = lround(params.start);
|
||||||
|
const long end_temp = lround(params.end);
|
||||||
|
const int n_blocks = std::max(1, int((start_temp - end_temp) / base_temp_tower_temp_step) + 1);
|
||||||
|
|
||||||
|
const ConfigOptionFloats* nozzle_diameter_config = printer_config->option<ConfigOptionFloats>("nozzle_diameter");
|
||||||
|
size_t nozzle_id = static_cast<size_t>(std::max(params.extruder_id, 0));
|
||||||
|
double nozzle_diameter = base_temp_tower_nozzle_diameter;
|
||||||
|
if (nozzle_diameter_config && !nozzle_diameter_config->values.empty()) {
|
||||||
|
nozzle_id = std::min(nozzle_id, nozzle_diameter_config->values.size() - 1);
|
||||||
|
nozzle_diameter = nozzle_diameter_config->values[nozzle_id];
|
||||||
|
}
|
||||||
|
if (nozzle_diameter <= 0.0)
|
||||||
|
nozzle_diameter = base_temp_tower_nozzle_diameter;
|
||||||
|
const double nozzle_scale = nozzle_diameter / base_temp_tower_nozzle_diameter;
|
||||||
|
|
||||||
|
std::vector<size_t> obj_idxs;
|
||||||
|
for (int i = 0; i < n_blocks; ++i) {
|
||||||
|
const long temp = start_temp - long(i) * base_temp_tower_temp_step;
|
||||||
|
const size_t count_before = model().objects.size();
|
||||||
|
add_model(false, Slic3r::resources_dir() + "/calib/temperature_tower/temperature_tower.drc");
|
||||||
|
if (model().objects.size() <= count_before)
|
||||||
|
break; // model failed to load — don't index into an empty list
|
||||||
|
// The cut replaces the object at the END of the list, so re-acquire
|
||||||
|
// the index after every operation.
|
||||||
|
size_t obj_idx = model().objects.size() - 1;
|
||||||
|
|
||||||
|
// Isolate this temperature's block (full-tower coordinates, the same
|
||||||
|
// 500-down-to-temp indexing the monolithic flow cuts with).
|
||||||
|
const double block_bottom = double(lround(double(500 - temp) / base_temp_tower_temp_step)) * base_temp_tower_block_height;
|
||||||
|
auto obj_bb = model().objects[obj_idx]->bounding_box_exact();
|
||||||
|
if (block_bottom + base_temp_tower_block_height < obj_bb.size().z()) {
|
||||||
|
cut_horizontal(obj_idx, 0, block_bottom + base_temp_tower_block_height - EPSILON, ModelObjectCutAttribute::KeepLower);
|
||||||
|
obj_idx = model().objects.size() - 1;
|
||||||
|
}
|
||||||
|
if (block_bottom > 0) {
|
||||||
|
cut_horizontal(obj_idx, 0, block_bottom + EPSILON, ModelObjectCutAttribute::KeepUpper);
|
||||||
|
obj_idx = model().objects.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModelObject* obj = model().objects[obj_idx];
|
||||||
|
if (std::abs(nozzle_scale - 1.0) > EPSILON)
|
||||||
|
obj->scale(nozzle_scale, nozzle_scale, nozzle_scale);
|
||||||
|
obj->name = std::string("temp_") + std::to_string(temp);
|
||||||
|
obj->config.set_key_value("layer_height", new ConfigOptionFloat(nozzle_diameter / 2));
|
||||||
|
obj->config.set_key_value("alternate_extra_wall", new ConfigOptionBool(false));
|
||||||
|
obj->config.set_key_value("seam_slope_type", new ConfigOptionEnum<SeamScarfType>(SeamScarfType::None));
|
||||||
|
obj->config.set_key_value("overhang_reverse", new ConfigOptionBool(false));
|
||||||
|
obj->config.set_key_value("precise_z_height", new ConfigOptionBool(false));
|
||||||
|
obj->ensure_on_bed();
|
||||||
|
obj_idxs.emplace_back(obj_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Space the blocks along the belt with strictly increasing layer ranges:
|
||||||
|
// each block must start past the previous block's highest slicing plane,
|
||||||
|
// which trails its far edge by height / tan(angle). Anchor the row near
|
||||||
|
// the gantry so the whole test stays in the plate area.
|
||||||
|
const double cot_a = 1. / std::max(0.1, std::tan(belt_angle_rad));
|
||||||
|
double cursor = 20.;
|
||||||
|
for (size_t idx : obj_idxs) {
|
||||||
|
ModelObject* obj = model().objects[idx];
|
||||||
|
ModelInstance* inst = obj->instances.front();
|
||||||
|
const BoundingBoxf3 bb = obj->instance_bounding_box(0);
|
||||||
|
inst->set_offset(Y, inst->get_offset(Y) + (cursor - bb.min.y()));
|
||||||
|
obj->invalidate_bounding_box();
|
||||||
|
cursor += bb.size().y() + bb.size().z() * cot_a + 5.;
|
||||||
|
}
|
||||||
|
|
||||||
|
printer_config->set_key_value("resonance_avoidance", new ConfigOptionBool{false});
|
||||||
|
filament_config->set_key_value("nozzle_temperature_initial_layer", new ConfigOptionInts(1, (int)start_temp));
|
||||||
|
filament_config->set_key_value("nozzle_temperature", new ConfigOptionInts(1, (int)start_temp));
|
||||||
|
print_config->set_key_value("enable_wrapping_detection", new ConfigOptionBool(false));
|
||||||
|
print_config->set_key_value("initial_layer_print_height", new ConfigOptionFloat(nozzle_diameter / 2));
|
||||||
|
print_config->set_key_value("skirt_loops", new ConfigOptionInt(0));
|
||||||
|
|
||||||
|
changed_objects(obj_idxs);
|
||||||
|
wxGetApp().get_tab(Preset::TYPE_PRINT)->update_dirty();
|
||||||
|
wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_dirty();
|
||||||
|
wxGetApp().get_tab(Preset::TYPE_PRINT)->reload_config();
|
||||||
|
wxGetApp().get_tab(Preset::TYPE_FILAMENT)->reload_config();
|
||||||
|
|
||||||
|
p->background_process.fff_print()->set_calib_params(params);
|
||||||
|
}
|
||||||
|
|
||||||
void Plater::calib_max_vol_speed(const Calib_Params& params)
|
void Plater::calib_max_vol_speed(const Calib_Params& params)
|
||||||
{
|
{
|
||||||
const auto calib_vol_speed_name = wxString::Format(L"Max volumetric speed test");
|
const auto calib_vol_speed_name = wxString::Format(L"Max volumetric speed test");
|
||||||
@@ -13256,6 +13545,8 @@ void Plater::calib_max_vol_speed(const Calib_Params& params)
|
|||||||
cut_horizontal(0, 0, height, ModelObjectCutAttribute::KeepLower);
|
cut_horizontal(0, 0, height, ModelObjectCutAttribute::KeepLower);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_calib_apply_belt_mode();
|
||||||
|
|
||||||
auto new_params = params;
|
auto new_params = params;
|
||||||
auto mm3_per_mm = Flow(line_width, layer_height, nozzle_diameter).mm3_per_mm() * filament_config->option<ConfigOptionFloatsNullable>("filament_flow_ratio")->get_at(0);
|
auto mm3_per_mm = Flow(line_width, layer_height, nozzle_diameter).mm3_per_mm() * filament_config->option<ConfigOptionFloatsNullable>("filament_flow_ratio")->get_at(0);
|
||||||
new_params.end = params.end / mm3_per_mm;
|
new_params.end = params.end / mm3_per_mm;
|
||||||
@@ -13320,6 +13611,7 @@ void Plater::calib_retraction(const Calib_Params& params)
|
|||||||
cut_horizontal(0, 0, height, ModelObjectCutAttribute::KeepLower);
|
cut_horizontal(0, 0, height, ModelObjectCutAttribute::KeepLower);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_calib_apply_belt_mode();
|
||||||
p->background_process.fff_print()->set_calib_params(params);
|
p->background_process.fff_print()->set_calib_params(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13365,6 +13657,7 @@ void Plater::calib_VFA(const Calib_Params& params)
|
|||||||
cut_horizontal(0, 0, height, ModelObjectCutAttribute::KeepLower);
|
cut_horizontal(0, 0, height, ModelObjectCutAttribute::KeepLower);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_calib_apply_belt_mode();
|
||||||
p->background_process.fff_print()->set_calib_params(params);
|
p->background_process.fff_print()->set_calib_params(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13377,6 +13670,8 @@ void Plater::calib_input_shaping_freq(const Calib_Params& params)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
add_model(false, Slic3r::resources_dir() + (params.test_model < 1 ? "/calib/input_shaping/ringing_tower.drc" : "/calib/input_shaping/fast_tower_test.drc"));
|
add_model(false, Slic3r::resources_dir() + (params.test_model < 1 ? "/calib/input_shaping/ringing_tower.drc" : "/calib/input_shaping/fast_tower_test.drc"));
|
||||||
|
if (params.test_model < 1)
|
||||||
|
belt_calib_flip_ringing_tower(model());
|
||||||
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||||
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
||||||
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||||
@@ -13428,6 +13723,7 @@ void Plater::calib_input_shaping_freq(const Calib_Params& params)
|
|||||||
wxGetApp().get_tab(Preset::TYPE_PRINT)->update_ui_from_settings();
|
wxGetApp().get_tab(Preset::TYPE_PRINT)->update_ui_from_settings();
|
||||||
wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_ui_from_settings();
|
wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_ui_from_settings();
|
||||||
|
|
||||||
|
_calib_apply_belt_mode();
|
||||||
p->background_process.fff_print()->set_calib_params(params);
|
p->background_process.fff_print()->set_calib_params(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13440,6 +13736,8 @@ void Plater::calib_input_shaping_damp(const Calib_Params& params)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
add_model(false, Slic3r::resources_dir() + (params.test_model < 1 ? "/calib/input_shaping/ringing_tower.drc" : "/calib/input_shaping/fast_tower_test.drc"));
|
add_model(false, Slic3r::resources_dir() + (params.test_model < 1 ? "/calib/input_shaping/ringing_tower.drc" : "/calib/input_shaping/fast_tower_test.drc"));
|
||||||
|
if (params.test_model < 1)
|
||||||
|
belt_calib_flip_ringing_tower(model());
|
||||||
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||||
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
||||||
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||||
@@ -13490,6 +13788,7 @@ void Plater::calib_input_shaping_damp(const Calib_Params& params)
|
|||||||
wxGetApp().get_tab(Preset::TYPE_PRINT)->update_ui_from_settings();
|
wxGetApp().get_tab(Preset::TYPE_PRINT)->update_ui_from_settings();
|
||||||
wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_ui_from_settings();
|
wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_ui_from_settings();
|
||||||
|
|
||||||
|
_calib_apply_belt_mode();
|
||||||
p->background_process.fff_print()->set_calib_params(params);
|
p->background_process.fff_print()->set_calib_params(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13505,6 +13804,8 @@ void Plater::Calib_Cornering(const Calib_Params& params)
|
|||||||
? "/calib/input_shaping/ringing_tower.drc"
|
? "/calib/input_shaping/ringing_tower.drc"
|
||||||
: (params.test_model == 1 ? "/calib/input_shaping/fast_tower_test.drc" : "/calib/cornering/SCV-V2.drc");
|
: (params.test_model == 1 ? "/calib/input_shaping/fast_tower_test.drc" : "/calib/cornering/SCV-V2.drc");
|
||||||
add_model(false, Slic3r::resources_dir() + cornering_model_path);
|
add_model(false, Slic3r::resources_dir() + cornering_model_path);
|
||||||
|
if (params.test_model == 0)
|
||||||
|
belt_calib_flip_ringing_tower(model());
|
||||||
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
auto print_config = &wxGetApp().preset_bundle->prints.get_edited_preset().config;
|
||||||
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
auto filament_config = &wxGetApp().preset_bundle->filaments.get_edited_preset().config;
|
||||||
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
auto printer_config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||||
@@ -13555,6 +13856,7 @@ void Plater::Calib_Cornering(const Calib_Params& params)
|
|||||||
wxGetApp().get_tab(Preset::TYPE_PRINT)->update_ui_from_settings();
|
wxGetApp().get_tab(Preset::TYPE_PRINT)->update_ui_from_settings();
|
||||||
wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_ui_from_settings();
|
wxGetApp().get_tab(Preset::TYPE_FILAMENT)->update_ui_from_settings();
|
||||||
|
|
||||||
|
_calib_apply_belt_mode();
|
||||||
p->background_process.fff_print()->set_calib_params(params);
|
p->background_process.fff_print()->set_calib_params(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -951,6 +951,8 @@ private:
|
|||||||
void _calib_pa_pattern_gen_gcode();
|
void _calib_pa_pattern_gen_gcode();
|
||||||
void _calib_pa_tower(const Calib_Params& params);
|
void _calib_pa_tower(const Calib_Params& params);
|
||||||
void _calib_pa_select_added_objects();
|
void _calib_pa_select_added_objects();
|
||||||
|
void _calib_apply_belt_mode();
|
||||||
|
void _calib_temp_belt_sectioned(const Calib_Params& params, double belt_angle_rad);
|
||||||
|
|
||||||
void cut_horizontal(size_t obj_idx, size_t instance_idx, double z, ModelObjectCutAttributes attributes);
|
void cut_horizontal(size_t obj_idx, size_t instance_idx, double z, ModelObjectCutAttributes attributes);
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,19 @@ std::vector<wxString> make_shaper_type_labels()
|
|||||||
return labels;
|
return labels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ORCA-Belt: PA Line / PA Pattern have belt plumbing in place (drawn on the
|
||||||
|
// belt surface via BeltGCodeWriter world-coordinates mode) but are not
|
||||||
|
// validated yet — belt printers are restricted to the PA Tower for now.
|
||||||
|
bool is_belt_printer_selected()
|
||||||
|
{
|
||||||
|
if (auto* preset_bundle = wxGetApp().preset_bundle) {
|
||||||
|
const auto& cfg = preset_bundle->printers.get_edited_preset().config;
|
||||||
|
const auto* opt = cfg.option<ConfigOptionBool>("belt_printer");
|
||||||
|
return opt != nullptr && opt->value;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PA_Calibration_Dlg::PA_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
|
PA_Calibration_Dlg::PA_Calibration_Dlg(wxWindow* parent, wxWindowID id, Plater* plater)
|
||||||
@@ -288,6 +301,14 @@ void PA_Calibration_Dlg::on_start(wxCommandEvent& event) {
|
|||||||
m_params.mode = CalibMode::Calib_PA_Tower;
|
m_params.mode = CalibMode::Calib_PA_Tower;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ORCA-Belt: backstop in case the selection slipped past the UI guards.
|
||||||
|
if (is_belt_printer_selected() && m_params.mode != CalibMode::Calib_PA_Tower) {
|
||||||
|
MessageDialog msg_dlg(nullptr, _L("PA Line and PA Pattern tests are not enabled yet on belt printers.\nPlease use the PA Tower method instead."),
|
||||||
|
wxEmptyString, wxICON_WARNING | wxOK);
|
||||||
|
msg_dlg.ShowModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_params.print_numbers = m_cbPrintNum->GetValue();
|
m_params.print_numbers = m_cbPrintNum->GetValue();
|
||||||
ParseStringValues(m_tiBMAccels->GetTextCtrl()->GetValue().ToStdString(), m_params.accelerations);
|
ParseStringValues(m_tiBMAccels->GetTextCtrl()->GetValue().ToStdString(), m_params.accelerations);
|
||||||
ParseStringValues(m_tiBMSpeeds->GetTextCtrl()->GetValue().ToStdString(), m_params.speeds);
|
ParseStringValues(m_tiBMSpeeds->GetTextCtrl()->GetValue().ToStdString(), m_params.speeds);
|
||||||
@@ -314,6 +335,9 @@ void PA_Calibration_Dlg::on_extruder_type_changed(wxCommandEvent& event) {
|
|||||||
event.Skip();
|
event.Skip();
|
||||||
}
|
}
|
||||||
void PA_Calibration_Dlg::on_method_changed(wxCommandEvent& event) {
|
void PA_Calibration_Dlg::on_method_changed(wxCommandEvent& event) {
|
||||||
|
// ORCA-Belt: only the PA Tower method is enabled on belt printers so far.
|
||||||
|
if (is_belt_printer_selected() && m_rbMethod->GetSelection() != 0)
|
||||||
|
m_rbMethod->SetSelection(0, true);
|
||||||
PA_Calibration_Dlg::reset_params();
|
PA_Calibration_Dlg::reset_params();
|
||||||
event.Skip();
|
event.Skip();
|
||||||
}
|
}
|
||||||
@@ -324,6 +348,17 @@ void PA_Calibration_Dlg::on_dpi_changed(const wxRect& suggested_rect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PA_Calibration_Dlg::on_show(wxShowEvent& event) {
|
void PA_Calibration_Dlg::on_show(wxShowEvent& event) {
|
||||||
|
// ORCA-Belt: the dialog is cached across printer switches, so refresh the
|
||||||
|
// belt restriction on every show.
|
||||||
|
if (is_belt_printer_selected()) {
|
||||||
|
m_rbMethod->SetSelection(0);
|
||||||
|
const wxString tip = _L("Not enabled yet on belt printers — use the PA Tower method instead.");
|
||||||
|
m_rbMethod->SetRadioTooltip(1, tip);
|
||||||
|
m_rbMethod->SetRadioTooltip(2, tip);
|
||||||
|
} else {
|
||||||
|
m_rbMethod->SetRadioTooltip(1, wxEmptyString);
|
||||||
|
m_rbMethod->SetRadioTooltip(2, wxEmptyString);
|
||||||
|
}
|
||||||
PA_Calibration_Dlg::reset_params();
|
PA_Calibration_Dlg::reset_params();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user