fix more visual state anomalies
This commit is contained in:
parent
8d6a2351f6
commit
c9aa37831a
9 changed files with 151 additions and 65 deletions
|
|
@ -1003,6 +1003,7 @@ export const conf = {
|
|||
widget: {},
|
||||
// legacy localStorage settings (like octo print)
|
||||
local: {
|
||||
'model.opacity': 1.0,
|
||||
'model.edges': false,
|
||||
'cam.anim.trans': true,
|
||||
'cam.anim.model': false,
|
||||
|
|
|
|||
|
|
@ -290,7 +290,7 @@ function prepareSlices(callback, scale = 1, offset = 0) {
|
|||
if (scale === 1) {
|
||||
// clear wireframe
|
||||
const scheme = getColorScheme();
|
||||
widget.setWireframe(false, scheme.wireframe.color, scheme.wireframe.opacity);
|
||||
widget._setWireframe(false, scheme.wireframe.color, scheme.wireframe.opacity);
|
||||
widget.setOpacity(camOrLaser ? scheme.views.SLICE.sliced_opacity ?? 0.2 : scheme.views.PREVIEW?.sliced_opacity ?? 0.0);
|
||||
widget.setColor(scheme.widget.deselected);
|
||||
api.hide.alert(alert);
|
||||
|
|
|
|||
|
|
@ -54,6 +54,19 @@ export async function init_sync() {
|
|||
console.log({octoprint:ohost});
|
||||
}
|
||||
|
||||
// One-time migration: Initialize model.opacity from existing ghosting state
|
||||
if (api.local.getFloat('model.opacity') === null) {
|
||||
const wireframeOpacity = api.local.getFloat('model.wireframe.opacity');
|
||||
const wireframe = api.local.getBoolean('model.wireframe', false);
|
||||
|
||||
// If ghosting was enabled (wireframe off, opacity < 1), preserve it
|
||||
if (!wireframe && wireframeOpacity !== null && wireframeOpacity < 1) {
|
||||
api.local.set('model.opacity', wireframeOpacity);
|
||||
} else {
|
||||
api.local.set('model.opacity', 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
// load workspace from url
|
||||
if (SETUP.wrk) {
|
||||
set_ctrl.import_url(`${proto}//${SETUP.wrk[0]}`, false);
|
||||
|
|
@ -262,9 +275,26 @@ function setup_keybd_nav() {
|
|||
|
||||
// rendering options
|
||||
$('render-edges').onclick = () => { api.view.set_edges({ toggle: true }); api.conf.save() };
|
||||
$('render-ghost').onclick = () => { api.view.set_wireframe(false, 0, api.view.is_arrange() ? 0.4 : 0.25); };
|
||||
$('render-wire').onclick = () => { api.view.set_wireframe(true, 0, api.space.is_dark() ? 0.25 : 0.5); };
|
||||
$('render-solid').onclick = () => { api.view.set_wireframe(false, 0, 1); };
|
||||
$('render-ghost').onclick = () => {
|
||||
const opacity = api.view.is_arrange() ? 0.4 : 0.25;
|
||||
api.view.set_wireframe(false);
|
||||
api.visuals.set_opacity(opacity);
|
||||
// Also save to old key for backwards compatibility
|
||||
api.local.set('model.wireframe.opacity', opacity);
|
||||
api.conf.save();
|
||||
};
|
||||
$('render-wire').onclick = () => {
|
||||
api.view.set_wireframe(true, 0, api.space.is_dark() ? 0.25 : 0.5);
|
||||
api.visuals.set_opacity(1.0);
|
||||
api.conf.save();
|
||||
};
|
||||
$('render-solid').onclick = () => {
|
||||
api.view.set_wireframe(false);
|
||||
api.visuals.set_opacity(1.0);
|
||||
// Also save to old key for backwards compatibility
|
||||
api.local.set('model.wireframe.opacity', 1.0);
|
||||
api.conf.save();
|
||||
};
|
||||
$('mesh-export-stl').onclick = () => { settingsOps.export_objects('stl') };
|
||||
$('mesh-export-obj').onclick = () => { settingsOps.export_objects('obj') };
|
||||
$('mesh-merge').onclick = selection.merge;
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ class KeyboardControl {
|
|||
|
||||
let api = this.#api;
|
||||
let isWireframe = api.local.getBoolean('model.wireframe');
|
||||
let isGhost = api.local.getFloat('model.wireframe.opacity') < 1;
|
||||
let isGhost = !isWireframe && (api.local.getFloat('model.opacity') ?? 1.0) < 1;
|
||||
|
||||
switch (evt.keyCode) {
|
||||
case this.#cca('`'): this.#api.show.slices(0); break;
|
||||
|
|
|
|||
|
|
@ -38,7 +38,12 @@ export const local = {
|
|||
get: (key) => localGet(key),
|
||||
getItem: (key) => localGet(key),
|
||||
getInt: (key) => parseInt(localGet(key)),
|
||||
getFloat: (key) => parseFloat(localGet(key)),
|
||||
getFloat: (key) => {
|
||||
const val = localGet(key);
|
||||
if (val === undefined || val === null) return null;
|
||||
const parsed = parseFloat(val);
|
||||
return isNaN(parsed) ? null : parsed;
|
||||
},
|
||||
getBoolean: localGetBoolean,
|
||||
toggle: (key, val, def) => {
|
||||
return localSet(key, val ?? !localGetBoolean(key, def));
|
||||
|
|
|
|||
|
|
@ -190,8 +190,8 @@ class VisualStateManager {
|
|||
*/
|
||||
setVisualState({ edges, wires, opacity }) {
|
||||
this.widget.cache.vizstate = this.getVisualState();
|
||||
this.widget.setEdges(edges ?? false);
|
||||
this.widget.setWireframe(wires ?? false);
|
||||
this.widget._setEdges(edges ?? false);
|
||||
this.widget._setWireframe(wires ?? false);
|
||||
if (opacity !== undefined) {
|
||||
this.widget.setOpacity(opacity);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,28 +54,27 @@ function setViewMode(mode) {
|
|||
api.visuals.update_speeds();
|
||||
api.visuals.set_visible_layer();
|
||||
api.visuals.set_widget_visibility(true);
|
||||
api.view.set_edges(api.local.getBoolean('model.edges'));
|
||||
api.view.set_wireframe(api.local.getBoolean('model.wireframe'));
|
||||
// Only set opacity to 1 if wireframe is disabled
|
||||
if (!api.local.getBoolean('model.wireframe')) {
|
||||
api.widgets.setOpacity(1);
|
||||
}
|
||||
// Restore global visual state (opacity, edges, wireframe)
|
||||
api.widgets.each(w => w.applyGlobalVisualState());
|
||||
break;
|
||||
case VIEWS.SLICE:
|
||||
$('act-slice').classList.add('selected');
|
||||
api.visuals.update_speeds();
|
||||
api.visuals.update_slider_max();
|
||||
api.visuals.set_widget_visibility(true);
|
||||
!isCAM && api.view.set_edges(false);
|
||||
// Hide edges without persisting (preserve user preference)
|
||||
!isCAM && api.view.set_edges(false, false);
|
||||
break;
|
||||
case VIEWS.PREVIEW:
|
||||
$('act-preview').classList.add('selected');
|
||||
api.visuals.set_widget_visibility(true);
|
||||
!isCAM && api.view.set_edges(false);
|
||||
// Hide edges without persisting (preserve user preference)
|
||||
!isCAM && api.view.set_edges(false, false);
|
||||
break;
|
||||
case VIEWS.ANIMATE:
|
||||
$('act-animate').classList.add('selected');
|
||||
!isCAM && api.view.set_edges(false);
|
||||
// Hide edges without persisting (preserve user preference)
|
||||
!isCAM && api.view.set_edges(false, false);
|
||||
break;
|
||||
default:
|
||||
console.log("invalid view mode: "+mode);
|
||||
|
|
|
|||
|
|
@ -114,6 +114,25 @@ function setVisibleLayer(h, l) {
|
|||
showSlices();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set global opacity for all widgets.
|
||||
* Updates material opacity for all widgets and persists to localStorage.
|
||||
* This is the primary opacity control - all widgets share the same opacity.
|
||||
* @param {number} value - Opacity value (0.0 to 1.0)
|
||||
*/
|
||||
function setOpacity(value) {
|
||||
api.local.set('model.opacity', value);
|
||||
// Also update old key for backwards compatibility
|
||||
api.local.set('model.wireframe.opacity', value);
|
||||
api.widgets.each(w => {
|
||||
const mat = w.getMaterial();
|
||||
mat.opacity = value;
|
||||
mat.transparent = value < 1.0;
|
||||
mat.visible = value > 0.0;
|
||||
});
|
||||
space.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set wireframe rendering mode for all widgets.
|
||||
* Updates 3D scene after applying to all widgets.
|
||||
|
|
@ -144,7 +163,7 @@ function setWireframe(bool, color, opacity) {
|
|||
const saved = api.local.getFloat('model.wireframe.opacity');
|
||||
opacity = saved !== null ? saved : (api.space.is_dark() ? 0.25 : 0.5);
|
||||
}
|
||||
api.widgets.each((w) => { w.setWireframe(bool, color, opacity) });
|
||||
api.widgets.each((w) => { w._setWireframe(bool, color, opacity) });
|
||||
space.update();
|
||||
}
|
||||
|
||||
|
|
@ -154,15 +173,18 @@ function setWireframe(bool, color, opacity) {
|
|||
* Otherwise sets edge visibility to bool value.
|
||||
* Persists state to local storage and updates scene.
|
||||
* @param {boolean|object} bool - Enable/disable edges, or {toggle: true} to toggle
|
||||
* @param {boolean} [persist=true] - Whether to save state to local storage
|
||||
*/
|
||||
function setEdges(bool) {
|
||||
function setEdges(bool, persist = true) {
|
||||
if (bool && bool.toggle) {
|
||||
api.local.toggle('model.edges');
|
||||
bool = api.local.getBoolean('model.edges');
|
||||
} else {
|
||||
api.local.set('model.edges', bool);
|
||||
if (persist) {
|
||||
api.local.set('model.edges', bool);
|
||||
}
|
||||
}
|
||||
bool = api.local.getBoolean('model.edges');
|
||||
api.widgets.each(w => w.setEdges(bool));
|
||||
api.widgets.each(w => w._setEdges(bool));
|
||||
space.update();
|
||||
}
|
||||
|
||||
|
|
@ -188,19 +210,8 @@ function updateSliderMax(set) {
|
|||
*/
|
||||
function hideSlices() {
|
||||
STACKS.clear();
|
||||
// Restore saved wireframe state instead of forcing wireframe off
|
||||
const wireframe = api.local.getBoolean('model.wireframe');
|
||||
const color = api.local.getInt('model.wireframe.color') || 0;
|
||||
const saved = api.local.getFloat('model.wireframe.opacity');
|
||||
const scheme = getColorScheme();
|
||||
const opacity = saved !== null ? saved : (scheme.views.ARRANGE?.model_opacity ?? 1.0);
|
||||
|
||||
if (wireframe) {
|
||||
api.widgets.each(widget => widget.setWireframe(wireframe, color, opacity));
|
||||
} else {
|
||||
api.widgets.setOpacity(opacity);
|
||||
api.widgets.each(widget => widget.setWireframe(false));
|
||||
}
|
||||
// Restore global visual state
|
||||
api.widgets.each(widget => widget.applyGlobalVisualState());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -284,7 +295,7 @@ function updateStackLabelState() {
|
|||
|
||||
/**
|
||||
* Apply saved visual state to a widget.
|
||||
* Restores edges and wireframe settings from local storage.
|
||||
* Delegates to widget's applyGlobalVisualState method.
|
||||
* Only applies in ARRANGE view mode.
|
||||
* @param {Widget} widget - Widget to apply visual state to
|
||||
*/
|
||||
|
|
@ -294,25 +305,7 @@ function applyVisualState(widget) {
|
|||
if (viewMode !== VIEWS.ARRANGE) {
|
||||
return;
|
||||
}
|
||||
const edges = api.local.getBoolean('model.edges');
|
||||
const wireframe = api.local.getBoolean('model.wireframe');
|
||||
|
||||
// Always apply edges setting
|
||||
if (edges) {
|
||||
widget.setEdges(edges);
|
||||
}
|
||||
|
||||
// Apply wireframe and its associated opacity
|
||||
const color = api.local.getInt('model.wireframe.color') || 0;
|
||||
const saved = api.local.getFloat('model.wireframe.opacity');
|
||||
const opacity = saved !== null ? saved : 1;
|
||||
|
||||
if (wireframe) {
|
||||
widget.setWireframe(wireframe, color, opacity);
|
||||
} else if (saved !== null) {
|
||||
// Even if wireframe is off, restore the saved opacity
|
||||
widget.setOpacity(opacity);
|
||||
}
|
||||
widget.applyGlobalVisualState();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -381,6 +374,7 @@ export const visuals = {
|
|||
hide_slices: hideSlices,
|
||||
hide_slider: hideSlider,
|
||||
set_edges: setEdges,
|
||||
set_opacity: setOpacity,
|
||||
set_progress: setProgress,
|
||||
set_visible_layer: setVisibleLayer,
|
||||
set_widget_visibility: setWidgetVisibility,
|
||||
|
|
|
|||
|
|
@ -148,9 +148,12 @@ class Widget extends WidgetCore {
|
|||
*/
|
||||
setVisualState({ edges, wires, opacity}) {
|
||||
this.cache.vizstate = this.getVisualState();
|
||||
this.setEdges(edges ?? false);
|
||||
this.setWireframe(wires ?? false);
|
||||
this.setOpacity(opacity ?? 1);
|
||||
this._setEdges(edges ?? false);
|
||||
this._setWireframe(wires ?? false);
|
||||
// Only set opacity if explicitly provided (don't default to 1)
|
||||
if (opacity !== undefined) {
|
||||
this.setOpacity(opacity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -169,6 +172,39 @@ class Widget extends WidgetCore {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply global visual state from localStorage to this widget.
|
||||
* Reads opacity, edges, and wireframe settings from global config.
|
||||
* This is the primary method for syncing widget appearance with global preferences.
|
||||
*/
|
||||
applyGlobalVisualState() {
|
||||
if (!this.api) {
|
||||
return;
|
||||
}
|
||||
|
||||
const material = this.getMaterial();
|
||||
|
||||
// Apply global opacity
|
||||
const opacity = this.api.local.getFloat('model.opacity') ?? 1.0;
|
||||
material.opacity = opacity;
|
||||
material.transparent = opacity < 1.0;
|
||||
material.visible = opacity > 0.0;
|
||||
|
||||
// Apply global edges
|
||||
const edges = this.api.local.getBoolean('model.edges', false);
|
||||
this._setEdges(edges);
|
||||
|
||||
// Apply global wireframe
|
||||
const wireframe = this.api.local.getBoolean('model.wireframe', false);
|
||||
if (wireframe) {
|
||||
const color = this.api.local.getInt('model.wireframe.color') || 0;
|
||||
const wfOpacity = this.api.local.getFloat('model.wireframe.opacity') ?? 0.5;
|
||||
this._setWireframe(true, color, wfOpacity);
|
||||
} else {
|
||||
this._setWireframe(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set material opacity
|
||||
*/
|
||||
|
|
@ -185,16 +221,23 @@ class Widget extends WidgetCore {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set edge rendering - public interface for core widget rotation/scale
|
||||
* Delegates to internal _setEdges method
|
||||
*/
|
||||
setEdges(set) {
|
||||
clearTimeout(this._setimer);
|
||||
if (set === true && this.outline) {
|
||||
this._setimer = setTimeout(() => this._setEdges(set), 10);
|
||||
} else {
|
||||
this._setEdges(set);
|
||||
}
|
||||
this._setEdges(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set edge rendering (internal method - use api.view.set_edges for global control)
|
||||
* @private
|
||||
*/
|
||||
_setEdges(set) {
|
||||
this.__setEdges(set);
|
||||
}
|
||||
|
||||
__setEdges(set) {
|
||||
if (!(this.api && this.api.conf)) {
|
||||
// missing api features in engine mode
|
||||
return;
|
||||
|
|
@ -253,15 +296,26 @@ class Widget extends WidgetCore {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set wireframe rendering
|
||||
* Set wireframe rendering - public interface for core widget rotation/scale
|
||||
* Delegates to internal _setWireframe method
|
||||
*/
|
||||
setWireframe(set, color, opacity) {
|
||||
this._setWireframe(set, color, opacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set wireframe rendering (internal method - use api.view.set_wireframe for global control)
|
||||
* @private
|
||||
*/
|
||||
_setWireframe(set, color, opacity) {
|
||||
if (!(this.api && this.api.conf)) {
|
||||
// missing api features in engine mode
|
||||
return;
|
||||
}
|
||||
let mesh = this.mesh,
|
||||
widget = this;
|
||||
// Save current opacity before removing wire
|
||||
const currentOpacity = this.getMaterial().opacity;
|
||||
if (this.wire) {
|
||||
this.setOpacity(solid_opacity);
|
||||
mesh.remove(this.wire);
|
||||
|
|
@ -287,6 +341,9 @@ class Widget extends WidgetCore {
|
|||
}
|
||||
if (opacity !== undefined) {
|
||||
widget.setOpacity(opacity);
|
||||
} else if (!set && this.wire === null) {
|
||||
// Restore original opacity when removing wireframe without explicit opacity
|
||||
widget.setOpacity(currentOpacity);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue