@volter/editor-blender 0.1.150 → 0.1.151
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.
- package/LICENSE +18 -9
- package/README.md +5 -5
- package/contributions/blender-animation-editors.tsx +17 -69
- package/contributions/blender-display-settings.ts +36 -0
- package/contributions/blender-export-gltf.ts +228 -0
- package/contributions/blender-export.verbs.ts +39 -0
- package/contributions/blender-gltf-export.verbs.ts +312 -0
- package/contributions/blender-header-menus.tsx +37 -133
- package/contributions/blender-icon-trace.mjs +8 -98
- package/contributions/blender-icons.source.mjs +96 -860
- package/contributions/blender-mixer-pose.ts +369 -0
- package/contributions/blender-model-opening.tsx +3 -7
- package/contributions/blender-node-editor.document.tsx +28 -180
- package/contributions/blender-node-geometry.ts +44 -230
- package/contributions/blender-node-panels.source.mjs +7 -121
- package/contributions/blender-outliner-authoring.ts +61 -415
- package/contributions/blender-outliner-model.ts +15 -112
- package/contributions/blender-play-area.tsx +100 -0
- package/contributions/blender-playback-movie.ts +409 -0
- package/contributions/blender-pose.ts +18 -368
- package/contributions/blender-properties-context.ts +1 -1
- package/contributions/blender-properties-model.ts +15 -90
- package/contributions/blender-properties-tab.tsx +3 -50
- package/contributions/blender-properties-view.tsx +15 -147
- package/contributions/blender-remedy.ts +14 -0
- package/contributions/blender-runtime-skin.ts +103 -99
- package/contributions/blender-runtime.document.tsx +217 -339
- package/contributions/blender-scene-mixer.ts +51 -0
- package/contributions/blender-screenshot.service.ts +28 -0
- package/contributions/blender-timeline-geometry.ts +16 -136
- package/contributions/blender-timeline.document.tsx +142 -332
- package/contributions/blender-uv-editor.document.tsx +48 -112
- package/contributions/blender-uv-geometry.ts +14 -179
- package/contributions/blender-version.status.tsx +43 -23
- package/contributions/blender.environment.ts +2 -2
- package/contributions/blender.icons.json +0 -9
- package/contributions/blender.keymap.ts +9 -51
- package/contributions/blender.node-panels.json +4 -13
- package/contributions/blender.page.ts +63 -0
- package/contributions/blender.status.tsx +9 -145
- package/contributions/blender.style.ts +25 -152
- package/contributions/blender.verbs.ts +52 -0
- package/contributions/model.layout.ts +2 -34
- package/contributions/models.finder.ts +12 -32
- package/contributions/properties-bone-constraints.inspector.tsx +1 -16
- package/contributions/properties-bone.inspector.tsx +2 -31
- package/contributions/properties-collection.inspector.tsx +1 -34
- package/contributions/properties-constraints.inspector.tsx +1 -21
- package/contributions/properties-data.inspector.tsx +2 -39
- package/contributions/properties-material.inspector.tsx +2 -33
- package/contributions/properties-modifiers.inspector.tsx +2 -30
- package/contributions/properties-object.inspector.tsx +2 -44
- package/contributions/properties-output.inspector.tsx +1 -27
- package/contributions/properties-particles.inspector.tsx +2 -46
- package/contributions/properties-physics.inspector.tsx +2 -41
- package/contributions/properties-render.inspector.tsx +1 -45
- package/contributions/properties-scene.inspector.tsx +1 -35
- package/contributions/properties-texture.inspector.tsx +1 -49
- package/contributions/properties-view-layer.inspector.tsx +1 -29
- package/contributions/properties-world.inspector.tsx +2 -39
- package/contributions/shading.layout.ts +3 -78
- package/contributions/uv-editing.layout.ts +3 -70
- package/dist-node/serving.mjs +13 -30
- package/host/blender-runtime-host.ts +541 -372
- package/host/scene-sound.ts +143 -0
- package/node/blender-mcp.ts +49 -93
- package/package.json +1209 -46
- package/producticons/blender-icons.otf +0 -0
- package/producticons/blender-product-icon-theme.json +129 -0
- package/serving/blender-routes.ts +9 -43
- package/serving/blender-wasm-artifact.ts +29 -166
- package/serving/index.ts +1 -1
- package/src/api.ts +9 -0
- package/src/layouts.tsx +1 -17
- package/src/looks.ts +1 -12
- package/src/model-document-saving.ts +25 -0
- package/src/model-play-extension.ts +173 -0
- package/src/node-view-state.ts +3 -25
- package/src/play-mode.ts +64 -38
- package/src/presentation.ts +6 -18
- package/src/regions.ts +3 -43
- package/src/start-document.ts +15 -0
- package/src/timeline-view-state.ts +4 -39
- package/src/uv-view-state.ts +4 -20
- package/themes/blender.json +299 -0
- package/view/blender-view-routes.ts +1 -1
- package/contributions/blender-game-panel.tsx +0 -809
- package/contributions/blender-palette.source.mjs +0 -350
- package/contributions/blender-play-skin.ts +0 -508
- package/contributions/blender-screenshot.menu.ts +0 -32
- package/contributions/blender.command.ts +0 -109
- package/contributions/blender.palette.json +0 -102
- package/src/play-live.ts +0 -27
|
@@ -1,36 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* THE UV EDITOR — Blender's UV editor over the engine's own mesh data, in a UV
|
|
3
|
-
* Editing workspace (WORK.md §Blender in the tab is Blender, "Inspection
|
|
4
|
-
* parity", I5; ARCHITECTURE-CORE §Blender north star).
|
|
5
|
-
*
|
|
6
|
-
* Every constant and colour it draws with is `./blender-uv-geometry.ts`, read
|
|
7
|
-
* from Blender's source at the engine's pin and cited there; every number it
|
|
8
|
-
* draws comes from `session.py`'s `rna_uv_layout` door. Blender's UI layer is
|
|
9
|
-
* never run, ported or recorded: this is OUR panel over RNA.
|
|
10
|
-
*
|
|
11
|
-
* ## SVG, not canvas 2D
|
|
12
|
-
*
|
|
13
|
-
* The same reason the node view gives, measured for this data: a UV layout is
|
|
14
|
-
* one `<path>` per island wash, one per island outline and one `<circle>` per
|
|
15
|
-
* drawn corner, and the browser's own renderer does the transform, the
|
|
16
|
-
* antialiasing and the hit testing. MEASURED on
|
|
17
|
-
* `examples/first-person/src/models/arena-weapons.blend`'s largest UV mesh
|
|
18
|
-
* (`LauncherMuzzleCollar`, 1,120 corners / 280 faces): 280 face paths, 280
|
|
19
|
-
* outline paths and 1,120 dots. The DOT CAP below is what keeps that honest on
|
|
20
|
-
* a mesh an order of magnitude larger, and the geometry lives in its own
|
|
21
|
-
* module precisely so a canvas can take over behind it when a measurement says
|
|
22
|
-
* so.
|
|
23
|
-
*
|
|
24
|
-
* ## It never edits, and every gesture that would is refused BY NAME
|
|
25
|
-
*
|
|
26
|
-
* Pinning, unwrapping, selecting and moving a UV all write the mesh, and
|
|
27
|
-
* editing parity is not the program. The refusals are the view's own, reached
|
|
28
|
-
* from the verb table and from the pointer handlers through one function
|
|
29
|
-
* (`refuseUvViewGesture`), so the two cannot drift into two vocabularies.
|
|
30
|
-
*/
|
|
31
1
|
|
|
2
|
+
|
|
3
|
+
import { bytesFromBase64 } from '@volter/blender-engine/browser/three/blender-base64';
|
|
32
4
|
import type { BlenderUvLayout } from '@volter/blender-engine/browser/rna';
|
|
33
|
-
import {
|
|
5
|
+
import type { ViewVerb } from '@volter/sdk/commands';
|
|
34
6
|
import { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react';
|
|
35
7
|
import {
|
|
36
8
|
blenderRnaVersion,
|
|
@@ -59,24 +31,16 @@ import {
|
|
|
59
31
|
UV_TILE_GRID,
|
|
60
32
|
UV_VERT_DOT_SIZE,
|
|
61
33
|
UV_VERT_OUTLINE_WIDTH,
|
|
62
|
-
uvBytes,
|
|
63
34
|
uvFloat32,
|
|
64
35
|
uvRgba,
|
|
65
36
|
uvUint32,
|
|
66
37
|
} from './blender-uv-geometry';
|
|
67
38
|
|
|
68
39
|
export const point = 'workspace.document';
|
|
69
|
-
/** Blender
|
|
70
|
-
* reads "UV Editor" when `SpaceImage.mode` is `UV`, which is the mode
|
|
71
|
-
* Blender's UV Editing workspace opens it in (measured on the engine: that
|
|
72
|
-
* workspace's `IMAGE_EDITOR` area answers `mode: 'UV'`). */
|
|
40
|
+
/** Blender’s SPACE_IMAGE title is UV Editor when SpaceImage.mode is UV. */
|
|
73
41
|
export const title = 'UV Editor';
|
|
74
42
|
|
|
75
|
-
/**
|
|
76
|
-
* says so. A dot is an element; 20,000 of them is a DOM the browser spends
|
|
77
|
-
* more time laying out than the read took, and a silent thinning is exactly
|
|
78
|
-
* the "named grade of acceptable partiality" the anti-shim rule forbids — so
|
|
79
|
-
* the cap is a WARNING with a number in it, never a quiet decimation. */
|
|
43
|
+
/** Warn rather than silently thin the UV corner dots beyond this DOM drawing limit. */
|
|
80
44
|
const DOT_CAP = 8000;
|
|
81
45
|
|
|
82
46
|
const ZOOM_MIN = 16;
|
|
@@ -92,60 +56,48 @@ const REFUSALS = {
|
|
|
92
56
|
'Unwrapping runs `bpy.ops.uv.unwrap`, which rewrites every UV on the mesh. Editing parity is not the program.',
|
|
93
57
|
} as const;
|
|
94
58
|
|
|
95
|
-
/**
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
title,
|
|
104
|
-
verbs: [
|
|
105
|
-
{ id: 'state', run: () => uvViewState() },
|
|
106
|
-
{
|
|
107
|
-
id: 'view-all',
|
|
108
|
-
title: 'UV Editor: View All',
|
|
109
|
-
run: () => {
|
|
110
|
-
requestUvViewAll();
|
|
111
|
-
return uvViewState();
|
|
112
|
-
},
|
|
59
|
+
/** The page half registers each UV action as a volter.blender-uv-view.<verb> command. */
|
|
60
|
+
export const UV_VIEW_VERBS: readonly ViewVerb[] = [
|
|
61
|
+
{ id: 'state', run: () => uvViewState() },
|
|
62
|
+
{
|
|
63
|
+
id: 'view-all',
|
|
64
|
+
run: () => {
|
|
65
|
+
requestUvViewAll();
|
|
66
|
+
return uvViewState();
|
|
113
67
|
},
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
},
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: 'zoom',
|
|
71
|
+
run: (args) => {
|
|
72
|
+
const to = Number(args?.['to'] ?? args?.['zoom']);
|
|
73
|
+
if (!Number.isFinite(to))
|
|
74
|
+
throw new Error('zoom needs a numeric `to` — CSS pixels per UV unit, 16 … 8192.');
|
|
75
|
+
const { transform } = uvViewState();
|
|
76
|
+
setUvViewState({
|
|
77
|
+
transform: { ...transform, zoom: Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, to)) },
|
|
78
|
+
});
|
|
79
|
+
return uvViewState();
|
|
127
80
|
},
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: 'pan',
|
|
84
|
+
run: (args) => {
|
|
85
|
+
const du = Number(args?.['u'] ?? 0);
|
|
86
|
+
const dv = Number(args?.['v'] ?? 0);
|
|
87
|
+
if (!Number.isFinite(du) || !Number.isFinite(dv))
|
|
88
|
+
throw new Error('pan needs numeric `u` and `v`, in UV units.');
|
|
89
|
+
const { transform } = uvViewState();
|
|
90
|
+
setUvViewState({
|
|
91
|
+
transform: { ...transform, cx: transform.cx + du, cy: transform.cy + dv },
|
|
92
|
+
});
|
|
93
|
+
return uvViewState();
|
|
142
94
|
},
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
95
|
+
},
|
|
96
|
+
{ id: 'move-uv', run: () => refuse(REFUSALS.move('the active UV map')) },
|
|
97
|
+
{ id: 'select', run: () => refuse(REFUSALS.select) },
|
|
98
|
+
{ id: 'pin', run: () => refuse(REFUSALS.pin) },
|
|
99
|
+
{ id: 'unwrap', run: () => refuse(REFUSALS.unwrap) },
|
|
100
|
+
];
|
|
149
101
|
|
|
150
102
|
function refuse(text: string): unknown {
|
|
151
103
|
refuseUvViewGesture(text);
|
|
@@ -170,14 +122,12 @@ function prepare(layout: BlenderUvLayout | null): Prepared | null {
|
|
|
170
122
|
// A PIN IS ONE BYTE, not a float: the door ships `array("b")` bytes
|
|
171
123
|
// straight off `MeshUVLoopLayer.pin` and answers null when nothing is
|
|
172
124
|
// pinned, so an absent payload is "no pins" rather than "unknown".
|
|
173
|
-
pins: layout.pinBase64 ?
|
|
125
|
+
pins: layout.pinBase64 ? bytesFromBase64(layout.pinBase64) : null,
|
|
174
126
|
};
|
|
175
127
|
}
|
|
176
128
|
|
|
177
129
|
export default function BlenderUvEditor() {
|
|
178
|
-
//
|
|
179
|
-
// own version, bumped by every write and by a presented frame, rather than
|
|
180
|
-
// the properties model's — which moves only when a frame ships.
|
|
130
|
+
// Use the RNA door’s version, bumped by each write and presented frame.
|
|
181
131
|
const version = useSyncExternalStore(subscribeBlenderRna, blenderRnaVersion, blenderRnaVersion);
|
|
182
132
|
useSyncExternalStore(subscribeUvView, uvViewVersion, uvViewVersion);
|
|
183
133
|
const framed = useRef(0);
|
|
@@ -377,18 +327,7 @@ export default function BlenderUvEditor() {
|
|
|
377
327
|
style={{
|
|
378
328
|
display: 'flex',
|
|
379
329
|
flexDirection: 'column',
|
|
380
|
-
//
|
|
381
|
-
// MEASURED difference between a drawer panel and an EDITOR GROUP. In
|
|
382
|
-
// the drawer this view's `height: '100%'` worked, because the drawer
|
|
383
|
-
// group imposes a height on its panel. As a `workspace.document` in
|
|
384
|
-
// `volter:area:uv` it resolved to ZERO against an auto-height content
|
|
385
|
-
// box — measured live 2026-09-19, `volter.blender-uv-view.state`
|
|
386
|
-
// answered `size: {w: 782, h: 0}` with a fully READ subject beneath
|
|
387
|
-
// it (24 corners, 6 faces, the ProbeChecker backdrop found), which is
|
|
388
|
-
// a view that has everything to draw and nowhere to draw it. The node
|
|
389
|
-
// view already had `position: absolute; inset: 0` for its own reason
|
|
390
|
-
// and was `{w: 1294, h: 467}` in the same layout, which is what named
|
|
391
|
-
// the difference.
|
|
330
|
+
// Fill the host box with absolute positioning so the editor area imposes its height.
|
|
392
331
|
position: 'absolute',
|
|
393
332
|
inset: 0,
|
|
394
333
|
overflow: 'hidden',
|
|
@@ -397,10 +336,7 @@ export default function BlenderUvEditor() {
|
|
|
397
336
|
font: '11px Inter, system-ui, sans-serif',
|
|
398
337
|
}}
|
|
399
338
|
>
|
|
400
|
-
{/*
|
|
401
|
-
other way costs: an SVG at `height: 100%` inside an auto-height parent
|
|
402
|
-
settles at ~30 px, because each side's height is a function of the
|
|
403
|
-
other's. */}
|
|
339
|
+
{/* Impose SVG height from the parent box rather than percentage sizing against auto-height content. */}
|
|
404
340
|
<div ref={canvas} style={{ flex: 1, minHeight: 0, position: 'relative' }}>
|
|
405
341
|
<svg
|
|
406
342
|
width={size.w}
|
|
@@ -1,120 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* source at the engine's pin (5.2.0, `fbe6228777e7`) — WORK.md §Blender in the
|
|
4
|
-
* tab is Blender, "Inspection parity", I5; ARCHITECTURE-CORE §Blender north
|
|
5
|
-
* star ("the reference is Blender's SOURCE as well as its frames").
|
|
6
|
-
*
|
|
7
|
-
* The drawing this describes is `MeshUVs` — the overlay class that REPLACED
|
|
8
|
-
* the 5.2-era `overlay_edit_uv.cc`, which is why that filename does not exist
|
|
9
|
-
* in the checkout — at `draw/engines/overlay/overlay_mesh.hh:483-760`, plus
|
|
10
|
-
* the four shaders it binds
|
|
11
|
-
* (`shaders/overlay_edit_uv_{edges_frag,verts_vert,verts_frag,faces_vert,face_dots_vert}.glsl`),
|
|
12
|
-
* `space_image.cc`'s `image_create` for the space's own defaults and
|
|
13
|
-
* `userdef_default_theme.c`'s `.space_image` for the colours.
|
|
14
|
-
*
|
|
15
|
-
* ## What is NOT mode-gated, and why every flag below reads "unselected"
|
|
16
|
-
*
|
|
17
|
-
* Blender draws UVs only when `show_uv_edit_ = space_mode_is_uv &&
|
|
18
|
-
* object_mode_is_edit` (`overlay_mesh.hh:593`). Both halves belong to its UI
|
|
19
|
-
* layer, which this editor never runs (orchestrator ruling 1, 2026-09-19:
|
|
20
|
-
* "inspection is not mode-gated"). So the view reads the DATA in whatever mode
|
|
21
|
-
* the engine is in and names the mode in its status line.
|
|
22
|
-
*
|
|
23
|
-
* The same ruling's second half decides the selection colours: "the view reads
|
|
24
|
-
* the flags the data carries and draws everything unselected when the flags
|
|
25
|
-
* are absent". MEASURED at this pin: `MeshUVLoopLayer`
|
|
26
|
-
* (`makesrna/intern/rna_mesh.cc:2380-2456`) declares `uv`, `pin`, `name`,
|
|
27
|
-
* `active`, `active_render` and `active_clone` — and NO vertex or edge
|
|
28
|
-
* selection at all, because UV selection lives in the BMesh an edit-mode
|
|
29
|
-
* session holds. `pin` IS declared, so pins are drawn; everything else takes
|
|
30
|
-
* its unselected branch, and the SELECTED constants below are recorded beside
|
|
31
|
-
* them rather than dropped, because a colour that is never reached is still
|
|
32
|
-
* the specification of the one that is.
|
|
33
|
-
*/
|
|
1
|
+
import { bytesFromBase64 } from '@volter/blender-engine/browser/three/blender-base64';
|
|
2
|
+
|
|
34
3
|
|
|
35
4
|
// ------------------------------------------------------------------- colours
|
|
36
5
|
|
|
37
|
-
/**
|
|
38
|
-
* `.space_image` (`release/datafiles/userdef/userdef_default_theme.c:578-608`),
|
|
39
|
-
* whole. Alpha is carried where Blender carries it: `face` is `0x0a` and
|
|
40
|
-
* `face_select` `0x3c` out of 255, which is what makes a UV island read as a
|
|
41
|
-
* wash rather than a fill.
|
|
42
|
-
*/
|
|
6
|
+
/** Values used by the read-only UV view from Blender's space_image theme. */
|
|
43
7
|
export const UV_THEME = {
|
|
44
|
-
/** `.back` `RGBA(0x30303000)` (`:580`) — alpha ZERO, so the space paints no
|
|
45
|
-
* backdrop of its own and the region's own surface shows. The node editor's
|
|
46
|
-
* `.back` is the same shape (`:661`), and U8's ruling 2 is what gives this
|
|
47
|
-
* view a surface colour of its own instead. */
|
|
48
8
|
back: '#303030',
|
|
49
|
-
backAlpha: 0,
|
|
50
|
-
/** `.grid` `RGBA(0x303030ff)` (`:586`) — FULL alpha, unlike the node
|
|
51
|
-
* editor's. See `UV_GRID_OPEN` below for what that measures to. */
|
|
52
9
|
grid: '#303030',
|
|
53
|
-
/** `.wire_edit` (`:587`) — the unselected edge under the OUTLINE line style,
|
|
54
|
-
* halved by the shader when `use_edge_select` is on
|
|
55
|
-
* (`overlay_edit_uv_edges_frag.glsl:27`). */
|
|
56
|
-
wireEdit: '#c0c0c0',
|
|
57
|
-
/** `.vertex_select` (`:588`) — `theme.colors.vert_select`, the fill of a
|
|
58
|
-
* selected UV vertex (`overlay_edit_uv_verts_vert.glsl:20`). */
|
|
59
|
-
vertexSelect: '#ff8500',
|
|
60
|
-
/** `.edge_select` (`:589`) — mixed into every line style by
|
|
61
|
-
* `selection_fac` (`overlay_edit_uv_edges_frag.glsl:31,41,46,50`). */
|
|
62
|
-
edgeSelect: '#ff8500',
|
|
63
|
-
/** `.face` `RGBA(0xffffff0a)` (`:590`) — the unselected island wash. */
|
|
64
10
|
face: '#ffffff',
|
|
65
11
|
faceAlpha: 0x0a / 255,
|
|
66
|
-
/** `.face_select` `RGBA(0xff85003c)` (`:591`). */
|
|
67
|
-
faceSelect: '#ff8500',
|
|
68
|
-
faceSelectAlpha: 0x3c / 255,
|
|
69
|
-
/** `.editmesh_active` `RGBA(0xffffff40)` (`:596`) — the ACTIVE face, which
|
|
70
|
-
* `overlay_edit_uv_faces_vert.glsl:25` puts over the selected colour. */
|
|
71
|
-
editmeshActive: '#ffffff',
|
|
72
|
-
editmeshActiveAlpha: 0x40 / 255,
|
|
73
|
-
/** `.uv_shadow` (`:606`) — the SHADOW line style, which is what the
|
|
74
|
-
* wireframe pass draws outside UV edit mode
|
|
75
|
-
* (`edit_uv_line_style_from_space_image`, `overlay_mesh.hh:1091-1111`).
|
|
76
|
-
* **That is this view's line style**, because this view is never in UV edit
|
|
77
|
-
* mode: `sima->mode == SI_MODE_UV` is a space flag, and there is no space. */
|
|
78
12
|
uvShadow: '#707070',
|
|
79
|
-
/** `.preview_back` (`:601`). Recorded whole because the theme block is, and
|
|
80
|
-
* named as NOT USED here: it is `TH_PREVIEW_BACK`, the backdrop behind a
|
|
81
|
-
* node/texture preview, not the UV tile's. */
|
|
82
|
-
previewBack: '#727272',
|
|
83
|
-
/** `TH_VERTEX` and `TH_FACEDOT` and `TH_WIRE` are NOT DECLARED in
|
|
84
|
-
* `.space_image` — checked key by key against `:578-608`. `resources.cc`
|
|
85
|
-
* resolves each straight off the active space's `ThemeSpace`
|
|
86
|
-
* (`:407-408`, `:361-362`, and the facedot case beside them), so the C
|
|
87
|
-
* struct's zero stands and all three are BLACK — the same reading I4
|
|
88
|
-
* recorded for `TH_VERTEX_UNREFERENCED`. Both shaders force alpha to 1 over
|
|
89
|
-
* that zero (`…verts_vert.glsl:19`: `float4(color.rgb, 1.0f)`;
|
|
90
|
-
* `…face_dots_vert.glsl:18`: `float4(theme.colors.wire.rgb, 1.0f)`), so an
|
|
91
|
-
* unselected UV vertex and an unselected face dot are opaque black. */
|
|
92
13
|
vertex: '#000000',
|
|
93
14
|
faceDot: '#000000',
|
|
94
|
-
wire: '#000000',
|
|
95
15
|
} as const;
|
|
96
16
|
|
|
97
|
-
/**
|
|
17
|
+
/** space_image sizes used by the vertex, edge and face-dot drawing. */
|
|
98
18
|
export const UV_SIZES = {
|
|
99
|
-
/** `.vertex_size = 3` (`:592`). */
|
|
100
19
|
vertexSize: 3,
|
|
101
|
-
/** `.edge_width = 1` (`:593`). */
|
|
102
20
|
edgeWidth: 1,
|
|
103
|
-
/** `.outline_width = 1` (`:594`). */
|
|
104
|
-
outlineWidth: 1,
|
|
105
|
-
/** `.facedot_size = 3` (`:595`). */
|
|
106
21
|
facedotSize: 3,
|
|
107
22
|
} as const;
|
|
108
23
|
|
|
109
24
|
// ------------------------------------------------------------------ geometry
|
|
110
25
|
|
|
111
|
-
/**
|
|
112
|
-
* `UI_SCALE_FAC` is 1 here, for the reason I5's node work already measured:
|
|
113
|
-
* `U.widget_unit` is `int(roundf(18 * scale_factor)) + 2 * pixelsize` = 20 at
|
|
114
|
-
* scale 1 (`windowmanager/intern/wm_window.cc:779`), which is the value every
|
|
115
|
-
* node constant divides by — so the session's scale factor is 1 and every
|
|
116
|
-
* `* UI_SCALE_FAC` below is the identity.
|
|
117
|
-
*/
|
|
26
|
+
/** At scale 1, U.widget_unit is 20; UI_SCALE_FAC is one for these normalized geometry constants. */
|
|
118
27
|
export const UI_SCALE_FAC = 1;
|
|
119
28
|
|
|
120
29
|
/**
|
|
@@ -140,11 +49,6 @@ export const UV_VERT_OUTLINE_WIDTH = 0.75;
|
|
|
140
49
|
* (`overlay_mesh.hh:727-729`) — 3, with no `√2` and no outline. */
|
|
141
50
|
export const UV_FACEDOT_SIZE = UV_SIZES.facedotSize * UI_SCALE_FAC;
|
|
142
51
|
|
|
143
|
-
/** A DASHED EDGE'S PERIOD: `4.0f * UI_SCALE_FAC` (`overlay_mesh.hh:670`) — 4
|
|
144
|
-
* pixels, of which the shader inks the first HALF
|
|
145
|
-
* (`…edges_frag.glsl:38`: `fract(line_distance / dash_length) < 0.5`). */
|
|
146
|
-
export const UV_DASH_LENGTH = 4 * UI_SCALE_FAC;
|
|
147
|
-
|
|
148
52
|
/**
|
|
149
53
|
* THE THREE OPACITIES, and all three default to 1.0 —
|
|
150
54
|
* `image_create` (`editors/space_image/space_image.cc:116-118`) sets
|
|
@@ -164,8 +68,6 @@ export const UV_DASH_LENGTH = 4 * UI_SCALE_FAC;
|
|
|
164
68
|
* says so.
|
|
165
69
|
*/
|
|
166
70
|
export const UV_OPACITY = 1;
|
|
167
|
-
export const UV_FACE_OPACITY = 1;
|
|
168
|
-
export const UV_EDGE_OPACITY = 1;
|
|
169
71
|
|
|
170
72
|
/**
|
|
171
73
|
* THE TILE GRID. `image_create` (`space_image.cc:129-133`) sets
|
|
@@ -177,63 +79,15 @@ export const UV_EDGE_OPACITY = 1;
|
|
|
177
79
|
export const UV_TILE_GRID: readonly [number, number] = [1, 1];
|
|
178
80
|
export const UV_GRID_SUBDIV: readonly [number, number] = [10, 10];
|
|
179
81
|
|
|
180
|
-
/**
|
|
181
|
-
* THE GRID'S VISIBILITY IS THE ONE THING THE SOURCE HERE CANNOT SETTLE, and it
|
|
182
|
-
* is named rather than guessed at.
|
|
183
|
-
*
|
|
184
|
-
* `.space_image.grid` is `#303030` at FULL alpha (`:586`) — unlike the node
|
|
185
|
-
* editor's, whose alpha is zero, which is why that view draws no grid at all.
|
|
186
|
-
* But the image region is cleared by `DRW_draw_view`
|
|
187
|
-
* (`space_image.cc:758`), the overlay ENGINE's own clear, and
|
|
188
|
-
* `draw/engines/overlay`'s grid pass is not in this checkout's sparse set. So
|
|
189
|
-
* the source states the grid's colour and not the value it sits on, and
|
|
190
|
-
* `#303030` on this view's own `#303030` surface is INVISIBLE — measured, not
|
|
191
|
-
* assumed. The view draws the grid at the theme's colour anyway, because
|
|
192
|
-
* drawing a different one would be inventing a mark; the contrast is the
|
|
193
|
-
* first thing the owed sighted frame-beside-frame read settles.
|
|
194
|
-
*/
|
|
82
|
+
/** Equal grid and surface colours leave this view's tile grid without contrast. */
|
|
195
83
|
export const UV_GRID_OPEN =
|
|
196
|
-
"Blender's
|
|
197
|
-
"(userdef_default_theme.c:586) and this view's surface is #303030, so the tile " +
|
|
198
|
-
"grid draws at the theme's colour and is invisible against it. The image " +
|
|
199
|
-
"region's own clear is the overlay engine's (DRW_draw_view, space_image.cc:758), " +
|
|
200
|
-
'which is not in the source checkout — the sighted read settles the contrast.';
|
|
84
|
+
"Blender's space_image grid and this view's surface are both #303030, so the tile grid is invisible against it.";
|
|
201
85
|
|
|
202
|
-
/**
|
|
203
|
-
* THE LINE STYLE THIS VIEW DRAWS, and why it is not the default one.
|
|
204
|
-
*
|
|
205
|
-
* `edit_uv_line_style_from_space_image` (`overlay_mesh.hh:1091-1111`) returns
|
|
206
|
-
* one of five: with `sima->mode == SI_MODE_UV` it maps `dt_uv` — whose enum is
|
|
207
|
-
* `OUTLINE, DASH, BLACK, WHITE` in that order (`rna_space.cc:4010-4016`) and
|
|
208
|
-
* whose `image_create` leaves it at 0, so Blender's stock UV editor draws
|
|
209
|
-
* **OUTLINE** — and OTHERWISE it returns `SHADOW`.
|
|
210
|
-
*
|
|
211
|
-
* There is no `SpaceImage` here and therefore no `mode`, so the honest branch
|
|
212
|
-
* is the else: **SHADOW**, `theme.colors.uv_shadow` `#707070`
|
|
213
|
-
* (`…edges_frag.glsl:48-50`). The OUTLINE constants are carried beside it
|
|
214
|
-
* because that is what the frame-beside-frame read will be against once a
|
|
215
|
-
* mode-carrying surface exists.
|
|
216
|
-
*/
|
|
86
|
+
/** Without a SpaceImage edit-mode context, draw the SHADOW UV line style. */
|
|
217
87
|
export const UV_LINE_STYLES = {
|
|
218
|
-
|
|
219
|
-
/** `mix(wire_edit, edge_select, selection_fac)` with `use_edge_select`
|
|
220
|
-
* off (`…edges_frag.glsl:34`), over a black outer line (`:36`). */
|
|
221
|
-
inner: UV_THEME.wireEdit,
|
|
222
|
-
outer: '#000000',
|
|
223
|
-
},
|
|
224
|
-
dash: { inner: '#595959', outer: null, period: UV_DASH_LENGTH },
|
|
225
|
-
black: { inner: '#000000', outer: null },
|
|
226
|
-
white: { inner: '#ffffff', outer: null },
|
|
227
|
-
shadow: { inner: UV_THEME.uvShadow, outer: null },
|
|
88
|
+
shadow: { inner: UV_THEME.uvShadow },
|
|
228
89
|
} as const;
|
|
229
90
|
|
|
230
|
-
/** `0.35` in the DASH style's `float4(float3(0.35f), 1.0f)`
|
|
231
|
-
* (`…edges_frag.glsl:39`) is a LINEAR value, and every other colour in that
|
|
232
|
-
* shader comes from the theme as sRGB — so the dash's grey is `0.35`
|
|
233
|
-
* linear = **#595959** sRGB, which is what `UV_LINE_STYLES.dash.inner`
|
|
234
|
-
* carries. (`0.35^(1/2.4)` with the sRGB knee: 0.3487 → 0.6236 → 159.) */
|
|
235
|
-
export const UV_DASH_LINEAR_GREY = 0.35;
|
|
236
|
-
|
|
237
91
|
/**
|
|
238
92
|
* THE EDGE'S DRAWN WIDTH. `…edges_frag.glsl:53-54` inks out to
|
|
239
93
|
* `max(theme.sizes.edge - 0.5, 0)` and fades over the next
|
|
@@ -251,23 +105,13 @@ export const UV_PIN_COLOR = '#ff0000';
|
|
|
251
105
|
|
|
252
106
|
// -------------------------------------------------------------- the decoding
|
|
253
107
|
|
|
254
|
-
/** One base64 payload as bytes. The doors ship typed-array bytes because a UV
|
|
255
|
-
* layout is `loops` corners and JSON numbers would be four times the wire for
|
|
256
|
-
* the same values — the shape I4's weights already established. */
|
|
257
|
-
export function uvBytes(base64: string): Uint8Array {
|
|
258
|
-
const binary = atob(base64);
|
|
259
|
-
const bytes = new Uint8Array(binary.length);
|
|
260
|
-
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
261
|
-
return bytes;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
108
|
export function uvFloat32(base64: string): Float32Array {
|
|
265
|
-
const bytes =
|
|
109
|
+
const bytes = bytesFromBase64(base64);
|
|
266
110
|
return new Float32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength >> 2);
|
|
267
111
|
}
|
|
268
112
|
|
|
269
113
|
export function uvUint32(base64: string): Uint32Array {
|
|
270
|
-
const bytes =
|
|
114
|
+
const bytes = bytesFromBase64(base64);
|
|
271
115
|
return new Uint32Array(bytes.buffer, bytes.byteOffset, bytes.byteLength >> 2);
|
|
272
116
|
}
|
|
273
117
|
|
|
@@ -279,15 +123,7 @@ export function uvRgba(hex: string, alpha: number): string {
|
|
|
279
123
|
return `rgba(${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}, ${Math.round(alpha * 1000) / 1000})`;
|
|
280
124
|
}
|
|
281
125
|
|
|
282
|
-
/**
|
|
283
|
-
* `.space_image.text` `#e6e6e6` (`userdef_default_theme.c:582`) for the
|
|
284
|
-
* status line, and Blender's selection orange `#ed5700` (the palette's
|
|
285
|
-
* `viewport.selection`, itself derived from `.space_view3d.select`) for a
|
|
286
|
-
* refusal, which is the same hue I2's rail already wears for "this is the
|
|
287
|
-
* one you are looking at". They live here because a `@volter/editor-blender` view may
|
|
288
|
-
* not import the editor's theme (the package's host-import pin is ZERO) and
|
|
289
|
-
* because the style-token ratchet counts LITERALS in a `.tsx`, which is
|
|
290
|
-
* exactly the right pressure: a colour with a citation is a constant. */
|
|
126
|
+
/** UV chrome constants name the source theme text and selection colours. The view imports no host theme module. */
|
|
291
127
|
export const UV_CHROME = {
|
|
292
128
|
text: '#e6e6e6',
|
|
293
129
|
refusal: '#ed5700',
|
|
@@ -297,9 +133,8 @@ export const UV_CHROME = {
|
|
|
297
133
|
rule: '#232323',
|
|
298
134
|
/** The status line's own box. Blender's editor footers are one text row in
|
|
299
135
|
* a `widget_unit`-tall strip; at `U.widget_unit` 20 (`wm_window.cc:779`)
|
|
300
|
-
* and 11-px text
|
|
301
|
-
*
|
|
302
|
-
* already use. Named here for the reason the colours are. */
|
|
136
|
+
* and 11-px text, the strip uses 3 px of vertical padding and a 12-px gap
|
|
137
|
+
* between status items. */
|
|
303
138
|
statusPadding: '3px 8px',
|
|
304
139
|
statusGap: 12,
|
|
305
140
|
} as const;
|
|
@@ -16,12 +16,18 @@
|
|
|
16
16
|
* It is read through the SDK's host door and NOT through `@editor/*`, and
|
|
17
17
|
* its two inline styles spell the theme tokens directly
|
|
18
18
|
* (`var(--volter-space-2)`, `var(--volter-font-md)`) rather than importing the
|
|
19
|
-
* host's `fontSizeVar`/`spaceVar` handles: `@volter/editor-blender`
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
19
|
+
* host's `fontSizeVar`/`spaceVar` handles: `@volter/editor-blender` imports
|
|
20
|
+
* nothing from the host, and a token NAME is the published contract while the
|
|
21
|
+
* handle object is the host's own sugar. The inline `var()` values read the
|
|
22
|
+
* same published theme tokens as `@volter/sdk/widgets`.
|
|
23
|
+
*
|
|
24
|
+
* After it, where the editor runs from a checkout, `build <commit>` (`*`
|
|
25
|
+
* with uncommitted changes), the session's own answer of which code it serves
|
|
26
|
+
* (`@volter/sdk/serving-engine`), apart from the project's pin: one version is
|
|
27
|
+
* many builds of a checkout, and the commit tells them apart. Its tooltip names
|
|
28
|
+
* the branch and how far it is behind the last-fetched `origin/main`, as when
|
|
29
|
+
* the editor started. An installed release has no checkout, and shows the
|
|
30
|
+
* version alone.
|
|
25
31
|
*
|
|
26
32
|
* Nothing before a project is open (there is no version to tell the truth
|
|
27
33
|
* about yet), per the registry contract's "an item that is currently
|
|
@@ -34,7 +40,8 @@
|
|
|
34
40
|
*/
|
|
35
41
|
|
|
36
42
|
import { editorHost } from '@volter/sdk/host';
|
|
37
|
-
import {
|
|
43
|
+
import { servingEngine, type ServingEngine } from '@volter/sdk/serving-engine';
|
|
44
|
+
import { useEffect, useState, useSyncExternalStore } from 'react';
|
|
38
45
|
|
|
39
46
|
export const point = 'workspace.status';
|
|
40
47
|
export const title = 'Version';
|
|
@@ -50,35 +57,39 @@ export default function BlenderVersionStatus() {
|
|
|
50
57
|
project.engineVersion,
|
|
51
58
|
project.engineVersion,
|
|
52
59
|
);
|
|
60
|
+
// (the build beside the pin: from a checkout, its commit, so two builds of one version are told apart)
|
|
61
|
+
const [engine, setEngine] = useState<ServingEngine | null>(null);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
let live = true;
|
|
64
|
+
void servingEngine().then((answer) => {
|
|
65
|
+
if (live) setEngine(answer);
|
|
66
|
+
});
|
|
67
|
+
return () => {
|
|
68
|
+
live = false;
|
|
69
|
+
};
|
|
70
|
+
}, []);
|
|
53
71
|
if (version === null) return null;
|
|
72
|
+
const commit = engine?.commit ?? null;
|
|
73
|
+
const build = commit === null ? null : `build ${commit}${engine?.dirty === true ? '*' : ''}`;
|
|
74
|
+
const from = commit === null ? '' : `\nThis editor runs from a checkout, as it was when the editor started: ` +
|
|
75
|
+
`${engine?.branch ?? 'a detached HEAD'} at ${commit}${engine?.dirty === true ? ', with uncommitted changes' : ''}` +
|
|
76
|
+
`${typeof engine?.behindOriginMain === 'number' ? `, ${String(engine.behindOriginMain)} commit(s) behind the last-fetched origin/main` : ''}`;
|
|
54
77
|
return (
|
|
55
78
|
<span
|
|
56
79
|
className="volter-status-copy"
|
|
57
80
|
data-testid="status-blender-version"
|
|
58
81
|
data-engine-version={version}
|
|
59
|
-
//
|
|
60
|
-
// `5.2.0` is `#878787` (135, a flat plateau — 53 px at 134 and 33 at 135
|
|
61
|
-
// across the digits) with a 9.0 CSS cap height (18 device). 135 is the
|
|
62
|
-
// DIMMEST text in Blender's window and the same ink its mouse hints take
|
|
63
|
-
// at the other end of the band, so the band is one voice and this
|
|
64
|
-
// inherits it rather than pinning `content.muted` (194) and becoming the
|
|
65
|
-
// loudest thing in the quietest strip. The cap puts the size on the
|
|
66
|
-
// `md`/`base` rung (11px -> 8.8), not `sm` (10px -> 8.0).
|
|
82
|
+
// Inherit the status band’s colour and base font size.
|
|
67
83
|
style={{
|
|
68
84
|
display: 'inline-flex',
|
|
69
85
|
alignItems: 'center',
|
|
70
86
|
gap: 'var(--volter-space-2)',
|
|
71
87
|
fontSize: 'var(--volter-font-md)',
|
|
72
88
|
}}
|
|
73
|
-
|
|
89
|
+
data-engine-commit={commit ?? undefined}
|
|
90
|
+
title={`This project is built against engine ${version}${from}`}
|
|
74
91
|
>
|
|
75
|
-
{/*
|
|
76
|
-
on `modeling.png` at device x 3369..3371, y 2069..2097: 1.5 CSS wide,
|
|
77
|
-
14.5 CSS tall, and inked 134 — the TEXT's own colour, not a boundary.
|
|
78
|
-
It was drawn here at `boundary.strong` (#3c3c3c, 60), which against a
|
|
79
|
-
#171717 band is all but invisible; `currentColor` is both the measured
|
|
80
|
-
colour and the one that cannot drift from the number beside it. The
|
|
81
|
-
height rung closest to 14.5 is `icon-md` (14). */}
|
|
92
|
+
{/* The thin divider inherits the number’s ink and uses the icon-md height. */}
|
|
82
93
|
<span
|
|
83
94
|
aria-hidden="true"
|
|
84
95
|
style={{
|
|
@@ -88,6 +99,15 @@ export default function BlenderVersionStatus() {
|
|
|
88
99
|
}}
|
|
89
100
|
/>
|
|
90
101
|
{version}
|
|
102
|
+
{build !== null && (
|
|
103
|
+
<>
|
|
104
|
+
<span
|
|
105
|
+
aria-hidden="true"
|
|
106
|
+
style={{ width: 'var(--volter-stroke-resting)', height: 'var(--volter-icon-md)', background: 'currentColor' }}
|
|
107
|
+
/>
|
|
108
|
+
{build}
|
|
109
|
+
</>
|
|
110
|
+
)}
|
|
91
111
|
</span>
|
|
92
112
|
);
|
|
93
113
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* BLENDER'S WORLD STUDIO LIGHTS: the eight HDRIs Blender ships for Material Preview
|
|
3
3
|
* (`datafiles/studiolights/world`, copied from Blender 5.2), registered as environment images
|
|
4
|
-
* (`@volter/sdk/
|
|
4
|
+
* (`@volter/sdk/environment-images`) so a view can light by one and draw it behind
|
|
5
5
|
* the scene. Material Preview opens on Forest. All CC0, by Greg Zaal (Poly Haven); the terms
|
|
6
6
|
* and each original are in `studiolights/world/license.txt`.
|
|
7
7
|
*/
|
|
8
|
-
import type { EnvironmentImageSet } from '@volter/sdk/
|
|
8
|
+
import type { EnvironmentImageSet } from '@volter/sdk/environment-images';
|
|
9
9
|
import city from './studiolights/world/city.exr?url';
|
|
10
10
|
import courtyard from './studiolights/world/courtyard.exr?url';
|
|
11
11
|
import forest from './studiolights/world/forest.exr?url';
|
|
@@ -970,9 +970,6 @@
|
|
|
970
970
|
"object-group": {
|
|
971
971
|
"path": "M2.45 5.59L13.55 5.59A1.43 1.43 0 0 0 13.55 2.72L2.45 2.72A1.43 1.43 0 0 0 2.45 5.59ZM12.12 4.16L12.12 11.84A1.43 1.43 0 0 0 14.99 11.84L14.99 4.16A1.43 1.43 0 0 0 12.12 4.16ZM13.55 10.41L2.45 10.41A1.43 1.43 0 0 0 2.45 13.28L13.55 13.28A1.43 1.43 0 0 0 13.55 10.41ZM3.88 11.84L3.88 4.16A1.43 1.43 0 0 0 1.01 4.16L1.01 11.84A1.43 1.43 0 0 0 3.88 11.84ZM-0.66 0.68A0.37 0.37 0 0 0 -1.03 1.05L-1.03 4.1A0.37 0.37 0 0 0 -0.66 4.46L2.39 4.46A0.37 0.37 0 0 0 2.75 4.1L2.75 1.05A0.37 0.37 0 0 0 2.39 0.68ZM13.61 0.68A0.37 0.37 0 0 0 13.25 1.05L13.25 4.1A0.37 0.37 0 0 0 13.61 4.46L16.66 4.46A0.37 0.37 0 0 0 17.03 4.1L17.03 1.05A0.37 0.37 0 0 0 16.66 0.68ZM-0.66 11.54A0.37 0.37 0 0 0 -1.03 11.9L-1.03 14.95A0.37 0.37 0 0 0 -0.66 15.32L2.39 15.32A0.37 0.37 0 0 0 2.75 14.95L2.75 11.9A0.37 0.37 0 0 0 2.39 11.54ZM13.61 11.54A0.37 0.37 0 0 0 13.25 11.9L13.25 14.95A0.37 0.37 0 0 0 13.61 15.32L16.66 15.32A0.37 0.37 0 0 0 17.03 14.95L17.03 11.9A0.37 0.37 0 0 0 16.66 11.54Z"
|
|
972
972
|
},
|
|
973
|
-
"outliner": {
|
|
974
|
-
"path": "M-0.23 3.29L5.65 3.29L5.65 -0.23L-0.23 -0.23ZM2.12 15.06L3.29 15.06L3.29 4.47L2.12 4.47ZM2.12 9.17L6.83 9.17L6.83 8L2.12 8ZM2.12 15.05L6.83 15.05L6.83 13.88L2.12 13.88ZM8 10.35L16.24 10.35L16.24 6.83L8 6.83ZM8 16.24L16.24 16.24L16.24 12.71L8 12.71Z"
|
|
975
|
-
},
|
|
976
973
|
"outliner-camera": {
|
|
977
974
|
"path": "M1.53 4.58A1.1 1.1 0 0 0 0.44 5.68L0.44 11.54A1.1 1.1 0 0 0 1.53 12.64L9.34 12.64A1.1 1.1 0 0 0 10.44 11.54L10.44 5.68A1.1 1.1 0 0 0 9.34 4.58ZM15.56 12.39L15.56 4.83L10.93 8.61ZM1.47 2.14A1.53 1.53 0 1 0 4.52 2.14A1.53 1.53 0 1 0 1.47 2.14ZM5.62 2.14A1.53 1.53 0 1 0 8.67 2.14A1.53 1.53 0 1 0 5.62 2.14Z",
|
|
978
975
|
"tone": "object"
|
|
@@ -1030,9 +1027,6 @@
|
|
|
1030
1027
|
"plus": {
|
|
1031
1028
|
"path": "M7.11 1.9L7.11 14.1A0.89 0.89 0 0 0 8.89 14.1L8.89 1.9A0.89 0.89 0 0 0 7.11 1.9ZM1.9 8.89L14.1 8.89A0.89 0.89 0 0 0 14.1 7.11L1.9 7.11A0.89 0.89 0 0 0 1.9 8.89Z"
|
|
1032
1029
|
},
|
|
1033
|
-
"properties": {
|
|
1034
|
-
"path": "M2.71 -0.23A2.94 2.94 0 0 0 -0.23 2.71L-0.23 3.89A2.94 2.94 0 0 0 2.71 6.83L13.29 6.83A2.94 2.94 0 0 0 16.24 3.89L16.24 2.71A2.94 2.94 0 0 0 13.29 -0.23ZM6.83 0.94L12.71 0.94L13.32 1.02L13.89 1.25L14.37 1.63L14.75 2.11L14.98 2.68L15.06 3.29L14.98 3.9L14.75 4.47L14.37 4.96L13.89 5.33L13.32 5.57L12.71 5.65L6.83 5.65ZM2.71 9.17A2.94 2.94 0 0 0 -0.23 12.11L-0.23 13.29A2.94 2.94 0 0 0 2.71 16.24L13.29 16.24A2.94 2.94 0 0 0 16.24 13.29L16.24 12.11A2.94 2.94 0 0 0 13.29 9.17ZM10.35 10.34L12.71 10.34L13.32 10.42L13.89 10.66L14.37 11.03L14.75 11.52L14.98 12.09L15.06 12.7L14.98 13.31L14.75 13.87L14.37 14.36L13.89 14.74L13.32 14.97L12.71 15.05L10.35 15.05Z"
|
|
1035
|
-
},
|
|
1036
1030
|
"properties-bone": {
|
|
1037
1031
|
"path": "M5.5 1C4.68 1 4 1.68 4 2.5L4 3.5L4 4L3.5 4L2.5 4C1.68 4 1 4.68 1 5.5C1 6.32 1.68 7 2.5 7L3.5 7C3.93 7 4.27 6.79 4.54 6.5L9.5 11.46C9.21 11.73 9 12.07 9 12.5L9 13.5C9 14.32 9.67 15 10.5 15C11.32 15 12 14.32 12 13.5L12 12.5L12 12L12.5 12L13.5 12C14.32 12 15 11.32 15 10.5C15 9.68 14.32 9 13.5 9L12.5 9C12.07 9 11.73 9.21 11.46 9.5L6.5 4.54C6.79 4.27 7 3.93 7 3.5L7 2.5C7 1.68 6.33 1 5.5 1Z",
|
|
1038
1032
|
"tone": "data"
|
|
@@ -1227,9 +1221,6 @@
|
|
|
1227
1221
|
"up-right-and-down-left-from-center": {
|
|
1228
1222
|
"path": "M2.75 14.46L14.46 2.75A0.85 0.85 0 0 0 13.25 1.54L1.54 13.25A0.85 0.85 0 0 0 2.75 14.46ZM9.22 2.27L14.59 2.27A0.85 0.85 0 0 0 14.59 0.56L9.22 0.56A0.85 0.85 0 0 0 9.22 2.27ZM13.73 1.41L13.73 6.78A0.85 0.85 0 0 0 15.44 6.78L15.44 1.41A0.85 0.85 0 0 0 13.73 1.41ZM6.78 13.73L1.41 13.73A0.85 0.85 0 0 0 1.41 15.44L6.78 15.44A0.85 0.85 0 0 0 6.78 13.73ZM2.27 14.59L2.27 9.22A0.85 0.85 0 0 0 0.56 9.22L0.56 14.59A0.85 0.85 0 0 0 2.27 14.59Z"
|
|
1229
1223
|
},
|
|
1230
|
-
"users": {
|
|
1231
|
-
"path": "M1.78 4.34A3.05 3.05 0 1 0 7.88 4.34A3.05 3.05 0 1 0 1.78 4.34ZM0.31 13.61L0.37 12.91L0.53 12.22L0.81 11.56L1.18 10.96L1.64 10.42L2.17 9.96L2.78 9.59L3.43 9.32L4.12 9.15L4.83 9.1L5.53 9.15L6.22 9.32L6.88 9.59L7.48 9.96L8.02 10.42L8.48 10.96L8.85 11.56L9.12 12.22L9.29 12.91L9.34 13.61L11.05 13.61L10.97 12.64L10.75 11.69L10.37 10.79L9.86 9.95L9.23 9.21L8.49 8.58L7.65 8.07L6.75 7.69L5.8 7.47L4.83 7.39L3.85 7.47L2.91 7.69L2 8.07L1.17 8.58L0.43 9.21L-0.21 9.95L-0.72 10.79L-1.09 11.69L-1.32 12.64L-1.39 13.61ZM-1.39 13.61A0.85 0.85 0 1 0 0.31 13.61A0.85 0.85 0 1 0 -1.39 13.61ZM9.34 13.61A0.85 0.85 0 1 0 11.05 13.61A0.85 0.85 0 1 0 9.34 13.61ZM11.54 3.16L11.8 3.09L12.07 3.05L12.35 3.06L12.61 3.11L12.87 3.21L13.11 3.33L13.33 3.5L13.52 3.7L13.67 3.92L13.79 4.16L13.88 4.42L13.92 4.69L13.92 4.96L13.88 5.23L13.79 5.49L13.67 5.74L13.52 5.96L13.33 6.16L13.11 6.32L12.87 6.45L12.61 6.54L12.35 6.59L12.07 6.6L11.8 6.57L11.54 6.5L11 7.97L11.5 8.11L12.01 8.17L12.52 8.15L13.03 8.06L13.51 7.89L13.96 7.64L14.37 7.33L14.73 6.96L15.02 6.54L15.25 6.08L15.41 5.59L15.49 5.08L15.49 4.57L15.41 4.06L15.25 3.57L15.02 3.11L14.73 2.69L14.37 2.32L13.96 2.01L13.51 1.77L13.03 1.6L12.52 1.5L12.01 1.48L11.5 1.54L11 1.68ZM10.49 2.42A0.79 0.79 0 1 0 12.06 2.42A0.79 0.79 0 1 0 10.49 2.42ZM10.49 7.24A0.79 0.79 0 1 0 12.06 7.24A0.79 0.79 0 1 0 10.49 7.24ZM9.04 11.66L9.26 11.16L9.56 10.71L9.92 10.3L10.34 9.95L10.81 9.68L11.32 9.47L11.85 9.35L12.39 9.31L12.94 9.35L13.47 9.47L13.97 9.68L14.44 9.95L14.86 10.3L15.23 10.71L15.52 11.16L15.75 11.66L17.29 11.1L16.96 10.37L16.53 9.71L16 9.12L15.38 8.61L14.7 8.21L13.96 7.91L13.18 7.73L12.39 7.67L11.6 7.73L10.82 7.91L10.09 8.21L9.4 8.61L8.79 9.12L8.26 9.71L7.82 10.37L7.49 11.1ZM7.45 11.38A0.82 0.82 0 1 0 9.08 11.38A0.82 0.82 0 1 0 7.45 11.38ZM15.7 11.38A0.82 0.82 0 1 0 17.34 11.38A0.82 0.82 0 1 0 15.7 11.38Z"
|
|
1232
|
-
},
|
|
1233
1224
|
"viewport-camera": {
|
|
1234
1225
|
"path": "M4 12L4 10.5C4 10.22 4.22 10 4.5 10L10.5 10C10.78 10 11 10.22 11 10.5L11 15.5C11 15.78 10.78 16 10.5 16L5.71 16C5.57 16 5.45 15.95 5.35 15.85L4.15 14.65C4.05 14.55 4 14.43 4 14.29L4 13L3 13L3 13.5C3 13.78 2.78 14 2.5 14C2.22 14 2 13.78 2 13.5L2 11.5C2 11.22 2.22 11 2.5 11C2.78 11 3 11.22 3 11.5L3 12ZM5.91 15L5 14.09L5 11L10 11L10 15ZM13.22 13.5L15 14.59L15 12.41ZM15.15 15.85C15.24 15.94 15.36 16 15.5 16C15.78 16 16 15.78 16 15.5L16 11.5C16 11.22 15.78 11 15.5 11C15.36 11 15.24 11.06 15.15 11.15L12.04 13.04C11.87 13.12 11.75 13.3 11.75 13.5C11.75 13.7 11.87 13.88 12.04 13.96ZM6.38 4.22C6.02 4.3 5.65 4.17 5.41 3.89C4.95 3.34 4.27 3 3.5 3C2.12 3 1 4.12 1 5.5C1 6.88 2.12 8 3.5 8L10.5 8C12.43 8 14 6.43 14 4.5C14 2.57 12.43 1 10.5 1C8.91 1 7.56 2.06 7.14 3.52C7.04 3.88 6.75 4.15 6.38 4.22ZM6.61 2.24C6.42 2.56 6.28 2.89 6.18 3.25C5.94 2.97 5.67 2.73 5.36 2.54C4.82 2.2 4.18 2 3.5 2C1.57 2 0 3.57 0 5.5C0 7.43 1.57 9 3.5 9L10.5 9C12.99 9 15 6.99 15 4.5C15 2.01 12.99 0 10.5 0C8.84 0 7.38 0.9 6.61 2.24ZM10.5 5C10.78 5 11 4.78 11 4.5C11 4.22 10.78 4 10.5 4C10.22 4 10 4.22 10 4.5C10 4.78 10.22 5 10.5 5ZM10.5 6C11.33 6 12 5.33 12 4.5C12 3.67 11.33 3 10.5 3C9.67 3 9 3.67 9 4.5C9 5.33 9.67 6 10.5 6ZM4.5 5.5C4.5 6.05 4.05 6.5 3.5 6.5C2.95 6.5 2.5 6.05 2.5 5.5C2.5 4.95 2.95 4.5 3.5 4.5C4.05 4.5 4.5 4.95 4.5 5.5Z"
|
|
1235
1226
|
},
|