mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-28 11:21:30 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a7e8e784c | ||
|
|
c09902aa44 | ||
|
|
bd3140d202 | ||
|
|
88586eea44 |
@@ -44,6 +44,14 @@ jobs:
|
|||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: ${{ inputs.artifact }}
|
name: ${{ inputs.artifact }}
|
||||||
|
# run_unit_tests.sh installs the plugin tests' numpy with the uv the build stages
|
||||||
|
# beside them; the Windows arm64 build bundles none, so put one on PATH there.
|
||||||
|
- name: Install uv
|
||||||
|
if: runner.os == 'Windows' && runner.arch == 'ARM64'
|
||||||
|
uses: astral-sh/setup-uv@v10.2.0
|
||||||
|
with:
|
||||||
|
version: "0.11.21" # ORCA_UV_VERSION in CMakeLists.txt
|
||||||
|
enable-cache: false
|
||||||
- uses: lukka/get-cmake@latest
|
- uses: lukka/get-cmake@latest
|
||||||
with:
|
with:
|
||||||
cmakeVersion: "~4.3.0" # use most recent 4.3.x version
|
cmakeVersion: "~4.3.0" # use most recent 4.3.x version
|
||||||
|
|||||||
@@ -52,4 +52,6 @@ internal_docs/
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
*.opc
|
*.opc
|
||||||
|
/.test/
|
||||||
docs/superpowers/
|
docs/superpowers/
|
||||||
|
ctest_results.xml
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
# G-code preview while dragging
|
|
||||||
|
|
||||||
The sliced preview draws every toolpath segment of the plate as an instanced box. On a large
|
|
||||||
plate that is tens of millions of segments, and the frame is GPU-bound: the cost is the number of
|
|
||||||
instances drawn, not anything the CPU does per frame. Dragging the camera over such a plate cannot
|
|
||||||
keep up. The `preview_reduced_detail_mode` preference (*Graphics > G-code Preview*, off by
|
|
||||||
default) lets the preview draw less while the user drags and put the full toolpaths back when they
|
|
||||||
let go.
|
|
||||||
|
|
||||||
| Preference | Values | Effect |
|
|
||||||
|---|---|---|
|
|
||||||
| `preview_reduced_detail_mode` | `off`, `solid`, `shell` | what is drawn while dragging |
|
|
||||||
|
|
||||||
libvgcode (`src/libvgcode`) builds and binds the reduced toolpath set, `GCodeViewer` maps the
|
|
||||||
preferences onto it and draws the solid model, and `GLCanvas3D` decides when the user is dragging.
|
|
||||||
The OpenGL ES path keeps a single set and ignores the preference.
|
|
||||||
|
|
||||||
## Two sets, one walk
|
|
||||||
|
|
||||||
`ViewerImpl::update_enabled_entities()` walks the visible vertex range once and fills two segment
|
|
||||||
index buffers side by side: the **full** set and the **reduced** set (segments and options).
|
|
||||||
Building them together is what makes switching free: starting or ending a drag is a buffer
|
|
||||||
binding, never a rebuild. A change of mode does rebuild. Nothing is built while the mode
|
|
||||||
is off, and the reduced buffers are then uploaded empty so that the last set does not stay
|
|
||||||
allocated.
|
|
||||||
|
|
||||||
Whatever the mode leaves out, the bottom and top layers of the visible range are kept whole: they
|
|
||||||
are the faces the range cuts open, and the top is what the user is looking at.
|
|
||||||
|
|
||||||
### Modes
|
|
||||||
|
|
||||||
- `EndLayersOnly` (`solid` in the preference) keeps only the two end layers. `GCodeViewer` then
|
|
||||||
draws the sliced objects and the prime tower as opaque solids, see below.
|
|
||||||
- `ShellOnly` (`shell`) keeps every role but the sparse infill, internal solid infill, internal
|
|
||||||
bridge infill and gap fill, which lie under the skins or between the walls. Walls, top and
|
|
||||||
bottom surfaces, bridges, supports and the prime tower are drawn whole, so from outside the
|
|
||||||
print looks as it does at rest. The roles come from
|
|
||||||
the slicer, so the classification is exact wherever the slicer's is; what it cannot express is
|
|
||||||
the inside of the prime tower and of a support, which share one role with their outside and are
|
|
||||||
kept whole. A profile with no top or bottom shell layers, or no walls, leaves the infill on the
|
|
||||||
surface, so `GCodeViewer` turns the hiding off for such a print when it loads it, from the
|
|
||||||
print's default region settings; per-object overrides are not consulted.
|
|
||||||
|
|
||||||
## The solid model
|
|
||||||
|
|
||||||
The preview already loads the sliced objects as shells for its translucent ghost.
|
|
||||||
`GCodeViewer::render_solid_model()` draws those shells opaque, in their filament colors, with the
|
|
||||||
`gouraud` shader, whose z range cuts them to the visible layer range. The two toolpath layers of
|
|
||||||
the reduced set are drawn afterwards and cap the cut with what was really printed there. The
|
|
||||||
shells hold only the objects, so while this mode is on the prime tower is added from its sliced
|
|
||||||
mesh, positioned as the print placed it. It is added or removed on its own when the mode changes,
|
|
||||||
without reloading the objects, keeps its opaque color so that it never appears among the
|
|
||||||
translucent shells, and stays out of their bounding box. Supports have no mesh and are not shown,
|
|
||||||
and `load_shells()` drops every non-model-part volume, so a negative volume is not cut out.
|
|
||||||
|
|
||||||
A plate whose shells are not loaded keeps drawing toolpaths, since the solid model would leave
|
|
||||||
only the end layers.
|
|
||||||
|
|
||||||
## Deciding that the user is dragging
|
|
||||||
|
|
||||||
`GLCanvas3D::_update_preview_interaction()` runs at the top of every preview frame, before the
|
|
||||||
canvas decides whether to reuse its cached scene, so that the switch lands in that frame. Dragging
|
|
||||||
is `GLCanvas3D::is_user_interacting()`, the same answer the scene cache reads: the camera, the
|
|
||||||
navigator, a gizmo, the rectangle selection or either slider being held. A slider reports this from
|
|
||||||
ImGui's active id rather than its dirty flag, which is raised and consumed inside one frame. A
|
|
||||||
wheel step has no duration, so it holds the reduced set for a 150 ms settle time instead, and the
|
|
||||||
frame that restores the toolpaths is scheduled for when that time runs out, since the render timer
|
|
||||||
only wakes the idle loop. A drag cut short by focus or capture loss is ended explicitly, and a
|
|
||||||
button release wakes the idle loop, because on some platforms nothing else would until the next
|
|
||||||
input.
|
|
||||||
|
|
||||||
## Reused scene frames
|
|
||||||
|
|
||||||
`GLCanvas3D` keeps its last scene pass for frames that only rebuild the overlay (`SceneCache`). Its
|
|
||||||
key covers the canvas size, the camera and hover state, not what the toolpath sets draw, so a frame
|
|
||||||
that reuses the scene must never be one on which the set is switched.
|
|
||||||
`_update_preview_interaction()` therefore reports whether the bound set changed, and a frame on
|
|
||||||
which it did redraws the scene. The canvas neither captures nor reuses the scene while the user
|
|
||||||
drags, so no reduced frame outlives a drag, and the frame that ends a wheel's settle time is
|
|
||||||
requested as a full frame.
|
|
||||||
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
# This file is made to support the unit tests workflow.
|
# This file is made to support the unit tests workflow.
|
||||||
# It should only require the directories build/tests, scripts/, and tests/ to function,
|
# It should only require the directories build/tests, scripts/, and tests/ to function,
|
||||||
# and cmake (with ctest) installed.
|
# and cmake (with ctest) installed -- plus network access to PyPI whenever numpy has to
|
||||||
|
# be installed into a freshly built test tree (see below).
|
||||||
# (otherwise, update the workflow too, but try to avoid to keep things self-contained)
|
# (otherwise, update the workflow too, but try to avoid to keep things self-contained)
|
||||||
#
|
#
|
||||||
# Usage: run_unit_tests.sh [TEST_DIR] [BUILD_CONFIG]
|
# Usage: run_unit_tests.sh [TEST_DIR] [BUILD_CONFIG]
|
||||||
@@ -18,6 +19,52 @@ cd "${ROOT_DIR}" || exit 1
|
|||||||
TEST_DIR="${1:-build/tests}"
|
TEST_DIR="${1:-build/tests}"
|
||||||
BUILD_CONFIG="${2:-}"
|
BUILD_CONFIG="${2:-}"
|
||||||
|
|
||||||
|
# The slic3rutils plugin-host tests build numpy arrays through the CPython copied next
|
||||||
|
# to the test binary (see tests/slic3rutils/CMakeLists.txt), which ships no numpy.
|
||||||
|
# Install it with the uv staged beside that runtime -- the tool the app installs plugin
|
||||||
|
# dependencies with -- straight into the interpreter's own site-packages: no pip needed
|
||||||
|
# in the runtime, no PYTHONPATH. Re-checked every run because a rebuild of the test
|
||||||
|
# target re-copies the runtime; needs network whenever it installs. Pinned so a numpy
|
||||||
|
# release cannot change results on its own.
|
||||||
|
NUMPY_VERSION="2.5.3"
|
||||||
|
|
||||||
|
# Without numpy those tests assert the numpy-absent error path instead, so a local run
|
||||||
|
# only warns. Under CI it fails the run, which would otherwise stay green while silently
|
||||||
|
# dropping the array coverage. (The Flatpak leg runs this inside `flatpak build`, whose
|
||||||
|
# minimal environment has no CI, and its offline build stages no uv, so numpy stays
|
||||||
|
# best-effort there.)
|
||||||
|
numpy_unavailable() {
|
||||||
|
if [ -n "${CI:-}" ]; then
|
||||||
|
echo "error: $1" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "warning: $1; the numpy-backed binding tests will cover only the numpy-absent path."
|
||||||
|
}
|
||||||
|
|
||||||
|
has_pinned_numpy() {
|
||||||
|
"${python_exe}" -c "import sys, numpy; sys.exit(numpy.__version__ != '${NUMPY_VERSION}')" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
find_args=("${TEST_DIR}" \( -path '*/python/bin/python3' -o -path '*/python/python.exe' \))
|
||||||
|
# Multi-config trees hold one copy per configuration; only bootstrap the one being run.
|
||||||
|
[ -n "${BUILD_CONFIG}" ] && find_args+=(-path "*/${BUILD_CONFIG}/*")
|
||||||
|
python_exe="$(find "${find_args[@]}" -print -quit 2>/dev/null)"
|
||||||
|
|
||||||
|
if [ -z "${python_exe}" ]; then
|
||||||
|
numpy_unavailable "no bundled Python under ${TEST_DIR}"
|
||||||
|
elif ! has_pinned_numpy; then
|
||||||
|
uv_exe="${python_exe%/python/*}/tools/uv/uv"
|
||||||
|
# Builds that bundle no uv (Windows arm64) fall back to one on PATH.
|
||||||
|
[ -x "${uv_exe}" ] || uv_exe="$(command -v uv)"
|
||||||
|
echo "Installing numpy ${NUMPY_VERSION} into the embedded test interpreter (${python_exe})..."
|
||||||
|
if [ -z "${uv_exe}" ]; then
|
||||||
|
numpy_unavailable "no uv staged beside the tests or on PATH"
|
||||||
|
elif ! "${uv_exe}" pip install --python "${python_exe}" --only-binary :all: "numpy==${NUMPY_VERSION}" \
|
||||||
|
|| ! has_pinned_numpy; then
|
||||||
|
numpy_unavailable "could not install numpy ${NUMPY_VERSION} into ${python_exe}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Run the whole suite, excluding tests tagged [NotWorking] and tests labelled RequiresApp,
|
# Run the whole suite, excluding tests tagged [NotWorking] and tests labelled RequiresApp,
|
||||||
# which run the built orca-slicer binary that this directory does not contain.
|
# which run the built orca-slicer binary that this directory does not contain.
|
||||||
# --no-tests=error fails the job if the filter matches nothing (instead of passing green).
|
# --no-tests=error fails the job if the filter matches nothing (instead of passing green).
|
||||||
|
|||||||
@@ -205,13 +205,6 @@ void AppConfig::set_defaults()
|
|||||||
if (get("seq_top_layer_only").empty())
|
if (get("seq_top_layer_only").empty())
|
||||||
set("seq_top_layer_only", "1");
|
set("seq_top_layer_only", "1");
|
||||||
|
|
||||||
// what the preview draws while the user drags it
|
|
||||||
{
|
|
||||||
const std::string mode = get("preview_reduced_detail_mode");
|
|
||||||
if (mode != "off" && mode != "solid" && mode != "shell")
|
|
||||||
set("preview_reduced_detail_mode", "off");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
||||||
if (get("preview_dim_previous_layers").empty())
|
if (get("preview_dim_previous_layers").empty())
|
||||||
set_bool("preview_dim_previous_layers", false);
|
set_bool("preview_dim_previous_layers", false);
|
||||||
|
|||||||
+5
-10
@@ -2504,8 +2504,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
|||||||
|
|
||||||
{
|
{
|
||||||
LifecycleEventContext ctx;
|
LifecycleEventContext ctx;
|
||||||
ctx.id = std::to_string(print->model().id().id);
|
ctx.name = std::to_string(print->model().id().id);
|
||||||
ctx.name = print->get_model_name();
|
|
||||||
ctx.code = LifecycleEvtCode::Ok;
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
ctx.msg = path;
|
ctx.msg = path;
|
||||||
ctx.cancellation_check = [print]() { return print->canceled(); };
|
ctx.cancellation_check = [print]() { return print->canceled(); };
|
||||||
@@ -2557,8 +2556,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
LifecycleEventContext ctx;
|
LifecycleEventContext ctx;
|
||||||
ctx.id = std::to_string(print->model().id().id);
|
ctx.name = std::to_string(print->model().id().id);
|
||||||
ctx.name = print->get_model_name();
|
|
||||||
ctx.code = LifecycleEvtCode::Error;
|
ctx.code = LifecycleEvtCode::Error;
|
||||||
ctx.msg = std::string(path) + "\n" + err_msg;
|
ctx.msg = std::string(path) + "\n" + err_msg;
|
||||||
ctx.cancellation_check = [print]() { return print->canceled(); };
|
ctx.cancellation_check = [print]() { return print->canceled(); };
|
||||||
@@ -2582,8 +2580,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
|||||||
boost::nowide::remove(path_tmp.c_str());
|
boost::nowide::remove(path_tmp.c_str());
|
||||||
{
|
{
|
||||||
LifecycleEventContext ctx;
|
LifecycleEventContext ctx;
|
||||||
ctx.id = std::to_string(print->model().id().id);
|
ctx.name = std::to_string(print->model().id().id);
|
||||||
ctx.name = print->get_model_name();
|
|
||||||
ctx.code = LifecycleEvtCode::Error;
|
ctx.code = LifecycleEvtCode::Error;
|
||||||
ctx.msg = std::string(path) + "\n" + ex.what();
|
ctx.msg = std::string(path) + "\n" + ex.what();
|
||||||
ctx.cancellation_check = [print]() { return print->canceled(); };
|
ctx.cancellation_check = [print]() { return print->canceled(); };
|
||||||
@@ -2696,8 +2693,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
|||||||
if (ret) {
|
if (ret) {
|
||||||
{
|
{
|
||||||
LifecycleEventContext ctx;
|
LifecycleEventContext ctx;
|
||||||
ctx.id = std::to_string(print->model().id().id);
|
ctx.name = std::to_string(print->model().id().id);
|
||||||
ctx.name = print->get_model_name();
|
|
||||||
ctx.code = LifecycleEvtCode::Error;
|
ctx.code = LifecycleEvtCode::Error;
|
||||||
ctx.msg = std::string(path) + "\nFailed to rename the output G-code file: " + ret.message();
|
ctx.msg = std::string(path) + "\nFailed to rename the output G-code file: " + ret.message();
|
||||||
ctx.cancellation_check = [print]() { return print->canceled(); };
|
ctx.cancellation_check = [print]() { return print->canceled(); };
|
||||||
@@ -2716,8 +2712,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
|||||||
|
|
||||||
{
|
{
|
||||||
LifecycleEventContext ctx;
|
LifecycleEventContext ctx;
|
||||||
ctx.id = std::to_string(print->model().id().id);
|
ctx.name = std::to_string(print->model().id().id);
|
||||||
ctx.name = print->get_model_name();
|
|
||||||
ctx.code = LifecycleEvtCode::Ok;
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
ctx.msg = path;
|
ctx.msg = path;
|
||||||
ctx.cancellation_check = [print]() { return print->canceled(); };
|
ctx.cancellation_check = [print]() { return print->canceled(); };
|
||||||
|
|||||||
+5
-10
@@ -2717,8 +2717,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
|||||||
|
|
||||||
{
|
{
|
||||||
LifecycleEventContext ctx;
|
LifecycleEventContext ctx;
|
||||||
ctx.id = std::to_string(m_model.id().id);
|
ctx.name = std::to_string(m_model.id().id);
|
||||||
ctx.name = get_model_name();
|
|
||||||
ctx.code = LifecycleEvtCode::Ok;
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
ctx.cancellation_check = [this]() { return canceled(); };
|
ctx.cancellation_check = [this]() { return canceled(); };
|
||||||
fire_lifecycle_event(LifecycleEvent::SliceStarted, ctx);
|
fire_lifecycle_event(LifecycleEvent::SliceStarted, ctx);
|
||||||
@@ -3345,8 +3344,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
|||||||
|
|
||||||
{
|
{
|
||||||
LifecycleEventContext ctx;
|
LifecycleEventContext ctx;
|
||||||
ctx.id = std::to_string(m_model.id().id);
|
ctx.name = std::to_string(m_model.id().id);
|
||||||
ctx.name = get_model_name();
|
|
||||||
ctx.code = LifecycleEvtCode::Ok;
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
ctx.cancellation_check = [this]() { return canceled(); };
|
ctx.cancellation_check = [this]() { return canceled(); };
|
||||||
fire_lifecycle_event(LifecycleEvent::SliceGeometryFinished, ctx);
|
fire_lifecycle_event(LifecycleEvent::SliceGeometryFinished, ctx);
|
||||||
@@ -4952,8 +4950,7 @@ void Print::export_gcode_from_previous_file(const std::string& file, GCodeProces
|
|||||||
{
|
{
|
||||||
{
|
{
|
||||||
LifecycleEventContext ctx;
|
LifecycleEventContext ctx;
|
||||||
ctx.id = std::to_string(m_model.id().id);
|
ctx.name = std::to_string(m_model.id().id);
|
||||||
ctx.name = get_model_name();
|
|
||||||
ctx.code = LifecycleEvtCode::Ok;
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
ctx.msg = file;
|
ctx.msg = file;
|
||||||
ctx.cancellation_check = [this]() { return canceled(); };
|
ctx.cancellation_check = [this]() { return canceled(); };
|
||||||
@@ -4983,8 +4980,7 @@ void Print::export_gcode_from_previous_file(const std::string& file, GCodeProces
|
|||||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": found errors when process gcode file %1%") %file.c_str();
|
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": found errors when process gcode file %1%") %file.c_str();
|
||||||
{
|
{
|
||||||
LifecycleEventContext ctx;
|
LifecycleEventContext ctx;
|
||||||
ctx.id = std::to_string(m_model.id().id);
|
ctx.name = std::to_string(m_model.id().id);
|
||||||
ctx.name = get_model_name();
|
|
||||||
ctx.code = LifecycleEvtCode::Error;
|
ctx.code = LifecycleEvtCode::Error;
|
||||||
ctx.msg = file + "\n" + ex.what();
|
ctx.msg = file + "\n" + ex.what();
|
||||||
ctx.cancellation_check = [this]() { return canceled(); };
|
ctx.cancellation_check = [this]() { return canceled(); };
|
||||||
@@ -4998,8 +4994,7 @@ void Print::export_gcode_from_previous_file(const std::string& file, GCodeProces
|
|||||||
|
|
||||||
{
|
{
|
||||||
LifecycleEventContext ctx;
|
LifecycleEventContext ctx;
|
||||||
ctx.id = std::to_string(m_model.id().id);
|
ctx.name = std::to_string(m_model.id().id);
|
||||||
ctx.name = get_model_name();
|
|
||||||
ctx.code = LifecycleEvtCode::Ok;
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
ctx.msg = file;
|
ctx.msg = file;
|
||||||
ctx.cancellation_check = [this]() { return canceled(); };
|
ctx.cancellation_check = [this]() { return canceled(); };
|
||||||
|
|||||||
@@ -158,21 +158,6 @@ enum class EGCodeExtrusionRole : uint8_t
|
|||||||
|
|
||||||
static constexpr std::size_t GCODE_EXTRUSION_ROLES_COUNT = static_cast<std::size_t>(EGCodeExtrusionRole::COUNT);
|
static constexpr std::size_t GCODE_EXTRUSION_ROLES_COUNT = static_cast<std::size_t>(EGCodeExtrusionRole::COUNT);
|
||||||
|
|
||||||
//
|
|
||||||
// What the reduced set, drawn while the user is dragging, holds in place of the full toolpaths
|
|
||||||
//
|
|
||||||
enum class EReducedDetailMode : uint8_t
|
|
||||||
{
|
|
||||||
// nothing: no reduced set is built
|
|
||||||
Off,
|
|
||||||
// only the bottom and top layers of the visible range, for a caller that draws the print
|
|
||||||
// itself some other way
|
|
||||||
EndLayersOnly,
|
|
||||||
// every role but the infill under the skins and the gap fill: what can be seen from outside
|
|
||||||
ShellOnly,
|
|
||||||
COUNT
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Option types
|
// Option types
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -114,19 +114,6 @@ public:
|
|||||||
//
|
//
|
||||||
bool is_dim_previous_layers() const;
|
bool is_dim_previous_layers() const;
|
||||||
void set_dim_previous_layers(bool value);
|
void set_dim_previous_layers(bool value);
|
||||||
//
|
|
||||||
// The reduced set drawn while the user drags: what the mode keeps, and always the bottom and
|
|
||||||
// top layers of the visible range. While a mode is set it is built alongside the full set, so
|
|
||||||
// set_reduced_detail() rebuilds nothing. Ignored on the OpenGL ES path.
|
|
||||||
//
|
|
||||||
EReducedDetailMode get_reduced_detail_mode() const;
|
|
||||||
void set_reduced_detail_mode(EReducedDetailMode mode);
|
|
||||||
// Whether the shell mode hides the infill roles; off for a profile that leaves them on the
|
|
||||||
// surface, with no top or bottom shell or no walls.
|
|
||||||
bool get_reduced_detail_hide_infill() const;
|
|
||||||
void set_reduced_detail_hide_infill(bool value);
|
|
||||||
void set_reduced_detail(bool value);
|
|
||||||
bool is_reduced_detail() const;
|
|
||||||
float get_dim_previous_layers_brightness() const;
|
float get_dim_previous_layers_brightness() const;
|
||||||
void set_dim_previous_layers_brightness(float value);
|
void set_dim_previous_layers_brightness(float value);
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -25,11 +25,6 @@ struct Settings
|
|||||||
// ORCA: how bright those darkened layers are rendered, 1.0 = unchanged, 0.0 = black
|
// ORCA: how bright those darkened layers are rendered, 1.0 = unchanged, 0.0 = black
|
||||||
float dim_previous_layers_brightness{ 0.4f };
|
float dim_previous_layers_brightness{ 0.4f };
|
||||||
bool spiral_vase_mode{ false };
|
bool spiral_vase_mode{ false };
|
||||||
// what the reduced set holds and whether it is drawn. Ignored on the OpenGL ES path.
|
|
||||||
EReducedDetailMode reduced_detail_mode{ EReducedDetailMode::Off };
|
|
||||||
// whether the shell mode may hide the infill roles: false when the profile leaves them on the surface
|
|
||||||
bool reduced_detail_hide_infill{ true };
|
|
||||||
bool reduced_detail{ false };
|
|
||||||
//
|
//
|
||||||
// Required update flags
|
// Required update flags
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -34,10 +34,6 @@ static const char* Segments_Vertex_Shader =
|
|||||||
// ORCA: 0 during the shadow caster pass - the bias below shifts eye_position but not
|
// ORCA: 0 during the shadow caster pass - the bias below shifts eye_position but not
|
||||||
// world_position, so the caster would write a depth the receiver never looks up.
|
// world_position, so the caster would write a depth the receiver never looks up.
|
||||||
"uniform float bias_scale;\n"
|
"uniform float bias_scale;\n"
|
||||||
// draw the instances last to first, top layers before the ones they hide, so that early depth
|
|
||||||
// rejection discards most of the hidden fragments; set when the camera looks down on the print
|
|
||||||
"uniform int reverse_order;\n"
|
|
||||||
"uniform int instance_count;\n"
|
|
||||||
"in int vertex_id;\n"
|
"in int vertex_id;\n"
|
||||||
"out vec3 color;\n"
|
"out vec3 color;\n"
|
||||||
"// ORCA: realistic view - the light the shadow map is able to block, kept apart from the\n"
|
"// ORCA: realistic view - the light the shadow map is able to block, kept apart from the\n"
|
||||||
@@ -63,8 +59,7 @@ static const char* Segments_Vertex_Shader =
|
|||||||
" return top_diffuse + front_diffuse + top_specular;\n"
|
" return top_diffuse + front_diffuse + top_specular;\n"
|
||||||
"}\n"
|
"}\n"
|
||||||
"void main() {\n"
|
"void main() {\n"
|
||||||
" int instance = (reverse_order != 0) ? instance_count - 1 - gl_InstanceID : gl_InstanceID;\n"
|
" int id_a = int(texelFetch(segment_index_tex, gl_InstanceID).r);\n"
|
||||||
" int id_a = int(texelFetch(segment_index_tex, instance).r);\n"
|
|
||||||
" int id_b = id_a + 1;\n"
|
" int id_b = id_a + 1;\n"
|
||||||
" vec3 pos_a = texelFetch(position_tex, id_a).xyz;\n"
|
" vec3 pos_a = texelFetch(position_tex, id_a).xyz;\n"
|
||||||
" vec3 pos_b = texelFetch(position_tex, id_b).xyz;\n"
|
" vec3 pos_b = texelFetch(position_tex, id_b).xyz;\n"
|
||||||
|
|||||||
@@ -92,36 +92,6 @@ bool Viewer::is_dim_previous_layers() const
|
|||||||
return m_impl->is_dim_previous_layers();
|
return m_impl->is_dim_previous_layers();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Viewer::set_reduced_detail(bool value)
|
|
||||||
{
|
|
||||||
m_impl->set_reduced_detail(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Viewer::get_reduced_detail_hide_infill() const
|
|
||||||
{
|
|
||||||
return m_impl->get_reduced_detail_hide_infill();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Viewer::set_reduced_detail_hide_infill(bool value)
|
|
||||||
{
|
|
||||||
m_impl->set_reduced_detail_hide_infill(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Viewer::set_dim_previous_layers(bool value)
|
void Viewer::set_dim_previous_layers(bool value)
|
||||||
{
|
{
|
||||||
m_impl->set_dim_previous_layers(value);
|
m_impl->set_dim_previous_layers(value);
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <cfloat>
|
|
||||||
|
|
||||||
namespace libvgcode {
|
namespace libvgcode {
|
||||||
|
|
||||||
@@ -764,8 +763,6 @@ void ViewerImpl::init(const std::string& opengl_context_version)
|
|||||||
m_uni_segments_height_width_angle_tex_id = glGetUniformLocation(m_segments_shader_id, "height_width_angle_tex");
|
m_uni_segments_height_width_angle_tex_id = glGetUniformLocation(m_segments_shader_id, "height_width_angle_tex");
|
||||||
m_uni_segments_colors_tex_id = glGetUniformLocation(m_segments_shader_id, "color_tex");
|
m_uni_segments_colors_tex_id = glGetUniformLocation(m_segments_shader_id, "color_tex");
|
||||||
m_uni_segments_segment_index_tex_id = glGetUniformLocation(m_segments_shader_id, "segment_index_tex");
|
m_uni_segments_segment_index_tex_id = glGetUniformLocation(m_segments_shader_id, "segment_index_tex");
|
||||||
m_uni_segments_reverse_order_id = glGetUniformLocation(m_segments_shader_id, "reverse_order");
|
|
||||||
m_uni_segments_instance_count_id = glGetUniformLocation(m_segments_shader_id, "instance_count");
|
|
||||||
// ORCA: realistic view
|
// ORCA: realistic view
|
||||||
m_uni_segments_shadow_map_id = glGetUniformLocation(m_segments_shader_id, "shadow_map");
|
m_uni_segments_shadow_map_id = glGetUniformLocation(m_segments_shader_id, "shadow_map");
|
||||||
m_uni_segments_shadow_light_vp_id = glGetUniformLocation(m_segments_shader_id, "shadow_light_vp");
|
m_uni_segments_shadow_light_vp_id = glGetUniformLocation(m_segments_shader_id, "shadow_light_vp");
|
||||||
@@ -902,17 +899,9 @@ void ViewerImpl::reset()
|
|||||||
#else
|
#else
|
||||||
m_enabled_segments_count = 0;
|
m_enabled_segments_count = 0;
|
||||||
m_enabled_options_count = 0;
|
m_enabled_options_count = 0;
|
||||||
m_enabled_segments_reduced_count = 0;
|
|
||||||
m_enabled_options_reduced_count = 0;
|
|
||||||
m_enabled_segments_reduced_tex_size = 0;
|
|
||||||
m_enabled_options_reduced_tex_size = 0;
|
|
||||||
|
|
||||||
m_settings_used_for_ranges = std::nullopt;
|
m_settings_used_for_ranges = std::nullopt;
|
||||||
|
|
||||||
delete_textures(m_enabled_options_reduced_tex_id);
|
|
||||||
delete_buffers(m_enabled_options_reduced_buf_id);
|
|
||||||
delete_textures(m_enabled_segments_reduced_tex_id);
|
|
||||||
delete_buffers(m_enabled_segments_reduced_buf_id);
|
|
||||||
delete_textures(m_enabled_options_tex_id);
|
delete_textures(m_enabled_options_tex_id);
|
||||||
delete_buffers(m_enabled_options_buf_id);
|
delete_buffers(m_enabled_options_buf_id);
|
||||||
delete_textures(m_enabled_segments_tex_id);
|
delete_textures(m_enabled_segments_tex_id);
|
||||||
@@ -1172,17 +1161,6 @@ void ViewerImpl::load(GCodeInputData&& gcode_data)
|
|||||||
glsafe(glGenTextures(1, &m_enabled_options_tex_id));
|
glsafe(glGenTextures(1, &m_enabled_options_tex_id));
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_tex_id));
|
||||||
|
|
||||||
// create (but do not fill) the reduced counterparts of the two buffers above
|
|
||||||
glsafe(glGenBuffers(1, &m_enabled_segments_reduced_buf_id));
|
|
||||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_buf_id));
|
|
||||||
glsafe(glGenTextures(1, &m_enabled_segments_reduced_tex_id));
|
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_tex_id));
|
|
||||||
|
|
||||||
glsafe(glGenBuffers(1, &m_enabled_options_reduced_buf_id));
|
|
||||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_options_reduced_buf_id));
|
|
||||||
glsafe(glGenTextures(1, &m_enabled_options_reduced_tex_id));
|
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_reduced_tex_id));
|
|
||||||
|
|
||||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, old_bound_texture));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, old_bound_texture));
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
@@ -1194,18 +1172,6 @@ void ViewerImpl::load(GCodeInputData&& gcode_data)
|
|||||||
update_colors();
|
update_colors();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef ENABLE_OPENGL_ES
|
|
||||||
// the roles that lie under a skin or between walls, never seen from outside the print
|
|
||||||
static bool is_hidden_in_shell(EGCodeExtrusionRole role)
|
|
||||||
{
|
|
||||||
return role == EGCodeExtrusionRole::InternalInfill ||
|
|
||||||
role == EGCodeExtrusionRole::SolidInfill ||
|
|
||||||
role == EGCodeExtrusionRole::InternalBridgeInfill ||
|
|
||||||
role == EGCodeExtrusionRole::GapFill;
|
|
||||||
}
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
|
||||||
|
|
||||||
|
|
||||||
void ViewerImpl::update_enabled_entities()
|
void ViewerImpl::update_enabled_entities()
|
||||||
{
|
{
|
||||||
if (m_vertices.empty())
|
if (m_vertices.empty())
|
||||||
@@ -1213,17 +1179,6 @@ void ViewerImpl::update_enabled_entities()
|
|||||||
|
|
||||||
std::vector<uint32_t> enabled_segments;
|
std::vector<uint32_t> enabled_segments;
|
||||||
std::vector<uint32_t> enabled_options;
|
std::vector<uint32_t> enabled_options;
|
||||||
#ifndef ENABLE_OPENGL_ES
|
|
||||||
// the reduced set is filled by the same walk, so switching to it costs no rebuild. Whatever the
|
|
||||||
// mode leaves out, the bottom and top layers of the visible range are kept whole: they are the
|
|
||||||
// surfaces the range cuts open
|
|
||||||
const EReducedDetailMode reduced_mode = m_settings.reduced_detail_mode;
|
|
||||||
const bool build_reduced = reduced_mode != EReducedDetailMode::Off;
|
|
||||||
const bool hide_infill = m_settings.reduced_detail_hide_infill;
|
|
||||||
std::vector<uint32_t> enabled_segments_reduced;
|
|
||||||
std::vector<uint32_t> enabled_options_reduced;
|
|
||||||
const Interval& layers_range = m_layers.get_view_range();
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
|
||||||
Interval range = m_view_range.get_visible();
|
Interval range = m_view_range.get_visible();
|
||||||
|
|
||||||
// when top layer only visualization is enabled, we need to render
|
// when top layer only visualization is enabled, we need to render
|
||||||
@@ -1271,20 +1226,6 @@ void ViewerImpl::update_enabled_entities()
|
|||||||
enabled_options.push_back(static_cast<uint32_t>(i));
|
enabled_options.push_back(static_cast<uint32_t>(i));
|
||||||
else
|
else
|
||||||
enabled_segments.push_back(static_cast<uint32_t>(i));
|
enabled_segments.push_back(static_cast<uint32_t>(i));
|
||||||
|
|
||||||
#ifndef ENABLE_OPENGL_ES
|
|
||||||
if (build_reduced) {
|
|
||||||
const bool end_layer = v.layer_id == layers_range[0] || v.layer_id == layers_range[1];
|
|
||||||
if (end_layer)
|
|
||||||
(v.is_option() ? enabled_options_reduced : enabled_segments_reduced).push_back(static_cast<uint32_t>(i));
|
|
||||||
else if (reduced_mode == EReducedDetailMode::ShellOnly) {
|
|
||||||
if (v.is_option())
|
|
||||||
enabled_options_reduced.push_back(static_cast<uint32_t>(i));
|
|
||||||
else if (!v.is_extrusion() || !hide_infill || !is_hidden_in_shell(v.role))
|
|
||||||
enabled_segments_reduced.push_back(static_cast<uint32_t>(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_OPENGL_ES
|
#ifdef ENABLE_OPENGL_ES
|
||||||
@@ -1313,23 +1254,6 @@ void ViewerImpl::update_enabled_entities()
|
|||||||
else
|
else
|
||||||
glsafe(glBufferData(GL_TEXTURE_BUFFER, 0, nullptr, GL_STATIC_DRAW));
|
glsafe(glBufferData(GL_TEXTURE_BUFFER, 0, nullptr, GL_STATIC_DRAW));
|
||||||
|
|
||||||
m_enabled_segments_reduced_count = enabled_segments_reduced.size();
|
|
||||||
m_enabled_options_reduced_count = enabled_options_reduced.size();
|
|
||||||
m_enabled_segments_reduced_tex_size = enabled_segments_reduced.size() * sizeof(uint32_t);
|
|
||||||
m_enabled_options_reduced_tex_size = enabled_options_reduced.size() * sizeof(uint32_t);
|
|
||||||
|
|
||||||
// uploaded even when nothing was built, so that the last reduced set is released as soon as
|
|
||||||
// the preference is switched off
|
|
||||||
assert(m_enabled_segments_reduced_buf_id > 0);
|
|
||||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_buf_id));
|
|
||||||
glsafe(glBufferData(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_tex_size,
|
|
||||||
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, m_enabled_options_reduced_tex_size,
|
|
||||||
enabled_options_reduced.empty() ? nullptr : enabled_options_reduced.data(), GL_STATIC_DRAW));
|
|
||||||
|
|
||||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
@@ -1537,23 +1461,6 @@ void ViewerImpl::toggle_top_layer_only_view_range()
|
|||||||
update_colors_texture();
|
update_colors_texture();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Both decide which vertices land in the reduced set, so the sets are 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_hide_infill(bool value)
|
|
||||||
{
|
|
||||||
if (m_settings.reduced_detail_hide_infill == value)
|
|
||||||
return;
|
|
||||||
m_settings.reduced_detail_hide_infill = value;
|
|
||||||
m_settings.update_enabled_entities = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ORCA: enable/disable darkening of the layers the layer slider is not scrubbed to
|
// ORCA: enable/disable darkening of the layers the layer slider is not scrubbed to
|
||||||
void ViewerImpl::set_dim_previous_layers(bool value)
|
void ViewerImpl::set_dim_previous_layers(bool value)
|
||||||
{
|
{
|
||||||
@@ -1947,8 +1854,6 @@ size_t ViewerImpl::get_used_gpu_memory() const
|
|||||||
ret += m_colors_tex_size;
|
ret += m_colors_tex_size;
|
||||||
ret += m_enabled_segments_tex_size;
|
ret += m_enabled_segments_tex_size;
|
||||||
ret += m_enabled_options_tex_size;
|
ret += m_enabled_options_tex_size;
|
||||||
ret += m_enabled_segments_reduced_tex_size;
|
|
||||||
ret += m_enabled_options_reduced_tex_size;
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -2165,8 +2070,7 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
|
|||||||
#ifdef ENABLE_OPENGL_ES
|
#ifdef ENABLE_OPENGL_ES
|
||||||
if (m_texture_data.get_enabled_segments_count() == 0)
|
if (m_texture_data.get_enabled_segments_count() == 0)
|
||||||
#else
|
#else
|
||||||
const ActiveSet segments = active_segments();
|
if (m_enabled_segments_count == 0)
|
||||||
if (segments.count == 0)
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -2186,15 +2090,6 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
|
|||||||
glsafe(glUniformMatrix4fv(m_uni_segments_view_matrix_id, 1, GL_FALSE, view_matrix.data()));
|
glsafe(glUniformMatrix4fv(m_uni_segments_view_matrix_id, 1, GL_FALSE, view_matrix.data()));
|
||||||
glsafe(glUniformMatrix4fv(m_uni_segments_projection_matrix_id, 1, GL_FALSE, projection_matrix.data()));
|
glsafe(glUniformMatrix4fv(m_uni_segments_projection_matrix_id, 1, GL_FALSE, projection_matrix.data()));
|
||||||
glsafe(glUniform3fv(m_uni_segments_camera_position_id, 1, camera_position.data()));
|
glsafe(glUniform3fv(m_uni_segments_camera_position_id, 1, camera_position.data()));
|
||||||
// The segments come in print order, bottom layer first. Seen from above, that is back to front,
|
|
||||||
// and every hidden fragment is shaded before the one that covers it. Drawing them last to first
|
|
||||||
// lets the depth test reject the hidden ones instead. The camera looks down when the world's
|
|
||||||
// up axis points towards it, which is the view matrix's (2, 2) entry being positive.
|
|
||||||
const bool top_down = !m_rendering_shadow_casters && view_matrix[10] > 0.0f;
|
|
||||||
glsafe(glUniform1i(m_uni_segments_reverse_order_id, top_down ? 1 : 0));
|
|
||||||
#ifndef ENABLE_OPENGL_ES
|
|
||||||
glsafe(glUniform1i(m_uni_segments_instance_count_id, static_cast<int>(segments.count)));
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
|
||||||
// ORCA: realistic view. The depth pass writes the map it would otherwise read, so it shades
|
// ORCA: realistic view. The depth pass writes the map it would otherwise read, so it shades
|
||||||
// with the lookup off.
|
// with the lookup off.
|
||||||
glsafe(glUniform1i(m_uni_segments_shadow_map_id, m_shadow_map_texture_unit));
|
glsafe(glUniform1i(m_uni_segments_shadow_map_id, m_shadow_map_texture_unit));
|
||||||
@@ -2243,10 +2138,10 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
|
|||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
||||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
||||||
glsafe(glActiveTexture(GL_TEXTURE3));
|
glsafe(glActiveTexture(GL_TEXTURE3));
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, segments.tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_segments_tex_id));
|
||||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, segments.buf_id));
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, m_enabled_segments_buf_id));
|
||||||
|
|
||||||
m_segment_template.render(segments.count);
|
m_segment_template.render(m_enabled_segments_count);
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
if (curr_cull_face)
|
if (curr_cull_face)
|
||||||
@@ -2272,8 +2167,7 @@ void ViewerImpl::render_options(const Mat4x4& view_matrix, const Mat4x4& project
|
|||||||
#ifdef ENABLE_OPENGL_ES
|
#ifdef ENABLE_OPENGL_ES
|
||||||
if (m_texture_data.get_enabled_options_count() == 0)
|
if (m_texture_data.get_enabled_options_count() == 0)
|
||||||
#else
|
#else
|
||||||
const ActiveSet options = active_options();
|
if (m_enabled_options_count == 0)
|
||||||
if (options.count == 0)
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -2331,10 +2225,10 @@ void ViewerImpl::render_options(const Mat4x4& view_matrix, const Mat4x4& project
|
|||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
||||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
||||||
glsafe(glActiveTexture(GL_TEXTURE3));
|
glsafe(glActiveTexture(GL_TEXTURE3));
|
||||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, options.tex_id));
|
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_tex_id));
|
||||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, options.buf_id));
|
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, m_enabled_options_buf_id));
|
||||||
|
|
||||||
m_option_template.render(options.count);
|
m_option_template.render(m_enabled_options_count);
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
if (!curr_cull_face)
|
if (!curr_cull_face)
|
||||||
|
|||||||
@@ -109,21 +109,6 @@ public:
|
|||||||
// 0.0 = black
|
// 0.0 = black
|
||||||
bool is_dim_previous_layers() const { return m_settings.dim_previous_layers; }
|
bool is_dim_previous_layers() const { return m_settings.dim_previous_layers; }
|
||||||
void set_dim_previous_layers(bool value);
|
void set_dim_previous_layers(bool value);
|
||||||
//
|
|
||||||
// Draw from the reduced set; it is already built, so this is just a buffer binding.
|
|
||||||
//
|
|
||||||
void set_reduced_detail(bool value) {
|
|
||||||
#ifdef ENABLE_OPENGL_ES
|
|
||||||
// no reduced set is built on OpenGL ES
|
|
||||||
value = false;
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
|
||||||
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);
|
|
||||||
bool get_reduced_detail_hide_infill() const { return m_settings.reduced_detail_hide_infill; }
|
|
||||||
void set_reduced_detail_hide_infill(bool value);
|
|
||||||
float get_dim_previous_layers_brightness() const { return m_settings.dim_previous_layers_brightness; }
|
float get_dim_previous_layers_brightness() const { return m_settings.dim_previous_layers_brightness; }
|
||||||
void set_dim_previous_layers_brightness(float value);
|
void set_dim_previous_layers_brightness(float value);
|
||||||
|
|
||||||
@@ -377,8 +362,6 @@ private:
|
|||||||
int m_uni_segments_height_width_angle_tex_id{ -1 };
|
int m_uni_segments_height_width_angle_tex_id{ -1 };
|
||||||
int m_uni_segments_colors_tex_id{ -1 };
|
int m_uni_segments_colors_tex_id{ -1 };
|
||||||
int m_uni_segments_segment_index_tex_id{ -1 };
|
int m_uni_segments_segment_index_tex_id{ -1 };
|
||||||
int m_uni_segments_reverse_order_id{ -1 };
|
|
||||||
int m_uni_segments_instance_count_id{ -1 };
|
|
||||||
int m_uni_segments_shadow_map_id{ -1 };
|
int m_uni_segments_shadow_map_id{ -1 };
|
||||||
int m_uni_segments_shadow_light_vp_id{ -1 };
|
int m_uni_segments_shadow_light_vp_id{ -1 };
|
||||||
int m_uni_segments_shadow_intensity_id{ -1 };
|
int m_uni_segments_shadow_intensity_id{ -1 };
|
||||||
@@ -516,15 +499,6 @@ private:
|
|||||||
unsigned int m_enabled_options_tex_id{ 0 };
|
unsigned int m_enabled_options_tex_id{ 0 };
|
||||||
size_t m_enabled_options_count{ 0 };
|
size_t m_enabled_options_count{ 0 };
|
||||||
//
|
//
|
||||||
// OpenGL buffers to store the reduced set drawn while Settings::reduced_detail is set
|
|
||||||
//
|
|
||||||
unsigned int m_enabled_segments_reduced_buf_id{ 0 };
|
|
||||||
unsigned int m_enabled_segments_reduced_tex_id{ 0 };
|
|
||||||
size_t m_enabled_segments_reduced_count{ 0 };
|
|
||||||
unsigned int m_enabled_options_reduced_buf_id{ 0 };
|
|
||||||
unsigned int m_enabled_options_reduced_tex_id{ 0 };
|
|
||||||
size_t m_enabled_options_reduced_count{ 0 };
|
|
||||||
//
|
|
||||||
// Caches for size of data sent to gpu, in bytes
|
// Caches for size of data sent to gpu, in bytes
|
||||||
//
|
//
|
||||||
size_t m_positions_tex_size{ 0 };
|
size_t m_positions_tex_size{ 0 };
|
||||||
@@ -532,27 +506,6 @@ private:
|
|||||||
size_t m_colors_tex_size{ 0 };
|
size_t m_colors_tex_size{ 0 };
|
||||||
size_t m_enabled_segments_tex_size{ 0 };
|
size_t m_enabled_segments_tex_size{ 0 };
|
||||||
size_t m_enabled_options_tex_size{ 0 };
|
size_t m_enabled_options_tex_size{ 0 };
|
||||||
size_t m_enabled_segments_reduced_tex_size{ 0 };
|
|
||||||
size_t m_enabled_options_reduced_tex_size{ 0 };
|
|
||||||
|
|
||||||
// The set the next draw reads from: the reduced one while dragging, if one is built.
|
|
||||||
bool use_reduced_set() const { return m_settings.reduced_detail && m_settings.reduced_detail_mode != EReducedDetailMode::Off; }
|
|
||||||
struct ActiveSet
|
|
||||||
{
|
|
||||||
size_t count{ 0 };
|
|
||||||
unsigned int buf_id{ 0 };
|
|
||||||
unsigned int tex_id{ 0 };
|
|
||||||
};
|
|
||||||
ActiveSet active_segments() const {
|
|
||||||
if (use_reduced_set())
|
|
||||||
return { m_enabled_segments_reduced_count, m_enabled_segments_reduced_buf_id, m_enabled_segments_reduced_tex_id };
|
|
||||||
return { m_enabled_segments_count, m_enabled_segments_buf_id, m_enabled_segments_tex_id };
|
|
||||||
}
|
|
||||||
ActiveSet active_options() const {
|
|
||||||
if (use_reduced_set())
|
|
||||||
return { m_enabled_options_reduced_count, m_enabled_options_reduced_buf_id, m_enabled_options_reduced_tex_id };
|
|
||||||
return { m_enabled_options_count, m_enabled_options_buf_id, m_enabled_options_tex_id };
|
|
||||||
}
|
|
||||||
#endif // ENABLE_OPENGL_ES
|
#endif // ENABLE_OPENGL_ES
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1267,8 +1267,6 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
|
|||||||
if (current_top_layer_only != required_top_layer_only)
|
if (current_top_layer_only != required_top_layer_only)
|
||||||
m_viewer.toggle_top_layer_only_view_range();
|
m_viewer.toggle_top_layer_only_view_range();
|
||||||
|
|
||||||
read_reduced_detail_preferences();
|
|
||||||
|
|
||||||
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
// 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(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")));
|
m_viewer.set_dim_previous_layers_brightness(0.01f * std::stoi(get_app_config()->get("preview_dim_previous_layers_brightness")));
|
||||||
@@ -1492,10 +1490,6 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
|
|||||||
|
|
||||||
// BBS: data for rendering color arrangement recommendation
|
// BBS: data for rendering color arrangement recommendation
|
||||||
m_nozzle_nums = print.config().option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
m_nozzle_nums = print.config().option<ConfigOptionFloats>("nozzle_diameter")->values.size();
|
||||||
// the shell drag mode hides the infill only where the profile covers it: with no top or bottom
|
|
||||||
// shell, or no walls, the infill is the surface. Per-object settings are not consulted.
|
|
||||||
const PrintRegionConfig& region = print.default_region_config();
|
|
||||||
m_viewer.set_reduced_detail_hide_infill(region.top_shell_layers.value > 0 && region.bottom_shell_layers.value > 0 && region.wall_loops.value > 0);
|
|
||||||
// Orca hack: Hide filament group for non-bbl printers
|
// Orca hack: Hide filament group for non-bbl printers
|
||||||
if (!print.is_BBL_printer()) m_nozzle_nums = 1;
|
if (!print.is_BBL_printer()) m_nozzle_nums = 1;
|
||||||
std::vector<int> filament_maps = print.get_filament_maps();
|
std::vector<int> filament_maps = print.get_filament_maps();
|
||||||
@@ -1666,7 +1660,6 @@ void GCodeViewer::reset_shell()
|
|||||||
{
|
{
|
||||||
m_shells.volumes.clear();
|
m_shells.volumes.clear();
|
||||||
m_shells.print_id = -1;
|
m_shells.print_id = -1;
|
||||||
m_shells.with_wipe_tower = false;
|
|
||||||
m_shell_bounding_box = BoundingBoxf3();
|
m_shell_bounding_box = BoundingBoxf3();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1703,11 +1696,6 @@ void GCodeViewer::reset()
|
|||||||
void GCodeViewer::render_scene(int canvas_width, int canvas_height)
|
void GCodeViewer::render_scene(int canvas_width, int canvas_height)
|
||||||
{
|
{
|
||||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||||
// while dragging in the solid model mode, the objects stand in for their toolpaths, cut to the
|
|
||||||
// visible layer range; the toolpath set then holds only the range's bottom and top layers
|
|
||||||
if (m_viewer.is_reduced_detail() && solid_model_enabled())
|
|
||||||
render_solid_model(canvas_width, canvas_height);
|
|
||||||
else
|
|
||||||
render_shells(canvas_width, canvas_height);
|
render_shells(canvas_width, canvas_height);
|
||||||
|
|
||||||
if (m_viewer.get_extrusion_roles_count() == 0)
|
if (m_viewer.get_extrusion_roles_count() == 0)
|
||||||
@@ -2031,56 +2019,6 @@ void GCodeViewer::update_layers_slider_mode()
|
|||||||
// TODO m_layers_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder);
|
// TODO m_layers_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCodeViewer::set_interacting(bool interacting)
|
|
||||||
{
|
|
||||||
// with no shells to stand in for the toolpaths, the solid model would leave only the end layers
|
|
||||||
const bool usable = !solid_model_enabled() || !m_shells.volumes.empty();
|
|
||||||
m_viewer.set_reduced_detail(interacting && usable);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCodeViewer::read_reduced_detail_preferences()
|
|
||||||
{
|
|
||||||
m_reduced_detail_mode = reduced_detail_mode_from_string(get_app_config()->get("preview_reduced_detail_mode"));
|
|
||||||
apply_reduced_detail_settings();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCodeViewer::apply_reduced_detail_settings()
|
|
||||||
{
|
|
||||||
m_viewer.set_reduced_detail_mode(m_reduced_detail_mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCodeViewer::set_reduced_detail_mode(const std::string& mode)
|
|
||||||
{
|
|
||||||
const bool was_solid = solid_model_enabled();
|
|
||||||
m_reduced_detail_mode = reduced_detail_mode_from_string(mode);
|
|
||||||
apply_reduced_detail_settings();
|
|
||||||
reload_shells_if_solid_model_changed(was_solid);
|
|
||||||
}
|
|
||||||
|
|
||||||
libvgcode::EReducedDetailMode GCodeViewer::reduced_detail_mode_from_string(const std::string& mode)
|
|
||||||
{
|
|
||||||
if (mode == "solid")
|
|
||||||
return libvgcode::EReducedDetailMode::EndLayersOnly;
|
|
||||||
if (mode == "shell")
|
|
||||||
return libvgcode::EReducedDetailMode::ShellOnly;
|
|
||||||
return libvgcode::EReducedDetailMode::Off;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCodeViewer::reload_shells_if_solid_model_changed(bool was_enabled)
|
|
||||||
{
|
|
||||||
if (was_enabled == solid_model_enabled() || m_shells.print_id == -1)
|
|
||||||
return;
|
|
||||||
// only the prime tower comes and goes with the mode: a full reload would drop the shells
|
|
||||||
// whenever the print has moved on since they were loaded, leaving the solid model nothing to draw
|
|
||||||
if (wxGetApp().plater() == nullptr)
|
|
||||||
return;
|
|
||||||
// the shells are loaded from the current plate's print, which is not the plater's own
|
|
||||||
const Print& print = wxGetApp().plater()->get_partplate_list().get_current_fff_print();
|
|
||||||
if (static_cast<int>(print.id().id) != m_shells.print_id)
|
|
||||||
return;
|
|
||||||
update_shell_wipe_tower(print, m_gl_data_initialized);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range)
|
void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range)
|
||||||
{
|
{
|
||||||
m_viewer.set_layers_view_range(static_cast<uint32_t>(layers_z_range[0]), static_cast<uint32_t>(layers_z_range[1]));
|
m_viewer.set_layers_view_range(static_cast<uint32_t>(layers_z_range[0]), static_cast<uint32_t>(layers_z_range[1]));
|
||||||
@@ -2405,11 +2343,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
|
|||||||
void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_previewing)
|
void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_previewing)
|
||||||
{
|
{
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": initialized=%1%, force_previewing=%2%")%initialized %force_previewing;
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": initialized=%1%, force_previewing=%2%")%initialized %force_previewing;
|
||||||
// the shells can load before the first G-code does, so the preferences are read here as well
|
|
||||||
read_reduced_detail_preferences();
|
|
||||||
if ((print.id().id == m_shells.print_id)&&(print.get_modified_count() == m_shells.print_modify_count)) {
|
if ((print.id().id == m_shells.print_id)&&(print.get_modified_count() == m_shells.print_modify_count)) {
|
||||||
// the prime tower comes and goes on its own, without reloading the objects
|
|
||||||
update_shell_wipe_tower(print, initialized);
|
|
||||||
//BBS: update force previewing logic
|
//BBS: update force previewing logic
|
||||||
if (force_previewing)
|
if (force_previewing)
|
||||||
m_shells.previewing = force_previewing;
|
m_shells.previewing = force_previewing;
|
||||||
@@ -2518,45 +2452,10 @@ void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_p
|
|||||||
m_shells.print_id = print.id().id;
|
m_shells.print_id = print.id().id;
|
||||||
m_shells.print_modify_count = print.get_modified_count();
|
m_shells.print_modify_count = print.get_modified_count();
|
||||||
m_shells.previewing = true;
|
m_shells.previewing = true;
|
||||||
update_shell_wipe_tower(print, initialized);
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": shell loaded, id change to %1%, modify_count %2%, object count %3%, glvolume count %4%")
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(": shell loaded, id change to %1%, modify_count %2%, object count %3%, glvolume count %4%")
|
||||||
% m_shells.print_id % m_shells.print_modify_count % object_count %m_shells.volumes.volumes.size();
|
% m_shells.print_id % m_shells.print_modify_count % object_count %m_shells.volumes.volumes.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The prime tower as it was sliced, so that the solid model shows what the print shows. It keeps its
|
|
||||||
// opaque colour, so it never appears among the translucent shells, and stays out of their bounding box.
|
|
||||||
void GCodeViewer::update_shell_wipe_tower(const Print& print, bool initialized)
|
|
||||||
{
|
|
||||||
const bool with_wipe_tower = solid_model_enabled() && print.is_step_done(psWipeTower) && print.wipe_tower_data().wipe_tower_mesh_data;
|
|
||||||
if (with_wipe_tower == m_shells.with_wipe_tower)
|
|
||||||
return;
|
|
||||||
m_shells.with_wipe_tower = with_wipe_tower;
|
|
||||||
GLVolumePtrs& volumes = m_shells.volumes.volumes;
|
|
||||||
if (!with_wipe_tower) {
|
|
||||||
volumes.erase(std::remove_if(volumes.begin(), volumes.end(), [](GLVolume* volume) {
|
|
||||||
if (!volume->is_wipe_tower)
|
|
||||||
return false;
|
|
||||||
delete volume;
|
|
||||||
return true;
|
|
||||||
}), volumes.end());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const PrintConfig& config = print.config();
|
|
||||||
const int plate_idx = print.get_plate_index();
|
|
||||||
const Vec3d plate_origin = print.get_plate_origin();
|
|
||||||
const float x = static_cast<float>(config.wipe_tower_x.get_at(plate_idx) + plate_origin.x());
|
|
||||||
const float y = static_cast<float>(config.wipe_tower_y.get_at(plate_idx) + plate_origin.y());
|
|
||||||
const size_t first_new = volumes.size();
|
|
||||||
m_shells.volumes.load_real_wipe_tower_preview(1000 + plate_idx, x, y, print.wipe_tower_data().wipe_tower_mesh_data->real_wipe_tower_mesh,
|
|
||||||
print.wipe_tower_data().wipe_tower_mesh_data->real_brim_mesh, true,
|
|
||||||
static_cast<float>(config.wipe_tower_rotation_angle), false, initialized);
|
|
||||||
for (size_t i = first_new; i < volumes.size(); ++i) {
|
|
||||||
volumes[i]->zoom_to_volumes = false;
|
|
||||||
volumes[i]->force_native_color = true;
|
|
||||||
volumes[i]->set_render_color();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GCodeViewer::render_toolpaths()
|
void GCodeViewer::render_toolpaths()
|
||||||
{
|
{
|
||||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||||
@@ -2757,50 +2656,6 @@ void GCodeViewer::render_shells(int canvas_width, int canvas_height)
|
|||||||
glsafe(::glDepthMask(GL_TRUE));
|
glsafe(::glDepthMask(GL_TRUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The sliced objects and the prime tower drawn opaque, in their filament colours, cut to the
|
|
||||||
// visible layer range by the shader's z range. The toolpaths of the range's bottom and top layers
|
|
||||||
// are drawn afterwards and cap the cut.
|
|
||||||
void GCodeViewer::render_solid_model(int canvas_width, int canvas_height)
|
|
||||||
{
|
|
||||||
if (m_shells.volumes.empty())
|
|
||||||
return;
|
|
||||||
// gouraud_light has no z range, so it could not cut the model
|
|
||||||
GLShaderProgram* shader = wxGetApp().get_shader("gouraud");
|
|
||||||
if (shader == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const libvgcode::Interval& layers = m_viewer.get_layers_view_range();
|
|
||||||
const float z_top = m_viewer.get_layer_z(layers[1]) - m_z_offset + 0.001f;
|
|
||||||
const float z_bottom = (layers[0] > 0) ? m_viewer.get_layer_z(layers[0] - 1) - m_z_offset - 0.001f : -FLT_MAX;
|
|
||||||
|
|
||||||
std::vector<float> alphas;
|
|
||||||
alphas.reserve(m_shells.volumes.volumes.size());
|
|
||||||
for (GLVolume* volume : m_shells.volumes.volumes) {
|
|
||||||
alphas.push_back(volume->color.a());
|
|
||||||
volume->color.a(1.0f);
|
|
||||||
volume->set_render_color();
|
|
||||||
}
|
|
||||||
m_shells.volumes.set_z_range(z_bottom, z_top);
|
|
||||||
// gouraud also clips by this plane, which nothing else sets on the shells
|
|
||||||
m_shells.volumes.set_clipping_plane(ClippingPlane::ClipsNothing().get_data());
|
|
||||||
|
|
||||||
shader->start_using();
|
|
||||||
// the 3D view leaves its shadow settings on the shared program
|
|
||||||
shader->set_uniform("shadow_intensity", 0.0f);
|
|
||||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
|
||||||
shader->set_uniform("z_far", camera.get_far_z());
|
|
||||||
shader->set_uniform("z_near", camera.get_near_z());
|
|
||||||
m_shells.volumes.render(GLVolumeCollection::ERenderType::Opaque, false, camera.get_view_matrix(), camera.get_projection_matrix(), {canvas_width, canvas_height});
|
|
||||||
shader->stop_using();
|
|
||||||
|
|
||||||
m_shells.volumes.set_z_range(-FLT_MAX, FLT_MAX);
|
|
||||||
size_t k = 0;
|
|
||||||
for (GLVolume* volume : m_shells.volumes.volumes) {
|
|
||||||
volume->color.a(alphas[k++]);
|
|
||||||
volume->set_render_color();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//BBS
|
//BBS
|
||||||
void GCodeViewer::render_all_plates_stats(const std::vector<const GCodeProcessorResult*>& gcode_result_list, bool show /*= true*/) const {
|
void GCodeViewer::render_all_plates_stats(const std::vector<const GCodeProcessorResult*>& gcode_result_list, bool show /*= true*/) const {
|
||||||
if (!show)
|
if (!show)
|
||||||
|
|||||||
@@ -174,8 +174,6 @@ public:
|
|||||||
int print_id{-1};
|
int print_id{-1};
|
||||||
int print_modify_count{-1};
|
int print_modify_count{-1};
|
||||||
bool previewing{false};
|
bool previewing{false};
|
||||||
// the prime tower was loaded with the objects, for the solid model
|
|
||||||
bool with_wipe_tower{false};
|
|
||||||
};
|
};
|
||||||
//BBS
|
//BBS
|
||||||
ConflictResultOpt m_conflict_result;
|
ConflictResultOpt m_conflict_result;
|
||||||
@@ -236,17 +234,6 @@ private:
|
|||||||
|
|
||||||
bool m_legend_visible{ true };
|
bool m_legend_visible{ true };
|
||||||
bool m_legend_enabled{ true };
|
bool m_legend_enabled{ true };
|
||||||
// the reduced-detail preferences, pushed to libvgcode by apply_reduced_detail_settings()
|
|
||||||
libvgcode::EReducedDetailMode m_reduced_detail_mode{ libvgcode::EReducedDetailMode::Off };
|
|
||||||
void read_reduced_detail_preferences();
|
|
||||||
void apply_reduced_detail_settings();
|
|
||||||
static libvgcode::EReducedDetailMode reduced_detail_mode_from_string(const std::string& mode);
|
|
||||||
// in the solid model mode, the sliced objects are drawn as solid shapes instead of toolpaths
|
|
||||||
bool solid_model_enabled() const { return m_reduced_detail_mode == libvgcode::EReducedDetailMode::EndLayersOnly; }
|
|
||||||
void render_solid_model(int canvas_width, int canvas_height);
|
|
||||||
// the prime tower is only among the shells for the solid model, so it is added or removed when that changes
|
|
||||||
void reload_shells_if_solid_model_changed(bool was_enabled);
|
|
||||||
void update_shell_wipe_tower(const Print& print, bool initialized);
|
|
||||||
|
|
||||||
float m_legend_height;
|
float m_legend_height;
|
||||||
PrintEstimatedStatistics m_print_statistics;
|
PrintEstimatedStatistics m_print_statistics;
|
||||||
@@ -304,7 +291,7 @@ public:
|
|||||||
// void _render_calibration_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
// void _render_calibration_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||||
// void _render_calibration_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
// void _render_calibration_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||||
// void render_calibration_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
// void render_calibration_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, OpenGLManager& opengl_manager);
|
||||||
bool has_data() const { return m_viewer.get_extrusion_roles_count() != 0; }
|
bool has_data() const { return !m_viewer.get_extrusion_roles().empty(); }
|
||||||
|
|
||||||
bool can_export_toolpaths() const;
|
bool can_export_toolpaths() const;
|
||||||
std::vector<int> get_plater_extruder();
|
std::vector<int> get_plater_extruder();
|
||||||
@@ -376,14 +363,6 @@ public:
|
|||||||
void set_dim_previous_layers_brightness(float value) { m_viewer.set_dim_previous_layers_brightness(value); }
|
void set_dim_previous_layers_brightness(float value) { m_viewer.set_dim_previous_layers_brightness(value); }
|
||||||
float get_dim_previous_layers_brightness() const { return m_viewer.get_dim_previous_layers_brightness(); }
|
float get_dim_previous_layers_brightness() const { return m_viewer.get_dim_previous_layers_brightness(); }
|
||||||
|
|
||||||
// whether the mouse is holding either slider's handle
|
|
||||||
bool is_slider_dragging() const { return m_layers_slider->is_dragging() || m_moves_slider->is_dragging(); }
|
|
||||||
// while the user drags the camera or a slider, draw the reduced set, if the preference asks for one
|
|
||||||
void set_interacting(bool interacting);
|
|
||||||
bool is_reduced_detail() const { return m_viewer.is_reduced_detail(); }
|
|
||||||
// the preference's string value: "off", "solid" or "shell"
|
|
||||||
void set_reduced_detail_mode(const std::string& mode);
|
|
||||||
|
|
||||||
void set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range);
|
void set_layers_z_range(const std::array<unsigned int, 2>& layers_z_range);
|
||||||
|
|
||||||
bool is_legend_shown() const { return m_legend_visible && m_legend_enabled; }
|
bool is_legend_shown() const { return m_legend_visible && m_legend_enabled; }
|
||||||
|
|||||||
@@ -2084,11 +2084,6 @@ void GLCanvas3D::_render_frame(bool scene_dirty, bool only_init)
|
|||||||
const bool overlay_tick = m_fps_overlay_tick;
|
const bool overlay_tick = m_fps_overlay_tick;
|
||||||
m_fps_overlay_tick = false;
|
m_fps_overlay_tick = false;
|
||||||
|
|
||||||
// Whether the preview draws its reduced set is decided before the cached scene is consulted,
|
|
||||||
// since switching changes what the scene pass draws.
|
|
||||||
if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview && m_gcode_viewer.has_data() && _update_preview_interaction())
|
|
||||||
scene_dirty = true;
|
|
||||||
|
|
||||||
// An overlay-only frame reuses the last scene pass. The overlay is rebuilt either way, and drawn
|
// An overlay-only frame reuses the last scene pass. The overlay is rebuilt either way, and drawn
|
||||||
// below once it is known whether the frame differs from the one on screen.
|
// below once it is known whether the frame differs from the one on screen.
|
||||||
const bool reuse_scene = !scene_dirty && _can_reuse_cached_scene(camera);
|
const bool reuse_scene = !scene_dirty && _can_reuse_cached_scene(camera);
|
||||||
@@ -3282,16 +3277,9 @@ void GLCanvas3D::bind_event_handlers()
|
|||||||
if (m_selection_edit.kind != SelectionEdit::None)
|
if (m_selection_edit.kind != SelectionEdit::None)
|
||||||
finish_selection_edit();
|
finish_selection_edit();
|
||||||
ImGui::SetWindowFocus(nullptr);
|
ImGui::SetWindowFocus(nullptr);
|
||||||
// a drag cut short never sees its button release, which would leave the reduced set drawn
|
|
||||||
if (m_canvas_type == CanvasPreview && m_mouse.dragging && m_gcode_viewer.is_reduced_detail())
|
|
||||||
mouse_up_cleanup();
|
|
||||||
render();
|
render();
|
||||||
evt.Skip();
|
evt.Skip();
|
||||||
});
|
});
|
||||||
m_canvas->Bind(wxEVT_MOUSE_CAPTURE_LOST, [this](wxMouseCaptureLostEvent&) {
|
|
||||||
if (m_canvas_type == CanvasPreview && m_mouse.dragging && m_gcode_viewer.is_reduced_detail())
|
|
||||||
mouse_up_cleanup();
|
|
||||||
});
|
|
||||||
m_event_handlers_bound = true;
|
m_event_handlers_bound = true;
|
||||||
|
|
||||||
m_canvas->Bind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
|
m_canvas->Bind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
|
||||||
@@ -3367,17 +3355,6 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
|||||||
m_overlay_dirty |= imgui_requires_extra_frame;
|
m_overlay_dirty |= imgui_requires_extra_frame;
|
||||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||||
m_dirty |= GLTexture::Compressor::has_compressed_texture_to_refresh();
|
m_dirty |= GLTexture::Compressor::has_compressed_texture_to_refresh();
|
||||||
// the render timer only wakes the idle loop; the frame that puts the preview's toolpaths back
|
|
||||||
// after a wheel burst has to be asked for here, once the settle time is really up
|
|
||||||
if (m_preview_settle_pending) {
|
|
||||||
const auto now = std::chrono::steady_clock::now();
|
|
||||||
if (now >= m_preview_interaction_until) {
|
|
||||||
m_preview_settle_pending = false;
|
|
||||||
m_dirty = true;
|
|
||||||
}
|
|
||||||
else // the timer fired early
|
|
||||||
schedule_extra_frame(static_cast<int>(std::chrono::duration_cast<std::chrono::milliseconds>(m_preview_interaction_until - now).count()) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_dirty && !m_overlay_dirty)
|
if (!m_dirty && !m_overlay_dirty)
|
||||||
return;
|
return;
|
||||||
@@ -3908,10 +3885,6 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// only a wheel the panels did not take moves the camera
|
|
||||||
if (m_canvas_type == CanvasPreview)
|
|
||||||
note_preview_interaction();
|
|
||||||
|
|
||||||
#ifdef __WXMSW__
|
#ifdef __WXMSW__
|
||||||
// For some reason the Idle event is not being generated after the mouse scroll event in case of scrolling with the two fingers on the touch pad,
|
// For some reason the Idle event is not being generated after the mouse scroll event in case of scrolling with the two fingers on the touch pad,
|
||||||
// if the event is not allowed to be passed further.
|
// if the event is not allowed to be passed further.
|
||||||
@@ -4012,11 +3985,6 @@ void GLCanvas3D::on_fps_overlay_timer(wxTimerEvent& evt)
|
|||||||
wxWakeUpIdle();
|
wxWakeUpIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
void GLCanvas3D::schedule_extra_frame(int milliseconds)
|
||||||
{
|
{
|
||||||
// Schedule idle event right now
|
// Schedule idle event right now
|
||||||
@@ -5663,9 +5631,6 @@ void GLCanvas3D::mouse_up_cleanup()
|
|||||||
m_mouse.ignore_left_up = false;
|
m_mouse.ignore_left_up = false;
|
||||||
m_mouse.ignore_right_up = false;
|
m_mouse.ignore_right_up = false;
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
// the frame that follows a release puts the preview's toolpaths back, and on some platforms
|
|
||||||
// no idle event follows a button release until the next input
|
|
||||||
wxWakeUpIdle();
|
|
||||||
|
|
||||||
if (m_canvas->HasCapture())
|
if (m_canvas->HasCapture())
|
||||||
m_canvas->ReleaseMouse();
|
m_canvas->ReleaseMouse();
|
||||||
@@ -7814,20 +7779,13 @@ bool GLCanvas3D::_is_scene_cacheable() const
|
|||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// The scene follows the cursor while the user drags, under a gizmo that draws at the cursor, and
|
// The scene follows the cursor during a drag, under a gizmo that draws at the cursor, and while
|
||||||
// while the cursor is on the layer height bar, where the object shader draws a band at its height.
|
// the cursor is on the layer height bar, where the object shader draws a band at its height.
|
||||||
const GLGizmoBase* gizmo = m_gizmos.get_current();
|
const GLGizmoBase* gizmo = m_gizmos.get_current();
|
||||||
const bool cursor_on_layers_bar = is_layers_editing_enabled() &&
|
const bool cursor_on_layers_bar = is_layers_editing_enabled() &&
|
||||||
m_layers_editing.bar_rect_contains(*this, (float)m_mouse.position.x(), (float)m_mouse.position.y());
|
m_layers_editing.bar_rect_contains(*this, (float)m_mouse.position.x(), (float)m_mouse.position.y());
|
||||||
return !is_user_interacting() && (gizmo == nullptr || !gizmo->render_follows_cursor()) && !cursor_on_layers_bar;
|
return !m_mouse.dragging && !m_gizmos.is_dragging() && !m_rectangle_selection.is_dragging() &&
|
||||||
}
|
(gizmo == nullptr || !gizmo->render_follows_cursor()) && !cursor_on_layers_bar;
|
||||||
|
|
||||||
// Whether the user is holding something that moves the scene: the camera, the navigator, a gizmo,
|
|
||||||
// the rectangle selection or a preview slider.
|
|
||||||
bool GLCanvas3D::is_user_interacting() const
|
|
||||||
{
|
|
||||||
return m_mouse.dragging || m_navigator_dragging || m_gizmos.is_dragging() || m_rectangle_selection.is_dragging() ||
|
|
||||||
m_gcode_viewer.is_slider_dragging();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GLCanvas3D::_is_frame_skipping_enabled() const
|
bool GLCanvas3D::_is_frame_skipping_enabled() const
|
||||||
@@ -8818,24 +8776,6 @@ void GLCanvas3D::_render_wireframe_overlay()
|
|||||||
shader->stop_using();
|
shader->stop_using();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The reduced set is drawn while the camera, the navigator or either slider is dragged. A wheel
|
|
||||||
// step has no duration, so it holds the reduced set for a settle time instead, and the frame that
|
|
||||||
// restores the full toolpaths is scheduled for when that time runs out. Returns whether what the scene
|
|
||||||
// pass draws changed, since a frame that reuses the cached scene would hide the change.
|
|
||||||
bool GLCanvas3D::_update_preview_interaction()
|
|
||||||
{
|
|
||||||
const auto now = std::chrono::steady_clock::now();
|
|
||||||
const bool settling = now < m_preview_interaction_until;
|
|
||||||
const bool dragging = is_user_interacting();
|
|
||||||
const bool was_reduced = m_gcode_viewer.is_reduced_detail();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
return m_gcode_viewer.is_reduced_detail() != was_reduced;
|
|
||||||
}
|
|
||||||
|
|
||||||
//BBS: GUI refactor: add canvas size as parameters
|
//BBS: GUI refactor: add canvas size as parameters
|
||||||
void GLCanvas3D::_render_gcode(int canvas_width, int canvas_height)
|
void GLCanvas3D::_render_gcode(int canvas_width, int canvas_height)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -656,10 +656,6 @@ private:
|
|||||||
ECursorType m_cursor_type;
|
ECursorType m_cursor_type;
|
||||||
GLSelectionRectangle m_rectangle_selection;
|
GLSelectionRectangle m_rectangle_selection;
|
||||||
bool m_navigator_dragging{ false };
|
bool m_navigator_dragging{ false };
|
||||||
// until when a wheel step keeps the preview's reduced set drawn
|
|
||||||
std::chrono::time_point<std::chrono::steady_clock> m_preview_interaction_until{};
|
|
||||||
// whether the frame that restores the toolpaths once that time is up is still owed
|
|
||||||
bool m_preview_settle_pending{ false };
|
|
||||||
|
|
||||||
//BBS:add plate related logic
|
//BBS:add plate related logic
|
||||||
mutable std::vector<int> m_hover_volume_idxs;
|
mutable std::vector<int> m_hover_volume_idxs;
|
||||||
@@ -1226,10 +1222,6 @@ public:
|
|||||||
void msw_rescale() { m_gcode_viewer.invalidate_legend(); }
|
void msw_rescale() { m_gcode_viewer.invalidate_legend(); }
|
||||||
|
|
||||||
void request_extra_frame() { m_extra_frame_requested = true; }
|
void request_extra_frame() { m_extra_frame_requested = true; }
|
||||||
// whether the user is holding the camera, the navigator, a gizmo, the rectangle selection or a preview slider
|
|
||||||
bool is_user_interacting() const;
|
|
||||||
// a wheel step is over before the next frame, so it holds the preview's reduced set for a settle time
|
|
||||||
void note_preview_interaction();
|
|
||||||
|
|
||||||
void schedule_extra_frame(int milliseconds);
|
void schedule_extra_frame(int milliseconds);
|
||||||
|
|
||||||
@@ -1380,9 +1372,6 @@ private:
|
|||||||
//BBS: GUI refactor: add canvas size as parameters
|
//BBS: GUI refactor: add canvas size as parameters
|
||||||
void _render_gcode(int canvas_width, int canvas_height);
|
void _render_gcode(int canvas_width, int canvas_height);
|
||||||
void _render_gcode_overlay(int canvas_width, int canvas_height);
|
void _render_gcode_overlay(int canvas_width, int canvas_height);
|
||||||
// decides whether the preview draws its reduced set this frame and returns whether what the scene
|
|
||||||
// pass draws changed; runs before the cached scene is consulted
|
|
||||||
bool _update_preview_interaction();
|
|
||||||
//BBS: render a plane for assemble
|
//BBS: render a plane for assemble
|
||||||
void _render_plane() const;
|
void _render_plane() const;
|
||||||
void _render_selection();
|
void _render_selection();
|
||||||
|
|||||||
@@ -483,11 +483,6 @@ 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());
|
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)
|
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();
|
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||||
@@ -496,7 +491,6 @@ bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int
|
|||||||
|
|
||||||
ImGuiContext& context = *GImGui;
|
ImGuiContext& context = *GImGui;
|
||||||
const ImGuiID id = window->GetID(str_id);
|
const ImGuiID id = window->GetID(str_id);
|
||||||
m_imgui_id = id;
|
|
||||||
|
|
||||||
const ImVec2 pos = window->DC.CursorPos;
|
const ImVec2 pos = window->DC.CursorPos;
|
||||||
const ImRect draw_region(pos, pos + size);
|
const ImRect draw_region(pos, pos + size);
|
||||||
@@ -889,7 +883,6 @@ bool IMSlider::vertical_slider(const char* str_id, int* higher_value, int* lower
|
|||||||
|
|
||||||
ImGuiContext& context = *GImGui;
|
ImGuiContext& context = *GImGui;
|
||||||
const ImGuiID id = window->GetID(str_id);
|
const ImGuiID id = window->GetID(str_id);
|
||||||
m_imgui_id = id;
|
|
||||||
|
|
||||||
const ImVec2 pos = window->DC.CursorPos;
|
const ImVec2 pos = window->DC.CursorPos;
|
||||||
const ImRect draw_region(pos, pos + size);
|
const ImRect draw_region(pos, pos + size);
|
||||||
|
|||||||
@@ -118,9 +118,6 @@ public:
|
|||||||
|
|
||||||
//BBS update scroll value changed
|
//BBS update scroll value changed
|
||||||
bool is_dirty() { return m_dirty; }
|
bool is_dirty() { return m_dirty; }
|
||||||
// whether the mouse is 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; }
|
void set_as_dirty(bool dirty = true) { m_dirty = dirty; }
|
||||||
bool is_need_post_tick_event() { return m_is_need_post_tick_changed_event; }
|
bool is_need_post_tick_event() { return m_is_need_post_tick_changed_event; }
|
||||||
void reset_post_tick_event(bool val = false) {
|
void reset_post_tick_event(bool val = false) {
|
||||||
@@ -185,8 +182,6 @@ private:
|
|||||||
int m_higher_value;
|
int m_higher_value;
|
||||||
int m_one_layer_value; // ORCA
|
int m_one_layer_value; // ORCA
|
||||||
bool m_dirty = false;
|
bool m_dirty = false;
|
||||||
// the ImGui id of the slider widget, as of its last render
|
|
||||||
unsigned int m_imgui_id = 0;
|
|
||||||
|
|
||||||
bool m_render_as_disabled{ false };
|
bool m_render_as_disabled{ false };
|
||||||
|
|
||||||
|
|||||||
@@ -12800,6 +12800,8 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
|||||||
notification_manager->set_slicing_progress_export_possible();
|
notification_manager->set_slicing_progress_export_possible();
|
||||||
|
|
||||||
// Reset the "export G-code path" name, so that the automatic background processing will be enabled again.
|
// Reset the "export G-code path" name, so that the automatic background processing will be enabled again.
|
||||||
|
const std::string lifecycle_job_name = this->background_process.fff_print() ?
|
||||||
|
this->background_process.fff_print()->output_filename() : std::string();
|
||||||
this->background_process.reset_export();
|
this->background_process.reset_export();
|
||||||
// This bool stops showing export finished notification even when process_completed_with_error is false
|
// This bool stops showing export finished notification even when process_completed_with_error is false
|
||||||
bool has_error = false;
|
bool has_error = false;
|
||||||
@@ -12846,18 +12848,8 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
|||||||
|
|
||||||
{
|
{
|
||||||
Slic3r::LifecycleEventContext ctx;
|
Slic3r::LifecycleEventContext ctx;
|
||||||
if (const PrintBase* print = this->background_process.current_print()) {
|
ctx.name = lifecycle_job_name;
|
||||||
const Model& model = print->model();
|
ctx.code = evt.cancelled() ? Slic3r::LifecycleEvtCode::Warn : (has_error ? Slic3r::LifecycleEvtCode::Error : Slic3r::LifecycleEvtCode::Ok);
|
||||||
ctx.id = std::to_string(model.id().id);
|
|
||||||
if (model.model_info)
|
|
||||||
ctx.name = model.model_info->model_name;
|
|
||||||
} else {
|
|
||||||
// Realistically Printbase* print will never be null because select_technology already asserts an active print
|
|
||||||
// and the worker thread asserts it before processing.
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": slicing completed without an active print; lifecycle event has no model ID";
|
|
||||||
}
|
|
||||||
ctx.code = evt.cancelled() ? Slic3r::LifecycleEvtCode::Warn :
|
|
||||||
(has_error ? Slic3r::LifecycleEvtCode::Error : Slic3r::LifecycleEvtCode::Ok);
|
|
||||||
ctx.msg = evt.cancelled() ? "cancelled" : (has_error ? lifecycle_error_msg : std::string());
|
ctx.msg = evt.cancelled() ? "cancelled" : (has_error ? lifecycle_error_msg : std::string());
|
||||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::SlicingJobComplete, ctx);
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::SlicingJobComplete, ctx);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ wxBoxSizer* PreferencesDialog::create_item_combobox(wxString title, wxString too
|
|||||||
return sizer;
|
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, std::function<void(std::string)> onchange, 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, const wxString wiki_url)
|
||||||
{
|
{
|
||||||
assert(vlist.size() == config_name_index.size());
|
assert(vlist.size() == config_name_index.size());
|
||||||
unsigned int current_index = 0;
|
unsigned int current_index = 0;
|
||||||
@@ -338,9 +338,8 @@ wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxString too
|
|||||||
auto [sizer, combobox] = create_item_combobox_base(title, tooltip, param, vlist, current_index);
|
auto [sizer, combobox] = create_item_combobox_base(title, tooltip, param, vlist, current_index);
|
||||||
|
|
||||||
//// save config
|
//// save config
|
||||||
combobox->GetDropDown().Bind(wxEVT_COMBOBOX, [this, param, config_name_index, onchange](wxCommandEvent& e) {
|
combobox->GetDropDown().Bind(wxEVT_COMBOBOX, [this, param, config_name_index](wxCommandEvent& e) {
|
||||||
app_config->set(param, config_name_index[e.GetSelection()]);
|
app_config->set(param, config_name_index[e.GetSelection()]);
|
||||||
if (onchange != nullptr) onchange(config_name_index[e.GetSelection()]);
|
|
||||||
e.Skip();
|
e.Skip();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2045,32 +2044,6 @@ void PreferencesDialog::create_items()
|
|||||||
"preview_default_view_type", PreviewViewTypeLabels, PreviewViewTypeValues);
|
"preview_default_view_type", PreviewViewTypeLabels, PreviewViewTypeValues);
|
||||||
g_sizer->Add(item_preview_view_type);
|
g_sizer->Add(item_preview_view_type);
|
||||||
|
|
||||||
auto item_reduced_detail_mode = create_item_combobox(
|
|
||||||
_L("Simplify preview while dragging"),
|
|
||||||
_L("What the sliced preview draws while you drag the camera or a preview slider, or zoom with the mouse wheel, so that large prints stay responsive. "
|
|
||||||
"The full toolpaths are restored as soon as you let go.\n"
|
|
||||||
"Off: the full toolpaths.\n"
|
|
||||||
"Solid model: the sliced objects and the prime tower as solid shapes in their filament colors, cut to the visible layer range, "
|
|
||||||
"with its bottom and top layers drawn as toolpaths. Supports are not shown, and negative volumes are not cut out.\n"
|
|
||||||
"Shell only: every layer without its sparse infill, internal solid infill and gap fill, which lie under the walls and skins. "
|
|
||||||
"Walls, top and bottom surfaces, bridges, supports and the prime tower are drawn whole, so the print looks the same from outside.\n"
|
|
||||||
"The bottom and top of the visible layer range are always drawn whole."),
|
|
||||||
"preview_reduced_detail_mode",
|
|
||||||
{_L("Off"), _L("Solid model"), _L("Shell only")},
|
|
||||||
{"off", "solid", "shell"},
|
|
||||||
// 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_dim_previous_layers = create_item_checkbox(
|
auto item_dim_previous_layers = create_item_checkbox(
|
||||||
_L("Dim lower layers"),
|
_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."),
|
_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."),
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ public:
|
|||||||
wxBoxSizer *create_item_title(wxString title);
|
wxBoxSizer *create_item_title(wxString title);
|
||||||
wxBoxSizer *create_item_label(wxString label, const wxString tooltip = "", const wxString wiki_url = "");
|
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::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, std::function<void(std::string)> 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_region_combobox(wxString title, wxString tooltip);
|
wxBoxSizer *create_item_region_combobox(wxString title, wxString tooltip);
|
||||||
wxBoxSizer *create_item_language_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);
|
wxBoxSizer *create_item_loglevel_combobox(wxString title, wxString tooltip, std::vector<wxString> vlist);
|
||||||
|
|||||||
@@ -345,16 +345,21 @@ boost::filesystem::path find_bundled_python_home()
|
|||||||
fs::path bundle_python = fs::path(resources_dir()).parent_path() / "MacOS" / "python";
|
fs::path bundle_python = fs::path(resources_dir()).parent_path() / "MacOS" / "python";
|
||||||
if (valid_python_home(bundle_python))
|
if (valid_python_home(bundle_python))
|
||||||
return bundle_python;
|
return bundle_python;
|
||||||
#elif defined(_WIN32)
|
#elif !defined(_WIN32)
|
||||||
fs::path exe_python = boost::dll::program_location().parent_path() / "python";
|
|
||||||
if (valid_python_home(exe_python))
|
|
||||||
return exe_python;
|
|
||||||
#else
|
|
||||||
fs::path linux_python = fs::path(resources_dir()).parent_path() / "lib" / "python";
|
fs::path linux_python = fs::path(resources_dir()).parent_path() / "lib" / "python";
|
||||||
if (valid_python_home(linux_python))
|
if (valid_python_home(linux_python))
|
||||||
return linux_python;
|
return linux_python;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Next to the executable: the Windows install layout, and the runtime copied
|
||||||
|
// beside every platform's unit-test binary (tests/slic3rutils/CMakeLists.txt).
|
||||||
|
// The CI test runner only receives the build/tests tree, so the candidates
|
||||||
|
// below -- all of which point into the deps or install trees -- never resolve
|
||||||
|
// there.
|
||||||
|
fs::path exe_python = boost::dll::program_location().parent_path() / "python";
|
||||||
|
if (valid_python_home(exe_python))
|
||||||
|
return exe_python;
|
||||||
|
|
||||||
fs::path configured_python = ORCA_BUNDLED_PYTHON_ROOT;
|
fs::path configured_python = ORCA_BUNDLED_PYTHON_ROOT;
|
||||||
if (!configured_python.empty() && valid_python_home(configured_python))
|
if (!configured_python.empty() && valid_python_home(configured_python))
|
||||||
return configured_python;
|
return configured_python;
|
||||||
|
|||||||
@@ -15,9 +15,6 @@
|
|||||||
#include "libslic3r/Layer.hpp"
|
#include "libslic3r/Layer.hpp"
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
#include "libslic3r/GCodeReader.hpp"
|
#include "libslic3r/GCodeReader.hpp"
|
||||||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
|
||||||
#include "libslic3r/Exception.hpp"
|
|
||||||
#include "libslic3r/LifecycleEvents.hpp"
|
|
||||||
|
|
||||||
#include "test_helpers.hpp"
|
#include "test_helpers.hpp"
|
||||||
#include "test_utils.hpp"
|
#include "test_utils.hpp"
|
||||||
@@ -25,10 +22,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <memory>
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
using namespace Slic3r::Test;
|
using namespace Slic3r::Test;
|
||||||
@@ -230,88 +224,8 @@ std::string resolved_output_name(Model& model, const std::string& format, const
|
|||||||
return print.output_filename(filename_base);
|
return print.output_filename(filename_base);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ScopedLifecycleHook
|
|
||||||
{
|
|
||||||
explicit ScopedLifecycleHook(LifecycleHookFn hook) { set_lifecycle_hook_fn(std::move(hook)); }
|
|
||||||
~ScopedLifecycleHook() { set_lifecycle_hook_fn(nullptr); }
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST_CASE("Slicing lifecycle events identify the model", "[Print][LifecycleEvents]")
|
|
||||||
{
|
|
||||||
struct ObservedEvent {
|
|
||||||
LifecycleEvent event;
|
|
||||||
std::string id;
|
|
||||||
std::string name;
|
|
||||||
};
|
|
||||||
std::vector<ObservedEvent> events;
|
|
||||||
ScopedLifecycleHook hook([&](LifecycleEvent event, const LifecycleEventContext& ctx) {
|
|
||||||
events.push_back({ event, ctx.id, ctx.name });
|
|
||||||
});
|
|
||||||
|
|
||||||
Print print;
|
|
||||||
Model model;
|
|
||||||
ModelInfo info;
|
|
||||||
info.model_name = "Lifecycle test model";
|
|
||||||
model.model_info = std::make_shared<ModelInfo>(std::move(info));
|
|
||||||
init_print({cube(20)}, print, model);
|
|
||||||
|
|
||||||
print.process();
|
|
||||||
ScopedTemporaryFile temp(".gcode");
|
|
||||||
print.export_gcode(temp.string(), nullptr, nullptr);
|
|
||||||
GCodeProcessorResult result;
|
|
||||||
print.export_gcode_from_previous_file(temp.string(), &result);
|
|
||||||
|
|
||||||
const std::string expected_id = std::to_string(print.model().id().id);
|
|
||||||
const std::vector<LifecycleEvent> expected_events = {
|
|
||||||
LifecycleEvent::SliceStarted,
|
|
||||||
LifecycleEvent::SliceGeometryFinished,
|
|
||||||
LifecycleEvent::GCodeExportStarted,
|
|
||||||
LifecycleEvent::GCodeExportFinished,
|
|
||||||
LifecycleEvent::GCodeExportStarted,
|
|
||||||
LifecycleEvent::GCodeExportFinished,
|
|
||||||
};
|
|
||||||
REQUIRE(events.size() == expected_events.size());
|
|
||||||
for (size_t i = 0; i < expected_events.size(); ++i) {
|
|
||||||
CHECK(events[i].event == expected_events[i]);
|
|
||||||
CHECK(events[i].id == expected_id);
|
|
||||||
CHECK(events[i].name == "Lifecycle test model");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Slicing lifecycle event name is empty without model metadata", "[Print][LifecycleEvents]")
|
|
||||||
{
|
|
||||||
std::string event_id;
|
|
||||||
std::string event_name = "unset";
|
|
||||||
ScopedLifecycleHook hook([&](LifecycleEvent event, const LifecycleEventContext& ctx) {
|
|
||||||
if (event == LifecycleEvent::SliceStarted) {
|
|
||||||
event_id = ctx.id;
|
|
||||||
event_name = ctx.name;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Print print;
|
|
||||||
Model model;
|
|
||||||
init_print({cube(20)}, print, model);
|
|
||||||
print.process();
|
|
||||||
|
|
||||||
CHECK(event_id == std::to_string(print.model().id().id));
|
|
||||||
CHECK(event_name.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Output filenames with numeric statistics fail before slicing finishes", "[Print][Regression]")
|
|
||||||
{
|
|
||||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
|
||||||
config.set_key_value("filename_format", new ConfigOptionString("{int(total_weight*10) / 10.0}"));
|
|
||||||
|
|
||||||
Print print;
|
|
||||||
Model model;
|
|
||||||
init_print({cube(20)}, print, model, config);
|
|
||||||
|
|
||||||
CHECK_THROWS_AS(print.output_filename(), PlaceholderParserError);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Print: {first_object_name} names the first printable object on the plate", "[Print]")
|
TEST_CASE("Print: {first_object_name} names the first printable object on the plate", "[Print]")
|
||||||
{
|
{
|
||||||
Model model;
|
Model model;
|
||||||
|
|||||||
@@ -49,9 +49,16 @@ if (WIN32)
|
|||||||
COMMENT "Copying Python runtime for slic3rutils plugin host API tests"
|
COMMENT "Copying Python runtime for slic3rutils plugin host API tests"
|
||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
elseif (APPLE)
|
elseif (NOT FLATPAK)
|
||||||
|
# The CI unit-test runner only receives the build/tests tree, so both the
|
||||||
|
# interpreter and the libpython the test binary links have to travel next to
|
||||||
|
# the executable; find_bundled_python_home() picks the copy up from there.
|
||||||
|
if (APPLE)
|
||||||
target_link_options(${_TEST_NAME}_tests PRIVATE
|
target_link_options(${_TEST_NAME}_tests PRIVATE
|
||||||
"LINKER:-rpath,@executable_path/python/lib")
|
"LINKER:-rpath,@executable_path/python/lib")
|
||||||
|
else ()
|
||||||
|
set_property(TARGET ${_TEST_NAME}_tests APPEND PROPERTY BUILD_RPATH "$ORIGIN/python/lib")
|
||||||
|
endif ()
|
||||||
|
|
||||||
add_custom_command(TARGET ${_TEST_NAME}_tests POST_BUILD
|
add_custom_command(TARGET ${_TEST_NAME}_tests POST_BUILD
|
||||||
COMMAND ${CMAKE_COMMAND} -E rm -rf
|
COMMAND ${CMAKE_COMMAND} -E rm -rf
|
||||||
@@ -59,7 +66,7 @@ elseif (APPLE)
|
|||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||||
"${CMAKE_PREFIX_PATH}/libpython"
|
"${CMAKE_PREFIX_PATH}/libpython"
|
||||||
"$<TARGET_FILE_DIR:${_TEST_NAME}_tests>/python"
|
"$<TARGET_FILE_DIR:${_TEST_NAME}_tests>/python"
|
||||||
COMMENT "Copying Python runtime for macOS plugin host API tests"
|
COMMENT "Copying Python runtime for the plugin host API tests"
|
||||||
VERBATIM
|
VERBATIM
|
||||||
)
|
)
|
||||||
elseif (FLATPAK)
|
elseif (FLATPAK)
|
||||||
@@ -76,4 +83,16 @@ elseif (FLATPAK)
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# scripts/run_unit_tests.sh installs the tests' numpy into that runtime with this uv,
|
||||||
|
# staged where the app build tree keeps it (<exe dir>/tools/uv, see src/CMakeLists.txt).
|
||||||
|
if (ORCA_BUNDLED_UV_EXECUTABLE)
|
||||||
|
add_custom_command(TARGET ${_TEST_NAME}_tests POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:${_TEST_NAME}_tests>/tools/uv"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${ORCA_BUNDLED_UV_EXECUTABLE}"
|
||||||
|
"$<TARGET_FILE_DIR:${_TEST_NAME}_tests>/tools/uv/${ORCA_BUNDLED_UV_FILENAME}"
|
||||||
|
COMMENT "Copying uv for the plugin host API tests"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
orcaslicer_discover_tests(${_TEST_NAME}_tests)
|
orcaslicer_discover_tests(${_TEST_NAME}_tests)
|
||||||
|
|||||||
Reference in New Issue
Block a user