mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-14 04:27:36 +00:00
Let the User Choose What the Simplified Preview Leaves Out While Dragging
The reduced toolpath set was only ever bound during a camera drag. The slider check read is_dirty(), which the slider raises inside its own render and _render_gcode() consumes at the end of the same frame, so it was never true at the start of the next one; the only time it was true was the frame after a keyboard or wheel step, which then drew one coarse frame followed by one full frame. The navigator cube and the mouse wheel never engaged it at all, and the extra frame requested on every level change was a full-detail frame drawn for nothing, since the level is chosen before the draw. The reduced buffers were also built and uploaded on every slider tick with the preference off. Dragging is now read from ImGui's active id for the sliders and from the navigator's own flag, a wheel step holds the reduced set for a 150 ms settle time with the restoring frame scheduled for when it runs out, and nothing is built while the preference is off. What the reduced set leaves out is now a choice: skip layers only, skip the internal infill roles, or keep the shell only. The shell is found geometrically at load, once per print: every layer is rasterized into a half-millimetre occupancy grid and closed so that sparse infill reads as solid, a cell is on the shell when any of its six neighbours is empty, and a segment is kept when at least half of its cells are. Being geometric it works for the wipe tower, whose every segment shares one role and which is the bulk of a tall print, where dropping roles could not. The layer stride is a preference too, one layer in every N with both ends of the visible range always whole. On the 636-layer three-filament fixture, frames during a drag against 448467 segments drawn in 334 ms at full detail: skip layers only, 1 in 4 110691 segments 83 ms skip internal infill, 1 in 4 85661 segments 68 ms shell only, 1 in 4 54382 segments 52 ms skip internal infill, every layer 347208 segments 255 ms shell only, every layer 215797 segments 170 ms The shell classification takes 63 ms once per load, split across threads. At rest the preview is pixel identical to before in every configuration, and the frame after a release or a settled wheel burst is the full one.
This commit is contained in:
@@ -202,10 +202,30 @@ void AppConfig::set_defaults()
|
||||
if (get("seq_top_layer_only").empty())
|
||||
set("seq_top_layer_only", "1");
|
||||
|
||||
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
||||
// ORCA: simplify the preview while the user is dragging: what is left out and one layer in how many is kept
|
||||
if (get("preview_reduced_detail_while_dragging").empty())
|
||||
set_bool("preview_reduced_detail_while_dragging", false);
|
||||
|
||||
{
|
||||
const std::string mode = get("preview_reduced_detail_mode");
|
||||
if (mode != "layers" && mode != "no_infill" && mode != "shell")
|
||||
set("preview_reduced_detail_mode", "no_infill");
|
||||
}
|
||||
|
||||
if (get("preview_reduced_detail_layer_stride").empty())
|
||||
set("preview_reduced_detail_layer_stride", "4");
|
||||
else {
|
||||
int stride = 4;
|
||||
try {
|
||||
stride = std::stoi(get("preview_reduced_detail_layer_stride"));
|
||||
}
|
||||
catch (...) {
|
||||
stride = 4;
|
||||
}
|
||||
set("preview_reduced_detail_layer_stride", std::to_string(std::max(1, std::min(stride, 20))));
|
||||
}
|
||||
|
||||
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
||||
if (get("preview_dim_previous_layers").empty())
|
||||
set_bool("preview_dim_previous_layers", false);
|
||||
|
||||
|
||||
@@ -158,6 +158,23 @@ enum class EGCodeExtrusionRole : uint8_t
|
||||
|
||||
static constexpr std::size_t GCODE_EXTRUSION_ROLES_COUNT = static_cast<std::size_t>(EGCodeExtrusionRole::COUNT);
|
||||
|
||||
//
|
||||
// ORCA: what the reduced toolpath set drawn while the user is dragging leaves out, on top of
|
||||
// keeping only one layer in every Viewer::get_reduced_detail_layer_stride() layers
|
||||
//
|
||||
enum class EReducedDetailMode : uint8_t
|
||||
{
|
||||
// no reduced set is built and Viewer::set_reduced_detail() has no effect
|
||||
Off,
|
||||
// every role is kept, only layers are skipped
|
||||
LayersOnly,
|
||||
// the interior infill roles are left out
|
||||
NoInternalInfill,
|
||||
// only the segments on the visible surface of the print are kept
|
||||
ShellOnly,
|
||||
COUNT
|
||||
};
|
||||
|
||||
//
|
||||
// Option types
|
||||
//
|
||||
|
||||
@@ -99,14 +99,18 @@ public:
|
||||
bool is_dim_previous_layers() const;
|
||||
void set_dim_previous_layers(bool value);
|
||||
//
|
||||
// ORCA: draw the preview from a reduced set of entities - the interior infill roles dropped and
|
||||
// one layer in every set_reduced_detail_layer_stride() kept, always keeping the top of the
|
||||
// visible layer range. Meant to be held only while the user drags the camera or a slider: the
|
||||
// reduced set is built alongside the full one, so toggling it never rebuilds anything.
|
||||
// Has no effect on the OpenGL ES path.
|
||||
// ORCA: draw the preview from a reduced set of entities: one layer in every
|
||||
// get_reduced_detail_layer_stride() layers is kept, and on top of that the mode decides which
|
||||
// roles or segments are left out. The bottom and top of the visible layer range are always kept
|
||||
// whole. Meant to be held only while the user drags the camera or a slider: the reduced set is
|
||||
// built alongside the full one, so toggling it never rebuilds anything. Nothing is built while
|
||||
// the mode is EReducedDetailMode::Off. Has no effect on the OpenGL ES path.
|
||||
//
|
||||
void set_reduced_detail(bool value);
|
||||
bool is_reduced_detail() const;
|
||||
EReducedDetailMode get_reduced_detail_mode() const;
|
||||
void set_reduced_detail_mode(EReducedDetailMode mode);
|
||||
uint32_t get_reduced_detail_layer_stride() const;
|
||||
void set_reduced_detail_layer_stride(uint32_t value);
|
||||
float get_dim_previous_layers_brightness() const;
|
||||
void set_dim_previous_layers_brightness(float value);
|
||||
|
||||
@@ -26,11 +26,12 @@ struct Settings
|
||||
float dim_previous_layers_brightness{ 0.4f };
|
||||
bool spiral_vase_mode{ false };
|
||||
// ORCA: while the user drags the camera or a slider, the preview can be drawn from a reduced
|
||||
// set of entities: the interior infill roles dropped and one layer in every
|
||||
// reduced_detail_layer_stride kept. The reduced sets are built alongside the full ones in
|
||||
// update_enabled_entities(), so turning this on and off costs nothing but a buffer binding.
|
||||
// set of entities: one layer in every reduced_detail_layer_stride kept, and on top of that
|
||||
// whatever reduced_detail_mode leaves out. The reduced sets are built alongside the full ones
|
||||
// in update_enabled_entities(), so holding reduced_detail costs nothing but a buffer binding.
|
||||
// Ignored on the OpenGL ES path, which keeps a single set of entities.
|
||||
bool reduced_detail{ false };
|
||||
EReducedDetailMode reduced_detail_mode{ EReducedDetailMode::Off };
|
||||
uint32_t reduced_detail_layer_stride{ 4 };
|
||||
//
|
||||
// Required update flags
|
||||
|
||||
@@ -87,6 +87,21 @@ bool Viewer::is_reduced_detail() const
|
||||
return m_impl->is_reduced_detail();
|
||||
}
|
||||
|
||||
EReducedDetailMode Viewer::get_reduced_detail_mode() const
|
||||
{
|
||||
return m_impl->get_reduced_detail_mode();
|
||||
}
|
||||
|
||||
void Viewer::set_reduced_detail_mode(EReducedDetailMode mode)
|
||||
{
|
||||
m_impl->set_reduced_detail_mode(mode);
|
||||
}
|
||||
|
||||
uint32_t Viewer::get_reduced_detail_layer_stride() const
|
||||
{
|
||||
return m_impl->get_reduced_detail_layer_stride();
|
||||
}
|
||||
|
||||
void Viewer::set_reduced_detail_layer_stride(uint32_t value)
|
||||
{
|
||||
m_impl->set_reduced_detail_layer_stride(value);
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <numeric>
|
||||
#include <cfloat>
|
||||
#include <future>
|
||||
#include <thread>
|
||||
|
||||
namespace libvgcode {
|
||||
|
||||
@@ -893,6 +896,7 @@ void ViewerImpl::reset()
|
||||
m_enabled_options_count = 0;
|
||||
m_enabled_segments_reduced_count = 0;
|
||||
m_enabled_options_reduced_count = 0;
|
||||
m_shell_bitset = BitSet<>();
|
||||
|
||||
m_settings_used_for_ranges = std::nullopt;
|
||||
|
||||
@@ -1170,14 +1174,254 @@ void ViewerImpl::load(GCodeInputData&& gcode_data)
|
||||
}
|
||||
|
||||
#ifndef ENABLE_OPENGL_ES
|
||||
// ORCA: the roles that sit inside the part and are hidden by its walls from every angle. Dropping
|
||||
// them is the least visible half of the reduced set built below.
|
||||
// ORCA: the roles that sit inside the part and are hidden by its walls from every angle
|
||||
static bool is_interior_infill(EGCodeExtrusionRole role)
|
||||
{
|
||||
return role == EGCodeExtrusionRole::InternalInfill ||
|
||||
role == EGCodeExtrusionRole::SolidInfill ||
|
||||
role == EGCodeExtrusionRole::InternalBridgeInfill;
|
||||
}
|
||||
|
||||
bool ViewerImpl::reduced_set_keeps(size_t i, const PathVertex& v) const
|
||||
{
|
||||
switch (m_settings.reduced_detail_mode) {
|
||||
case EReducedDetailMode::NoInternalInfill: return !is_interior_infill(v.role);
|
||||
case EReducedDetailMode::ShellOnly: return m_shell_bitset[i];
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// A 2D occupancy grid over the print's footprint, one byte per cell. Only the rectangle a layer
|
||||
// touches is ever cleared or scanned, so a grid the size of the whole print costs no more than
|
||||
// the layer needs.
|
||||
struct OccupancyGrid
|
||||
{
|
||||
int nx{ 0 };
|
||||
int ny{ 0 };
|
||||
std::vector<uint8_t> cells;
|
||||
// bounding rectangle of the set cells, inclusive; empty while min > max
|
||||
int min_x{ 0 };
|
||||
int min_y{ 0 };
|
||||
int max_x{ -1 };
|
||||
int max_y{ -1 };
|
||||
|
||||
OccupancyGrid(int nx, int ny) : nx(nx), ny(ny), cells(static_cast<size_t>(nx) * static_cast<size_t>(ny), 0) {}
|
||||
|
||||
bool empty() const { return min_x > max_x; }
|
||||
uint8_t at(int x, int y) const { return cells[static_cast<size_t>(y) * nx + x]; }
|
||||
uint8_t& at(int x, int y) { return cells[static_cast<size_t>(y) * nx + x]; }
|
||||
|
||||
void set(int x, int y) {
|
||||
at(x, y) = 1;
|
||||
if (empty()) {
|
||||
min_x = max_x = x;
|
||||
min_y = max_y = y;
|
||||
}
|
||||
else {
|
||||
min_x = std::min(min_x, x);
|
||||
max_x = std::max(max_x, x);
|
||||
min_y = std::min(min_y, y);
|
||||
max_y = std::max(max_y, y);
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
for (int y = min_y; y <= max_y; ++y)
|
||||
std::fill_n(&at(min_x, y), max_x - min_x + 1, uint8_t(0));
|
||||
min_x = min_y = 0;
|
||||
max_x = max_y = -1;
|
||||
}
|
||||
|
||||
// grow the bounding rectangle by r cells, staying inside the grid
|
||||
void grow(int r) {
|
||||
if (empty())
|
||||
return;
|
||||
min_x = std::max(0, min_x - r);
|
||||
min_y = std::max(0, min_y - r);
|
||||
max_x = std::min(nx - 1, max_x + r);
|
||||
max_y = std::min(ny - 1, max_y + r);
|
||||
}
|
||||
};
|
||||
|
||||
// Morphological closing with a square window of the given radius: fills gaps up to 2 * radius
|
||||
// cells wide, so that sparse infill or support reads as the solid area it is part of. Separable
|
||||
// passes over running window sums keep the cost linear in the rectangle's area.
|
||||
static void close_gaps(OccupancyGrid& grid, int radius, std::vector<int>& window_sum)
|
||||
{
|
||||
if (grid.empty() || radius <= 0)
|
||||
return;
|
||||
// the dilated area needs room to grow
|
||||
grid.grow(radius);
|
||||
const auto pass = [&](bool horizontal, bool dilate) {
|
||||
const int outer_n = horizontal ? grid.max_y - grid.min_y + 1 : grid.max_x - grid.min_x + 1;
|
||||
const int inner_n = horizontal ? grid.max_x - grid.min_x + 1 : grid.max_y - grid.min_y + 1;
|
||||
window_sum.assign(inner_n + 1, 0);
|
||||
for (int o = 0; o < outer_n; ++o) {
|
||||
const auto cell = [&](int i) -> uint8_t& {
|
||||
return horizontal ? grid.at(grid.min_x + i, grid.min_y + o) : grid.at(grid.min_x + o, grid.min_y + i);
|
||||
};
|
||||
for (int i = 0; i < inner_n; ++i)
|
||||
window_sum[i + 1] = window_sum[i] + cell(i);
|
||||
for (int i = 0; i < inner_n; ++i) {
|
||||
const int count = window_sum[std::min(inner_n, i + radius + 1)] - window_sum[std::max(0, i - radius)];
|
||||
// cells outside the rectangle are empty, which is what a shrinking erosion has to see
|
||||
cell(i) = dilate ? (count > 0) : (count == 2 * radius + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
pass(true, true);
|
||||
pass(false, true);
|
||||
pass(true, false);
|
||||
pass(false, false);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// ORCA: mark the extrusion segments that lie on the visible surface of the print, so that
|
||||
// EReducedDetailMode::ShellOnly can leave out everything the walls hide. Each layer is rasterized
|
||||
// into a coarse occupancy grid and closed, so that its footprint is solid whatever the infill;
|
||||
// a cell is then on the shell when it is filled and any of its six neighbours (four in the layer,
|
||||
// the layer below, the layer above) is not. A segment is kept when at least half of the cells it
|
||||
// crosses are shell cells: walls run along the shell, infill only touches it at the ends.
|
||||
// Purely geometric, so it works as well for the wipe tower, whose every segment shares one role,
|
||||
// as for the objects.
|
||||
void ViewerImpl::update_shell_bitset()
|
||||
{
|
||||
m_shell_bitset = BitSet<>(m_vertices.size());
|
||||
if (m_vertices.size() < 2 || m_layers.empty())
|
||||
return;
|
||||
|
||||
float min_x = FLT_MAX;
|
||||
float min_y = FLT_MAX;
|
||||
float max_x = -FLT_MAX;
|
||||
float max_y = -FLT_MAX;
|
||||
for (const PathVertex& v : m_vertices) {
|
||||
if (!v.is_extrusion())
|
||||
continue;
|
||||
min_x = std::min(min_x, v.position[0]);
|
||||
min_y = std::min(min_y, v.position[1]);
|
||||
max_x = std::max(max_x, v.position[0]);
|
||||
max_y = std::max(max_y, v.position[1]);
|
||||
}
|
||||
if (min_x > max_x)
|
||||
return;
|
||||
|
||||
// Half a millimetre separates a wall from the wall behind it; a print too large for that at
|
||||
// 1024 cells across gets coarser cells rather than a bigger grid. Gaps of up to 5 mm read as
|
||||
// solid: wide enough to swallow sparse infill, narrow enough to leave real holes open.
|
||||
static constexpr int MAX_CELLS = 1024;
|
||||
const float cell = std::max(0.5f, std::max(max_x - min_x, max_y - min_y) / static_cast<float>(MAX_CELLS));
|
||||
const int radius = static_cast<int>(std::ceil(2.5f / cell));
|
||||
// room for the closing to grow into, plus the neighbour lookups
|
||||
const int margin = radius + 2;
|
||||
const float origin_x = min_x - static_cast<float>(margin) * cell;
|
||||
const float origin_y = min_y - static_cast<float>(margin) * cell;
|
||||
const int nx = static_cast<int>((max_x - min_x) / cell) + 1 + 2 * margin;
|
||||
const int ny = static_cast<int>((max_y - min_y) / cell) + 1 + 2 * margin;
|
||||
|
||||
const auto cell_of = [&](float x, float y) {
|
||||
const int cx = std::clamp(static_cast<int>((x - origin_x) / cell), margin, nx - 1 - margin);
|
||||
const int cy = std::clamp(static_cast<int>((y - origin_y) / cell), margin, ny - 1 - margin);
|
||||
return std::make_pair(cx, cy);
|
||||
};
|
||||
|
||||
// calls f(cx, cy) once per cell the segment starting at vertex i passes through
|
||||
const auto for_each_cell = [&](size_t i, auto&& f) {
|
||||
const Vec3& a = m_vertices[i].position;
|
||||
const Vec3& b = m_vertices[i + 1].position;
|
||||
const float dx = b[0] - a[0];
|
||||
const float dy = b[1] - a[1];
|
||||
const int steps = static_cast<int>(std::sqrt(dx * dx + dy * dy) / (0.5f * cell)) + 1;
|
||||
int last_x = -1;
|
||||
int last_y = -1;
|
||||
for (int s = 0; s <= steps; ++s) {
|
||||
const float t = static_cast<float>(s) / static_cast<float>(steps);
|
||||
const auto [cx, cy] = cell_of(a[0] + t * dx, a[1] + t * dy);
|
||||
if (cx != last_x || cy != last_y) {
|
||||
f(cx, cy);
|
||||
last_x = cx;
|
||||
last_y = cy;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const size_t layers_count = m_layers.count();
|
||||
// the segments of a layer: [first, last), where segment i runs from vertex i to vertex i + 1
|
||||
const auto layer_segments = [&](size_t layer) {
|
||||
const size_t first = m_layer_first_vertex[layer];
|
||||
const size_t last = (layer + 1 < layers_count) ? m_layer_first_vertex[layer + 1] : m_vertices.size() - 1;
|
||||
return std::make_pair(first, std::min(last, m_vertices.size() - 1));
|
||||
};
|
||||
const auto is_drawn_extrusion = [&](size_t i) { return m_vertices[i].is_extrusion() && m_valid_lines_bitset[i]; };
|
||||
|
||||
const OccupancyGrid nothing(nx, ny);
|
||||
|
||||
// Classifies the layers in [first_layer, last_layer) and returns the segments kept. Each call
|
||||
// owns its grids, so the layer range can be split across threads.
|
||||
const auto classify_layers = [&](size_t first_layer, size_t last_layer) {
|
||||
std::vector<uint32_t> kept;
|
||||
std::vector<OccupancyGrid> footprints(3, OccupancyGrid(nx, ny));
|
||||
OccupancyGrid shell_cells(nx, ny);
|
||||
std::vector<int> window_sum;
|
||||
const auto footprint = [&](size_t layer) -> OccupancyGrid& { return footprints[layer % 3]; };
|
||||
const auto prepare = [&](size_t layer) {
|
||||
OccupancyGrid& g = footprint(layer);
|
||||
g.clear();
|
||||
const auto [first, last] = layer_segments(layer);
|
||||
for (size_t i = first; i < last; ++i) {
|
||||
if (is_drawn_extrusion(i))
|
||||
for_each_cell(i, [&](int x, int y) { g.set(x, y); });
|
||||
}
|
||||
close_gaps(g, radius, window_sum);
|
||||
};
|
||||
|
||||
if (first_layer > 0)
|
||||
prepare(first_layer - 1);
|
||||
prepare(first_layer);
|
||||
for (size_t layer = first_layer; layer < last_layer; ++layer) {
|
||||
if (layer + 1 < layers_count)
|
||||
prepare(layer + 1);
|
||||
const OccupancyGrid& below = (layer > 0) ? footprint(layer - 1) : nothing;
|
||||
const OccupancyGrid& cur = footprint(layer);
|
||||
const OccupancyGrid& above = (layer + 1 < layers_count) ? footprint(layer + 1) : nothing;
|
||||
|
||||
shell_cells.clear();
|
||||
for (int y = cur.min_y; y <= cur.max_y; ++y) {
|
||||
for (int x = cur.min_x; x <= cur.max_x; ++x) {
|
||||
if (!cur.at(x, y))
|
||||
continue;
|
||||
if (!cur.at(x - 1, y) || !cur.at(x + 1, y) || !cur.at(x, y - 1) || !cur.at(x, y + 1) ||
|
||||
!below.at(x, y) || !above.at(x, y))
|
||||
shell_cells.set(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
const auto [first, last] = layer_segments(layer);
|
||||
for (size_t i = first; i < last; ++i) {
|
||||
if (!is_drawn_extrusion(i))
|
||||
continue;
|
||||
int total = 0;
|
||||
int on_shell = 0;
|
||||
for_each_cell(i, [&](int x, int y) { ++total; on_shell += shell_cells.at(x, y); });
|
||||
if (2 * on_shell >= total)
|
||||
kept.push_back(static_cast<uint32_t>(i));
|
||||
}
|
||||
}
|
||||
return kept;
|
||||
};
|
||||
|
||||
const size_t workers = std::clamp<size_t>(std::thread::hardware_concurrency(), 1, 8);
|
||||
const size_t chunk = std::max<size_t>(16, (layers_count + workers - 1) / workers);
|
||||
std::vector<std::future<std::vector<uint32_t>>> futures;
|
||||
for (size_t first = 0; first < layers_count; first += chunk)
|
||||
futures.emplace_back(std::async(std::launch::async, classify_layers, first, std::min(layers_count, first + chunk)));
|
||||
for (auto& f : futures) {
|
||||
for (uint32_t i : f.get())
|
||||
m_shell_bitset.set(i);
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_OPENGL_ES
|
||||
|
||||
void ViewerImpl::update_enabled_entities()
|
||||
@@ -1189,12 +1433,17 @@ void ViewerImpl::update_enabled_entities()
|
||||
std::vector<uint32_t> enabled_options;
|
||||
#ifndef ENABLE_OPENGL_ES
|
||||
// ORCA: the reduced sets are filled by the same walk, so switching to them costs no rebuild.
|
||||
const bool build_reduced = m_settings.reduced_detail_mode != EReducedDetailMode::Off;
|
||||
std::vector<uint32_t> enabled_segments_reduced;
|
||||
std::vector<uint32_t> enabled_options_reduced;
|
||||
const uint32_t layer_stride = std::max<uint32_t>(1, m_settings.reduced_detail_layer_stride);
|
||||
// Whatever else is dropped, the top of the visible layer range is kept whole: it is the surface
|
||||
// the user is looking at, and in top-layer-only mode it is the only layer drawn at full color.
|
||||
const uint32_t kept_layer = m_layers.get_view_range()[1];
|
||||
// Whatever else is dropped, both ends of the visible layer range are kept whole: the top is the
|
||||
// surface the user is looking at, and the only layer drawn at full color in top-layer-only
|
||||
// mode; the bottom is exposed whenever the range is cut short.
|
||||
const Interval& layers_range = m_layers.get_view_range();
|
||||
if (build_reduced && m_settings.reduced_detail_mode == EReducedDetailMode::ShellOnly &&
|
||||
m_shell_bitset.size != m_vertices.size())
|
||||
update_shell_bitset();
|
||||
#endif // ENABLE_OPENGL_ES
|
||||
Interval range = m_view_range.get_visible();
|
||||
|
||||
@@ -1245,11 +1494,14 @@ void ViewerImpl::update_enabled_entities()
|
||||
enabled_segments.push_back(static_cast<uint32_t>(i));
|
||||
|
||||
#ifndef ENABLE_OPENGL_ES
|
||||
if (v.layer_id != kept_layer && (v.layer_id % layer_stride) != 0)
|
||||
if (!build_reduced)
|
||||
continue;
|
||||
const bool whole_layer = v.layer_id == layers_range[0] || v.layer_id == layers_range[1];
|
||||
if (!whole_layer && (v.layer_id % layer_stride) != 0)
|
||||
continue;
|
||||
if (v.is_option())
|
||||
enabled_options_reduced.push_back(static_cast<uint32_t>(i));
|
||||
else if (!(v.is_extrusion() && is_interior_infill(v.role)))
|
||||
else if (whole_layer || !v.is_extrusion() || reduced_set_keeps(i, v))
|
||||
enabled_segments_reduced.push_back(static_cast<uint32_t>(i));
|
||||
#endif // ENABLE_OPENGL_ES
|
||||
}
|
||||
@@ -1283,15 +1535,17 @@ void ViewerImpl::update_enabled_entities()
|
||||
m_enabled_segments_reduced_count = enabled_segments_reduced.size();
|
||||
m_enabled_options_reduced_count = enabled_options_reduced.size();
|
||||
|
||||
assert(m_enabled_segments_reduced_buf_id > 0);
|
||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_buf_id));
|
||||
glsafe(glBufferData(GL_TEXTURE_BUFFER, enabled_segments_reduced.size() * sizeof(uint32_t),
|
||||
enabled_segments_reduced.empty() ? nullptr : enabled_segments_reduced.data(), GL_STATIC_DRAW));
|
||||
if (build_reduced) {
|
||||
assert(m_enabled_segments_reduced_buf_id > 0);
|
||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_buf_id));
|
||||
glsafe(glBufferData(GL_TEXTURE_BUFFER, enabled_segments_reduced.size() * sizeof(uint32_t),
|
||||
enabled_segments_reduced.empty() ? nullptr : enabled_segments_reduced.data(), GL_STATIC_DRAW));
|
||||
|
||||
assert(m_enabled_options_reduced_buf_id > 0);
|
||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_options_reduced_buf_id));
|
||||
glsafe(glBufferData(GL_TEXTURE_BUFFER, enabled_options_reduced.size() * sizeof(uint32_t),
|
||||
enabled_options_reduced.empty() ? nullptr : enabled_options_reduced.data(), GL_STATIC_DRAW));
|
||||
assert(m_enabled_options_reduced_buf_id > 0);
|
||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_options_reduced_buf_id));
|
||||
glsafe(glBufferData(GL_TEXTURE_BUFFER, enabled_options_reduced.size() * sizeof(uint32_t),
|
||||
enabled_options_reduced.empty() ? nullptr : enabled_options_reduced.data(), GL_STATIC_DRAW));
|
||||
}
|
||||
|
||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
||||
#endif // ENABLE_OPENGL_ES
|
||||
@@ -1471,8 +1725,16 @@ void ViewerImpl::toggle_top_layer_only_view_range()
|
||||
update_colors_texture();
|
||||
}
|
||||
|
||||
// ORCA: how many layers the reduced set keeps one of. Changing it changes which vertices land in
|
||||
// the reduced set, so the sets have to be rebuilt.
|
||||
// ORCA: what the reduced set leaves out, and how many layers it keeps one of. Either changes which
|
||||
// vertices land in the reduced set, so the sets have to be rebuilt.
|
||||
void ViewerImpl::set_reduced_detail_mode(EReducedDetailMode mode)
|
||||
{
|
||||
if (m_settings.reduced_detail_mode == mode)
|
||||
return;
|
||||
m_settings.reduced_detail_mode = mode;
|
||||
m_settings.update_enabled_entities = true;
|
||||
}
|
||||
|
||||
void ViewerImpl::set_reduced_detail_layer_stride(uint32_t value)
|
||||
{
|
||||
value = std::max<uint32_t>(1, value);
|
||||
|
||||
@@ -97,6 +97,9 @@ public:
|
||||
//
|
||||
void set_reduced_detail(bool value) { m_settings.reduced_detail = value; }
|
||||
bool is_reduced_detail() const { return m_settings.reduced_detail; }
|
||||
EReducedDetailMode get_reduced_detail_mode() const { return m_settings.reduced_detail_mode; }
|
||||
void set_reduced_detail_mode(EReducedDetailMode mode);
|
||||
uint32_t get_reduced_detail_layer_stride() const { return m_settings.reduced_detail_layer_stride; }
|
||||
void set_reduced_detail_layer_stride(uint32_t value);
|
||||
float get_dim_previous_layers_brightness() const { return m_settings.dim_previous_layers_brightness; }
|
||||
void set_dim_previous_layers_brightness(float value);
|
||||
@@ -316,6 +319,13 @@ private:
|
||||
// Variables used for toolpaths visibiliity
|
||||
//
|
||||
BitSet<> m_valid_lines_bitset;
|
||||
#ifndef ENABLE_OPENGL_ES
|
||||
//
|
||||
// ORCA: bit set for the extrusion segments that lie on the visible surface of the print,
|
||||
// computed on demand by update_shell_bitset() for EReducedDetailMode::ShellOnly
|
||||
//
|
||||
BitSet<> m_shell_bitset;
|
||||
#endif // ENABLE_OPENGL_ES
|
||||
//
|
||||
// Variables used for toolpaths coloring
|
||||
//
|
||||
@@ -505,25 +515,33 @@ private:
|
||||
size_t m_enabled_segments_tex_size{ 0 };
|
||||
size_t m_enabled_options_tex_size{ 0 };
|
||||
|
||||
// The set the next draw reads from: the reduced one only while the user is dragging.
|
||||
// The set the next draw reads from: the reduced one only while the user is dragging, and only
|
||||
// if a reduced set is being built at all.
|
||||
bool use_reduced_set() const {
|
||||
return m_settings.reduced_detail && m_settings.reduced_detail_mode != EReducedDetailMode::Off;
|
||||
}
|
||||
size_t active_segments_count() const {
|
||||
return m_settings.reduced_detail ? m_enabled_segments_reduced_count : m_enabled_segments_count;
|
||||
return use_reduced_set() ? m_enabled_segments_reduced_count : m_enabled_segments_count;
|
||||
}
|
||||
unsigned int active_segments_buf_id() const {
|
||||
return m_settings.reduced_detail ? m_enabled_segments_reduced_buf_id : m_enabled_segments_buf_id;
|
||||
return use_reduced_set() ? m_enabled_segments_reduced_buf_id : m_enabled_segments_buf_id;
|
||||
}
|
||||
unsigned int active_segments_tex_id() const {
|
||||
return m_settings.reduced_detail ? m_enabled_segments_reduced_tex_id : m_enabled_segments_tex_id;
|
||||
return use_reduced_set() ? m_enabled_segments_reduced_tex_id : m_enabled_segments_tex_id;
|
||||
}
|
||||
size_t active_options_count() const {
|
||||
return m_settings.reduced_detail ? m_enabled_options_reduced_count : m_enabled_options_count;
|
||||
return use_reduced_set() ? m_enabled_options_reduced_count : m_enabled_options_count;
|
||||
}
|
||||
unsigned int active_options_buf_id() const {
|
||||
return m_settings.reduced_detail ? m_enabled_options_reduced_buf_id : m_enabled_options_buf_id;
|
||||
return use_reduced_set() ? m_enabled_options_reduced_buf_id : m_enabled_options_buf_id;
|
||||
}
|
||||
unsigned int active_options_tex_id() const {
|
||||
return m_settings.reduced_detail ? m_enabled_options_reduced_tex_id : m_enabled_options_tex_id;
|
||||
return use_reduced_set() ? m_enabled_options_reduced_tex_id : m_enabled_options_tex_id;
|
||||
}
|
||||
|
||||
// Whether the segment starting at vertex i belongs to the reduced set under the current mode
|
||||
bool reduced_set_keeps(size_t i, const PathVertex& v) const;
|
||||
void update_shell_bitset();
|
||||
#endif // ENABLE_OPENGL_ES
|
||||
|
||||
void update_view_full_range();
|
||||
|
||||
@@ -1172,8 +1172,13 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
|
||||
if (current_top_layer_only != required_top_layer_only)
|
||||
m_viewer.toggle_top_layer_only_view_range();
|
||||
|
||||
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
||||
// ORCA: simplify the preview while the user is dragging
|
||||
m_reduced_detail_while_dragging = get_app_config()->get_bool("preview_reduced_detail_while_dragging");
|
||||
m_reduced_detail_mode = reduced_detail_mode_from_string(get_app_config()->get("preview_reduced_detail_mode"));
|
||||
m_reduced_detail_layer_stride = static_cast<unsigned int>(std::max(1, std::stoi(get_app_config()->get("preview_reduced_detail_layer_stride"))));
|
||||
apply_reduced_detail_settings();
|
||||
|
||||
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
||||
m_viewer.set_dim_previous_layers(get_app_config()->get_bool("preview_dim_previous_layers"));
|
||||
m_viewer.set_dim_previous_layers_brightness(0.01f * std::stoi(get_app_config()->get("preview_dim_previous_layers_brightness")));
|
||||
|
||||
@@ -1908,13 +1913,44 @@ void GCodeViewer::update_layers_slider_mode()
|
||||
// TODO m_layers_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder);
|
||||
}
|
||||
|
||||
bool GCodeViewer::set_interacting(bool interacting)
|
||||
void GCodeViewer::set_interacting(bool interacting)
|
||||
{
|
||||
const bool reduced = m_reduced_detail_while_dragging && interacting;
|
||||
if (reduced == m_viewer.is_reduced_detail())
|
||||
return false;
|
||||
m_viewer.set_reduced_detail(reduced);
|
||||
return true;
|
||||
m_viewer.set_reduced_detail(m_reduced_detail_while_dragging && interacting);
|
||||
}
|
||||
|
||||
// ORCA: libvgcode only builds a reduced set while its mode is not Off, so the preference switch is
|
||||
// folded into the mode it is given. Every setter goes through here.
|
||||
void GCodeViewer::apply_reduced_detail_settings()
|
||||
{
|
||||
m_viewer.set_reduced_detail_mode(m_reduced_detail_while_dragging ? m_reduced_detail_mode : libvgcode::EReducedDetailMode::Off);
|
||||
m_viewer.set_reduced_detail_layer_stride(m_reduced_detail_layer_stride);
|
||||
}
|
||||
|
||||
void GCodeViewer::set_reduced_detail_while_dragging(bool value)
|
||||
{
|
||||
m_reduced_detail_while_dragging = value;
|
||||
apply_reduced_detail_settings();
|
||||
}
|
||||
|
||||
void GCodeViewer::set_reduced_detail_mode(const std::string& mode)
|
||||
{
|
||||
m_reduced_detail_mode = reduced_detail_mode_from_string(mode);
|
||||
apply_reduced_detail_settings();
|
||||
}
|
||||
|
||||
void GCodeViewer::set_reduced_detail_layer_stride(unsigned int value)
|
||||
{
|
||||
m_reduced_detail_layer_stride = std::max(1u, value);
|
||||
apply_reduced_detail_settings();
|
||||
}
|
||||
|
||||
libvgcode::EReducedDetailMode GCodeViewer::reduced_detail_mode_from_string(const std::string& mode)
|
||||
{
|
||||
if (mode == "layers")
|
||||
return libvgcode::EReducedDetailMode::LayersOnly;
|
||||
if (mode == "shell")
|
||||
return libvgcode::EReducedDetailMode::ShellOnly;
|
||||
return libvgcode::EReducedDetailMode::NoInternalInfill;
|
||||
}
|
||||
|
||||
void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range)
|
||||
|
||||
@@ -230,8 +230,11 @@ private:
|
||||
|
||||
bool m_legend_visible{ true };
|
||||
bool m_legend_enabled{ true };
|
||||
// ORCA: whether the reduced-detail-while-dragging preference is on
|
||||
// ORCA: the reduced-detail-while-dragging preferences, pushed to libvgcode by apply_reduced_detail_settings()
|
||||
bool m_reduced_detail_while_dragging{ false };
|
||||
libvgcode::EReducedDetailMode m_reduced_detail_mode{ libvgcode::EReducedDetailMode::NoInternalInfill };
|
||||
unsigned int m_reduced_detail_layer_stride{ 4 };
|
||||
void apply_reduced_detail_settings();
|
||||
|
||||
float m_legend_height;
|
||||
PrintEstimatedStatistics m_print_statistics;
|
||||
@@ -342,10 +345,14 @@ public:
|
||||
float get_dim_previous_layers_brightness() const { return m_viewer.get_dim_previous_layers_brightness(); }
|
||||
|
||||
// ORCA: while the user drags the camera or a slider, draw the preview from libvgcode's reduced
|
||||
// toolpath set. Returns true when the detail level actually changed, so the caller can queue the
|
||||
// frame that puts the full detail back.
|
||||
bool set_interacting(bool interacting);
|
||||
void set_reduced_detail_while_dragging(bool value) { m_reduced_detail_while_dragging = value; }
|
||||
// toolpath set, if the preference asks for one
|
||||
void set_interacting(bool interacting);
|
||||
bool is_reduced_detail() const { return m_viewer.is_reduced_detail(); }
|
||||
void set_reduced_detail_while_dragging(bool value);
|
||||
// the preference's string value: "layers", "no_infill" or "shell"
|
||||
void set_reduced_detail_mode(const std::string& mode);
|
||||
void set_reduced_detail_layer_stride(unsigned int value);
|
||||
static libvgcode::EReducedDetailMode reduced_detail_mode_from_string(const std::string& mode);
|
||||
|
||||
void set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range);
|
||||
|
||||
|
||||
@@ -3240,6 +3240,12 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
||||
m_dirty |= imgui_requires_extra_frame;
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
m_dirty |= GLTexture::Compressor::has_compressed_texture_to_refresh();
|
||||
// ORCA: the render timer only wakes the idle loop; the frame that puts the preview's full detail
|
||||
// back after a wheel burst has to be asked for here, once the settle time is really up
|
||||
if (m_preview_settle_pending && std::chrono::steady_clock::now() >= m_preview_interaction_until) {
|
||||
m_preview_settle_pending = false;
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
if (!m_dirty)
|
||||
return;
|
||||
@@ -3945,6 +3951,9 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
||||
evt.SetY(evt.GetY() * scale);
|
||||
#endif
|
||||
|
||||
if (m_canvas_type == CanvasPreview)
|
||||
note_preview_interaction();
|
||||
|
||||
if (wxGetApp().imgui()->update_mouse_data(evt)) {
|
||||
if (m_canvas_type == CanvasPreview) {
|
||||
IMSlider* m_layers_slider = get_gcode_viewer().get_layers_slider();
|
||||
@@ -4051,6 +4060,11 @@ void GLCanvas3D::on_set_color_timer(wxTimerEvent& evt)
|
||||
}
|
||||
|
||||
|
||||
void GLCanvas3D::note_preview_interaction()
|
||||
{
|
||||
m_preview_interaction_until = std::chrono::steady_clock::now() + std::chrono::milliseconds(150);
|
||||
}
|
||||
|
||||
void GLCanvas3D::schedule_extra_frame(int milliseconds)
|
||||
{
|
||||
// Schedule idle event right now
|
||||
@@ -8513,11 +8527,19 @@ void GLCanvas3D::_render_gcode(int canvas_width, int canvas_height)
|
||||
IMSlider *layers_slider = m_gcode_viewer.get_layers_slider();
|
||||
IMSlider *moves_slider = m_gcode_viewer.get_moves_slider();
|
||||
|
||||
// ORCA: dragging the camera or either slider is the only time the preview has to keep up with
|
||||
// continuous input, so that is when the reduced toolpath set earns its visible coarseness.
|
||||
// A change of detail level needs one more frame to draw the result of the change.
|
||||
if (m_gcode_viewer.set_interacting(m_mouse.dragging || layers_slider->is_dirty() || moves_slider->is_dirty()))
|
||||
request_extra_frame();
|
||||
// ORCA: dragging the camera, the navigator or either slider is when the preview has to keep up
|
||||
// with continuous input, so that is when the reduced toolpath set earns its visible coarseness.
|
||||
// A wheel step has no duration, so it holds the reduced set for a settle time instead, and the
|
||||
// frame that restores the full detail is scheduled for when that time runs out. The level is
|
||||
// chosen before the draw, so a change lands in this very frame.
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const bool settling = now < m_preview_interaction_until;
|
||||
const bool dragging = m_mouse.dragging || m_navigator_dragging || layers_slider->is_dragging() || moves_slider->is_dragging();
|
||||
m_gcode_viewer.set_interacting(dragging || settling);
|
||||
if (settling && !dragging && m_gcode_viewer.is_reduced_detail()) {
|
||||
m_preview_settle_pending = true;
|
||||
schedule_extra_frame(static_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(m_preview_interaction_until - now).count()) + 1);
|
||||
}
|
||||
|
||||
m_gcode_viewer.render(canvas_width, canvas_height, SLIDER_RIGHT_MARGIN * GCODE_VIEWER_SLIDER_SCALE);
|
||||
|
||||
|
||||
@@ -591,6 +591,10 @@ private:
|
||||
ECursorType m_cursor_type;
|
||||
GLSelectionRectangle m_rectangle_selection;
|
||||
bool m_navigator_dragging{ false };
|
||||
// ORCA: until when a discrete preview interaction (a wheel step) keeps the reduced toolpath set bound
|
||||
std::chrono::time_point<std::chrono::steady_clock> m_preview_interaction_until{};
|
||||
// whether the frame that restores the full detail once that time is up is still owed
|
||||
bool m_preview_settle_pending{ false };
|
||||
|
||||
//BBS:add plate related logic
|
||||
mutable std::vector<int> m_hover_volume_idxs;
|
||||
@@ -1138,6 +1142,9 @@ public:
|
||||
void msw_rescale() { m_gcode_viewer.invalidate_legend(); }
|
||||
|
||||
void request_extra_frame() { m_extra_frame_requested = true; }
|
||||
// ORCA: a wheel step is over before the next frame, so it holds the reduced preview for a short
|
||||
// settle time instead: a burst of steps stays cheap and the full frame lands once they stop
|
||||
void note_preview_interaction();
|
||||
|
||||
void schedule_extra_frame(int milliseconds);
|
||||
|
||||
|
||||
@@ -482,6 +482,11 @@ void IMSlider::draw_background_and_groove(const ImRect& bg_rect, const ImRect& g
|
||||
ImGui::RenderFrame(groove.Min, groove.Max, groove_col, false, 0.5 * groove.GetWidth());
|
||||
}
|
||||
|
||||
bool IMSlider::is_dragging() const
|
||||
{
|
||||
return GImGui != nullptr && m_imgui_id != 0 && GImGui->ActiveId == m_imgui_id && GImGui->IO.MouseDown[0];
|
||||
}
|
||||
|
||||
bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int v_max, const ImVec2& size, float scale)
|
||||
{
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
@@ -490,6 +495,7 @@ bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int
|
||||
|
||||
ImGuiContext& context = *GImGui;
|
||||
const ImGuiID id = window->GetID(str_id);
|
||||
m_imgui_id = id;
|
||||
|
||||
const ImVec2 pos = window->DC.CursorPos;
|
||||
const ImRect draw_region(pos, pos + size);
|
||||
@@ -882,6 +888,7 @@ bool IMSlider::vertical_slider(const char* str_id, int* higher_value, int* lower
|
||||
|
||||
ImGuiContext& context = *GImGui;
|
||||
const ImGuiID id = window->GetID(str_id);
|
||||
m_imgui_id = id;
|
||||
|
||||
const ImVec2 pos = window->DC.CursorPos;
|
||||
const ImRect draw_region(pos, pos + size);
|
||||
|
||||
@@ -118,6 +118,9 @@ public:
|
||||
|
||||
//BBS update scroll value changed
|
||||
bool is_dirty() { return m_dirty; }
|
||||
// ORCA: whether the mouse is currently holding this slider's handle. Read from ImGui's active
|
||||
// id rather than from the dirty flag, which is raised and consumed inside a single frame.
|
||||
bool is_dragging() const;
|
||||
void set_as_dirty(bool dirty = true) { m_dirty = dirty; }
|
||||
bool is_need_post_tick_event() { return m_is_need_post_tick_changed_event; }
|
||||
void reset_post_tick_event(bool val = false) {
|
||||
@@ -182,6 +185,8 @@ private:
|
||||
int m_higher_value;
|
||||
int m_one_layer_value; // ORCA
|
||||
bool m_dirty = false;
|
||||
// ORCA: the ImGui id of the slider widget, as of its last render
|
||||
unsigned int m_imgui_id = 0;
|
||||
|
||||
bool m_render_as_disabled{ false };
|
||||
|
||||
|
||||
@@ -318,7 +318,7 @@ wxBoxSizer* PreferencesDialog::create_item_combobox(wxString title, wxString too
|
||||
return sizer;
|
||||
}
|
||||
|
||||
wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::vector<std::string> config_name_index, const wxString wiki_url)
|
||||
wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::vector<std::string> config_name_index, std::function<void(std::string)> onchange, const wxString wiki_url)
|
||||
{
|
||||
assert(vlist.size() == config_name_index.size());
|
||||
unsigned int current_index = 0;
|
||||
@@ -333,9 +333,16 @@ wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxString too
|
||||
|
||||
auto [sizer, combobox] = create_item_combobox_base(title, tooltip, param, vlist, current_index);
|
||||
|
||||
// ORCA: this one is only meaningful while the simplification it configures is enabled
|
||||
if (param == "preview_reduced_detail_mode") {
|
||||
m_reduced_detail_mode_combo = combobox;
|
||||
combobox->Enable(app_config->get_bool("preview_reduced_detail_while_dragging"));
|
||||
}
|
||||
|
||||
//// save config
|
||||
combobox->GetDropDown().Bind(wxEVT_COMBOBOX, [this, param, config_name_index](wxCommandEvent& e) {
|
||||
combobox->GetDropDown().Bind(wxEVT_COMBOBOX, [this, param, config_name_index, onchange](wxCommandEvent& e) {
|
||||
app_config->set(param, config_name_index[e.GetSelection()]);
|
||||
if (onchange != nullptr) onchange(config_name_index[e.GetSelection()]);
|
||||
e.Skip();
|
||||
});
|
||||
|
||||
@@ -700,11 +707,15 @@ wxBoxSizer *PreferencesDialog::create_item_spinctrl(wxString title, wxString tit
|
||||
auto input = new SpinInput(m_parent, wxEmptyString, side_label, wxDefaultPosition, DESIGN_INPUT_SIZE, wxSP_ARROW_KEYS, min, max, stoi(app_config->get(param)));
|
||||
input->SetToolTip(tip);
|
||||
|
||||
// ORCA: this one is only meaningful while the dimming it controls is enabled
|
||||
// ORCA: these are only meaningful while the option they belong to is enabled
|
||||
if (param == "preview_dim_previous_layers_brightness") {
|
||||
m_dim_previous_layers_brightness_input = input;
|
||||
input->Enable(app_config->get_bool("preview_dim_previous_layers"));
|
||||
}
|
||||
else if (param == "preview_reduced_detail_layer_stride") {
|
||||
m_reduced_detail_layer_stride_input = input;
|
||||
input->Enable(app_config->get_bool("preview_reduced_detail_while_dragging"));
|
||||
}
|
||||
|
||||
m_sizer->Add(input, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
@@ -1058,6 +1069,10 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too
|
||||
}
|
||||
// ORCA: apply the reduced-detail preference immediately to the currently loaded preview
|
||||
else if (param == "preview_reduced_detail_while_dragging") {
|
||||
if (m_reduced_detail_mode_combo)
|
||||
m_reduced_detail_mode_combo->Enable(app_config->get_bool(param));
|
||||
if (m_reduced_detail_layer_stride_input)
|
||||
m_reduced_detail_layer_stride_input->Enable(app_config->get_bool(param));
|
||||
if (Plater* plater = wxGetApp().plater()) {
|
||||
if (GLCanvas3D* canvas = plater->get_preview_canvas3D()) {
|
||||
canvas->get_gcode_viewer().set_reduced_detail_while_dragging(app_config->get_bool(param));
|
||||
@@ -1950,11 +1965,57 @@ void PreferencesDialog::create_items()
|
||||
|
||||
auto item_reduced_detail_while_dragging = create_item_checkbox(
|
||||
_L("Simplify preview while dragging"),
|
||||
_L("While dragging the camera or a preview slider, draw only part of the toolpaths so that large prints stay responsive. Internal infill is left out and only every fourth layer is drawn, and the full detail is restored as soon as you let go."),
|
||||
_L("While dragging the camera or a preview slider, or zooming with the mouse wheel, draw only part of the toolpaths so that large prints stay responsive. "
|
||||
"The two options below choose what is left out. The full detail is restored as soon as you let go."),
|
||||
"preview_reduced_detail_while_dragging"
|
||||
);
|
||||
g_sizer->Add(item_reduced_detail_while_dragging);
|
||||
|
||||
auto item_reduced_detail_mode = create_item_combobox(
|
||||
_L("Simplification"),
|
||||
_L("What the simplified preview leaves out while dragging, on top of skipping layers.\n"
|
||||
"Skip layers only: every toolpath of the drawn layers is kept.\n"
|
||||
"Skip internal infill: sparse and solid infill hidden inside the walls is left out.\n"
|
||||
"Shell only: only the toolpaths on the visible surface of the print are drawn, including the outside of the prime tower. "
|
||||
"Removes the most, and holes narrower than 5 mm are treated as solid."),
|
||||
"preview_reduced_detail_mode",
|
||||
{_L("Skip layers only"), _L("Skip internal infill"), _L("Shell only")},
|
||||
{"layers", "no_infill", "shell"},
|
||||
// ORCA: apply the new mode immediately to the currently loaded preview
|
||||
[](std::string value) {
|
||||
if (Plater* plater = wxGetApp().plater()) {
|
||||
if (GLCanvas3D* canvas = plater->get_preview_canvas3D()) {
|
||||
canvas->get_gcode_viewer().set_reduced_detail_mode(value);
|
||||
canvas->set_as_dirty();
|
||||
canvas->request_extra_frame();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
g_sizer->Add(item_reduced_detail_mode);
|
||||
|
||||
auto item_reduced_detail_layer_stride = create_item_spinctrl(
|
||||
_L("Draw one layer in every"),
|
||||
"",
|
||||
_L("layers"),
|
||||
_L("How many layers the simplified preview keeps one of while dragging. 1 draws every layer, 4 draws every fourth. "
|
||||
"The bottom and top of the visible layer range are always drawn whole."),
|
||||
"preview_reduced_detail_layer_stride",
|
||||
1,
|
||||
20,
|
||||
// ORCA: apply the new stride immediately to the currently loaded preview
|
||||
[](int value) {
|
||||
if (Plater* plater = wxGetApp().plater()) {
|
||||
if (GLCanvas3D* canvas = plater->get_preview_canvas3D()) {
|
||||
canvas->get_gcode_viewer().set_reduced_detail_layer_stride(static_cast<unsigned int>(value));
|
||||
canvas->set_as_dirty();
|
||||
canvas->request_extra_frame();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
g_sizer->Add(item_reduced_detail_layer_stride);
|
||||
|
||||
auto item_dim_previous_layers = create_item_checkbox(
|
||||
_L("Dim lower layers"),
|
||||
_L("When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."),
|
||||
|
||||
@@ -73,6 +73,8 @@ public:
|
||||
::CheckBox * m_bambu_cloud_checkbox = {nullptr};
|
||||
::TextInput *m_backup_interval_textinput = {nullptr};
|
||||
::SpinInput *m_dim_previous_layers_brightness_input = {nullptr};
|
||||
::ComboBox * m_reduced_detail_mode_combo = {nullptr};
|
||||
::SpinInput *m_reduced_detail_layer_stride_input = {nullptr};
|
||||
::ComboBox * m_network_version_combo = {nullptr};
|
||||
std::vector<NetworkLibraryVersionInfo> m_available_versions;
|
||||
|
||||
@@ -86,7 +88,7 @@ public:
|
||||
wxBoxSizer *create_item_title(wxString title);
|
||||
wxBoxSizer *create_item_label(wxString label, const wxString tooltip = "", const wxString wiki_url = "");
|
||||
wxBoxSizer *create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::function<void(wxString)> onchange = {}, const wxString wiki_url = "");
|
||||
wxBoxSizer *create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::vector<std::string> config_name_index, const wxString wiki_url = "");
|
||||
wxBoxSizer *create_item_combobox(wxString title, wxString tooltip, std::string param, std::vector<wxString> vlist, std::vector<std::string> config_name_index, std::function<void(std::string)> onchange = {}, const wxString wiki_url = "");
|
||||
wxBoxSizer *create_item_region_combobox(wxString title, wxString tooltip);
|
||||
wxBoxSizer *create_item_language_combobox(wxString title, wxString tooltip);
|
||||
wxBoxSizer *create_item_loglevel_combobox(wxString title, wxString tooltip, std::vector<wxString> vlist);
|
||||
|
||||
Reference in New Issue
Block a user