@volter/blender-engine 0.1.151 → 0.1.153

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -99,6 +99,9 @@ export type WorkerRequest =
99
99
  /** ONE RESTRICTION COLUMN, written (`session.py`'s `outliner_set`). A column
100
100
  * Blender draws on no row of that type is refused by name. */
101
101
  | { id: number; op: 'outliner-set'; path: string; column: string; value: boolean }
102
+ /** THE EDITOR'S SELECTION, written only where it differs from Blender's (`session.py`'s
103
+ * `selection_set`): the named objects selected, the rest deselected, `active` made active when given. */
104
+ | { id: number; op: 'selection-set'; names: readonly string[]; active: string | null }
102
105
  /** A person's shading pick, kept in the file's own 3D View (`session.py`'s `_view_shading`);
103
106
  * UI state, so it marks nothing changed and rides the next save. */
104
107
  | { id: number; op: 'view-shading'; shading: 'WIREFRAME' | 'SOLID' | 'MATERIAL' | 'RENDERED' }
@@ -488,6 +488,13 @@ export class BlenderRuntime {
488
488
  })) as BlenderOutlinerWrite;
489
489
  }
490
490
 
491
+ /** Select the named objects (the rest deselected) and make `active` active when given; `changed`
492
+ * says whether Blender's selection differed, the only case that is written and saved. */
493
+ async selectionSet(names: readonly string[], active: string | null): Promise<{ changed: boolean }> {
494
+ await this.#ready();
495
+ return (await this.#request({ op: 'selection-set', names, active })) as { changed: boolean };
496
+ }
497
+
491
498
  /** Present the engine’s current state without capture. Documents ask on mount so an opened .blend draws before its first mutation. */
492
499
  async present(): Promise<void> {
493
500
  await this.#ready();
@@ -708,6 +708,41 @@ def _view_shading(shading):
708
708
  return {"shading": None}
709
709
 
710
710
 
711
+ def selection_set(names, active):
712
+ """THE EDITOR'S SELECTION, written into the view layer only where it differs from Blender's own.
713
+
714
+ `names` are the objects to select, every other object of the view layer deselected; `active`,
715
+ when given and in the view layer, becomes the active object (a deselect leaves Blender's active
716
+ base as it is, `object_select.cc::base_deselect_all_ex`). Selection is saved in the .blend, so a
717
+ write that changes it is a change to save. What changed is read back from Blender, never
718
+ assumed from the request: Blender keeps an object it will not select (selection disabled,
719
+ disabled in viewports, hidden: `base_select`) as it was, so a click on such a row changes
720
+ nothing and is not saved or presented."""
721
+ view_layer = bpy.context.view_layer
722
+
723
+ def held():
724
+ return {o.name for o in view_layer.objects if o.select_get()}, view_layer.objects.active
725
+
726
+ before = held()
727
+ wanted = set(names)
728
+ for o in view_layer.objects:
729
+ o.select_set(o.name in wanted)
730
+ target = bpy.data.objects.get(active) if active is not None else None
731
+ # (an object outside the view layer has no base to make active; Blender would refuse the assignment)
732
+ if target is not None and view_layer.objects.get(target.name) == target:
733
+ view_layer.objects.active = target
734
+ if held() == before:
735
+ return {"changed": False}
736
+ SESSION.mark_changed()
737
+ try:
738
+ SESSION.present()
739
+ except Exception as thrown: # noqa: BLE001
740
+ # Escalate present failures because the displayed document still holds its preceding state.
741
+ _say("@@VOLTER-ERROR the present after this call failed, so the Model document is "
742
+ "showing the state before it: " + repr(thrown))
743
+ return {"changed": True}
744
+
745
+
711
746
  def _subject_line(scene, view_layer):
712
747
  """The 3D Viewport's second overlay line, as `draw_selected_name` (`view3d_draw.cc`) writes it,
713
748
  less the two parts that follow the playhead: the tab draws `(frame)` and the marker on it
@@ -5842,6 +5877,8 @@ def _dispatch(request):
5842
5877
  _say("@@VOLTER-ERROR the present after this call failed, so the Model document is "
5843
5878
  "showing the state before it: " + repr(thrown))
5844
5879
  return answer
5880
+ if op == "selection-set":
5881
+ return selection_set(request["names"], request.get("active"))
5845
5882
  if op == "view-shading":
5846
5883
  return _view_shading(request["shading"])
5847
5884
  if op == "rna-set":
package/browser/worker.ts CHANGED
@@ -837,6 +837,8 @@ async function handle(request: WorkerRequest): Promise<unknown> {
837
837
  column: request.column,
838
838
  value: request.value,
839
839
  });
840
+ case 'selection-set':
841
+ return ask({ op: 'selection-set', names: request.names, active: request.active });
840
842
  case 'screenshot-view':
841
843
  return JSON.parse((await session.screenshotView(request.maxSize)) as string);
842
844
  case 'read-file':
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@volter/blender-engine",
3
3
  "author": "Volter AI, Inc.",
4
4
  "license": "GPL-3.0-or-later",
5
- "version": "0.1.151",
5
+ "version": "0.1.153",
6
6
  "publishConfig": {
7
7
  "access": "public"
8
8
  },