mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-22 08:22:58 +00:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3124943292 | ||
|
|
8240984ca8 | ||
|
|
ce41b5a3cb | ||
|
|
8fd93907fd | ||
|
|
8056b32450 | ||
|
|
fabd08af1e | ||
|
|
33a8265a11 | ||
|
|
d96682a798 | ||
|
|
64d04a75f3 | ||
|
|
bf22ef2a82 | ||
|
|
dfd3444ae7 |
@@ -1,62 +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. With the `preview_solid_model_while_dragging` preference (*Graphics > G-code Preview*,
|
||||
off by default), the preview draws the sliced objects as solid meshes while the user drags and
|
||||
puts the toolpaths back when they let go. A mesh costs its triangles once, however many layers it
|
||||
has.
|
||||
|
||||
`GCodeViewer` draws the solid model, libvgcode (`src/libvgcode`) keeps the toolpaths that cap it,
|
||||
and `GLCanvas3D` decides when the user is dragging. The OpenGL ES path ignores the preference.
|
||||
|
||||
## 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 colours, with the
|
||||
`gouraud` shader, whose z range cuts them to the visible layer range. The shells hold only the
|
||||
objects, so while the preference 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 preference changes, without
|
||||
reloading the objects, keeps its opaque colour so that it never appears among the translucent
|
||||
shells, and stays out of their bounding box. Supports have no mesh and are not shown.
|
||||
|
||||
A plate whose shells are not loaded keeps drawing toolpaths, since the solid model would leave
|
||||
only the end layers.
|
||||
|
||||
## End-layer set
|
||||
|
||||
The cut faces of the solid model are capped with what was really printed there: the toolpaths of
|
||||
the bottom and top layers of the visible range. While the preference is on,
|
||||
`ViewerImpl::update_enabled_entities()` fills a second, **reduced** index buffer holding just those
|
||||
two layers, in the same walk that fills the full one. Building both together is what makes
|
||||
switching free: starting or ending a drag is a buffer binding, never a rebuild.
|
||||
|
||||
## 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 the camera, the navigator 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 solid model 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 solid model 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 solid-model frame outlives a drag, and the frame that ends a wheel's settle time is
|
||||
requested as a full frame.
|
||||
|
||||
## Per-frame lookups
|
||||
|
||||
The segment template draws its box from 8 corners through an index buffer, so the vertex shader
|
||||
runs at most once per corner. `get_estimated_time_at()`, which the tool marker tooltip calls every
|
||||
frame, starts from the running time at the first vertex of the vertex's layer, kept per layer at
|
||||
load, and adds only that layer's vertices. The sum runs in vertex order, so it matches a full
|
||||
accumulation exactly while costing memory per layer rather than per vertex.
|
||||
@@ -205,10 +205,6 @@ void AppConfig::set_defaults()
|
||||
if (get("seq_top_layer_only").empty())
|
||||
set("seq_top_layer_only", "1");
|
||||
|
||||
// draw the sliced objects instead of their toolpaths while the user drags the preview
|
||||
if (get("preview_solid_model_while_dragging").empty())
|
||||
set_bool("preview_solid_model_while_dragging", false);
|
||||
|
||||
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
||||
if (get("preview_dim_previous_layers").empty())
|
||||
set_bool("preview_dim_previous_layers", false);
|
||||
|
||||
+46
-5
@@ -8,6 +8,7 @@
|
||||
#include "I18N.hpp"
|
||||
#include "GCode.hpp"
|
||||
#include "Exception.hpp"
|
||||
#include "LifecycleEvents.hpp"
|
||||
#include "ExtrusionEntity.hpp"
|
||||
#include "EdgeGrid.hpp"
|
||||
#include "Geometry/ConvexHull.hpp"
|
||||
@@ -2476,6 +2477,14 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
m_writer.set_is_bbl_machine(print->is_BBL_printer());
|
||||
print->set_started(psGCodeExport);
|
||||
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(print->model().id().id);
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
ctx.msg = path;
|
||||
fire_lifecycle_event(LifecycleEvent::GCodeExportStarted, ctx);
|
||||
}
|
||||
|
||||
// check if any custom gcode contains keywords used by the gcode processor to
|
||||
// produce time estimation and gcode toolpaths
|
||||
std::vector<std::pair<std::string, std::string>> validation_res = DoExport::validate_custom_gcode(*print);
|
||||
@@ -2511,12 +2520,22 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
m_processor.set_print(print);
|
||||
GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor);
|
||||
if (! file.is_open()) {
|
||||
BOOST_LOG_TRIVIAL(error) << std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n" << std::endl;
|
||||
std::string err_msg = std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n";
|
||||
BOOST_LOG_TRIVIAL(error) << err_msg << std::endl;
|
||||
if (!fs::exists(folder)) {
|
||||
//fs::create_directory(folder);
|
||||
BOOST_LOG_TRIVIAL(error) << "the parent path " + folder.string() +" is not there!!!" << std::endl;
|
||||
std::string add_err_msg = "the parent path " + folder.string() +" is not there!!!";
|
||||
BOOST_LOG_TRIVIAL(error) << add_err_msg << std::endl;
|
||||
err_msg += add_err_msg;
|
||||
}
|
||||
throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n");
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(print->model().id().id);
|
||||
ctx.code = LifecycleEvtCode::Error;
|
||||
ctx.msg = std::string(path) + "\n" + err_msg;
|
||||
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
|
||||
}
|
||||
throw Slic3r::RuntimeError(err_msg);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -2527,11 +2546,18 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
boost::nowide::remove(path_tmp.c_str());
|
||||
throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
|
||||
}
|
||||
} catch (std::exception & /* ex */) {
|
||||
} catch (std::exception &ex) {
|
||||
// Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown.
|
||||
// Close and remove the file.
|
||||
file.close();
|
||||
boost::nowide::remove(path_tmp.c_str());
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(print->model().id().id);
|
||||
ctx.code = LifecycleEvtCode::Error;
|
||||
ctx.msg = std::string(path) + "\n" + ex.what();
|
||||
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
file.close();
|
||||
@@ -2637,6 +2663,13 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
|
||||
std::error_code ret = rename_file(path_tmp, path);
|
||||
if (ret) {
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(print->model().id().id);
|
||||
ctx.code = LifecycleEvtCode::Error;
|
||||
ctx.msg = std::string(path) + "\nFailed to rename the output G-code file: " + ret.message();
|
||||
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
|
||||
}
|
||||
throw Slic3r::RuntimeError(
|
||||
std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' + "error code " + ret.message() + '\n' +
|
||||
"Is " + path_tmp + " locked?" + '\n');
|
||||
@@ -2647,7 +2680,15 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished" << log_memory_info();
|
||||
print->set_done(psGCodeExport);
|
||||
|
||||
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(print->model().id().id);
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
ctx.msg = path;
|
||||
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
|
||||
}
|
||||
|
||||
// Orca: label_object_enabled reflects whether objects are labeled in the g-code (EXCLUDE_OBJECT /
|
||||
// M486), which is driven by exclude_object for every printer
|
||||
if(result != nullptr)
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
#pragma once
|
||||
|
||||
// LifecycleEvents.hpp
|
||||
// --------------------
|
||||
// Application lifecycle events (project, slicing, plate editing, preset, printer connection, and
|
||||
// job activity) that other subsystems -- chiefly the plugin layer above libslic3r -- may want to
|
||||
// observe. Lives in libslic3r rather than the plugin layer because some events fire from inside
|
||||
// the slicing engine itself; see fire_lifecycle_event() below.
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
enum class LifecycleEvent {
|
||||
// Project (3mf)
|
||||
NewProject,
|
||||
ProjectOpened,
|
||||
ProjectBeforeSave,
|
||||
ProjectAfterSave,
|
||||
ProjectClosed,
|
||||
ProjectDirtyChanged,
|
||||
|
||||
// Slicing pipeline
|
||||
SliceStarted,
|
||||
SliceGeometryFinished,
|
||||
GCodeExportStarted,
|
||||
GCodeExportFinished,
|
||||
SlicingJobComplete,
|
||||
|
||||
// Plate/model editing
|
||||
ObjectAdded,
|
||||
ObjectDeleted,
|
||||
ObjectTransformed,
|
||||
ObjectChanged,
|
||||
ObjectRenamed,
|
||||
PlateCreated,
|
||||
PlateDeleted,
|
||||
PlateSelected,
|
||||
PlateRenamed,
|
||||
|
||||
// Preset
|
||||
PresetSelected,
|
||||
PresetSaved,
|
||||
|
||||
// Printer/device
|
||||
PrintStateChanged,
|
||||
DeviceOnlineChanged,
|
||||
DeviceDiscovered,
|
||||
DeviceSelected,
|
||||
DeviceConnected,
|
||||
DeviceDisconnected,
|
||||
UploadStarted,
|
||||
UploadFinished,
|
||||
|
||||
// Print/send jobs
|
||||
PrintJobStarted,
|
||||
PrintJobFinished,
|
||||
SendJobStarted,
|
||||
SendJobFinished,
|
||||
};
|
||||
|
||||
// Scoped so callers must qualify (LifecycleEvtCode::Error, not ERROR) -- ERROR/OK collide with
|
||||
// Windows macros (wingdi.h) as unqualified names.
|
||||
enum class LifecycleEvtCode { Ok, Error, Warn };
|
||||
|
||||
struct LifecycleEventContext
|
||||
{
|
||||
// The primary subject identifier for the event. This is event-specific (for example, a
|
||||
// project/output path, preset name, device id, or object name), must not contain status or
|
||||
// prose, and may be empty when the event has no single subject.
|
||||
std::string name;
|
||||
|
||||
// Outcome of the operation represented by the event. For state-change and start events,
|
||||
// Ok means that the event occurred; it does not imply that a future operation succeeded.
|
||||
LifecycleEvtCode code = LifecycleEvtCode::Ok;
|
||||
|
||||
// Optional human-readable detail or diagnostic text. It is not a stable parsing contract;
|
||||
// machine-readable data should be represented by a dedicated field or event instead.
|
||||
std::string msg;
|
||||
|
||||
// Stable subject/object identifier, when the source model provides one.
|
||||
std::string id;
|
||||
|
||||
// Previous value for rename and other before/after events.
|
||||
std::string previous_name;
|
||||
|
||||
// Device identifier for printer and job events.
|
||||
std::string device_id;
|
||||
|
||||
// Job identifier when the originating queue/task provides one.
|
||||
std::string job_id;
|
||||
|
||||
// Source subsystem or operation detail, suitable for filtering but not guaranteed to be
|
||||
// exhaustive across versions.
|
||||
std::string source;
|
||||
|
||||
// Plate, object, or volume index when the source uses an index rather than a stable id.
|
||||
int index = -1;
|
||||
|
||||
// Aggregate project dirty state for ProjectDirtyChanged.
|
||||
bool dirty = false;
|
||||
};
|
||||
|
||||
inline std::string lifecycle_event_to_string(LifecycleEvent event)
|
||||
{
|
||||
switch (event) {
|
||||
case LifecycleEvent::NewProject: return "NewProject";
|
||||
case LifecycleEvent::ProjectOpened: return "ProjectOpened";
|
||||
case LifecycleEvent::ProjectBeforeSave: return "ProjectBeforeSave";
|
||||
case LifecycleEvent::ProjectAfterSave: return "ProjectAfterSave";
|
||||
case LifecycleEvent::ProjectClosed: return "ProjectClosed";
|
||||
case LifecycleEvent::ProjectDirtyChanged: return "ProjectDirtyChanged";
|
||||
|
||||
case LifecycleEvent::SliceStarted: return "SliceStarted";
|
||||
case LifecycleEvent::SliceGeometryFinished: return "SliceGeometryFinished";
|
||||
case LifecycleEvent::GCodeExportStarted: return "GCodeExportStarted";
|
||||
case LifecycleEvent::GCodeExportFinished: return "GCodeExportFinished";
|
||||
case LifecycleEvent::SlicingJobComplete: return "SlicingJobComplete";
|
||||
|
||||
case LifecycleEvent::ObjectAdded: return "ObjectAdded";
|
||||
case LifecycleEvent::ObjectDeleted: return "ObjectDeleted";
|
||||
case LifecycleEvent::ObjectTransformed: return "ObjectTransformed";
|
||||
case LifecycleEvent::ObjectChanged: return "ObjectChanged";
|
||||
case LifecycleEvent::ObjectRenamed: return "ObjectRenamed";
|
||||
case LifecycleEvent::PlateCreated: return "PlateCreated";
|
||||
case LifecycleEvent::PlateDeleted: return "PlateDeleted";
|
||||
case LifecycleEvent::PlateSelected: return "PlateSelected";
|
||||
case LifecycleEvent::PlateRenamed: return "PlateRenamed";
|
||||
|
||||
case LifecycleEvent::PresetSelected: return "PresetSelected";
|
||||
case LifecycleEvent::PresetSaved: return "PresetSaved";
|
||||
case LifecycleEvent::PrintStateChanged: return "PrintStateChanged";
|
||||
|
||||
case LifecycleEvent::DeviceOnlineChanged: return "DeviceOnlineChanged";
|
||||
case LifecycleEvent::DeviceDiscovered: return "DeviceDiscovered";
|
||||
case LifecycleEvent::DeviceSelected: return "DeviceSelected";
|
||||
case LifecycleEvent::DeviceConnected: return "DeviceConnected";
|
||||
case LifecycleEvent::DeviceDisconnected: return "DeviceDisconnected";
|
||||
|
||||
case LifecycleEvent::UploadStarted: return "UploadStarted";
|
||||
case LifecycleEvent::UploadFinished: return "UploadFinished";
|
||||
case LifecycleEvent::PrintJobStarted: return "PrintJobStarted";
|
||||
case LifecycleEvent::PrintJobFinished: return "PrintJobFinished";
|
||||
case LifecycleEvent::SendJobStarted: return "SendJobStarted";
|
||||
case LifecycleEvent::SendJobFinished: return "SendJobFinished";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string lifecycle_evt_code_to_string(LifecycleEvtCode code)
|
||||
{
|
||||
switch (code) {
|
||||
case LifecycleEvtCode::Ok: return "Ok";
|
||||
case LifecycleEvtCode::Error: return "Error";
|
||||
case LifecycleEvtCode::Warn: return "Warn";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// Global cross-layer seam (mirrors ConfigBase::set_resolve_capability_fn): any libslic3r code can
|
||||
// fire a lifecycle event without depending on the plugin layer above it, which installs the
|
||||
// dispatcher here at startup. Not tied to Print/GCode specifically, since nothing here should
|
||||
// require callers to hold a Print& just to report an event.
|
||||
using LifecycleHookFn = std::function<void(LifecycleEvent, const LifecycleEventContext&)>;
|
||||
|
||||
inline LifecycleHookFn& lifecycle_hook_fn()
|
||||
{
|
||||
static LifecycleHookFn fn;
|
||||
return fn;
|
||||
}
|
||||
|
||||
inline void set_lifecycle_hook_fn(LifecycleHookFn fn) { lifecycle_hook_fn() = std::move(fn); }
|
||||
|
||||
inline void fire_lifecycle_event(LifecycleEvent event, const LifecycleEventContext& ctx)
|
||||
{
|
||||
if (const LifecycleHookFn& fn = lifecycle_hook_fn(); fn)
|
||||
fn(event, ctx);
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,7 @@
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "LifecycleEvents.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Time.hpp"
|
||||
#include "PlaceholderParser.hpp"
|
||||
@@ -2972,6 +2973,7 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
|
||||
// 1) Find the preset with a new_name or create a new one,
|
||||
// initialize it with the edited config.
|
||||
auto it = this->find_preset_internal(new_name);
|
||||
const bool preset_existed = (it != m_presets.end() && it->name == new_name);
|
||||
if (it != m_presets.end() && it->name == new_name) {
|
||||
// Preset with the same name found.
|
||||
Preset &preset = *it;
|
||||
@@ -3079,6 +3081,14 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
|
||||
this->get_selected_preset().save(&(parent_preset->config));
|
||||
else
|
||||
this->get_selected_preset().save(nullptr);
|
||||
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = new_name;
|
||||
ctx.msg = preset_existed ? "overwrite" : "new";
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
fire_lifecycle_event(LifecycleEvent::PresetSaved, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// A detached standalone preset for the Full Publish receiver: create a user preset holding
|
||||
|
||||
+39
-1
@@ -14,6 +14,7 @@
|
||||
#include "Flow.hpp"
|
||||
#include "Geometry/ConvexHull.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "LifecycleEvents.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
#include "Thread.hpp"
|
||||
#include "Time.hpp"
|
||||
@@ -2694,6 +2695,13 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
if (m_objects.empty())
|
||||
return;
|
||||
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(m_model.id().id);
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
fire_lifecycle_event(LifecycleEvent::SliceStarted, ctx);
|
||||
}
|
||||
|
||||
for (PrintObject *obj : m_objects)
|
||||
obj->clear_shared_object();
|
||||
|
||||
@@ -3312,6 +3320,13 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info();
|
||||
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(m_model.id().id);
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
fire_lifecycle_event(LifecycleEvent::SliceGeometryFinished, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
// G-code export process, running at a background thread.
|
||||
@@ -4911,6 +4926,14 @@ void Print::set_gcode_file_invalidated()
|
||||
//BBS: add gcode file preload logic
|
||||
void Print::export_gcode_from_previous_file(const std::string& file, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||
{
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(m_model.id().id);
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
ctx.msg = file;
|
||||
fire_lifecycle_event(LifecycleEvent::GCodeExportStarted, ctx);
|
||||
}
|
||||
|
||||
try {
|
||||
GCodeProcessor processor;
|
||||
GCodeProcessor::s_IsBBLPrinter = is_BBL_printer();
|
||||
@@ -4930,13 +4953,28 @@ void Print::export_gcode_from_previous_file(const std::string& file, GCodeProces
|
||||
*result = std::move(processor.extract_result());
|
||||
result->filament_change_sequence = filament_seq_loaded;
|
||||
result->nozzle_change_sequence = nozzle_seq_loaded;
|
||||
} catch (std::exception & /* ex */) {
|
||||
} catch (std::exception &ex) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": found errors when process gcode file %1%") %file.c_str();
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(m_model.id().id);
|
||||
ctx.code = LifecycleEvtCode::Error;
|
||||
ctx.msg = file + "\n" + ex.what();
|
||||
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
|
||||
}
|
||||
throw Slic3r::RuntimeError(
|
||||
std::string("Failed to process the G-code file ") + file + " from previous 3mf\n");
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": process the G-code file %1% successfully")%file.c_str();
|
||||
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = std::to_string(m_model.id().id);
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
ctx.msg = file;
|
||||
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<float, float> Print::object_skirt_offset(double margin_height) const
|
||||
|
||||
@@ -98,15 +98,6 @@ public:
|
||||
//
|
||||
bool is_dim_previous_layers() const;
|
||||
void set_dim_previous_layers(bool value);
|
||||
//
|
||||
// The reduced set holds only the bottom and top layers of the visible range, for a caller that
|
||||
// draws the print itself some other way while the user drags. While enabled it is built
|
||||
// alongside the full set, so set_reduced_detail() rebuilds nothing. Ignored on the OpenGL ES path.
|
||||
//
|
||||
void set_reduced_detail_enabled(bool value);
|
||||
bool is_reduced_detail_enabled() const;
|
||||
void set_reduced_detail(bool value);
|
||||
bool is_reduced_detail() const;
|
||||
float get_dim_previous_layers_brightness() const;
|
||||
void set_dim_previous_layers_brightness(float value);
|
||||
//
|
||||
|
||||
@@ -15,12 +15,7 @@ namespace libvgcode {
|
||||
//| 2--0-------5--7 |
|
||||
//| \ | | / |
|
||||
//| 3-------4 |
|
||||
// The eight corners the vertex shader knows how to place. Each is sent once and
|
||||
// referenced by INDEX_DATA below, so the post-transform cache can reuse it across
|
||||
// the triangles that share it: the shader runs 8 times per segment instead of 24.
|
||||
static constexpr const std::array<uint8_t, 8> VERTEX_DATA = { 0, 1, 2, 3, 4, 5, 6, 7 };
|
||||
|
||||
static constexpr const std::array<uint8_t, 24> INDEX_DATA = {
|
||||
static constexpr const std::array<uint8_t, 24> VERTEX_DATA = {
|
||||
0, 1, 2, // front spike
|
||||
0, 2, 3, // front spike
|
||||
0, 3, 4, // right/bottom body
|
||||
@@ -36,7 +31,7 @@ void SegmentTemplate::init()
|
||||
if (m_vao_id != 0)
|
||||
return;
|
||||
|
||||
m_size_in_bytes_gpu += (VERTEX_DATA.size() + INDEX_DATA.size()) * sizeof(uint8_t);
|
||||
m_size_in_bytes_gpu += VERTEX_DATA.size() * sizeof(uint8_t);
|
||||
|
||||
int curr_vertex_array;
|
||||
glsafe(glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &curr_vertex_array));
|
||||
@@ -56,22 +51,12 @@ void SegmentTemplate::init()
|
||||
glsafe(glVertexAttribIPointer(0, 1, GL_UNSIGNED_BYTE, 0, (const void*)0));
|
||||
#endif // ENABLE_OPENGL_ES
|
||||
|
||||
// The element buffer binding is part of the vao state, so it is left bound here
|
||||
// and restored together with the vao.
|
||||
glsafe(glGenBuffers(1, &m_ibo_id));
|
||||
glsafe(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo_id));
|
||||
glsafe(glBufferData(GL_ELEMENT_ARRAY_BUFFER, INDEX_DATA.size() * sizeof(uint8_t), INDEX_DATA.data(), GL_STATIC_DRAW));
|
||||
|
||||
glsafe(glBindBuffer(GL_ARRAY_BUFFER, curr_array_buffer));
|
||||
glsafe(glBindVertexArray(curr_vertex_array));
|
||||
}
|
||||
|
||||
void SegmentTemplate::shutdown()
|
||||
{
|
||||
if (m_ibo_id != 0) {
|
||||
glsafe(glDeleteBuffers(1, &m_ibo_id));
|
||||
m_ibo_id = 0;
|
||||
}
|
||||
if (m_vbo_id != 0) {
|
||||
glsafe(glDeleteBuffers(1, &m_vbo_id));
|
||||
m_vbo_id = 0;
|
||||
@@ -86,15 +71,14 @@ void SegmentTemplate::shutdown()
|
||||
|
||||
void SegmentTemplate::render(size_t count)
|
||||
{
|
||||
if (m_vao_id == 0 || m_vbo_id == 0 || m_ibo_id == 0 || count == 0)
|
||||
if (m_vao_id == 0 || m_vbo_id == 0 || count == 0)
|
||||
return;
|
||||
|
||||
int curr_vertex_array;
|
||||
glsafe(glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &curr_vertex_array));
|
||||
|
||||
glsafe(glBindVertexArray(m_vao_id));
|
||||
glsafe(glDrawElementsInstanced(GL_TRIANGLES, static_cast<GLsizei>(INDEX_DATA.size()), GL_UNSIGNED_BYTE,
|
||||
nullptr, static_cast<GLsizei>(count)));
|
||||
glsafe(glDrawArraysInstanced(GL_TRIANGLES, 0, static_cast<GLsizei>(VERTEX_DATA.size()), static_cast<GLsizei>(count)));
|
||||
glsafe(glBindVertexArray(curr_vertex_array));
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ private:
|
||||
//
|
||||
unsigned int m_vao_id{ 0 };
|
||||
unsigned int m_vbo_id{ 0 };
|
||||
unsigned int m_ibo_id{ 0 };
|
||||
//
|
||||
// Size of the data sent to gpu, in bytes.
|
||||
//
|
||||
|
||||
@@ -25,10 +25,6 @@ struct Settings
|
||||
// ORCA: how bright those darkened layers are rendered, 1.0 = unchanged, 0.0 = black
|
||||
float dim_previous_layers_brightness{ 0.4f };
|
||||
bool spiral_vase_mode{ false };
|
||||
// whether the reduced set (the visible range's end layers) is built, and whether it is drawn.
|
||||
// Ignored on the OpenGL ES path.
|
||||
bool reduced_detail_enabled{ false };
|
||||
bool reduced_detail{ false };
|
||||
//
|
||||
// Required update flags
|
||||
//
|
||||
|
||||
@@ -77,26 +77,6 @@ bool Viewer::is_dim_previous_layers() const
|
||||
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();
|
||||
}
|
||||
|
||||
void Viewer::set_reduced_detail_enabled(bool value)
|
||||
{
|
||||
m_impl->set_reduced_detail_enabled(value);
|
||||
}
|
||||
|
||||
bool Viewer::is_reduced_detail_enabled() const
|
||||
{
|
||||
return m_impl->is_reduced_detail_enabled();
|
||||
}
|
||||
|
||||
void Viewer::set_dim_previous_layers(bool value)
|
||||
{
|
||||
m_impl->set_dim_previous_layers(value);
|
||||
|
||||
@@ -875,12 +875,6 @@ void ViewerImpl::reset()
|
||||
m_travels_time = { 0.0f, 0.0f };
|
||||
m_vertices.clear();
|
||||
m_vertices_colors.clear();
|
||||
// swap rather than clear: these are sized by the print, and a reset means the memory
|
||||
// should go back, not sit reserved until the next load
|
||||
for (std::vector<float>& times : m_layer_start_times)
|
||||
std::vector<float>().swap(times);
|
||||
std::vector<uint32_t>().swap(m_layer_first_vertex);
|
||||
std::vector<float>().swap(m_colors_scratch);
|
||||
m_valid_lines_bitset.clear();
|
||||
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
||||
m_cog_marker.reset();
|
||||
@@ -891,15 +885,9 @@ void ViewerImpl::reset()
|
||||
#else
|
||||
m_enabled_segments_count = 0;
|
||||
m_enabled_options_count = 0;
|
||||
m_enabled_segments_reduced_count = 0;
|
||||
m_enabled_options_reduced_count = 0;
|
||||
|
||||
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_buffers(m_enabled_options_buf_id);
|
||||
delete_textures(m_enabled_segments_tex_id);
|
||||
@@ -1060,37 +1048,6 @@ void ViewerImpl::load(GCodeInputData&& gcode_data)
|
||||
v.layer_duration = m_layers.get_layer_time(m_settings.time_mode, static_cast<size_t>(v.layer_id));
|
||||
}
|
||||
|
||||
// Index of the first vertex of each layer, walked back to front so that a layer with no
|
||||
// vertex of its own inherits the next layer's index and the array stays non-decreasing.
|
||||
if (!m_layers.empty()) {
|
||||
const uint32_t vertices_count = static_cast<uint32_t>(m_vertices.size());
|
||||
m_layer_first_vertex.assign(m_layers.count(), vertices_count);
|
||||
for (uint32_t i = vertices_count; i > 0; --i) {
|
||||
const uint32_t layer_id = m_vertices[i - 1].layer_id;
|
||||
if (layer_id < m_layer_first_vertex.size())
|
||||
m_layer_first_vertex[layer_id] = i - 1;
|
||||
}
|
||||
for (size_t i = m_layer_first_vertex.size() - 1; i > 0; --i)
|
||||
m_layer_first_vertex[i - 1] = std::min(m_layer_first_vertex[i - 1], m_layer_first_vertex[i]);
|
||||
|
||||
// the running time at each layer's first vertex, summed in vertex order so that
|
||||
// get_estimated_time_at() matches a full accumulation exactly
|
||||
std::array<float, TIME_MODES_COUNT> running{};
|
||||
for (std::vector<float>& times : m_layer_start_times)
|
||||
times.assign(m_layer_first_vertex.size(), 0.0f);
|
||||
size_t layer = 0;
|
||||
for (size_t i = 0; i <= m_vertices.size(); ++i) {
|
||||
for (; layer < m_layer_first_vertex.size() && m_layer_first_vertex[layer] == i; ++layer) {
|
||||
for (size_t j = 0; j < TIME_MODES_COUNT; ++j)
|
||||
m_layer_start_times[j][layer] = running[j];
|
||||
}
|
||||
if (i < m_vertices.size()) {
|
||||
for (size_t j = 0; j < TIME_MODES_COUNT; ++j)
|
||||
running[j] += m_vertices[i].times[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_layers.empty())
|
||||
m_layers.set_view_range(0, static_cast<uint32_t>(m_layers.count()) - 1);
|
||||
|
||||
@@ -1159,17 +1116,6 @@ void ViewerImpl::load(GCodeInputData&& gcode_data)
|
||||
glsafe(glGenTextures(1, &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(glBindTexture(GL_TEXTURE_BUFFER, old_bound_texture));
|
||||
#endif // ENABLE_OPENGL_ES
|
||||
@@ -1188,14 +1134,6 @@ void ViewerImpl::update_enabled_entities()
|
||||
|
||||
std::vector<uint32_t> enabled_segments;
|
||||
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. It keeps the
|
||||
// bottom and top layers of the visible range: the surfaces the range cuts open
|
||||
const bool build_reduced = m_settings.reduced_detail_enabled;
|
||||
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();
|
||||
|
||||
// when top layer only visualization is enabled, we need to render
|
||||
@@ -1243,11 +1181,6 @@ void ViewerImpl::update_enabled_entities()
|
||||
enabled_options.push_back(static_cast<uint32_t>(i));
|
||||
else
|
||||
enabled_segments.push_back(static_cast<uint32_t>(i));
|
||||
|
||||
#ifndef ENABLE_OPENGL_ES
|
||||
if (build_reduced && (v.layer_id == layers_range[0] || v.layer_id == layers_range[1]))
|
||||
(v.is_option() ? enabled_options_reduced : enabled_segments_reduced).push_back(static_cast<uint32_t>(i));
|
||||
#endif // ENABLE_OPENGL_ES
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OPENGL_ES
|
||||
@@ -1276,21 +1209,6 @@ void ViewerImpl::update_enabled_entities()
|
||||
else
|
||||
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();
|
||||
|
||||
if (build_reduced) {
|
||||
assert(m_enabled_segments_reduced_buf_id > 0);
|
||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_segments_reduced_buf_id));
|
||||
glsafe(glBufferData(GL_TEXTURE_BUFFER, enabled_segments_reduced.size() * sizeof(uint32_t),
|
||||
enabled_segments_reduced.empty() ? nullptr : enabled_segments_reduced.data(), GL_STATIC_DRAW));
|
||||
|
||||
assert(m_enabled_options_reduced_buf_id > 0);
|
||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, m_enabled_options_reduced_buf_id));
|
||||
glsafe(glBufferData(GL_TEXTURE_BUFFER, enabled_options_reduced.size() * sizeof(uint32_t),
|
||||
enabled_options_reduced.empty() ? nullptr : enabled_options_reduced.data(), GL_STATIC_DRAW));
|
||||
}
|
||||
|
||||
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
|
||||
#endif // ENABLE_OPENGL_ES
|
||||
|
||||
@@ -1343,10 +1261,7 @@ void ViewerImpl::update_colors_texture()
|
||||
|
||||
// Based on current settings and slider position, we might want to render some
|
||||
// vertices as dark grey (or darkened, see above). Use either that or the normal color (from the cache).
|
||||
// Reused across calls: this runs on every slider tick, and the allocation alone is
|
||||
// 4 bytes per vertex of the whole print each time.
|
||||
std::vector<float>& colors = m_colors_scratch;
|
||||
colors.resize(m_vertices_colors.size());
|
||||
std::vector<float> colors(m_vertices_colors.size());
|
||||
assert(colors.size() == m_vertices.size() && m_vertices_colors.size() == m_vertices.size());
|
||||
for (size_t i=0; i<m_vertices.size(); ++i) {
|
||||
const PathVertex& v = m_vertices[i];
|
||||
@@ -1469,14 +1384,6 @@ void ViewerImpl::toggle_top_layer_only_view_range()
|
||||
update_colors_texture();
|
||||
}
|
||||
|
||||
void ViewerImpl::set_reduced_detail_enabled(bool value)
|
||||
{
|
||||
if (m_settings.reduced_detail_enabled == value)
|
||||
return;
|
||||
m_settings.reduced_detail_enabled = value;
|
||||
m_settings.update_enabled_entities = true;
|
||||
}
|
||||
|
||||
// ORCA: enable/disable darkening of the layers the layer slider is not scrubbed to
|
||||
void ViewerImpl::set_dim_previous_layers(bool value)
|
||||
{
|
||||
@@ -1609,19 +1516,8 @@ void ViewerImpl::set_view_visible_range(Interval::value_type min, Interval::valu
|
||||
|
||||
float ViewerImpl::get_estimated_time_at(size_t id) const
|
||||
{
|
||||
const size_t mode = static_cast<size_t>(m_settings.time_mode);
|
||||
if (mode >= TIME_MODES_COUNT || id >= m_vertices.size())
|
||||
return 0.0f;
|
||||
size_t first = 0;
|
||||
float time = 0.0f;
|
||||
const size_t layer = static_cast<size_t>(m_vertices[id].layer_id);
|
||||
if (layer < m_layer_first_vertex.size() && m_layer_first_vertex[layer] <= id) {
|
||||
first = m_layer_first_vertex[layer];
|
||||
time = m_layer_start_times[mode][layer];
|
||||
}
|
||||
for (size_t i = first; i <= id; ++i)
|
||||
time += m_vertices[i].times[mode];
|
||||
return time;
|
||||
return std::accumulate(m_vertices.begin(), m_vertices.begin() + id + 1, 0.0f,
|
||||
[this](float a, const PathVertex& v) { return a + v.times[static_cast<size_t>(m_settings.time_mode)]; });
|
||||
}
|
||||
|
||||
Color ViewerImpl::get_vertex_color(const PathVertex& v) const
|
||||
@@ -1826,10 +1722,6 @@ size_t ViewerImpl::get_used_cpu_memory() const
|
||||
ret += sizeof(m_extrusion_roles_colors);
|
||||
ret += sizeof(m_options_colors);
|
||||
ret += STDVEC_MEMSIZE(m_vertices, PathVertex);
|
||||
for (const std::vector<float>& times : m_layer_start_times)
|
||||
ret += STDVEC_MEMSIZE(times, float);
|
||||
ret += STDVEC_MEMSIZE(m_layer_first_vertex, uint32_t);
|
||||
ret += STDVEC_MEMSIZE(m_colors_scratch, float);
|
||||
ret += m_valid_lines_bitset.size_in_bytes_cpu();
|
||||
ret += m_height_range.size_in_bytes_cpu();
|
||||
ret += m_width_range.size_in_bytes_cpu();
|
||||
@@ -1895,11 +1787,7 @@ void ViewerImpl::update_view_full_range()
|
||||
const bool travels_visible = m_settings.options_visibility[size_t(EOptionType::Travels)];
|
||||
const bool wipes_visible = m_settings.options_visibility[size_t(EOptionType::Wipes)];
|
||||
|
||||
// every vertex before m_layer_first_vertex[layers_range[0]] has a smaller layer_id, so the loop
|
||||
// below would skip all of them anyway
|
||||
auto first_it = m_vertices.begin();
|
||||
if (layers_range[0] < m_layer_first_vertex.size())
|
||||
first_it += m_layer_first_vertex[layers_range[0]];
|
||||
while (first_it != m_vertices.end() &&
|
||||
(first_it->layer_id < layers_range[0] || !is_visible(*first_it, m_settings))) {
|
||||
++first_it;
|
||||
@@ -2086,8 +1974,7 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
|
||||
#ifdef ENABLE_OPENGL_ES
|
||||
if (m_texture_data.get_enabled_segments_count() == 0)
|
||||
#else
|
||||
const ActiveSet segments = active_segments();
|
||||
if (segments.count == 0)
|
||||
if (m_enabled_segments_count == 0)
|
||||
#endif // ENABLE_OPENGL_ES
|
||||
return;
|
||||
|
||||
@@ -2146,10 +2033,10 @@ void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projec
|
||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
||||
glsafe(glActiveTexture(GL_TEXTURE3));
|
||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, segments.tex_id));
|
||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, segments.buf_id));
|
||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_segments_tex_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
|
||||
|
||||
if (curr_cull_face)
|
||||
@@ -2175,8 +2062,7 @@ void ViewerImpl::render_options(const Mat4x4& view_matrix, const Mat4x4& project
|
||||
#ifdef ENABLE_OPENGL_ES
|
||||
if (m_texture_data.get_enabled_options_count() == 0)
|
||||
#else
|
||||
const ActiveSet options = active_options();
|
||||
if (options.count == 0)
|
||||
if (m_enabled_options_count == 0)
|
||||
#endif // ENABLE_OPENGL_ES
|
||||
return;
|
||||
|
||||
@@ -2234,10 +2120,10 @@ void ViewerImpl::render_options(const Mat4x4& view_matrix, const Mat4x4& project
|
||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_colors_tex_id));
|
||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, m_colors_buf_id));
|
||||
glsafe(glActiveTexture(GL_TEXTURE3));
|
||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, options.tex_id));
|
||||
glsafe(glTexBuffer(GL_TEXTURE_BUFFER, GL_R32UI, options.buf_id));
|
||||
glsafe(glBindTexture(GL_TEXTURE_BUFFER, m_enabled_options_tex_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
|
||||
|
||||
if (!curr_cull_face)
|
||||
|
||||
@@ -91,19 +91,6 @@ public:
|
||||
// 0.0 = black
|
||||
bool is_dim_previous_layers() const { return m_settings.dim_previous_layers; }
|
||||
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; }
|
||||
bool is_reduced_detail_enabled() const { return m_settings.reduced_detail_enabled; }
|
||||
void set_reduced_detail_enabled(bool value);
|
||||
float get_dim_previous_layers_brightness() const { return m_settings.dim_previous_layers_brightness; }
|
||||
void set_dim_previous_layers_brightness(float value);
|
||||
|
||||
@@ -247,20 +234,6 @@ private:
|
||||
//
|
||||
std::array<float, TIME_MODES_COUNT> m_total_time{ 0.0f, 0.0f };
|
||||
//
|
||||
// Running sum of the vertex estimated times at each layer's first vertex, for each time mode,
|
||||
// so that get_estimated_time_at() only accumulates the vertices of one layer.
|
||||
//
|
||||
std::array<std::vector<float>, TIME_MODES_COUNT> m_layer_start_times;
|
||||
//
|
||||
// For each layer L, the index of the first vertex whose layer_id is >= L (m_vertices.size()
|
||||
// if there is none). Derived from the vertices, so it stays exact whatever order they arrive in.
|
||||
//
|
||||
std::vector<uint32_t> m_layer_first_vertex;
|
||||
//
|
||||
// Scratch buffer for update_colors_texture(), kept alive across slider steps
|
||||
//
|
||||
std::vector<float> m_colors_scratch;
|
||||
//
|
||||
// Detected travel moves times
|
||||
//
|
||||
std::array<float, TIME_MODES_COUNT> m_travels_time{ 0.0f, 0.0f };
|
||||
@@ -487,15 +460,6 @@ private:
|
||||
unsigned int m_enabled_options_tex_id{ 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
|
||||
//
|
||||
size_t m_positions_tex_size{ 0 };
|
||||
@@ -503,25 +467,6 @@ private:
|
||||
size_t m_colors_tex_size{ 0 };
|
||||
size_t m_enabled_segments_tex_size{ 0 };
|
||||
size_t m_enabled_options_tex_size{ 0 };
|
||||
|
||||
// The set the next draw reads from: the reduced one while dragging, if one is built.
|
||||
bool use_reduced_set() const { return m_settings.reduced_detail && m_settings.reduced_detail_enabled; }
|
||||
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
|
||||
|
||||
void update_view_full_range();
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/plugin/PluginManager.hpp"
|
||||
#include "slic3r/Utils/NetworkAgentFactory.hpp"
|
||||
|
||||
#include "libslic3r/Time.hpp"
|
||||
@@ -355,7 +356,10 @@ namespace Slic3r
|
||||
obj->bind_state = "free";
|
||||
|
||||
obj->last_alive = Slic3r::Utils::get_current_time_utc();
|
||||
obj->m_is_online = true;
|
||||
// Route through set_online_state() (rather than writing m_is_online directly) so the
|
||||
// DeviceOnlineChanged lifecycle event fires consistently; same effective value/behavior
|
||||
// here since the object was already online in the common case.
|
||||
obj->set_online_state(true);
|
||||
obj->set_dev_name(dev_name);
|
||||
/* if (!obj->dev_ip.empty()) {
|
||||
Slic3r::GUI::wxGetApp().app_config->set_str("ip_address", obj->dev_id, obj->dev_ip);
|
||||
@@ -373,6 +377,10 @@ namespace Slic3r
|
||||
obj->bind_sec_link = sec_link;
|
||||
obj->dev_connection_name = connection_name;
|
||||
obj->bind_ssdp_version = ssdp_version;
|
||||
// Discovery establishes the initial reachability state. Do not report it as an
|
||||
// online transition; DeviceDiscovered below is the lifecycle event for a new
|
||||
// device. Subsequent updates route through set_online_state(), so a known device
|
||||
// still emits DeviceOnlineChanged when its reachability actually changes.
|
||||
obj->m_is_online = true;
|
||||
|
||||
//load access code
|
||||
@@ -389,6 +397,15 @@ namespace Slic3r
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " New Machine, dev_id= " << dev_id
|
||||
<< ", ip = " << dev_ip <<", printer_name = " << dev_name
|
||||
<< ", con_type= " << connect_type <<", signal= " << printer_signal << ", bind_state= " << bind_state;
|
||||
|
||||
// First discovery of a genuinely new device (not a periodic SSDP/heartbeat update to
|
||||
// an already-known one, which is handled in the branch above).
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = dev_id;
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
fire_lifecycle_event(LifecycleEvent::DeviceDiscovered, ctx);
|
||||
}
|
||||
}
|
||||
update_local_machine(*obj);
|
||||
}
|
||||
@@ -984,8 +1001,14 @@ namespace Slic3r
|
||||
}
|
||||
|
||||
void DeviceManager::OnSelectedMachineChanged(const std::string& /*pre_dev_id*/,
|
||||
const std::string& /*new_dev_id*/)
|
||||
const std::string& new_dev_id)
|
||||
{
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = new_dev_id; // empty string is a valid deselection
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
fire_lifecycle_event(LifecycleEvent::DeviceSelected, ctx);
|
||||
}
|
||||
if (MachineObject* obj_ = get_selected_machine()) {
|
||||
GUI::wxGetApp().sidebar().update_sync_status(obj_);
|
||||
if(m_agent->get_filament_sync_mode() == FilamentSyncMode::subscription)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "libslic3r/Time.hpp"
|
||||
#include "libslic3r/Thread.hpp"
|
||||
#include "slic3r/Utils/NetworkAgent.hpp"
|
||||
#include "slic3r/plugin/PluginManager.hpp"
|
||||
#include "slic3r/Utils/NetworkAgentFactory.hpp"
|
||||
#include "GuiColor.hpp"
|
||||
|
||||
@@ -2626,7 +2627,15 @@ void MachineObject::reset()
|
||||
|
||||
void MachineObject::set_print_state(std::string status)
|
||||
{
|
||||
const bool changed = (print_status != status);
|
||||
print_status = status;
|
||||
if (changed) {
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = dev_id;
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
ctx.msg = print_status;
|
||||
fire_lifecycle_event(LifecycleEvent::PrintStateChanged, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
int MachineObject::connect(bool use_openssl)
|
||||
@@ -2648,6 +2657,10 @@ int MachineObject::connect(bool use_openssl)
|
||||
int MachineObject::disconnect()
|
||||
{
|
||||
if (m_agent) {
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = dev_id;
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
fire_lifecycle_event(LifecycleEvent::DeviceDisconnected, ctx);
|
||||
return m_agent->disconnect_printer();
|
||||
}
|
||||
return -1;
|
||||
@@ -2678,8 +2691,16 @@ bool MachineObject::is_connecting()
|
||||
|
||||
void MachineObject::set_online_state(bool on_off)
|
||||
{
|
||||
const bool changed = (m_is_online != on_off);
|
||||
m_is_online = on_off;
|
||||
if (!on_off) m_active_state = NotActive;
|
||||
if (changed) {
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = dev_id;
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
ctx.msg = on_off ? "online" : "offline";
|
||||
fire_lifecycle_event(LifecycleEvent::DeviceOnlineChanged, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
bool MachineObject::is_info_ready(bool check_version) const
|
||||
@@ -4601,8 +4622,13 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
try {
|
||||
if (j.contains("event")) {
|
||||
if (j["event"].contains("event")) {
|
||||
if (j["event"]["event"].get<std::string>() == "client.disconnected")
|
||||
if (j["event"]["event"].get<std::string>() == "client.disconnected") {
|
||||
set_online_state(false);
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = dev_id;
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
fire_lifecycle_event(LifecycleEvent::DeviceDisconnected, ctx);
|
||||
}
|
||||
else if (j["event"]["event"].get<std::string>() == "client.connected")
|
||||
set_online_state(true);
|
||||
}
|
||||
|
||||
@@ -1177,8 +1177,6 @@ void GCodeViewer::load_as_gcode(const GCodeProcessorResult& gcode_result, const
|
||||
if (current_top_layer_only != required_top_layer_only)
|
||||
m_viewer.toggle_top_layer_only_view_range();
|
||||
|
||||
read_solid_model_preference();
|
||||
|
||||
// ORCA: darken the layers the preview layer slider is not scrubbed to
|
||||
m_viewer.set_dim_previous_layers(get_app_config()->get_bool("preview_dim_previous_layers"));
|
||||
m_viewer.set_dim_previous_layers_brightness(0.01f * std::stoi(get_app_config()->get("preview_dim_previous_layers_brightness")));
|
||||
@@ -1591,7 +1589,6 @@ void GCodeViewer::reset_shell()
|
||||
{
|
||||
m_shells.volumes.clear();
|
||||
m_shells.print_id = -1;
|
||||
m_shells.with_wipe_tower = false;
|
||||
m_shell_bounding_box = BoundingBoxf3();
|
||||
}
|
||||
|
||||
@@ -1628,14 +1625,9 @@ void GCodeViewer::reset()
|
||||
void GCodeViewer::render_scene(int canvas_width, int canvas_height)
|
||||
{
|
||||
glsafe(::glEnable(GL_DEPTH_TEST));
|
||||
// while dragging with the solid model on, 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())
|
||||
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().empty())
|
||||
return;
|
||||
|
||||
render_toolpaths();
|
||||
@@ -1933,41 +1925,6 @@ void GCodeViewer::update_layers_slider_mode()
|
||||
// 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
|
||||
m_viewer.set_reduced_detail(m_solid_model_while_dragging && interacting && !m_shells.volumes.empty());
|
||||
}
|
||||
|
||||
void GCodeViewer::set_solid_model_while_dragging(bool value)
|
||||
{
|
||||
const bool was_enabled = m_solid_model_while_dragging;
|
||||
m_solid_model_while_dragging = value;
|
||||
m_viewer.set_reduced_detail_enabled(value);
|
||||
reload_shells_if_solid_model_changed(was_enabled);
|
||||
}
|
||||
|
||||
void GCodeViewer::read_solid_model_preference()
|
||||
{
|
||||
m_solid_model_while_dragging = get_app_config()->get_bool("preview_solid_model_while_dragging");
|
||||
m_viewer.set_reduced_detail_enabled(m_solid_model_while_dragging);
|
||||
}
|
||||
|
||||
void GCodeViewer::reload_shells_if_solid_model_changed(bool was_enabled)
|
||||
{
|
||||
if (was_enabled == m_solid_model_while_dragging || 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)
|
||||
{
|
||||
m_viewer.set_layers_view_range(static_cast<uint32_t>(layers_z_range[0]), static_cast<uint32_t>(layers_z_range[1]));
|
||||
@@ -2292,11 +2249,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
|
||||
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;
|
||||
// the shells can load before the first G-code does, so the preferences are read here as well
|
||||
read_solid_model_preference();
|
||||
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
|
||||
if (force_previewing)
|
||||
m_shells.previewing = force_previewing;
|
||||
@@ -2405,45 +2358,10 @@ void GCodeViewer::load_shells(const Print& print, bool initialized, bool force_p
|
||||
m_shells.print_id = print.id().id;
|
||||
m_shells.print_modify_count = print.get_modified_count();
|
||||
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%")
|
||||
% 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 = m_solid_model_while_dragging && 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()
|
||||
{
|
||||
const Camera& camera = wxGetApp().plater()->get_camera();
|
||||
@@ -2644,50 +2562,6 @@ void GCodeViewer::render_shells(int canvas_width, int canvas_height)
|
||||
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
|
||||
void GCodeViewer::render_all_plates_stats(const std::vector<const GCodeProcessorResult*>& gcode_result_list, bool show /*= true*/) const {
|
||||
if (!show)
|
||||
@@ -3552,12 +3426,6 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
|
||||
std::vector<std::pair<ColorRGBA, std::pair<double, double>>> ret;
|
||||
ret.reserve(custom_gcode_per_print_z.size());
|
||||
|
||||
// Loop invariant, but built lazily: this lambda runs once per extruder on every frame
|
||||
// and most prints reach neither colour change below, so fetching it up front would cost
|
||||
// more than the per-item fetch it replaces.
|
||||
std::vector<float> zs;
|
||||
bool zs_built = false;
|
||||
|
||||
for (const auto& item : custom_gcode_per_print_z) {
|
||||
if (extruder_id + 1 != static_cast<unsigned char>(item.extruder))
|
||||
continue;
|
||||
@@ -3565,10 +3433,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
|
||||
if (item.type != ColorChange)
|
||||
continue;
|
||||
|
||||
if (!zs_built) {
|
||||
zs = m_viewer.get_layers_zs();
|
||||
zs_built = true;
|
||||
}
|
||||
const std::vector<float> zs = m_viewer.get_layers_zs();
|
||||
auto lower_b = std::lower_bound(zs.begin(), zs.end(),
|
||||
static_cast<float>(item.print_z - epsilon()));
|
||||
if (lower_b == zs.end())
|
||||
@@ -4717,8 +4582,6 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
|
||||
|
||||
// ORCA: Get layer Zs as doubles
|
||||
std::vector<double> layer_zs = get_layers_zs();
|
||||
// loop invariant, same reason as the layer Zs above
|
||||
const std::vector<float> layer_times = m_viewer.get_layers_estimated_times();
|
||||
|
||||
for (Slic3r::CustomGCode::Item custom_gcode : custom_gcode_per_print_z) {
|
||||
ImGui::Dummy({window_padding, window_padding});
|
||||
@@ -4738,6 +4601,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
|
||||
imgui.text(buf);
|
||||
ImGui::SameLine(max_len * 1.5);
|
||||
|
||||
std::vector<float> layer_times = m_viewer.get_layers_estimated_times();
|
||||
float custom_gcode_time = 0;
|
||||
if (layer > 0)
|
||||
{
|
||||
@@ -4786,7 +4650,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
|
||||
std::string print_str = _u8L("Model printing time");
|
||||
std::string total_str = _u8L("Total time");
|
||||
float max_len = window_padding + 2 * ImGui::GetStyle().ItemSpacing.x;
|
||||
if (m_viewer.get_layers_count() == 0)
|
||||
if (m_viewer.get_layers_estimated_times().empty())
|
||||
max_len += ImGui::CalcTextSize(total_str.c_str()).x;
|
||||
else {
|
||||
if (m_viewer.get_view_type() == libvgcode::EViewType::FeatureType)
|
||||
|
||||
@@ -174,8 +174,6 @@ public:
|
||||
int print_id{-1};
|
||||
int print_modify_count{-1};
|
||||
bool previewing{false};
|
||||
// the prime tower was loaded with the objects, for the solid model
|
||||
bool with_wipe_tower{false};
|
||||
};
|
||||
//BBS
|
||||
ConflictResultOpt m_conflict_result;
|
||||
@@ -235,13 +233,6 @@ private:
|
||||
|
||||
bool m_legend_visible{ true };
|
||||
bool m_legend_enabled{ true };
|
||||
// while dragging, the sliced objects are drawn as solid shapes instead of toolpaths
|
||||
bool m_solid_model_while_dragging{ false };
|
||||
void read_solid_model_preference();
|
||||
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;
|
||||
PrintEstimatedStatistics m_print_statistics;
|
||||
@@ -292,7 +283,7 @@ public:
|
||||
// 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(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;
|
||||
std::vector<int> get_plater_extruder();
|
||||
@@ -354,11 +345,6 @@ public:
|
||||
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(); }
|
||||
|
||||
// while the user drags the camera or a slider, draw the solid model, if the preference asks for it
|
||||
void set_interacting(bool interacting);
|
||||
bool is_reduced_detail() const { return m_viewer.is_reduced_detail(); }
|
||||
void set_solid_model_while_dragging(bool value);
|
||||
|
||||
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; }
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp"
|
||||
#include "slic3r/Utils/UndoRedo.hpp"
|
||||
#include "slic3r/Utils/MacDarkMode.hpp"
|
||||
#include "slic3r/plugin/PluginManager.hpp"
|
||||
|
||||
#include <slic3r/GUI/GUI_Utils.hpp>
|
||||
|
||||
@@ -2054,11 +2055,6 @@ void GLCanvas3D::_render_frame(bool scene_dirty, bool only_init)
|
||||
const bool overlay_tick = m_fps_overlay_tick;
|
||||
m_fps_overlay_tick = false;
|
||||
|
||||
// Whether the preview draws the solid model 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
|
||||
// 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);
|
||||
@@ -3238,16 +3234,9 @@ void GLCanvas3D::bind_event_handlers()
|
||||
if (m_selection_edit.kind != SelectionEdit::None)
|
||||
finish_selection_edit();
|
||||
ImGui::SetWindowFocus(nullptr);
|
||||
// a drag cut short never sees its button release, which would leave the solid model drawn
|
||||
if (m_canvas_type == CanvasPreview && m_mouse.dragging && m_gcode_viewer.is_reduced_detail())
|
||||
mouse_up_cleanup();
|
||||
render();
|
||||
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_canvas->Bind(wxEVT_GESTURE_PAN, &GLCanvas3D::on_gesture, this);
|
||||
@@ -3320,17 +3309,6 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
||||
m_overlay_dirty |= imgui_requires_extra_frame;
|
||||
#endif // ENABLE_ENHANCED_IMGUI_SLIDER_FLOAT
|
||||
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)
|
||||
return;
|
||||
@@ -3861,10 +3839,6 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
||||
return;
|
||||
}
|
||||
|
||||
// only a wheel the panels did not take moves the camera
|
||||
if (m_canvas_type == CanvasPreview)
|
||||
note_preview_interaction();
|
||||
|
||||
#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,
|
||||
// if the event is not allowed to be passed further.
|
||||
@@ -3965,11 +3939,6 @@ void GLCanvas3D::on_fps_overlay_timer(wxTimerEvent& evt)
|
||||
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)
|
||||
{
|
||||
// Schedule idle event right now
|
||||
@@ -5058,8 +5027,16 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
|
||||
//BBS: nofity object list to update
|
||||
wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items();
|
||||
|
||||
if (object_moved)
|
||||
if (object_moved) {
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||
ctx.msg = "moved";
|
||||
if (done.size() == 1)
|
||||
ctx.name = m_model->objects[done.begin()->first]->name;
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
|
||||
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED));
|
||||
}
|
||||
|
||||
// BBS: support wipe-tower for multi-plates
|
||||
for (int plate_id = 0; plate_id < wipe_tower_origins.size(); plate_id++) {
|
||||
@@ -5180,8 +5157,16 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
|
||||
//BBS: nofity object list to update
|
||||
wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items();
|
||||
|
||||
if (!done.empty())
|
||||
if (!done.empty()) {
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||
ctx.msg = "rotated";
|
||||
if (done.size() == 1)
|
||||
ctx.name = m_model->objects[done.begin()->first]->name;
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
|
||||
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED));
|
||||
}
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
@@ -5272,8 +5257,16 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
|
||||
//BBS: notify object info update
|
||||
wxGetApp().plater()->show_object_info();
|
||||
|
||||
if (!done.empty())
|
||||
if (!done.empty()) {
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||
ctx.msg = "scaled";
|
||||
if (done.size() == 1)
|
||||
ctx.name = m_model->objects[done.begin()->first]->name;
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
|
||||
|
||||
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED));
|
||||
}
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
@@ -5586,9 +5579,6 @@ void GLCanvas3D::mouse_up_cleanup()
|
||||
m_mouse.ignore_left_up = false;
|
||||
m_mouse.ignore_right_up = false;
|
||||
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())
|
||||
m_canvas->ReleaseMouse();
|
||||
@@ -8773,26 +8763,6 @@ void GLCanvas3D::_render_wireframe_overlay()
|
||||
shader->stop_using();
|
||||
}
|
||||
|
||||
// The solid model is drawn while the camera, the navigator or either slider is dragged. A wheel
|
||||
// step has no duration, so it holds the solid model for a settle time instead, and the frame that
|
||||
// restores the 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()
|
||||
{
|
||||
IMSlider* layers_slider = m_gcode_viewer.get_layers_slider();
|
||||
IMSlider* moves_slider = m_gcode_viewer.get_moves_slider();
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
const bool settling = now < m_preview_interaction_until;
|
||||
const bool dragging = m_mouse.dragging || m_navigator_dragging || layers_slider->is_dragging() || moves_slider->is_dragging();
|
||||
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
|
||||
void GLCanvas3D::_render_gcode(int canvas_width, int canvas_height)
|
||||
{
|
||||
|
||||
@@ -649,10 +649,6 @@ private:
|
||||
ECursorType m_cursor_type;
|
||||
GLSelectionRectangle m_rectangle_selection;
|
||||
bool m_navigator_dragging{ false };
|
||||
// until when a wheel step keeps the preview's solid model 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
|
||||
mutable std::vector<int> m_hover_volume_idxs;
|
||||
@@ -1219,8 +1215,6 @@ public:
|
||||
void msw_rescale() { m_gcode_viewer.invalidate_legend(); }
|
||||
|
||||
void request_extra_frame() { m_extra_frame_requested = true; }
|
||||
// a wheel step is over before the next frame, so it holds the preview's solid model for a settle time
|
||||
void note_preview_interaction();
|
||||
|
||||
void schedule_extra_frame(int milliseconds);
|
||||
|
||||
@@ -1367,9 +1361,6 @@ private:
|
||||
//BBS: GUI refactor: add canvas size as parameters
|
||||
void _render_gcode(int canvas_width, int canvas_height);
|
||||
void _render_gcode_overlay(int canvas_width, int canvas_height);
|
||||
// decides whether the preview draws its solid model 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
|
||||
void _render_plane() const;
|
||||
void _render_selection();
|
||||
|
||||
@@ -2182,6 +2182,11 @@ void GUI_App::init_networking_callbacks()
|
||||
obj->command_get_access_code();
|
||||
if (m_agent)
|
||||
m_agent->install_device_cert(obj->get_dev_id(), obj->is_lan_mode_printer());
|
||||
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = obj->get_dev_id();
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
fire_lifecycle_event(LifecycleEvent::DeviceConnected, ctx);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -2220,6 +2225,11 @@ void GUI_App::init_networking_callbacks()
|
||||
obj->command_get_version();
|
||||
event.SetInt(0);
|
||||
event.SetString(obj->get_dev_id());
|
||||
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = obj->get_dev_id();
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
fire_lifecycle_event(LifecycleEvent::DeviceConnected, ctx);
|
||||
} else if (state == ConnectStatus::ConnectStatusFailed) {
|
||||
// Orca: only update status if same device id
|
||||
if (m_device_manager->selected_machine != dev_id) return;
|
||||
@@ -2235,10 +2245,22 @@ void GUI_App::init_networking_callbacks()
|
||||
wxGetApp().show_dialog(text);
|
||||
}
|
||||
event.SetInt(-1);
|
||||
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = dev_id;
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
fire_lifecycle_event(LifecycleEvent::DeviceDisconnected, ctx);
|
||||
}
|
||||
} else if (state == ConnectStatus::ConnectStatusLost) {
|
||||
m_device_manager->set_selected_machine("");
|
||||
event.SetInt(-1);
|
||||
BOOST_LOG_TRIVIAL(info) << "set_on_local_connect_fn: state = lost";
|
||||
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = dev_id;
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
fire_lifecycle_event(LifecycleEvent::DeviceDisconnected, ctx);
|
||||
} else {
|
||||
event.SetInt(-1);
|
||||
BOOST_LOG_TRIVIAL(info) << "set_on_local_connect_fn: state = " << state;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/LifecycleEvents.hpp"
|
||||
#include "GUI_ObjectList.hpp"
|
||||
#include "GUI_Factories.hpp"
|
||||
//#include "GUI_ObjectLayers.hpp"
|
||||
@@ -10,6 +11,7 @@
|
||||
#include "BitmapComboBox.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "slic3r/Utils/UndoRedo.hpp"
|
||||
#include "slic3r/plugin/PluginManager.hpp"
|
||||
|
||||
#include "OptionsGroup.hpp"
|
||||
#include "Tab.hpp"
|
||||
@@ -1159,17 +1161,39 @@ void ObjectList::update_name_in_model(const wxDataViewItem& item) const
|
||||
if (m_objects_model->GetItemType(item) & itObject) {
|
||||
std::string name = m_objects_model->GetName(item).ToUTF8().data();
|
||||
if (obj->name != name) {
|
||||
const std::string previous_name = obj->name;
|
||||
obj->name = name;
|
||||
// if object has just one volume, rename this volume too
|
||||
if (obj->volumes.size() == 1)
|
||||
obj->volumes[0]->name = obj->name;
|
||||
Slic3r::save_object_mesh(*obj);
|
||||
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = name;
|
||||
ctx.previous_name = previous_name;
|
||||
ctx.id = std::to_string(obj->id().id);
|
||||
ctx.index = obj_idx;
|
||||
ctx.source = "object";
|
||||
fire_lifecycle_event(LifecycleEvent::ObjectRenamed, ctx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (volume_id < 0) return;
|
||||
obj->volumes[volume_id]->name = m_objects_model->GetName(item).ToUTF8().data();
|
||||
std::string name = m_objects_model->GetName(item).ToUTF8().data();
|
||||
if (obj->volumes[volume_id]->name == name)
|
||||
return;
|
||||
|
||||
const std::string previous_name = obj->volumes[volume_id]->name;
|
||||
obj->volumes[volume_id]->name = name;
|
||||
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = name;
|
||||
ctx.previous_name = previous_name;
|
||||
ctx.id = std::to_string(obj->volumes[volume_id]->id().id);
|
||||
ctx.index = volume_id;
|
||||
ctx.source = "volume";
|
||||
fire_lifecycle_event(LifecycleEvent::ObjectRenamed, ctx);
|
||||
}
|
||||
|
||||
void ObjectList::update_name_in_list(int obj_idx, int vol_idx) const
|
||||
@@ -3519,7 +3543,14 @@ void ObjectList::delete_all_connectors_for_object(int obj_idx)
|
||||
obj->delete_connectors();
|
||||
|
||||
if (obj->volumes.empty() || !obj->has_solid_mesh()) {
|
||||
const std::string deleted_obj_name = obj->name;
|
||||
model.delete_object(idx);
|
||||
{
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.name = deleted_obj_name;
|
||||
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectDeleted, ctx);
|
||||
}
|
||||
m_objects_model->Delete(m_objects_model->GetItemById(idx));
|
||||
continue;
|
||||
}
|
||||
@@ -4088,6 +4119,13 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed,
|
||||
const auto item = m_objects_model->AddObject(model_object, warning_bitmap, model_object->is_cut());
|
||||
Expand(m_objects_model->GetParent(item));
|
||||
|
||||
{
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.name = model_object->name;
|
||||
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectAdded, ctx);
|
||||
}
|
||||
|
||||
if (!do_info_update)
|
||||
return;
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
bool IMSlider::is_dragging() const
|
||||
{
|
||||
return GImGui != nullptr && m_imgui_id != 0 && GImGui->ActiveId == m_imgui_id && GImGui->IO.MouseDown[0];
|
||||
}
|
||||
|
||||
bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int v_max, const ImVec2& size, float scale)
|
||||
{
|
||||
ImGuiWindow* window = ImGui::GetCurrentWindow();
|
||||
@@ -496,7 +491,6 @@ bool IMSlider::horizontal_slider(const char* str_id, int* value, int v_min, int
|
||||
|
||||
ImGuiContext& context = *GImGui;
|
||||
const ImGuiID id = window->GetID(str_id);
|
||||
m_imgui_id = id;
|
||||
|
||||
const ImVec2 pos = window->DC.CursorPos;
|
||||
const ImRect draw_region(pos, pos + size);
|
||||
@@ -889,7 +883,6 @@ bool IMSlider::vertical_slider(const char* str_id, int* higher_value, int* lower
|
||||
|
||||
ImGuiContext& context = *GImGui;
|
||||
const ImGuiID id = window->GetID(str_id);
|
||||
m_imgui_id = id;
|
||||
|
||||
const ImVec2 pos = window->DC.CursorPos;
|
||||
const ImRect draw_region(pos, pos + size);
|
||||
|
||||
@@ -118,9 +118,6 @@ public:
|
||||
|
||||
//BBS update scroll value changed
|
||||
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; }
|
||||
bool is_need_post_tick_event() { return m_is_need_post_tick_changed_event; }
|
||||
void reset_post_tick_event(bool val = false) {
|
||||
@@ -185,8 +182,6 @@ private:
|
||||
int m_higher_value;
|
||||
int m_one_layer_value; // ORCA
|
||||
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 };
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "slic3r/GUI/NotificationManager.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
||||
#include "slic3r/plugin/PluginManager.hpp"
|
||||
|
||||
#include "libnest2d/common.hpp"
|
||||
|
||||
@@ -698,6 +699,13 @@ void ArrangeJob::finalize(bool canceled, std::exception_ptr &eptr) {
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":arrange m_unprintable: name: %4%, bed_id %1%, trans {%2%,%3%}") % ap.bed_idx % unscale<double>(ap.translation(X)) % unscale<double>(ap.translation(Y)) % ap.name;
|
||||
}
|
||||
|
||||
{
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||
ctx.msg = "arranged";
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
|
||||
}
|
||||
|
||||
m_plater->update();
|
||||
// BBS
|
||||
//wxGetApp().obj_manipul()->set_dirty();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
||||
#include "slic3r/plugin/PluginManager.hpp"
|
||||
#include "libnest2d/common.hpp"
|
||||
|
||||
#include <numeric>
|
||||
@@ -347,6 +348,13 @@ void FillBedJob::finalize(bool canceled, std::exception_ptr &eptr)
|
||||
m_plater->arrange();
|
||||
}
|
||||
m_plater->update();
|
||||
|
||||
{
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||
ctx.msg = "arranged";
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
m_plater->mark_plate_toolbar_image_dirty();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/NotificationManager.hpp"
|
||||
#include "slic3r/plugin/PluginManager.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
|
||||
|
||||
@@ -254,6 +255,12 @@ void OrientJob::finalize(bool canceled, std::exception_ptr &eptr)
|
||||
mesh.apply();
|
||||
}
|
||||
|
||||
if (!m_selected.empty()) {
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||
ctx.msg = "auto_oriented";
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
|
||||
}
|
||||
|
||||
m_plater->update();
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "PrintJob.hpp"
|
||||
#include "libslic3r/LifecycleEvents.hpp"
|
||||
#include "libslic3r/MTUtils.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
@@ -133,6 +134,13 @@ wxString PrintJob::get_http_error_msg(unsigned int status, std::string body)
|
||||
|
||||
void PrintJob::process(Ctl &ctl)
|
||||
{
|
||||
LifecycleEventContext start_ctx;
|
||||
start_ctx.name = m_project_name;
|
||||
start_ctx.device_id = m_dev_id;
|
||||
start_ctx.source = "print_job";
|
||||
fire_lifecycle_event(LifecycleEvent::PrintJobStarted, start_ctx);
|
||||
m_lifecycle_started = true;
|
||||
|
||||
/* display info */
|
||||
std::string msg;
|
||||
int curr_percent = 10;
|
||||
@@ -686,6 +694,7 @@ void PrintJob::process(Ctl &ctl)
|
||||
}
|
||||
wxQueueEvent(m_plater, evt);
|
||||
m_job_finished = true;
|
||||
m_lifecycle_success = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,6 +707,19 @@ void PrintJob::finalize(bool canceled, std::exception_ptr &eptr) {
|
||||
eptr = std::current_exception();
|
||||
}
|
||||
|
||||
if (m_lifecycle_started && !m_lifecycle_finished) {
|
||||
LifecycleEventContext finish_ctx;
|
||||
finish_ctx.name = m_project_name;
|
||||
finish_ctx.device_id = m_dev_id;
|
||||
finish_ctx.source = "print_job";
|
||||
finish_ctx.code = canceled ? LifecycleEvtCode::Warn :
|
||||
(eptr || !m_lifecycle_success ? LifecycleEvtCode::Error : LifecycleEvtCode::Ok);
|
||||
finish_ctx.msg = canceled ? "cancelled" : (eptr ? "exception" :
|
||||
(m_lifecycle_success ? "" : "failed"));
|
||||
fire_lifecycle_event(LifecycleEvent::PrintJobFinished, finish_ctx);
|
||||
m_lifecycle_finished = true;
|
||||
}
|
||||
|
||||
if (canceled || eptr)
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ class PrintJob : public Job
|
||||
std::function<void()> m_success_fun{nullptr};
|
||||
std::string m_dev_id;
|
||||
bool m_job_finished{ false };
|
||||
bool m_lifecycle_started{ false };
|
||||
bool m_lifecycle_finished{ false };
|
||||
bool m_lifecycle_success{ false };
|
||||
int m_print_job_completed_id = 0;
|
||||
wxString m_completed_evt_data;
|
||||
std::function<void()> m_enter_ip_address_fun_fail{ nullptr };
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "SendJob.hpp"
|
||||
#include "libslic3r/LifecycleEvents.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/Utils/NetworkAgent.hpp"
|
||||
#include "libslic3r/MTUtils.hpp"
|
||||
@@ -148,6 +149,13 @@ void SendJob::process(Ctl &ctl)
|
||||
}
|
||||
}
|
||||
|
||||
LifecycleEventContext start_ctx;
|
||||
start_ctx.name = m_project_name;
|
||||
start_ctx.device_id = m_dev_id;
|
||||
start_ctx.source = "send_job";
|
||||
fire_lifecycle_event(LifecycleEvent::SendJobStarted, start_ctx);
|
||||
m_lifecycle_started = true;
|
||||
|
||||
int total_plate_num = m_plater->get_partplate_list().get_plate_count();
|
||||
|
||||
PartPlate* plate = m_plater->get_partplate_list().get_plate(job_data.plate_idx);
|
||||
@@ -424,6 +432,19 @@ void SendJob::finalize(bool canceled, std::exception_ptr &eptr)
|
||||
eptr = std::current_exception();
|
||||
}
|
||||
|
||||
if (m_lifecycle_started && !m_lifecycle_finished) {
|
||||
LifecycleEventContext finish_ctx;
|
||||
finish_ctx.name = m_project_name;
|
||||
finish_ctx.device_id = m_dev_id;
|
||||
finish_ctx.source = "send_job";
|
||||
finish_ctx.code = canceled ? LifecycleEvtCode::Warn :
|
||||
(eptr ? LifecycleEvtCode::Error : (m_job_finished ? LifecycleEvtCode::Ok : LifecycleEvtCode::Error));
|
||||
finish_ctx.msg = canceled ? "cancelled" : (eptr ? "exception" :
|
||||
(m_job_finished ? "" : "failed"));
|
||||
fire_lifecycle_event(LifecycleEvent::SendJobFinished, finish_ctx);
|
||||
m_lifecycle_finished = true;
|
||||
}
|
||||
|
||||
if (canceled || eptr)
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ class SendJob : public Job
|
||||
PrintPrepareData job_data;
|
||||
std::string m_dev_id;
|
||||
bool m_job_finished{ false };
|
||||
bool m_lifecycle_started{ false };
|
||||
bool m_lifecycle_finished{ false };
|
||||
int m_print_job_completed_id = 0;
|
||||
bool m_is_check_mode{false};
|
||||
bool m_check_and_continue{false};
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "libslic3r/Tesselate.hpp"
|
||||
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/LifecycleEvents.hpp"
|
||||
|
||||
#include "I18N.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
@@ -2626,11 +2627,20 @@ void PartPlate::set_plate_name(const std::string& name)
|
||||
if (boost::equals(m_name, name))
|
||||
return;
|
||||
|
||||
const std::string previous_name = m_name;
|
||||
m_name = name;
|
||||
if (m_print != nullptr)
|
||||
m_print->set_plate_name(name);
|
||||
|
||||
invalidate_plate_name_texture();
|
||||
|
||||
if (m_plater != nullptr && !m_plater->is_loading_project()) {
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = name;
|
||||
ctx.previous_name = previous_name;
|
||||
ctx.index = m_plate_index;
|
||||
fire_lifecycle_event(LifecycleEvent::PlateRenamed, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
//get the print's object, result and index
|
||||
@@ -4741,9 +4751,16 @@ int PartPlateList::create_plate(bool adjust_position)
|
||||
|
||||
if (m_plater) {
|
||||
// In GUI mode
|
||||
wxGetApp().obj_list()->on_plate_added(plate);
|
||||
wxGetApp().obj_list()->on_plate_added(plate);
|
||||
}
|
||||
|
||||
if (m_plater != nullptr && m_intialized && !m_plater->is_loading_project()) {
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = plate->get_plate_name();
|
||||
ctx.index = new_index;
|
||||
fire_lifecycle_event(LifecycleEvent::PlateCreated, ctx);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":created a new plate %1%") % new_index;
|
||||
return new_index;
|
||||
}
|
||||
@@ -4842,6 +4859,7 @@ int PartPlateList::delete_plate(int index)
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":plate %1%, has an invalid index %2%") % index % plate->get_index();
|
||||
return -1;
|
||||
}
|
||||
const std::string plate_name = plate->get_plate_name();
|
||||
|
||||
if (m_plater) {
|
||||
// In GUI mode
|
||||
@@ -4924,6 +4942,13 @@ int PartPlateList::delete_plate(int index)
|
||||
|
||||
delete plate;
|
||||
|
||||
if (m_plater != nullptr && m_intialized && !m_plater->is_loading_project()) {
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = plate_name;
|
||||
ctx.index = index;
|
||||
fire_lifecycle_event(LifecycleEvent::PlateDeleted, ctx);
|
||||
}
|
||||
|
||||
// FIX: context of BackgroundSliceProcess and gcode preview need to be updated before ObjectList::reload_all_plates().
|
||||
#if 0
|
||||
if (m_plater != nullptr) {
|
||||
@@ -5032,6 +5057,7 @@ int PartPlateList::select_plate(int index)
|
||||
if (m_plate_list.empty() || index >= m_plate_list.size()) {
|
||||
return -1;
|
||||
}
|
||||
const int previous_index = m_current_plate;
|
||||
|
||||
// BBS: erase unnecessary snapshot
|
||||
if (get_curr_plate_index() != index && m_intialized) {
|
||||
@@ -5058,6 +5084,13 @@ int PartPlateList::select_plate(int index)
|
||||
//wxQueueEvent(m_plater, new SimpleEvent(EVT_GLCANVAS_PLATE_SELECT));
|
||||
}
|
||||
|
||||
if (previous_index != index && m_intialized && m_plater != nullptr && !m_plater->is_loading_project()) {
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = m_plate_list[index]->get_plate_name();
|
||||
ctx.index = index;
|
||||
fire_lifecycle_event(LifecycleEvent::PlateSelected, ctx);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+105
-13
@@ -57,6 +57,7 @@
|
||||
#include <wx/aui/aui.h>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/LifecycleEvents.hpp"
|
||||
#include "libslic3r/Format/STL.hpp"
|
||||
#include "libslic3r/Format/DRC.hpp"
|
||||
#include "libslic3r/Format/STEP.hpp"
|
||||
@@ -10144,7 +10145,14 @@ void Plater::priv::remove(size_t obj_idx)
|
||||
view3D->enable_layers_editing(false);
|
||||
|
||||
m_worker.cancel_all();
|
||||
std::string obj_name = (obj_idx < model.objects.size()) ? model.objects[obj_idx]->name : std::to_string(obj_idx);
|
||||
model.delete_object(obj_idx);
|
||||
{
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.name = obj_name;
|
||||
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectDeleted, ctx);
|
||||
}
|
||||
//BBS: notify partplate the instance removed
|
||||
partplate_list.notify_instance_removed(obj_idx, -1);
|
||||
update();
|
||||
@@ -10177,7 +10185,14 @@ bool Plater::priv::delete_object_from_model(size_t obj_idx, bool refresh_immedia
|
||||
if (obj->is_cut())
|
||||
sidebar->obj_list()->invalidate_cut_info_for_object(obj_idx);
|
||||
|
||||
std::string obj_name = obj->name;
|
||||
model.delete_object(obj_idx);
|
||||
{
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.name = obj_name;
|
||||
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectDeleted, ctx);
|
||||
}
|
||||
//BBS: notify partplate the instance removed
|
||||
partplate_list.notify_instance_removed(obj_idx, -1);
|
||||
|
||||
@@ -10223,10 +10238,22 @@ void Plater::priv::delete_all_objects_from_model()
|
||||
|
||||
void Plater::priv::reset(bool apply_presets_change)
|
||||
{
|
||||
// TakeSnapshot below and load_current_presets() further down each re-evaluate the
|
||||
// aggregate dirty flag against a baseline that hasn't been reset yet, so they can toggle
|
||||
// is_dirty() back and forth several times before it settles; coalesce those into one event.
|
||||
ProjectDirtyStateManager::NotificationSuppressor dirty_notify_suppressor(dirty_state);
|
||||
|
||||
Plater::TakeSnapshot snapshot(q, _u8L("Reset Project"), UndoRedo::SnapshotType::ProjectSeparator);
|
||||
|
||||
clear_warnings();
|
||||
|
||||
const std::string closed_project_name = into_u8(get_project_filename());
|
||||
if (!closed_project_name.empty() || !model.objects.empty()) {
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.name = closed_project_name;
|
||||
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ProjectClosed, ctx);
|
||||
}
|
||||
// A new project must not inherit the previous project's published selection (Feature A/B).
|
||||
m_has_pending_published = false;
|
||||
m_pending_published_keys.clear();
|
||||
@@ -12654,11 +12681,15 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
||||
notification_manager->set_slicing_progress_export_possible();
|
||||
|
||||
// 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 bool stops showing export finished notification even when process_completed_with_error is false
|
||||
bool has_error = false;
|
||||
std::string lifecycle_error_msg;
|
||||
if (evt.error()) {
|
||||
auto message = evt.format_error_message();
|
||||
lifecycle_error_msg = message.first;
|
||||
if (evt.critical_error()) {
|
||||
if (q->m_tracking_popup_menu) {
|
||||
// We don't want to pop-up a message box when tracking a pop-up menu.
|
||||
@@ -12696,6 +12727,14 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
||||
is_finished = true;
|
||||
}
|
||||
|
||||
{
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.name = lifecycle_job_name;
|
||||
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());
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::SlicingJobComplete, ctx);
|
||||
}
|
||||
|
||||
//BBS: set the current plater's slice result to valid
|
||||
if (!this->background_process.empty())
|
||||
this->background_process.get_current_plate()->update_slice_result_valid_state(evt.success());
|
||||
@@ -15229,21 +15268,33 @@ int Plater::new_project(bool skip_confirm, bool silent, const wxString& project_
|
||||
//get_partplate_list().reinit();
|
||||
//get_partplate_list().update_slice_context_to_current_plate(p->background_process);
|
||||
//p->preview->update_gcode_result(p->partplate_list.get_current_slice_result());
|
||||
reset(transfer_preset_changes);
|
||||
reset_project_dirty_after_save();
|
||||
reset_project_dirty_initial_presets();
|
||||
wxGetApp().update_saved_preset_from_current_preset();
|
||||
update_project_dirty_from_presets();
|
||||
if (!silent) {
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::NewProject, ctx);
|
||||
}
|
||||
{
|
||||
// Same rationale as in Plater::priv::reset(): the whole reset + preset-reload +
|
||||
// baseline-reset sequence below settles into its final dirty state only once it
|
||||
// completes, so hold notifications until then to avoid firing on transient flips.
|
||||
ProjectDirtyStateManager::NotificationSuppressor dirty_notify_suppressor(p->dirty_state);
|
||||
|
||||
//reset project
|
||||
p->project.reset();
|
||||
//set project name
|
||||
if (project_name.empty())
|
||||
p->set_project_name(_L("Untitled"));
|
||||
else
|
||||
p->set_project_name(project_name);
|
||||
reset(transfer_preset_changes);
|
||||
reset_project_dirty_after_save();
|
||||
reset_project_dirty_initial_presets();
|
||||
wxGetApp().update_saved_preset_from_current_preset();
|
||||
update_project_dirty_from_presets();
|
||||
|
||||
Plater::TakeSnapshot snapshot(this, "New Project", UndoRedo::SnapshotType::ProjectSeparator);
|
||||
//reset project
|
||||
p->project.reset();
|
||||
//set project name
|
||||
if (project_name.empty())
|
||||
p->set_project_name(_L("Untitled"));
|
||||
else
|
||||
p->set_project_name(project_name);
|
||||
|
||||
Plater::TakeSnapshot snapshot(this, "New Project", UndoRedo::SnapshotType::ProjectSeparator);
|
||||
}
|
||||
|
||||
Model m;
|
||||
model().load_from(m); // new id avoid same path name
|
||||
@@ -15361,6 +15412,13 @@ void Plater::load_project(wxString const& filename2,
|
||||
p->set_project_name(_L("Untitled"));
|
||||
}
|
||||
|
||||
{
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.name = into_u8(load_restore ? originfile : filename);
|
||||
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ProjectOpened, ctx);
|
||||
}
|
||||
|
||||
} else {
|
||||
if (using_exported_file()) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " using ecported set project filename: " << filename;
|
||||
@@ -15432,11 +15490,23 @@ int Plater::save_project(bool saveAs)
|
||||
if (full_pathnames) {
|
||||
save_strategy = save_strategy | SaveStrategy::FullPathSources;
|
||||
}
|
||||
{
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.name = into_u8(filename);
|
||||
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ProjectBeforeSave, ctx);
|
||||
}
|
||||
if (export_3mf(into_path(filename), save_strategy) < 0) {
|
||||
MessageDialog(this, _L("Failed to save the project.\nPlease check whether the folder exists online or if other programs have the project file open."),
|
||||
_L("Save project"), wxOK | wxICON_WARNING).ShowModal();
|
||||
return wxID_CANCEL;
|
||||
}
|
||||
{
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.name = into_u8(filename);
|
||||
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ProjectAfterSave, ctx);
|
||||
}
|
||||
|
||||
Slic3r::remove_backup(model(), false);
|
||||
|
||||
@@ -20756,6 +20826,14 @@ void Plater::changed_object(ModelObject &object){
|
||||
|
||||
// Check outside bed
|
||||
get_current_canvas3D()->requires_check_outside_state();
|
||||
|
||||
if (!is_loading_project()) {
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = object.name;
|
||||
ctx.id = std::to_string(object.id().id);
|
||||
ctx.source = "geometry";
|
||||
fire_lifecycle_event(LifecycleEvent::ObjectChanged, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
void Plater::changed_object(int obj_idx)
|
||||
@@ -20792,6 +20870,20 @@ void Plater::changed_objects(const std::vector<size_t>& object_idxs)
|
||||
|
||||
// update print
|
||||
this->p->schedule_background_process();
|
||||
|
||||
if (!is_loading_project()) {
|
||||
for (size_t obj_idx : object_idxs) {
|
||||
if (obj_idx >= p->model.objects.size() || p->model.objects[obj_idx] == nullptr)
|
||||
continue;
|
||||
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = p->model.objects[obj_idx]->name;
|
||||
ctx.id = std::to_string(p->model.objects[obj_idx]->id().id);
|
||||
ctx.index = static_cast<int>(obj_idx);
|
||||
ctx.source = "geometry";
|
||||
fire_lifecycle_event(LifecycleEvent::ObjectChanged, ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Plater::schedule_background_process(bool schedule/* = true*/)
|
||||
|
||||
@@ -1049,16 +1049,6 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too
|
||||
}
|
||||
}
|
||||
// ORCA: apply the preview dimming change immediately to the currently loaded preview
|
||||
// apply the solid model preference immediately to the currently loaded preview
|
||||
else if (param == "preview_solid_model_while_dragging") {
|
||||
if (Plater* plater = wxGetApp().plater()) {
|
||||
if (GLCanvas3D* canvas = plater->get_preview_canvas3D()) {
|
||||
canvas->get_gcode_viewer().set_solid_model_while_dragging(app_config->get_bool(param));
|
||||
canvas->set_as_dirty();
|
||||
canvas->request_extra_frame();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (param == "preview_dim_previous_layers") {
|
||||
if (m_dim_previous_layers_brightness_input)
|
||||
m_dim_previous_layers_brightness_input->Enable(app_config->get_bool(param));
|
||||
@@ -2029,15 +2019,6 @@ void PreferencesDialog::create_items()
|
||||
//// GRAPHICS > G-code Preview
|
||||
g_sizer->Add(create_item_title(_L("G-code Preview")), 1, wxEXPAND);
|
||||
|
||||
auto item_solid_model_while_dragging = create_item_checkbox(
|
||||
_L("Only render solid model when dragging"),
|
||||
_L("While dragging the camera or a preview slider, or zooming with the mouse wheel, draw the sliced objects and the prime tower as solid shapes "
|
||||
"in their filament colours instead of toolpaths, so that large prints stay responsive. They are cut to the visible layer range, with its bottom "
|
||||
"and top layers drawn as toolpaths. Supports are not shown. The toolpaths are restored as soon as you let go."),
|
||||
"preview_solid_model_while_dragging"
|
||||
);
|
||||
g_sizer->Add(item_solid_model_while_dragging);
|
||||
|
||||
auto item_dim_previous_layers = create_item_checkbox(
|
||||
_L("Dim lower layers"),
|
||||
_L("When scrubbing the layer slider in the sliced preview, render the layers below the current one darkened so that only the layer being viewed is shown at full brightness."),
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "MainFrame.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "libslic3r/LifecycleEvents.hpp"
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
@@ -17,13 +18,23 @@ namespace GUI {
|
||||
|
||||
void ProjectDirtyStateManager::update_from_undo_redo_stack(bool dirty)
|
||||
{
|
||||
const bool was_dirty = is_dirty();
|
||||
m_plater_dirty = dirty;
|
||||
notify_dirty_change(was_dirty, "undo_redo");
|
||||
if (const Plater *plater = wxGetApp().plater(); plater && wxGetApp().initialized())
|
||||
wxGetApp().mainframe->update_title();
|
||||
}
|
||||
|
||||
void ProjectDirtyStateManager::set_plater_dirty(bool is_dirty)
|
||||
{
|
||||
const bool was_dirty = this->is_dirty();
|
||||
m_plater_dirty = is_dirty;
|
||||
notify_dirty_change(was_dirty, "plater");
|
||||
}
|
||||
|
||||
void ProjectDirtyStateManager::update_from_presets()
|
||||
{
|
||||
const bool was_dirty = is_dirty();
|
||||
m_presets_dirty = false;
|
||||
// check switching of the presets only for exist/loaded project, but not for new
|
||||
GUI_App &app = wxGetApp();
|
||||
@@ -45,18 +56,53 @@ void ProjectDirtyStateManager::update_from_presets()
|
||||
}
|
||||
m_presets_dirty |= app.has_unsaved_preset_changes();
|
||||
m_project_config_dirty = m_initial_project_config != app.preset_bundle->project_config;
|
||||
notify_dirty_change(was_dirty, "presets");
|
||||
app.mainframe->update_title();
|
||||
}
|
||||
|
||||
void ProjectDirtyStateManager::reset_after_save()
|
||||
{
|
||||
const bool was_dirty = is_dirty();
|
||||
this->reset_initial_presets();
|
||||
m_plater_dirty = false;
|
||||
m_presets_dirty = false;
|
||||
m_project_config_dirty = false;
|
||||
notify_dirty_change(was_dirty, "save");
|
||||
wxGetApp().mainframe->update_title();
|
||||
}
|
||||
|
||||
void ProjectDirtyStateManager::notify_dirty_change(bool was_dirty, const char *source)
|
||||
{
|
||||
if (m_suppress_depth > 0)
|
||||
return;
|
||||
|
||||
const bool dirty = is_dirty();
|
||||
if (was_dirty == dirty)
|
||||
return;
|
||||
|
||||
LifecycleEventContext ctx;
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
ctx.dirty = dirty;
|
||||
ctx.source = source;
|
||||
fire_lifecycle_event(LifecycleEvent::ProjectDirtyChanged, ctx);
|
||||
}
|
||||
|
||||
void ProjectDirtyStateManager::begin_suppress_notifications()
|
||||
{
|
||||
if (m_suppress_depth == 0)
|
||||
m_suppress_entry_dirty = is_dirty();
|
||||
++m_suppress_depth;
|
||||
}
|
||||
|
||||
void ProjectDirtyStateManager::end_suppress_notifications()
|
||||
{
|
||||
assert(m_suppress_depth > 0);
|
||||
if (m_suppress_depth > 0)
|
||||
--m_suppress_depth;
|
||||
if (m_suppress_depth == 0)
|
||||
notify_dirty_change(m_suppress_entry_dirty, "batch");
|
||||
}
|
||||
|
||||
void ProjectDirtyStateManager::reset_initial_presets()
|
||||
{
|
||||
m_initial_presets.fill(std::string{});
|
||||
@@ -168,4 +214,3 @@ void ProjectDirtyStateManager::render_debug_window() const
|
||||
|
||||
} // namespace GUI
|
||||
} // namespace Slic3r
|
||||
|
||||
|
||||
@@ -14,21 +14,42 @@ public:
|
||||
void reset_after_save();
|
||||
void reset_initial_presets();
|
||||
|
||||
void set_plater_dirty(bool is_dirty) { m_plater_dirty = is_dirty; }
|
||||
void set_plater_dirty(bool is_dirty);
|
||||
bool is_dirty() const { return m_plater_dirty || m_project_config_dirty || m_presets_dirty; }
|
||||
bool is_presets_dirty() const { return m_presets_dirty; }
|
||||
|
||||
// RAII guard coalescing dirty-state updates: while any guard is alive, ProjectDirtyChanged
|
||||
// notifications are held back; when the outermost guard is destroyed, at most one
|
||||
// notification fires, reflecting only the net change across the whole guarded scope.
|
||||
class NotificationSuppressor
|
||||
{
|
||||
public:
|
||||
explicit NotificationSuppressor(ProjectDirtyStateManager &owner) : m_owner(owner) { m_owner.begin_suppress_notifications(); }
|
||||
~NotificationSuppressor() { m_owner.end_suppress_notifications(); }
|
||||
NotificationSuppressor(const NotificationSuppressor &) = delete;
|
||||
NotificationSuppressor &operator=(const NotificationSuppressor &) = delete;
|
||||
private:
|
||||
ProjectDirtyStateManager &m_owner;
|
||||
};
|
||||
|
||||
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||
void render_debug_window() const;
|
||||
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||
|
||||
private:
|
||||
void notify_dirty_change(bool was_dirty, const char *source);
|
||||
void begin_suppress_notifications();
|
||||
void end_suppress_notifications();
|
||||
|
||||
// Does the Undo / Redo stack indicate the project is dirty?
|
||||
bool m_plater_dirty { false };
|
||||
// Do the presets indicate the project is dirty?
|
||||
bool m_presets_dirty { false };
|
||||
// Is the project config dirty?
|
||||
bool m_project_config_dirty { false };
|
||||
// NotificationSuppressor nesting depth and the dirty state observed when the outermost guard began.
|
||||
int m_suppress_depth { 0 };
|
||||
bool m_suppress_entry_dirty { false };
|
||||
// Keeps track of preset names selected at the time of last project save.
|
||||
std::array<std::string, Preset::TYPE_COUNT> m_initial_presets;
|
||||
DynamicPrintConfig m_initial_project_config;
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "slic3r/Utils/NetworkAgentFactory.hpp"
|
||||
#include "slic3r/Utils/PresetUpdater.hpp"
|
||||
#include "slic3r/plugin/PluginConfig.hpp"
|
||||
#include "slic3r/plugin/PluginManager.hpp"
|
||||
#include "Plater.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "format.hpp"
|
||||
@@ -6964,6 +6965,20 @@ bool Tab::select_preset(
|
||||
}
|
||||
load_current_preset();
|
||||
|
||||
{
|
||||
Slic3r::LifecycleEventContext ctx;
|
||||
ctx.name = preset_name;
|
||||
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||
switch (m_type) {
|
||||
case Preset::TYPE_PRINT: ctx.msg = "print"; break;
|
||||
case Preset::TYPE_SLA_PRINT: ctx.msg = "sla_print"; break;
|
||||
case Preset::TYPE_FILAMENT: ctx.msg = "filament"; break;
|
||||
case Preset::TYPE_SLA_MATERIAL: ctx.msg = "sla_material"; break;
|
||||
case Preset::TYPE_PRINTER: ctx.msg = "printer"; break;
|
||||
default: break;
|
||||
}
|
||||
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::PresetSelected, ctx);
|
||||
}
|
||||
|
||||
if (delete_third_printer) {
|
||||
wxGetApp().CallAfter([filament_presets, process_presets]() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "TaskManager.hpp"
|
||||
|
||||
#include "libslic3r/Thread.hpp"
|
||||
#include "libslic3r/LifecycleEvents.hpp"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
@@ -210,6 +211,13 @@ int TaskManager::schedule(TaskStateInfo* task)
|
||||
assert(task->state() == TaskState::TS_PENDING);
|
||||
task->set_state(TaskState::TS_SENDING);
|
||||
|
||||
LifecycleEventContext start_ctx;
|
||||
start_ctx.name = task->params().project_name;
|
||||
start_ctx.device_id = task->params().dev_id;
|
||||
start_ctx.job_id = std::to_string(task->task_info_id);
|
||||
start_ctx.source = "task_manager";
|
||||
fire_lifecycle_event(LifecycleEvent::PrintJobStarted, start_ctx);
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "task_manager: schedule a task to dev_id = " << task->params().dev_id;
|
||||
boost::thread* new_sending_thread = new boost::thread();
|
||||
*new_sending_thread = Slic3r::create_thread(
|
||||
@@ -237,6 +245,16 @@ int TaskManager::schedule(TaskStateInfo* task)
|
||||
task->set_state(TaskState::TS_SEND_CANCELED);
|
||||
}
|
||||
}
|
||||
|
||||
LifecycleEventContext finish_ctx;
|
||||
finish_ctx.name = task->params().project_name;
|
||||
finish_ctx.device_id = task->params().dev_id;
|
||||
finish_ctx.job_id = std::to_string(task->task_info_id);
|
||||
finish_ctx.source = "task_manager";
|
||||
finish_ctx.code = result == 0 ? LifecycleEvtCode::Ok :
|
||||
(task->is_canceled() ? LifecycleEvtCode::Warn : LifecycleEvtCode::Error);
|
||||
finish_ctx.msg = result == 0 ? "" : (task->is_canceled() ? "cancelled" : "failed");
|
||||
fire_lifecycle_event(LifecycleEvent::PrintJobFinished, finish_ctx);
|
||||
|
||||
/* remove from sending task list */
|
||||
m_scedule_mutex.lock();
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "CrealityPrint.hpp"
|
||||
#include "../GUI/PrintHostDialogs.hpp"
|
||||
#include "../GUI/MainFrame.hpp"
|
||||
#include "slic3r/plugin/PluginManager.hpp"
|
||||
#include "Obico.hpp"
|
||||
#include "Flashforge.hpp"
|
||||
#include "SimplyPrint.hpp"
|
||||
@@ -360,12 +361,29 @@ void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job)
|
||||
{
|
||||
emit_progress(0); // Indicate the upload is starting
|
||||
|
||||
// Captured before upload_data is moved into upload() below.
|
||||
const std::string upload_filename = the_job.upload_data.source_path.filename().string();
|
||||
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = upload_filename;
|
||||
ctx.code = LifecycleEvtCode::Ok;
|
||||
fire_lifecycle_event(LifecycleEvent::UploadStarted, ctx);
|
||||
}
|
||||
|
||||
bool success = the_job.printhost->upload(std::move(the_job.upload_data),
|
||||
[this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); },
|
||||
[this](wxString error) { this->error_fn(std::move(error)); },
|
||||
[this](wxString tag, wxString host) { this->info_fn(std::move(tag), std::move(host)); }
|
||||
);
|
||||
|
||||
{
|
||||
LifecycleEventContext ctx;
|
||||
ctx.name = upload_filename;
|
||||
ctx.code = success ? LifecycleEvtCode::Ok : LifecycleEvtCode::Error;
|
||||
fire_lifecycle_event(LifecycleEvent::UploadFinished, ctx);
|
||||
}
|
||||
|
||||
if (success) {
|
||||
emit_progress(100);
|
||||
if (the_job.switch_to_device_tab) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/Exception.hpp"
|
||||
#include "libslic3r/LifecycleEvents.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r_version.h"
|
||||
|
||||
@@ -46,6 +47,16 @@ void install_capability_resolver()
|
||||
});
|
||||
}
|
||||
|
||||
// Global libslic3r-side seam (Slic3r::fire_lifecycle_event, in libslic3r/LifecycleEvents.hpp):
|
||||
// broadcasts to every loaded, enabled capability regardless of type, unlike the SlicingPipeline
|
||||
// hook below which only targets picker-selected SlicingPipeline capabilities.
|
||||
void install_lifecycle_event_hook()
|
||||
{
|
||||
set_lifecycle_hook_fn([](LifecycleEvent event, const LifecycleEventContext& ctx) {
|
||||
PluginManager::instance().dispatch_lifecycle_event(event, ctx);
|
||||
});
|
||||
}
|
||||
|
||||
// Print::process() fires this hook at each pipeline seam on the slicing worker
|
||||
// thread; here we run the picker-selected SlicingPipeline capabilities. Per
|
||||
// capability we acquire the GIL, honor cancellation, and convert a plugin
|
||||
@@ -122,12 +133,14 @@ void install()
|
||||
{
|
||||
install_capability_resolver();
|
||||
install_slicing_pipeline_hook();
|
||||
install_lifecycle_event_hook();
|
||||
}
|
||||
|
||||
void uninstall()
|
||||
{
|
||||
ConfigBase::set_resolve_capability_fn(nullptr);
|
||||
Print::set_slicing_pipeline_hook_fn(nullptr);
|
||||
set_lifecycle_hook_fn(nullptr);
|
||||
}
|
||||
|
||||
} // namespace Slic3r::plugin_hooks
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "PluginManager.hpp"
|
||||
|
||||
#include <exception>
|
||||
#include <libslic3r/Utils.hpp>
|
||||
#include <memory>
|
||||
#include <pybind11/embed.h>
|
||||
@@ -2089,4 +2090,24 @@ ExecutionResult PluginManager::run_script_capability(const std::string& plugin_k
|
||||
return result;
|
||||
}
|
||||
|
||||
void PluginManager::dispatch_lifecycle_event(LifecycleEvent evt, const LifecycleEventContext& ctx) {
|
||||
for (const auto& cap : get_plugin_capabilities()) {
|
||||
if (!cap || !cap->is_enabled()) continue;
|
||||
try {
|
||||
cap->on_lifecycle_event(evt, ctx);
|
||||
} catch (const std::exception& ex) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": plugin '" << cap->audit_plugin_key() << "/" << cap->name()
|
||||
<< "' on_lifecycle_event(" << lifecycle_event_to_string(evt) << ") threw: " << ex.what()
|
||||
<< " [ctx name='" << ctx.name << "', code=" << lifecycle_evt_code_to_string(ctx.code)
|
||||
<< ", msg='" << ctx.msg << "']";
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": plugin '" << cap->audit_plugin_key() << "/" << cap->name()
|
||||
<< "' on_lifecycle_event(" << lifecycle_event_to_string(evt)
|
||||
<< ") threw a non-standard exception"
|
||||
<< " [ctx name='" << ctx.name << "', code=" << lifecycle_evt_code_to_string(ctx.code)
|
||||
<< ", msg='" << ctx.msg << "']";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <libslic3r/Config.hpp>
|
||||
#include <libslic3r/LifecycleEvents.hpp>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@@ -20,7 +21,6 @@
|
||||
#include <pybind11/embed.h>
|
||||
|
||||
#include "CloudPluginService.hpp"
|
||||
#include "PluginFsUtils.hpp"
|
||||
#include "PluginDescriptor.hpp"
|
||||
#include "PluginLoader.hpp"
|
||||
#include "PluginConfig.hpp"
|
||||
@@ -210,6 +210,8 @@ public:
|
||||
|
||||
ExecutionResult run_script_capability(const std::string& plugin_key, const std::string& capability_name, std::string& error);
|
||||
|
||||
void dispatch_lifecycle_event(LifecycleEvent evt, const LifecycleEventContext& ctx);
|
||||
|
||||
private:
|
||||
PluginManager() = default;
|
||||
PluginManager(const PluginManager&) = delete;
|
||||
|
||||
@@ -132,6 +132,11 @@ public:
|
||||
{
|
||||
ORCA_PY_OVERRIDE_AUDITED([] {}, PYBIND11_OVERRIDE, void, Base, on_cancelled);
|
||||
}
|
||||
|
||||
void on_lifecycle_event(LifecycleEvent event, const LifecycleEventContext& ctx) override
|
||||
{
|
||||
ORCA_PY_OVERRIDE_AUDITED([] {}, PYBIND11_OVERRIDE, void, Base, on_lifecycle_event, event, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
class PyPluginInterfaceTrampoline : public PyPluginCommonTrampoline<PluginCapabilityInterface>
|
||||
|
||||
@@ -377,6 +377,62 @@ void bind_python_api(pybind11::module_& m)
|
||||
.value("FatalError", PluginResult::FatalError)
|
||||
.export_values();
|
||||
|
||||
py::enum_<LifecycleEvent>(m, "LifecycleEvent", "Application lifecycle moment passed to on_lifecycle_event")
|
||||
.value("NewProject", LifecycleEvent::NewProject)
|
||||
.value("ProjectOpened", LifecycleEvent::ProjectOpened)
|
||||
.value("ProjectBeforeSave", LifecycleEvent::ProjectBeforeSave)
|
||||
.value("ProjectAfterSave", LifecycleEvent::ProjectAfterSave)
|
||||
.value("ProjectClosed", LifecycleEvent::ProjectClosed)
|
||||
.value("ProjectDirtyChanged", LifecycleEvent::ProjectDirtyChanged)
|
||||
.value("SliceStarted", LifecycleEvent::SliceStarted)
|
||||
.value("SliceGeometryFinished", LifecycleEvent::SliceGeometryFinished)
|
||||
.value("GCodeExportStarted", LifecycleEvent::GCodeExportStarted)
|
||||
.value("GCodeExportFinished", LifecycleEvent::GCodeExportFinished)
|
||||
.value("SlicingJobComplete", LifecycleEvent::SlicingJobComplete)
|
||||
.value("ObjectAdded", LifecycleEvent::ObjectAdded)
|
||||
.value("ObjectDeleted", LifecycleEvent::ObjectDeleted)
|
||||
.value("ObjectTransformed", LifecycleEvent::ObjectTransformed)
|
||||
.value("ObjectChanged", LifecycleEvent::ObjectChanged)
|
||||
.value("ObjectRenamed", LifecycleEvent::ObjectRenamed)
|
||||
.value("PlateCreated", LifecycleEvent::PlateCreated)
|
||||
.value("PlateDeleted", LifecycleEvent::PlateDeleted)
|
||||
.value("PlateSelected", LifecycleEvent::PlateSelected)
|
||||
.value("PlateRenamed", LifecycleEvent::PlateRenamed)
|
||||
.value("PresetSelected", LifecycleEvent::PresetSelected)
|
||||
.value("PresetSaved", LifecycleEvent::PresetSaved)
|
||||
.value("PrintStateChanged", LifecycleEvent::PrintStateChanged)
|
||||
.value("DeviceOnlineChanged", LifecycleEvent::DeviceOnlineChanged)
|
||||
.value("DeviceDiscovered", LifecycleEvent::DeviceDiscovered)
|
||||
.value("DeviceSelected", LifecycleEvent::DeviceSelected)
|
||||
.value("DeviceConnected", LifecycleEvent::DeviceConnected)
|
||||
.value("DeviceDisconnected", LifecycleEvent::DeviceDisconnected)
|
||||
.value("UploadStarted", LifecycleEvent::UploadStarted)
|
||||
.value("UploadFinished", LifecycleEvent::UploadFinished)
|
||||
.value("PrintJobStarted", LifecycleEvent::PrintJobStarted)
|
||||
.value("PrintJobFinished", LifecycleEvent::PrintJobFinished)
|
||||
.value("SendJobStarted", LifecycleEvent::SendJobStarted)
|
||||
.value("SendJobFinished", LifecycleEvent::SendJobFinished)
|
||||
.export_values();
|
||||
|
||||
py::enum_<LifecycleEvtCode>(m, "LifecycleEvtCode", "Outcome code accompanying a LifecycleEventContext")
|
||||
.value("Ok", LifecycleEvtCode::Ok)
|
||||
.value("Error", LifecycleEvtCode::Error)
|
||||
.value("Warn", LifecycleEvtCode::Warn)
|
||||
.export_values();
|
||||
|
||||
py::class_<LifecycleEventContext>(m, "LifecycleEventContext", "Payload accompanying a LifecycleEvent")
|
||||
.def(py::init<>())
|
||||
.def_readonly("name", &LifecycleEventContext::name)
|
||||
.def_readonly("code", &LifecycleEventContext::code)
|
||||
.def_readonly("msg", &LifecycleEventContext::msg)
|
||||
.def_readonly("id", &LifecycleEventContext::id)
|
||||
.def_readonly("previous_name", &LifecycleEventContext::previous_name)
|
||||
.def_readonly("device_id", &LifecycleEventContext::device_id)
|
||||
.def_readonly("job_id", &LifecycleEventContext::job_id)
|
||||
.def_readonly("source", &LifecycleEventContext::source)
|
||||
.def_readonly("index", &LifecycleEventContext::index)
|
||||
.def_readonly("dirty", &LifecycleEventContext::dirty);
|
||||
|
||||
py::class_<PluginContext>(m, "PluginContext", "Context shared with plugin entry points")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("orca_version", &PluginContext::orca_version);
|
||||
@@ -401,6 +457,9 @@ void bind_python_api(pybind11::module_& m)
|
||||
.def("get_type", &PluginCapabilityInterface::get_type)
|
||||
.def("on_load", &PluginCapabilityInterface::on_load)
|
||||
.def("on_unload", &PluginCapabilityInterface::on_unload)
|
||||
.def("on_lifecycle_event", &PluginCapabilityInterface::on_lifecycle_event,
|
||||
"Override to react to an application lifecycle moment (LifecycleEvent) and its\n"
|
||||
"LifecycleEventContext payload. Available on every capability type.")
|
||||
.def("has_config_ui", &PluginCapabilityInterface::has_config_ui,
|
||||
"Override to return True to replace the host's default JSON editor with your own HTML\n"
|
||||
"UI, returned by get_config_ui(). Every capability is configurable and appears in the\n"
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <pybind11/embed.h>
|
||||
|
||||
#include <libslic3r/LifecycleEvents.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum class PluginCapabilityType { PrinterConnection = 0, Pages, Analysis, Importer, Exporter, Visualization, Script, SlicingPipeline, Unknown };
|
||||
@@ -167,6 +169,8 @@ public:
|
||||
virtual void on_unload() {}
|
||||
virtual void on_cancelled() {}
|
||||
|
||||
virtual void on_lifecycle_event(LifecycleEvent event, const LifecycleEventContext& ctx) { (void) event; (void) ctx; }
|
||||
|
||||
// ── C++-only host state, never exposed to Python. Set by the loader at materialization. ──
|
||||
//
|
||||
// The capability owns its own identity and enable flag: they are read once under the GIL, live
|
||||
|
||||
Reference in New Issue
Block a user