mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-18 22:42:37 +00:00
Merge branch 'main' into cad-mainline
This commit is contained in:
@@ -2982,6 +2982,8 @@ public:
|
||||
const double & opt_float(const t_config_option_key &opt_key, unsigned int idx) const;
|
||||
double & opt_float_nullable(const t_config_option_key &opt_key, unsigned int idx) { return this->option<ConfigOptionFloatsNullable>(opt_key)->get_at(idx); }
|
||||
const double & opt_float_nullable(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast<const ConfigOptionFloatsNullable *>(this->option(opt_key))->get_at(idx); }
|
||||
FloatOrPercent & opt_float_or_percent_nullable(const t_config_option_key &opt_key, unsigned int idx) { return this->option<ConfigOptionFloatsOrPercentsNullable>(opt_key)->get_at(idx); }
|
||||
const FloatOrPercent & opt_float_or_percent_nullable(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast<const ConfigOptionFloatsOrPercentsNullable *>(this->option(opt_key))->get_at(idx); }
|
||||
|
||||
int& opt_int(const t_config_option_key &opt_key) { return this->option<ConfigOptionInt>(opt_key)->value; }
|
||||
int opt_int(const t_config_option_key &opt_key) const { return dynamic_cast<const ConfigOptionInt*>(this->option(opt_key))->value; }
|
||||
|
||||
@@ -6726,6 +6726,7 @@ void GCode::append_full_config(const Print &print, std::string &str)
|
||||
"farthest_point_timelapse"sv,
|
||||
"compatible_printers"sv,
|
||||
"compatible_prints"sv,
|
||||
"filament_colour_type"sv,
|
||||
"print_host"sv,
|
||||
"print_host_webui"sv,
|
||||
"printhost_apikey"sv,
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <unordered_map>
|
||||
@@ -39,7 +40,11 @@ std::vector<ExtendedPoint<L::Dim>> estimate_points_properties(const POINTS&
|
||||
const AABBTreeLines::LinesDistancer<L>& unscaled_prev_layer,
|
||||
float flow_width,
|
||||
float max_line_length = -1.0f,
|
||||
float min_distance = -1.0f)
|
||||
float min_distance = -1.0f,
|
||||
// Maps an overhang distance onto the speed it will be printed at. Interior sampling
|
||||
// needs it to tell which of the points it could add would change the G-code, and is
|
||||
// skipped without it.
|
||||
const std::function<float(float)>& distance_to_speed = {})
|
||||
{
|
||||
bool looped = input_points.front() == input_points.back();
|
||||
std::function<size_t(size_t,size_t)> get_prev_index = [](size_t idx, size_t count) {
|
||||
@@ -120,6 +125,107 @@ std::vector<ExtendedPoint<L::Dim>> estimate_points_properties(const POINTS&
|
||||
points.push_back(next_point);
|
||||
}
|
||||
|
||||
// ORCA: Interior sampling
|
||||
// The passes below infer the support under a span from its endpoints alone, so an interior that is supported
|
||||
// differently from both ends is invisible to them: the outer perimeter of an overhang whose ends are caged by
|
||||
// full height walls reads as supported along its whole length. Probe the interior, keep the samples the
|
||||
// endpoint interpolation fails to predict, and bisect either side of each one, so a span that is only partly
|
||||
// unsupported gets points where its support actually changes instead of one reading spread across all of it.
|
||||
if (PREV_LAYER_BOUNDARY_OFFSET && ADD_INTERSECTIONS && min_distance > 0 && distance_to_speed) {
|
||||
// Probe at least this densely before treating matching samples as evidence that a span is uniform. The
|
||||
// segmentation pass below only splits lines of 2mm or more, and every pass here drops points closer
|
||||
// together than min_spacing, so finer discovery would not produce a more precise speed transition.
|
||||
const double max_probe_spacing = std::max(2., 4. * min_spacing);
|
||||
// A backstop for that length test, which on a non-finite length would never be met.
|
||||
constexpr int max_bisection_depth = 10;
|
||||
// Whether two readings are interchangeable. A segment is printed at the lower of the speeds its ends
|
||||
// read, so a sample that agrees on speed with what is already known cannot change the G-code, whatever
|
||||
// its distance says. The distances themselves are far too coarse a stand-in for this: the speed sections
|
||||
// interpolate, so readings a small fraction of min_distance apart can still be tens of mm/s apart.
|
||||
// The tolerance matches the one GCode.cpp applies when it decides a path has a variable speed at all.
|
||||
auto same_speed = [&distance_to_speed](float a, float b) {
|
||||
return std::abs(distance_to_speed(a) - distance_to_speed(b)) <= 1.f;
|
||||
};
|
||||
// Whether the first reading is printed slower than the second, once they are known to differ.
|
||||
auto prints_slower = [&distance_to_speed](float a, float b) { return distance_to_speed(a) < distance_to_speed(b); };
|
||||
|
||||
// Part of a segment still to bisect: its positions along the segment and bisections left.
|
||||
struct Subspan { double t0, t1; int depth; };
|
||||
|
||||
std::vector<ExtendedPoint<L::Dim>> sampled_points; // Populated lazily, on the first insertion
|
||||
std::vector<std::pair<double, float>> interior; // Samples of one segment, keyed by position along it
|
||||
std::vector<Subspan> pending;
|
||||
|
||||
for (size_t point_idx = 0; point_idx + 1 < points.size(); ++point_idx) {
|
||||
const ExtendedPoint<L::Dim>& curr = points[point_idx];
|
||||
const ExtendedPoint<L::Dim>& next = points[point_idx + 1];
|
||||
const Vec step = next.position - curr.position;
|
||||
const double line_len = step.norm();
|
||||
|
||||
interior.clear();
|
||||
if (line_len >= max_probe_spacing)
|
||||
pending.push_back({0., 1., max_bisection_depth});
|
||||
|
||||
while (!pending.empty()) {
|
||||
const Subspan subspan = pending.back();
|
||||
pending.pop_back();
|
||||
if (subspan.depth <= 0 || (subspan.t1 - subspan.t0) * line_len < max_probe_spacing)
|
||||
continue;
|
||||
|
||||
const double t = 0.5 * (subspan.t0 + subspan.t1);
|
||||
auto [distance, nearest_line, x] = unscaled_prev_layer.template distance_from_lines_extra<SIGNED_DISTANCE>(
|
||||
(curr.position + t * step).template cast<AABBScalar>());
|
||||
const float sampled = float(distance + boundary_offset);
|
||||
|
||||
interior.emplace_back(t, sampled);
|
||||
pending.push_back({subspan.t0, t, subspan.depth - 1});
|
||||
pending.push_back({t, subspan.t1, subspan.depth - 1});
|
||||
}
|
||||
|
||||
if (!interior.empty()) {
|
||||
std::sort(interior.begin(), interior.end(),
|
||||
[](const std::pair<double, float>& l, const std::pair<double, float>& r) { return l.first < r.first; });
|
||||
// Coarse probing keeps every sample it took until this pass can see which ones bracket a speed
|
||||
// transition. Matching samples cannot be discarded during discovery: one may be the last
|
||||
// supported point before a narrow unsupported pocket found by a later probe.
|
||||
size_t kept = 0;
|
||||
for (size_t i = 0; i < interior.size(); ++i) {
|
||||
const float sample = interior[i].second;
|
||||
const bool at_start = kept == 0; // Nothing kept yet, so the segment's own start precedes it
|
||||
const bool at_end = i + 1 == interior.size(); // And nothing follows the last sample but the segment's end
|
||||
const float before = at_start ? curr.distance : interior[kept - 1].second;
|
||||
const float after = at_end ? next.distance : interior[i + 1].second;
|
||||
// A sample is worth a point in the path only where it prints at a different speed from the
|
||||
// readings either side of it. Differing from one of the segment's own ends is not enough on
|
||||
// its own where the sample is the faster of the two: the segmentation pass below already
|
||||
// ends the slowdown an end reads, at a distance taken from how far out that end is rather
|
||||
// than from wherever bisection happened to stop, and a point here would leave the span
|
||||
// beside the end too short for that pass to run at all. Support an end cannot account for,
|
||||
// where the interior is the slower reading, is exactly what this pass is here to find.
|
||||
const bool worth_before = !same_speed(sample, before) && (!at_start || prints_slower(sample, before));
|
||||
const bool worth_after = !same_speed(sample, after) && (!at_end || prints_slower(sample, after));
|
||||
if (worth_before || worth_after)
|
||||
interior[kept++] = interior[i];
|
||||
}
|
||||
interior.resize(kept);
|
||||
}
|
||||
|
||||
if (!interior.empty() && sampled_points.empty()) {
|
||||
sampled_points.reserve(points.size() + 8);
|
||||
sampled_points.assign(points.begin(), points.begin() + point_idx + 1);
|
||||
}
|
||||
if (!sampled_points.empty()) {
|
||||
// Only a sub-span of max_probe_spacing or more is ever bisected, so these sit at least
|
||||
// 2 * min_spacing apart, and need none of the filtering the passes either side of this one do.
|
||||
for (const auto& [t, distance] : interior)
|
||||
sampled_points.push_back({curr.position + t * step, distance});
|
||||
sampled_points.push_back(next);
|
||||
}
|
||||
}
|
||||
if (!sampled_points.empty())
|
||||
points = std::move(sampled_points);
|
||||
}
|
||||
|
||||
// Segmentation handling
|
||||
if (PREV_LAYER_BOUNDARY_OFFSET && ADD_INTERSECTIONS) {
|
||||
std::vector<ExtendedPoint<L::Dim>> new_points;
|
||||
@@ -362,9 +468,28 @@ public:
|
||||
smallest_distance_with_lower_speed=-1.f;
|
||||
|
||||
// Orca: Pass to the point properties estimator the smallest ovehang distance that triggers a slowdown (smallest_distance_with_lower_speed)
|
||||
auto calculate_speed = [&speed_sections, &original_speed](float distance) {
|
||||
float final_speed;
|
||||
if (distance <= speed_sections.front().first) {
|
||||
final_speed = original_speed;
|
||||
} else if (distance >= speed_sections.back().first) {
|
||||
final_speed = speed_sections.back().second;
|
||||
} else {
|
||||
size_t section_idx = 0;
|
||||
while (distance > speed_sections[section_idx + 1].first) {
|
||||
section_idx++;
|
||||
}
|
||||
float t = (distance - speed_sections[section_idx].first) /
|
||||
(speed_sections[section_idx + 1].first - speed_sections[section_idx].first);
|
||||
t = std::clamp(t, 0.0f, 1.0f);
|
||||
final_speed = (1.0f - t) * speed_sections[section_idx].second + t * speed_sections[section_idx + 1].second;
|
||||
}
|
||||
return round(final_speed);
|
||||
};
|
||||
|
||||
std::vector<ExtendedPoint<3>> extended_points =
|
||||
estimate_points_properties<true, true, true, true>(path.polyline.points, prev_layer_boundaries[current_object], path.width, -1,
|
||||
smallest_distance_with_lower_speed);
|
||||
smallest_distance_with_lower_speed, calculate_speed);
|
||||
const auto width_inv = 1.0f / path.width;
|
||||
std::vector<ProcessedPoint> processed_points;
|
||||
processed_points.reserve(extended_points.size());
|
||||
@@ -423,25 +548,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
auto calculate_speed = [&speed_sections, &original_speed](float distance) {
|
||||
float final_speed;
|
||||
if (distance <= speed_sections.front().first) {
|
||||
final_speed = original_speed;
|
||||
} else if (distance >= speed_sections.back().first) {
|
||||
final_speed = speed_sections.back().second;
|
||||
} else {
|
||||
size_t section_idx = 0;
|
||||
while (distance > speed_sections[section_idx + 1].first) {
|
||||
section_idx++;
|
||||
}
|
||||
float t = (distance - speed_sections[section_idx].first) /
|
||||
(speed_sections[section_idx + 1].first - speed_sections[section_idx].first);
|
||||
t = std::clamp(t, 0.0f, 1.0f);
|
||||
final_speed = (1.0f - t) * speed_sections[section_idx].second + t * speed_sections[section_idx + 1].second;
|
||||
}
|
||||
return round(final_speed);
|
||||
};
|
||||
|
||||
float extrusion_speed = std::min(calculate_speed(curr.distance), calculate_speed(next.distance));
|
||||
// ORCA: Clamp resulting speed to lowest of calculated speed based on the overhang values and the current speed
|
||||
// Fixes bug where resulting overhang speed is higher than the current speed due to (for example) volumetric flow limits.
|
||||
|
||||
@@ -3246,9 +3246,9 @@ double Model::findMaxSpeed(const ModelObject* object) {
|
||||
if (objectKey == "outer_wall_speed")
|
||||
externalPerimeterSpeedObj = object->config.get().opt_float_nullable(objectKey, 0);
|
||||
if (objectKey == "small_perimeter_speed")
|
||||
smallPerimeterSpeedObj = object->config.get().opt_float_nullable(objectKey, 0);
|
||||
smallPerimeterSpeedObj = object->config.get().opt_float_or_percent_nullable(objectKey, 0).get_abs_value(externalPerimeterSpeedObj);
|
||||
if (objectKey == "small_support_perimeter_speed")
|
||||
smallSupportPerimeterSpeedObj = object->config.get().opt_float_nullable(objectKey, 0);
|
||||
smallSupportPerimeterSpeedObj = object->config.get().opt_float_or_percent_nullable(objectKey, 0).get_abs_value(supportSpeedObj);
|
||||
}
|
||||
objMaxSpeed = std::max(perimeterSpeedObj, std::max(externalPerimeterSpeedObj, std::max(infillSpeedObj, std::max(solidInfillSpeedObj, std::max(topSolidInfillSpeedObj, std::max(supportSpeedObj, std::max(smallPerimeterSpeedObj, std::max(smallSupportPerimeterSpeedObj, objMaxSpeed))))))));
|
||||
if (objMaxSpeed <= 0) objMaxSpeed = 250.;
|
||||
|
||||
@@ -49,7 +49,6 @@ static std::vector<std::string> s_project_options {
|
||||
"filament_multi_colour",
|
||||
"wipe_tower_x",
|
||||
"wipe_tower_y",
|
||||
"wipe_tower_rotation_angle",
|
||||
"curr_bed_type",
|
||||
"flush_multiplier",
|
||||
// Fast-purge mode: project-level purge control, inert at Default.
|
||||
|
||||
@@ -10426,6 +10426,16 @@ int DynamicPrintConfig::update_values_from_multi_to_multi_2(const std::vector<st
|
||||
|
||||
}
|
||||
|
||||
void set_variant_override(ConfigOptionVectorBase &target, const ConfigOptionVectorBase &source,
|
||||
const std::vector<int> &variant_index, int stride)
|
||||
{
|
||||
// A single-value object or region override applies to every nozzle variant.
|
||||
std::vector<int> indices = variant_index;
|
||||
if (source.size() == 1 && !source.is_nil(0))
|
||||
std::fill(indices.begin(), indices.end(), 0);
|
||||
target.set_to_index(&source, indices, stride);
|
||||
}
|
||||
|
||||
|
||||
//used for object/region config
|
||||
//use the smallest of multiple to single
|
||||
@@ -11503,7 +11513,7 @@ void update_static_print_config_from_dynamic(ConfigBase& config, const DynamicPr
|
||||
else {
|
||||
ConfigOptionVectorBase* opt_vec_src = static_cast<ConfigOptionVectorBase*>(opt_src);
|
||||
const ConfigOptionVectorBase* opt_vec_dest = static_cast<const ConfigOptionVectorBase*>(opt_dest);
|
||||
opt_vec_src->set_to_index(opt_vec_dest, variant_index, stride);
|
||||
set_variant_override(*opt_vec_src, *opt_vec_dest, variant_index, stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -842,6 +842,9 @@ extern std::set<std::string> printer_options_with_variant_1;
|
||||
extern std::set<std::string> printer_options_with_variant_2;
|
||||
extern std::set<std::string> empty_options;
|
||||
|
||||
void set_variant_override(ConfigOptionVectorBase &target, const ConfigOptionVectorBase &source,
|
||||
const std::vector<int> &variant_index, int stride = 1);
|
||||
|
||||
extern std::set<std::string> filament_dev_options;
|
||||
|
||||
extern void update_static_print_config_from_dynamic(ConfigBase& config, const DynamicPrintConfig& dest_config, std::vector<int> variant_index, std::set<std::string>& key_set1, int stride = 1);
|
||||
@@ -2394,6 +2397,55 @@ static void set_flush_volumes_matrix(std::vector<T> &out_matrix, const std::vect
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static bool has_zero_flush_volume_for_used_filaments(const std::vector<T> &fv_matrix,
|
||||
const std::vector<T> &flush_multipliers,
|
||||
const std::vector<int> &used_filaments)
|
||||
{
|
||||
if (used_filaments.size() < 2 || flush_multipliers.empty())
|
||||
return false;
|
||||
|
||||
if (fv_matrix.size() % flush_multipliers.size() != 0)
|
||||
return false;
|
||||
|
||||
const size_t matrix_len = fv_matrix.size() / flush_multipliers.size();
|
||||
const size_t row_len = size_t(std::sqrt(double(matrix_len)));
|
||||
if (row_len < 2 || row_len * row_len != matrix_len)
|
||||
return false;
|
||||
|
||||
std::vector<int> filtered_filaments;
|
||||
filtered_filaments.reserve(used_filaments.size());
|
||||
for (int filament_id : used_filaments) {
|
||||
if (filament_id <= 0 || filament_id > int(row_len))
|
||||
continue;
|
||||
if (std::find(filtered_filaments.begin(), filtered_filaments.end(), filament_id) == filtered_filaments.end())
|
||||
filtered_filaments.push_back(filament_id);
|
||||
}
|
||||
if (filtered_filaments.size() < 2)
|
||||
return false;
|
||||
|
||||
for (T multiplier : flush_multipliers) {
|
||||
if (multiplier == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
for (size_t nozzle_idx = 0; nozzle_idx < flush_multipliers.size(); nozzle_idx++) {
|
||||
const size_t block_offset = nozzle_idx * matrix_len;
|
||||
for (int from_id : filtered_filaments) {
|
||||
for (int to_id : filtered_filaments) {
|
||||
if (from_id == to_id)
|
||||
continue;
|
||||
|
||||
const size_t matrix_idx = block_offset + size_t(from_id - 1) * row_len + size_t(to_id - 1);
|
||||
if (matrix_idx < fv_matrix.size() && fv_matrix[matrix_idx] == 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t get_extruder_index(const GCodeConfig& config, unsigned int filament_id);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -3812,7 +3812,7 @@ static void apply_to_print_region_config(PrintRegionConfig &out, const DynamicPr
|
||||
else {
|
||||
ConfigOptionVectorBase* opt_vec_src = static_cast<ConfigOptionVectorBase*>(my_opt);
|
||||
const ConfigOptionVectorBase* opt_vec_dest = static_cast<const ConfigOptionVectorBase*>(it->second.get());
|
||||
opt_vec_src->set_to_index(opt_vec_dest, variant_index, 1);
|
||||
set_variant_override(*opt_vec_src, *opt_vec_dest, variant_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2909,7 +2909,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
float x = dynamic_cast<const ConfigOptionFloats*>(proj_cfg.option("wipe_tower_x"))->get_at(plate_id);
|
||||
float y = dynamic_cast<const ConfigOptionFloats*>(proj_cfg.option("wipe_tower_y"))->get_at(plate_id);
|
||||
float w = dynamic_cast<const ConfigOptionFloat*>(m_config->option("prime_tower_width"))->value;
|
||||
float a = dynamic_cast<const ConfigOptionFloat*>(proj_cfg.option("wipe_tower_rotation_angle"))->value;
|
||||
float a = dynamic_cast<const ConfigOptionFloat*>(m_config->option("wipe_tower_rotation_angle"))->value;
|
||||
// BBS
|
||||
float v = dynamic_cast<const ConfigOptionFloat*>(m_config->option("prime_volume"))->value;
|
||||
Vec3d plate_origin = ppl.get_plate(plate_id)->get_origin();
|
||||
@@ -10737,9 +10737,8 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
|
||||
wxString region = L"en";
|
||||
if (language.find("zh") == 0)
|
||||
region = L"zh";
|
||||
// Use the generic dual-nozzle PLA+PETG guide rather than the H2D-specific page
|
||||
// so the link is relevant for all dual-extrusion printers, not just Bambu H2D. (#12073)
|
||||
wxGetApp().open_browser_with_warning_dialog(wxString::Format(L"https://wiki.bambulab.com/%s/filament-acc/filament/pla-and-petg-dual-extrusion", region));
|
||||
// Although this link looks like it's only for the H2D, its guidance is generic.
|
||||
wxGetApp().open_browser_with_warning_dialog(wxString::Format(L"https://wiki.bambulab.com/%s/filament-acc/filament/h2d-pla-and-petg-mutual-support", region));
|
||||
return false;
|
||||
});
|
||||
}
|
||||
@@ -10873,24 +10872,14 @@ bool GLCanvas3D::is_flushing_matrix_error() {
|
||||
if (!Sidebar::should_show_SEMM_buttons())
|
||||
return false;
|
||||
|
||||
std::vector<int> plate_extruders = wxGetApp().plater()->get_partplate_list().get_curr_plate()->get_extruders(true);
|
||||
if (plate_extruders.size() < 2)
|
||||
return false;
|
||||
|
||||
const auto &project_config = wxGetApp().preset_bundle->project_config;
|
||||
const std::vector<double> &config_matrix = (project_config.option<ConfigOptionFloats>("flush_volumes_matrix"))->values;
|
||||
const std::vector<double> &config_multiplier = (project_config.option<ConfigOptionFloats>("flush_multiplier"))->values;
|
||||
|
||||
for (auto multiplier : config_multiplier) {
|
||||
if (multiplier == 0) return true;
|
||||
}
|
||||
|
||||
int matrix_len = config_matrix.size() / config_multiplier.size();
|
||||
int row_len = std::sqrt(matrix_len);
|
||||
for (int i = 0; i < config_matrix.size(); i++)
|
||||
{
|
||||
int relative_id = i % matrix_len;
|
||||
int row_id = relative_id / row_len;
|
||||
int col_id = relative_id % row_len;
|
||||
if (row_id != col_id && config_matrix[i] == 0) return true;
|
||||
}
|
||||
return false;
|
||||
return has_zero_flush_volume_for_used_filaments(config_matrix, config_multiplier, plate_extruders);
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_is_any_volume_outside() const
|
||||
|
||||
@@ -3273,15 +3273,12 @@ bool GUI_App::on_init_inner()
|
||||
}
|
||||
} */
|
||||
|
||||
copy_network_if_available();
|
||||
|
||||
if (scrn) {
|
||||
scrn->SetText(_L("Loading Plugins") + dots, 20);
|
||||
wxYield();
|
||||
}
|
||||
|
||||
on_init_network();
|
||||
|
||||
// Initialize plugins after network then register on_load callbacks so once the plugin loads finish, it gets registered automatically.
|
||||
// initialize() also installs the libslic3r hooks (capability resolver,
|
||||
// slicing-pipeline dispatcher) via plugin_hooks::install() -- no
|
||||
@@ -3310,6 +3307,9 @@ bool GUI_App::on_init_inner()
|
||||
}
|
||||
}
|
||||
|
||||
copy_network_if_available();
|
||||
on_init_network();
|
||||
|
||||
if (m_agent)
|
||||
plugin_mgr.set_cloud_agent(std::dynamic_pointer_cast<OrcaCloudServiceAgent>(m_agent->get_cloud_agent()));
|
||||
|
||||
|
||||
@@ -12706,7 +12706,7 @@ void Plater::priv::take_snapshot(const std::string& snapshot_name, const UndoRed
|
||||
ModelWipeTower& tower = model.wipe_tower;
|
||||
|
||||
tower.positions[plate_idx] = Vec2d(tower_x_opt->get_at(plate_idx), tower_y_opt->get_at(plate_idx));
|
||||
tower.rotation = proj_cfg.opt_float("wipe_tower_rotation_angle");
|
||||
tower.rotation = config.opt_float("wipe_tower_rotation_angle");
|
||||
}
|
||||
}
|
||||
const GLGizmosManager& gizmos = get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView ? assemble_view->get_canvas3d()->get_gizmos_manager() : view3D->get_canvas3d()->get_gizmos_manager();
|
||||
@@ -12816,7 +12816,7 @@ void Plater::priv::undo_redo_to(std::vector<UndoRedo::Snapshot>::const_iterator
|
||||
ModelWipeTower& tower = model.wipe_tower;
|
||||
|
||||
tower.positions[plate_idx] = Vec2d(tower_x_opt->get_at(plate_idx), tower_y_opt->get_at(plate_idx));
|
||||
tower.rotation = proj_cfg.opt_float("wipe_tower_rotation_angle");
|
||||
tower.rotation = config.opt_float("wipe_tower_rotation_angle");
|
||||
}
|
||||
}
|
||||
const int layer_range_idx = it_snapshot->snapshot_data.layer_range_idx;
|
||||
|
||||
Reference in New Issue
Block a user