@operato/scene-visualizer 10.0.0-beta.7 → 10.0.0-beta.9
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/dist/editors/index.d.ts +2 -0
- package/dist/editors/index.js +10 -0
- package/dist/editors/index.js.map +1 -1
- package/dist/editors/property-editor-gltf-fill-targets.d.ts +4 -1
- package/dist/editors/property-editor-gltf-fill-targets.js +178 -74
- package/dist/editors/property-editor-gltf-fill-targets.js.map +1 -1
- package/dist/editors/property-editor-gltf-info.d.ts +19 -4
- package/dist/editors/property-editor-gltf-info.js +279 -84
- package/dist/editors/property-editor-gltf-info.js.map +1 -1
- package/dist/editors/property-editor-gltf-play-targets.d.ts +25 -0
- package/dist/editors/property-editor-gltf-play-targets.js +388 -0
- package/dist/editors/property-editor-gltf-play-targets.js.map +1 -0
- package/dist/editors/property-editor-stocker-location.d.ts +13 -0
- package/dist/editors/property-editor-stocker-location.js +151 -0
- package/dist/editors/property-editor-stocker-location.js.map +1 -0
- package/dist/editors/property-editor-stocker-ports.d.ts +8 -0
- package/dist/editors/property-editor-stocker-ports.js +112 -0
- package/dist/editors/property-editor-stocker-ports.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/stocker-3d.d.ts +23 -0
- package/dist/stocker-3d.js +352 -0
- package/dist/stocker-3d.js.map +1 -0
- package/dist/stocker-port-3d.d.ts +14 -0
- package/dist/stocker-port-3d.js +80 -0
- package/dist/stocker-port-3d.js.map +1 -0
- package/dist/stocker-port.d.ts +254 -0
- package/dist/stocker-port.js +123 -0
- package/dist/stocker-port.js.map +1 -0
- package/dist/stocker.d.ts +340 -0
- package/dist/stocker.js +370 -0
- package/dist/stocker.js.map +1 -0
- package/dist/templates/index.d.ts +40 -0
- package/dist/templates/index.js +5 -1
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/stocker-port.d.ts +17 -0
- package/dist/templates/stocker-port.js +17 -0
- package/dist/templates/stocker-port.js.map +1 -0
- package/dist/templates/stocker.d.ts +27 -0
- package/dist/templates/stocker.js +38 -0
- package/dist/templates/stocker.js.map +1 -0
- package/package.json +2 -2
- package/translations/en.json +6 -0
- package/translations/ja.json +5 -0
- package/translations/ko.json +6 -1
- package/translations/ms.json +5 -0
- package/translations/zh.json +5 -0
package/dist/editors/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import './property-editor-location-increase-pattern.js';
|
|
2
2
|
import './property-editor-gltf-info.js';
|
|
3
3
|
import './property-editor-gltf-fill-targets.js';
|
|
4
|
+
import './property-editor-gltf-play-targets.js';
|
|
5
|
+
import './property-editor-stocker-location.js';
|
|
4
6
|
declare const _default: {
|
|
5
7
|
type: string;
|
|
6
8
|
element: string;
|
package/dist/editors/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import './property-editor-location-increase-pattern.js';
|
|
2
2
|
import './property-editor-gltf-info.js';
|
|
3
3
|
import './property-editor-gltf-fill-targets.js';
|
|
4
|
+
import './property-editor-gltf-play-targets.js';
|
|
5
|
+
import './property-editor-stocker-location.js';
|
|
4
6
|
export default [
|
|
5
7
|
{
|
|
6
8
|
type: 'location-increase-pattern',
|
|
@@ -13,6 +15,14 @@ export default [
|
|
|
13
15
|
{
|
|
14
16
|
type: 'gltf-fill-targets',
|
|
15
17
|
element: 'property-editor-gltf-fill-targets'
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: 'gltf-play-targets',
|
|
21
|
+
element: 'property-editor-gltf-play-targets'
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
type: 'stocker-location',
|
|
25
|
+
element: 'property-editor-stocker-location'
|
|
16
26
|
}
|
|
17
27
|
];
|
|
18
28
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/editors/index.ts"],"names":[],"mappings":"AAAA,OAAO,gDAAgD,CAAA;AACvD,OAAO,gCAAgC,CAAA;AACvC,OAAO,wCAAwC,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/editors/index.ts"],"names":[],"mappings":"AAAA,OAAO,gDAAgD,CAAA;AACvD,OAAO,gCAAgC,CAAA;AACvC,OAAO,wCAAwC,CAAA;AAC/C,OAAO,wCAAwC,CAAA;AAC/C,OAAO,uCAAuC,CAAA;AAE9C,eAAe;IACb;QACE,IAAI,EAAE,2BAA2B;QACjC,OAAO,EAAE,2CAA2C;KACrD;IACD;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,2BAA2B;KACrC;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE,mCAAmC;KAC7C;IACD;QACE,IAAI,EAAE,mBAAmB;QACzB,OAAO,EAAE,mCAAmC;KAC7C;IACD;QACE,IAAI,EAAE,kBAAkB;QACxB,OAAO,EAAE,kCAAkC;KAC5C;CACF,CAAA","sourcesContent":["import './property-editor-location-increase-pattern.js'\nimport './property-editor-gltf-info.js'\nimport './property-editor-gltf-fill-targets.js'\nimport './property-editor-gltf-play-targets.js'\nimport './property-editor-stocker-location.js'\n\nexport default [\n {\n type: 'location-increase-pattern',\n element: 'property-editor-location-increase-pattern'\n },\n {\n type: 'gltf-info',\n element: 'property-editor-gltf-info'\n },\n {\n type: 'gltf-fill-targets',\n element: 'property-editor-gltf-fill-targets'\n },\n {\n type: 'gltf-play-targets',\n element: 'property-editor-gltf-play-targets'\n },\n {\n type: 'stocker-location',\n element: 'property-editor-stocker-location'\n }\n]\n"]}
|
|
@@ -5,12 +5,15 @@ export default class GLTFFillTargetsEditor extends OxPropertyEditor {
|
|
|
5
5
|
static styles: import("lit").CSSResult[];
|
|
6
6
|
src: string | undefined;
|
|
7
7
|
private _meshNames;
|
|
8
|
+
private _mode;
|
|
8
9
|
private _component;
|
|
10
|
+
private _lastExternalValue;
|
|
9
11
|
updated(changes: PropertyValues<this>): void;
|
|
10
12
|
private _refreshMeshNames;
|
|
11
13
|
editorTemplate(value: any, _spec: PropertySpec): TemplateResult;
|
|
12
|
-
private
|
|
14
|
+
private _setMode;
|
|
13
15
|
private _onToggle;
|
|
16
|
+
private _applyValue;
|
|
14
17
|
private _highlightNode;
|
|
15
18
|
private _clearHighlight;
|
|
16
19
|
private _getRendererManager;
|
|
@@ -2,19 +2,68 @@
|
|
|
2
2
|
* Copyright © HatioLab Inc. All rights reserved.
|
|
3
3
|
*
|
|
4
4
|
* GLTF fillStyle 타겟 노드 선택 에디터.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 마우스 오버 시 해당 노드를 OutlinePass로 강조한다.
|
|
5
|
+
* All / None / Custom 모드로 명시적 설정.
|
|
6
|
+
* Custom 모드에서 개별 Mesh 체크박스 선택.
|
|
8
7
|
*/
|
|
9
8
|
import { __decorate } from "tslib";
|
|
10
9
|
import '@operato/i18n/ox-i18n.js';
|
|
11
|
-
import { css, html } from 'lit';
|
|
10
|
+
import { css, html, nothing } from 'lit';
|
|
12
11
|
import { customElement, property, state } from 'lit/decorators.js';
|
|
13
12
|
import { OxPropertyEditor } from '@operato/property-editor';
|
|
13
|
+
import { ScrollbarStyles } from '@operato/styles/scrollbar-styles.js';
|
|
14
|
+
function getMode(value) {
|
|
15
|
+
if (value === '*')
|
|
16
|
+
return 'all';
|
|
17
|
+
if (value === 'none' || !value)
|
|
18
|
+
return 'none';
|
|
19
|
+
if (Array.isArray(value) && value.length > 0)
|
|
20
|
+
return 'custom';
|
|
21
|
+
// [] (빈 배열) = custom with nothing selected
|
|
22
|
+
if (Array.isArray(value))
|
|
23
|
+
return 'custom';
|
|
24
|
+
return 'none';
|
|
25
|
+
}
|
|
14
26
|
let GLTFFillTargetsEditor = class GLTFFillTargetsEditor extends OxPropertyEditor {
|
|
15
27
|
static styles = [
|
|
16
28
|
...OxPropertyEditor.styles,
|
|
29
|
+
ScrollbarStyles,
|
|
17
30
|
css `
|
|
31
|
+
.mode-selector {
|
|
32
|
+
display: flex;
|
|
33
|
+
gap: 0;
|
|
34
|
+
margin-bottom: 6px;
|
|
35
|
+
border: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.15));
|
|
36
|
+
border-radius: 6px;
|
|
37
|
+
overflow: hidden;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.mode-btn {
|
|
41
|
+
flex: 1;
|
|
42
|
+
padding: 5px 0;
|
|
43
|
+
font-size: 11px;
|
|
44
|
+
font-weight: 500;
|
|
45
|
+
text-align: center;
|
|
46
|
+
cursor: pointer;
|
|
47
|
+
background: var(--md-sys-color-surface-container, #f3edf7);
|
|
48
|
+
color: var(--md-sys-color-on-surface, #1c1b1f);
|
|
49
|
+
border: none;
|
|
50
|
+
border-right: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.1));
|
|
51
|
+
transition: all 0.15s;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.mode-btn:last-child {
|
|
55
|
+
border-right: none;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.mode-btn:hover {
|
|
59
|
+
background: var(--md-sys-color-surface-container-highest, #e6e0e9);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.mode-btn[active] {
|
|
63
|
+
background: var(--md-sys-color-primary, #6750a4);
|
|
64
|
+
color: var(--md-sys-color-on-primary, #fff);
|
|
65
|
+
}
|
|
66
|
+
|
|
18
67
|
.node-list {
|
|
19
68
|
display: flex;
|
|
20
69
|
flex-direction: column;
|
|
@@ -27,44 +76,57 @@ let GLTFFillTargetsEditor = class GLTFFillTargetsEditor extends OxPropertyEditor
|
|
|
27
76
|
.node-item {
|
|
28
77
|
display: flex;
|
|
29
78
|
align-items: center;
|
|
30
|
-
gap:
|
|
31
|
-
padding:
|
|
79
|
+
gap: 6px;
|
|
80
|
+
padding: 4px 8px;
|
|
32
81
|
cursor: pointer;
|
|
33
|
-
border-radius:
|
|
82
|
+
border-radius: 4px;
|
|
83
|
+
transition: background 0.1s;
|
|
34
84
|
}
|
|
35
85
|
|
|
36
86
|
.node-item:hover {
|
|
37
|
-
background: rgba(
|
|
87
|
+
background: var(--md-sys-color-primary-container, rgba(103, 80, 164, 0.12));
|
|
38
88
|
}
|
|
39
89
|
|
|
40
|
-
.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
margin-bottom: 2px;
|
|
90
|
+
.node-item input[type='checkbox'] {
|
|
91
|
+
margin: 0;
|
|
92
|
+
accent-color: var(--md-sys-color-primary, #6750a4);
|
|
44
93
|
}
|
|
45
94
|
|
|
46
|
-
.
|
|
95
|
+
.node-item span {
|
|
96
|
+
font-size: 12px;
|
|
97
|
+
color: var(--md-sys-color-on-surface, #1c1b1f);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.mode-desc {
|
|
47
101
|
font-size: 11px;
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
102
|
+
color: var(--md-sys-color-on-surface-variant, #666);
|
|
103
|
+
padding: 4px 0;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.mini-btn {
|
|
107
|
+
font-size: 10px;
|
|
108
|
+
padding: 2px 8px;
|
|
109
|
+
border: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.15));
|
|
110
|
+
border-radius: 4px;
|
|
111
|
+
background: var(--md-sys-color-surface-container, #f3edf7);
|
|
112
|
+
color: var(--md-sys-color-on-surface, #1c1b1f);
|
|
53
113
|
cursor: pointer;
|
|
54
114
|
}
|
|
55
115
|
|
|
56
|
-
.
|
|
57
|
-
background:
|
|
116
|
+
.mini-btn:hover {
|
|
117
|
+
background: var(--md-sys-color-surface-container-highest, #e6e0e9);
|
|
58
118
|
}
|
|
59
119
|
|
|
60
120
|
.empty {
|
|
61
|
-
color: var(--md-sys-color-
|
|
121
|
+
color: var(--md-sys-color-on-surface-variant, #999);
|
|
62
122
|
font-size: 11px;
|
|
63
|
-
padding:
|
|
123
|
+
padding: 8px;
|
|
124
|
+
text-align: center;
|
|
64
125
|
}
|
|
65
126
|
`
|
|
66
127
|
];
|
|
67
128
|
_component = null;
|
|
129
|
+
_lastExternalValue = undefined;
|
|
68
130
|
updated(changes) {
|
|
69
131
|
if (changes.has('src')) {
|
|
70
132
|
this.dispatchEvent(new CustomEvent('i-need-selected', {
|
|
@@ -89,77 +151,118 @@ let GLTFFillTargetsEditor = class GLTFFillTargetsEditor extends OxPropertyEditor
|
|
|
89
151
|
this._meshNames = [];
|
|
90
152
|
return;
|
|
91
153
|
}
|
|
92
|
-
// Mesh 노드만 필터링 — 그룹/본 등은 fillStyle 적용 대상이 아님
|
|
93
154
|
this._meshNames = (this._component.nodeNames ?? []).filter((name) => {
|
|
94
155
|
const node = ro.getNode(name);
|
|
95
156
|
return node?.isMesh;
|
|
96
157
|
});
|
|
97
158
|
}
|
|
98
159
|
editorTemplate(value, _spec) {
|
|
99
|
-
const targets = Array.isArray(value) ? value : [];
|
|
100
|
-
// GLTF 로드 완료 후 nodeNames가 채워지므로, 비어있으면 재시도
|
|
101
160
|
if (!this._meshNames?.length) {
|
|
102
161
|
this._refreshMeshNames();
|
|
103
162
|
}
|
|
163
|
+
// 외부 value가 변경되면 모드 동기화
|
|
164
|
+
if (value !== this._lastExternalValue) {
|
|
165
|
+
this._lastExternalValue = value;
|
|
166
|
+
this._mode = getMode(value);
|
|
167
|
+
}
|
|
168
|
+
const mode = this._mode;
|
|
169
|
+
const meshCount = this._meshNames?.length ?? 0;
|
|
170
|
+
const targets = Array.isArray(this.value) ? this.value : (Array.isArray(value) ? value : []);
|
|
104
171
|
return html `
|
|
105
172
|
<fieldset fullwidth>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
<
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
173
|
+
${meshCount > 0
|
|
174
|
+
? html `
|
|
175
|
+
<div class="mode-selector">
|
|
176
|
+
<div class="mode-btn" ?active=${mode === 'all'} @click=${() => this._setMode('all')}>All</div>
|
|
177
|
+
<div class="mode-btn" ?active=${mode === 'none'} @click=${() => this._setMode('none')}>None</div>
|
|
178
|
+
<div class="mode-btn" ?active=${mode === 'custom'} @click=${() => this._setMode('custom')}>Custom</div>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
${mode === 'all'
|
|
182
|
+
? html `<div class="mode-desc">${meshCount} meshes — fillStyle applies to all</div>`
|
|
183
|
+
: nothing}
|
|
184
|
+
${mode === 'none'
|
|
185
|
+
? html `<div class="mode-desc">fillStyle not applied to any mesh</div>`
|
|
186
|
+
: nothing}
|
|
187
|
+
${mode === 'custom'
|
|
188
|
+
? html `
|
|
189
|
+
<div style="display:flex;align-items:center;gap:6px">
|
|
190
|
+
<div class="mode-desc" style="flex:1">${targets.length} / ${meshCount} selected</div>
|
|
191
|
+
<button class="mini-btn" @click=${() => this._applyValue([...(this._meshNames ?? [])])}>All</button>
|
|
192
|
+
<button class="mini-btn" @click=${() => this._applyValue([])}>Clear</button>
|
|
193
|
+
</div>
|
|
194
|
+
<div class="node-list">
|
|
195
|
+
${(this._meshNames ?? []).map(name => html `
|
|
196
|
+
<label
|
|
197
|
+
class="node-item"
|
|
198
|
+
@mouseenter=${() => this._highlightNode(name)}
|
|
199
|
+
@mouseleave=${() => this._clearHighlight()}
|
|
200
|
+
>
|
|
201
|
+
<input
|
|
202
|
+
type="checkbox"
|
|
203
|
+
.checked=${targets.includes(name)}
|
|
204
|
+
@change=${(e) => {
|
|
205
|
+
e.stopPropagation();
|
|
206
|
+
this._onToggle(name, e.target.checked, targets);
|
|
207
|
+
}}
|
|
208
|
+
/>
|
|
209
|
+
<span>${name}</span>
|
|
210
|
+
</label>
|
|
211
|
+
`)}
|
|
212
|
+
</div>
|
|
213
|
+
`
|
|
214
|
+
: nothing}
|
|
215
|
+
`
|
|
216
|
+
: html `<div class="empty">
|
|
217
|
+
<ox-i18n msgid="label.no-gltf-meshes">No meshes available</ox-i18n>
|
|
218
|
+
</div>`}
|
|
136
219
|
</fieldset>
|
|
137
220
|
`;
|
|
138
221
|
}
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
this.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
222
|
+
_setMode(mode) {
|
|
223
|
+
const prevMode = this._mode;
|
|
224
|
+
this._mode = mode;
|
|
225
|
+
let newValue;
|
|
226
|
+
switch (mode) {
|
|
227
|
+
case 'all':
|
|
228
|
+
newValue = '*';
|
|
229
|
+
break;
|
|
230
|
+
case 'none':
|
|
231
|
+
newValue = 'none';
|
|
232
|
+
break;
|
|
233
|
+
case 'custom':
|
|
234
|
+
// 이전 상태에 따라: All → 전체 선택, None → 전체 해제
|
|
235
|
+
if (prevMode === 'all') {
|
|
236
|
+
newValue = [...(this._meshNames ?? [])];
|
|
147
237
|
}
|
|
148
|
-
|
|
149
|
-
|
|
238
|
+
else if (prevMode === 'none') {
|
|
239
|
+
newValue = [];
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
// 이미 custom이면 현재 값 유지
|
|
243
|
+
newValue = Array.isArray(this.value) ? this.value : [];
|
|
244
|
+
}
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
this._applyValue(newValue);
|
|
150
248
|
}
|
|
151
|
-
_onToggle(name, checked) {
|
|
152
|
-
const
|
|
249
|
+
_onToggle(name, checked, current) {
|
|
250
|
+
const list = [...current];
|
|
153
251
|
if (checked) {
|
|
154
|
-
if (!
|
|
155
|
-
|
|
252
|
+
if (!list.includes(name))
|
|
253
|
+
list.push(name);
|
|
156
254
|
}
|
|
157
255
|
else {
|
|
158
|
-
const idx =
|
|
256
|
+
const idx = list.indexOf(name);
|
|
159
257
|
if (idx >= 0)
|
|
160
|
-
|
|
258
|
+
list.splice(idx, 1);
|
|
161
259
|
}
|
|
162
|
-
|
|
260
|
+
this._applyValue(list);
|
|
261
|
+
}
|
|
262
|
+
_applyValue(newValue) {
|
|
263
|
+
// 로컬 value 즉시 반영 (re-render)
|
|
264
|
+
this.value = newValue;
|
|
265
|
+
this.requestUpdate();
|
|
163
266
|
this.dispatchEvent(new CustomEvent('i-need-selected', {
|
|
164
267
|
bubbles: true,
|
|
165
268
|
composed: true,
|
|
@@ -185,14 +288,12 @@ let GLTFFillTargetsEditor = class GLTFFillTargetsEditor extends OxPropertyEditor
|
|
|
185
288
|
const ro = this._component?.realObject;
|
|
186
289
|
if (!ro)
|
|
187
290
|
return;
|
|
188
|
-
// 선택 상태의 아웃라인 복원 (전체 GLTF 오브젝트)
|
|
189
291
|
this._getRendererManager()?.setOutlineObjects(ro.object3d ? [ro.object3d] : []);
|
|
190
292
|
this._component?.invalidate();
|
|
191
293
|
}
|
|
192
294
|
_getRendererManager() {
|
|
193
295
|
const ro = this._component?.realObject;
|
|
194
296
|
const container = ro?.threeContainer;
|
|
195
|
-
// ThreeContainer._capability 또는 ModelLayer._threeCapability
|
|
196
297
|
return container?._capability?.rendererManager ?? container?._threeCapability?.rendererManager ?? null;
|
|
197
298
|
}
|
|
198
299
|
};
|
|
@@ -202,6 +303,9 @@ __decorate([
|
|
|
202
303
|
__decorate([
|
|
203
304
|
state()
|
|
204
305
|
], GLTFFillTargetsEditor.prototype, "_meshNames", void 0);
|
|
306
|
+
__decorate([
|
|
307
|
+
state()
|
|
308
|
+
], GLTFFillTargetsEditor.prototype, "_mode", void 0);
|
|
205
309
|
GLTFFillTargetsEditor = __decorate([
|
|
206
310
|
customElement('property-editor-gltf-fill-targets')
|
|
207
311
|
], GLTFFillTargetsEditor);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"property-editor-gltf-fill-targets.js","sourceRoot":"","sources":["../../src/editors/property-editor-gltf-fill-targets.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * GLTF fillStyle 타겟 노드 선택 에디터.\n * Specific 탭에서 GLTF 모델의 Mesh 노드 목록을 체크박스로 표시하고,\n * 선택된 노드 이름을 fillStyleTargets 배열로 저장한다.\n * 마우스 오버 시 해당 노드를 OutlinePass로 강조한다.\n */\n\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, PropertyValues, TemplateResult } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { OxPropertyEditor, PropertySpec } from '@operato/property-editor'\n\n@customElement('property-editor-gltf-fill-targets')\nexport default class GLTFFillTargetsEditor extends OxPropertyEditor {\n static styles = [\n ...OxPropertyEditor.styles,\n css`\n .node-list {\n display: flex;\n flex-direction: column;\n gap: 1px;\n max-height: 200px;\n overflow-y: auto;\n font-size: 12px;\n }\n\n .node-item {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 2px 4px;\n cursor: pointer;\n border-radius: 2px;\n }\n\n .node-item:hover {\n background: rgba(242, 101, 34, 0.15);\n }\n\n .toolbar {\n display: flex;\n gap: 2px;\n margin-bottom: 2px;\n }\n\n .toolbar button {\n font-size: 11px;\n padding: 1px 6px;\n border: 1px solid var(--md-sys-color-outline, #ccc);\n border-radius: 2px;\n background: transparent;\n color: var(--md-sys-color-on-surface, #333);\n cursor: pointer;\n }\n\n .toolbar button:hover {\n background: rgba(0, 0, 0, 0.06);\n }\n\n .empty {\n color: var(--md-sys-color-outline, #999);\n font-size: 11px;\n padding: 4px;\n }\n `\n ]\n\n @property({ type: String }) declare src: string | undefined\n\n @state() declare private _meshNames: string[]\n\n private _component: any = null\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('src')) {\n this.dispatchEvent(\n new CustomEvent('i-need-selected', {\n bubbles: true,\n composed: true,\n detail: {\n callback: (selected: any[]) => {\n this._component = selected?.[0]\n this._refreshMeshNames()\n }\n }\n })\n )\n }\n }\n\n private _refreshMeshNames() {\n if (!this._component) {\n this._meshNames = []\n return\n }\n\n const ro = this._component.realObject as any\n if (!ro?.getNode) {\n this._meshNames = []\n return\n }\n\n // Mesh 노드만 필터링 — 그룹/본 등은 fillStyle 적용 대상이 아님\n this._meshNames = ((this._component.nodeNames as string[]) ?? []).filter((name: string) => {\n const node = ro.getNode(name)\n return node?.isMesh\n })\n }\n\n editorTemplate(value: any, _spec: PropertySpec): TemplateResult {\n const targets: string[] = Array.isArray(value) ? value : []\n\n // GLTF 로드 완료 후 nodeNames가 채워지므로, 비어있으면 재시도\n if (!this._meshNames?.length) {\n this._refreshMeshNames()\n }\n\n return html`\n <fieldset fullwidth>\n <legend><ox-i18n msgid=\"label.fill-targets\">fillStyle Targets</ox-i18n></legend>\n ${(this._meshNames?.length ?? 0) > 0\n ? html`<div class=\"toolbar\">\n <button type=\"button\" @click=${() => this._setAll(true)}>All</button>\n <button type=\"button\" @click=${() => this._setAll(false)}>None</button>\n </div>`\n : ''}\n <div class=\"node-list\">\n ${(this._meshNames?.length ?? 0) === 0\n ? html`<div class=\"empty\">\n <ox-i18n msgid=\"label.no-gltf-meshes\">No meshes available</ox-i18n>\n </div>`\n : (this._meshNames ?? []).map(\n name => html`\n <label\n class=\"node-item\"\n @mouseenter=${() => this._highlightNode(name)}\n @mouseleave=${() => this._clearHighlight()}\n >\n <input\n type=\"checkbox\"\n .checked=${targets.includes(name)}\n @change=${(e: Event) => {\n e.stopPropagation()\n this._onToggle(name, (e.target as HTMLInputElement).checked)\n }}\n />\n <span>${name}</span>\n </label>\n `\n )}\n </div>\n </fieldset>\n `\n }\n\n private _setAll(select: boolean) {\n const newValue = select ? [...(this._meshNames ?? [])] : undefined\n\n this.dispatchEvent(\n new CustomEvent('i-need-selected', {\n bubbles: true,\n composed: true,\n detail: {\n callback: (selected: any[]) => {\n selected[0].set('fillStyleTargets', newValue)\n }\n }\n })\n )\n }\n\n private _onToggle(name: string, checked: boolean) {\n const current = [...(Array.isArray(this.value) ? this.value : [])]\n if (checked) {\n if (!current.includes(name)) current.push(name)\n } else {\n const idx = current.indexOf(name)\n if (idx >= 0) current.splice(idx, 1)\n }\n\n const newValue = current.length > 0 ? current : undefined\n\n this.dispatchEvent(\n new CustomEvent('i-need-selected', {\n bubbles: true,\n composed: true,\n detail: {\n callback: (selected: any[]) => {\n selected[0].set('fillStyleTargets', newValue)\n }\n }\n })\n )\n }\n\n // --- Outline 강조 ---\n\n private _highlightNode(name: string) {\n const ro = this._component?.realObject as any\n if (!ro?.getNode) return\n\n const node = ro.getNode(name)\n if (!node) return\n\n this._getRendererManager()?.setOutlineObjects([node])\n this._component?.invalidate()\n }\n\n private _clearHighlight() {\n const ro = this._component?.realObject as any\n if (!ro) return\n\n // 선택 상태의 아웃라인 복원 (전체 GLTF 오브젝트)\n this._getRendererManager()?.setOutlineObjects(ro.object3d ? [ro.object3d] : [])\n this._component?.invalidate()\n }\n\n private _getRendererManager(): any {\n const ro = this._component?.realObject as any\n const container = ro?.threeContainer as any\n // ThreeContainer._capability 또는 ModelLayer._threeCapability\n return container?._capability?.rendererManager ?? container?._threeCapability?.rendererManager ?? null\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"property-editor-gltf-fill-targets.js","sourceRoot":"","sources":["../../src/editors/property-editor-gltf-fill-targets.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * GLTF fillStyle 타겟 노드 선택 에디터.\n * All / None / Custom 모드로 명시적 설정.\n * Custom 모드에서 개별 Mesh 체크박스 선택.\n */\n\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, PropertyValues, TemplateResult, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { OxPropertyEditor, PropertySpec } from '@operato/property-editor'\nimport { ScrollbarStyles } from '@operato/styles/scrollbar-styles.js'\n\n// fillStyleTargets 값 규칙:\n// undefined 또는 미설정 → None (적용 안 함, 기본값)\n// '*' → All (전체 메시 적용)\n// ['mesh_0', ...] → Custom (선택 메시만 적용)\n\ntype FillMode = 'all' | 'none' | 'custom'\n\nfunction getMode(value: any): FillMode {\n if (value === '*') return 'all'\n if (value === 'none' || !value) return 'none'\n if (Array.isArray(value) && value.length > 0) return 'custom'\n // [] (빈 배열) = custom with nothing selected\n if (Array.isArray(value)) return 'custom'\n return 'none'\n}\n\n@customElement('property-editor-gltf-fill-targets')\nexport default class GLTFFillTargetsEditor extends OxPropertyEditor {\n static styles = [\n ...OxPropertyEditor.styles,\n ScrollbarStyles,\n css`\n .mode-selector {\n display: flex;\n gap: 0;\n margin-bottom: 6px;\n border: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.15));\n border-radius: 6px;\n overflow: hidden;\n }\n\n .mode-btn {\n flex: 1;\n padding: 5px 0;\n font-size: 11px;\n font-weight: 500;\n text-align: center;\n cursor: pointer;\n background: var(--md-sys-color-surface-container, #f3edf7);\n color: var(--md-sys-color-on-surface, #1c1b1f);\n border: none;\n border-right: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.1));\n transition: all 0.15s;\n }\n\n .mode-btn:last-child {\n border-right: none;\n }\n\n .mode-btn:hover {\n background: var(--md-sys-color-surface-container-highest, #e6e0e9);\n }\n\n .mode-btn[active] {\n background: var(--md-sys-color-primary, #6750a4);\n color: var(--md-sys-color-on-primary, #fff);\n }\n\n .node-list {\n display: flex;\n flex-direction: column;\n gap: 1px;\n max-height: 200px;\n overflow-y: auto;\n font-size: 12px;\n }\n\n .node-item {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 8px;\n cursor: pointer;\n border-radius: 4px;\n transition: background 0.1s;\n }\n\n .node-item:hover {\n background: var(--md-sys-color-primary-container, rgba(103, 80, 164, 0.12));\n }\n\n .node-item input[type='checkbox'] {\n margin: 0;\n accent-color: var(--md-sys-color-primary, #6750a4);\n }\n\n .node-item span {\n font-size: 12px;\n color: var(--md-sys-color-on-surface, #1c1b1f);\n }\n\n .mode-desc {\n font-size: 11px;\n color: var(--md-sys-color-on-surface-variant, #666);\n padding: 4px 0;\n }\n\n .mini-btn {\n font-size: 10px;\n padding: 2px 8px;\n border: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.15));\n border-radius: 4px;\n background: var(--md-sys-color-surface-container, #f3edf7);\n color: var(--md-sys-color-on-surface, #1c1b1f);\n cursor: pointer;\n }\n\n .mini-btn:hover {\n background: var(--md-sys-color-surface-container-highest, #e6e0e9);\n }\n\n .empty {\n color: var(--md-sys-color-on-surface-variant, #999);\n font-size: 11px;\n padding: 8px;\n text-align: center;\n }\n `\n ]\n\n @property({ type: String }) declare src: string | undefined\n\n @state() declare private _meshNames: string[]\n @state() declare private _mode: FillMode\n\n private _component: any = null\n private _lastExternalValue: any = undefined\n\n updated(changes: PropertyValues<this>) {\n if (changes.has('src')) {\n this.dispatchEvent(\n new CustomEvent('i-need-selected', {\n bubbles: true,\n composed: true,\n detail: {\n callback: (selected: any[]) => {\n this._component = selected?.[0]\n this._refreshMeshNames()\n }\n }\n })\n )\n }\n }\n\n private _refreshMeshNames() {\n if (!this._component) {\n this._meshNames = []\n return\n }\n\n const ro = this._component.realObject as any\n if (!ro?.getNode) {\n this._meshNames = []\n return\n }\n\n this._meshNames = ((this._component.nodeNames as string[]) ?? []).filter((name: string) => {\n const node = ro.getNode(name)\n return node?.isMesh\n })\n }\n\n editorTemplate(value: any, _spec: PropertySpec): TemplateResult {\n if (!this._meshNames?.length) {\n this._refreshMeshNames()\n }\n\n // 외부 value가 변경되면 모드 동기화\n if (value !== this._lastExternalValue) {\n this._lastExternalValue = value\n this._mode = getMode(value)\n }\n\n const mode = this._mode\n const meshCount = this._meshNames?.length ?? 0\n const targets: string[] = Array.isArray(this.value) ? this.value : (Array.isArray(value) ? value : [])\n\n return html`\n <fieldset fullwidth>\n ${meshCount > 0\n ? html`\n <div class=\"mode-selector\">\n <div class=\"mode-btn\" ?active=${mode === 'all'} @click=${() => this._setMode('all')}>All</div>\n <div class=\"mode-btn\" ?active=${mode === 'none'} @click=${() => this._setMode('none')}>None</div>\n <div class=\"mode-btn\" ?active=${mode === 'custom'} @click=${() => this._setMode('custom')}>Custom</div>\n </div>\n\n ${mode === 'all'\n ? html`<div class=\"mode-desc\">${meshCount} meshes — fillStyle applies to all</div>`\n : nothing}\n ${mode === 'none'\n ? html`<div class=\"mode-desc\">fillStyle not applied to any mesh</div>`\n : nothing}\n ${mode === 'custom'\n ? html`\n <div style=\"display:flex;align-items:center;gap:6px\">\n <div class=\"mode-desc\" style=\"flex:1\">${targets.length} / ${meshCount} selected</div>\n <button class=\"mini-btn\" @click=${() => this._applyValue([...(this._meshNames ?? [])])}>All</button>\n <button class=\"mini-btn\" @click=${() => this._applyValue([])}>Clear</button>\n </div>\n <div class=\"node-list\">\n ${(this._meshNames ?? []).map(\n name => html`\n <label\n class=\"node-item\"\n @mouseenter=${() => this._highlightNode(name)}\n @mouseleave=${() => this._clearHighlight()}\n >\n <input\n type=\"checkbox\"\n .checked=${targets.includes(name)}\n @change=${(e: Event) => {\n e.stopPropagation()\n this._onToggle(name, (e.target as HTMLInputElement).checked, targets)\n }}\n />\n <span>${name}</span>\n </label>\n `\n )}\n </div>\n `\n : nothing}\n `\n : html`<div class=\"empty\">\n <ox-i18n msgid=\"label.no-gltf-meshes\">No meshes available</ox-i18n>\n </div>`}\n </fieldset>\n `\n }\n\n private _setMode(mode: FillMode) {\n const prevMode = this._mode\n this._mode = mode\n\n let newValue: any\n switch (mode) {\n case 'all':\n newValue = '*'\n break\n case 'none':\n newValue = 'none'\n break\n case 'custom':\n // 이전 상태에 따라: All → 전체 선택, None → 전체 해제\n if (prevMode === 'all') {\n newValue = [...(this._meshNames ?? [])]\n } else if (prevMode === 'none') {\n newValue = []\n } else {\n // 이미 custom이면 현재 값 유지\n newValue = Array.isArray(this.value) ? this.value : []\n }\n break\n }\n\n this._applyValue(newValue)\n }\n\n private _onToggle(name: string, checked: boolean, current: string[]) {\n const list = [...current]\n if (checked) {\n if (!list.includes(name)) list.push(name)\n } else {\n const idx = list.indexOf(name)\n if (idx >= 0) list.splice(idx, 1)\n }\n\n this._applyValue(list)\n }\n\n private _applyValue(newValue: any) {\n // 로컬 value 즉시 반영 (re-render)\n this.value = newValue\n this.requestUpdate()\n\n this.dispatchEvent(\n new CustomEvent('i-need-selected', {\n bubbles: true,\n composed: true,\n detail: {\n callback: (selected: any[]) => {\n selected[0].set('fillStyleTargets', newValue)\n }\n }\n })\n )\n }\n\n // --- Outline 강조 ---\n\n private _highlightNode(name: string) {\n const ro = this._component?.realObject as any\n if (!ro?.getNode) return\n\n const node = ro.getNode(name)\n if (!node) return\n\n this._getRendererManager()?.setOutlineObjects([node])\n this._component?.invalidate()\n }\n\n private _clearHighlight() {\n const ro = this._component?.realObject as any\n if (!ro) return\n\n this._getRendererManager()?.setOutlineObjects(ro.object3d ? [ro.object3d] : [])\n this._component?.invalidate()\n }\n\n private _getRendererManager(): any {\n const ro = this._component?.realObject as any\n const container = ro?.threeContainer as any\n return container?._capability?.rendererManager ?? container?._threeCapability?.rendererManager ?? null\n }\n}\n"]}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import '@material/web/icon/icon.js';
|
|
2
|
-
import '@material/web/button/elevated-button.js';
|
|
3
2
|
import '@operato/i18n/ox-i18n.js';
|
|
4
3
|
import { PropertyValues, TemplateResult } from 'lit';
|
|
5
4
|
import { OxPropertyEditor, PropertySpec } from '@operato/property-editor';
|
|
@@ -7,17 +6,33 @@ import { Component } from '@hatiolab/things-scene';
|
|
|
7
6
|
export default class GLTFInfoEditor extends OxPropertyEditor {
|
|
8
7
|
static styles: import("lit").CSSResult[];
|
|
9
8
|
src: string | undefined;
|
|
9
|
+
private _component;
|
|
10
10
|
width: number;
|
|
11
11
|
height: number;
|
|
12
12
|
depth: number;
|
|
13
|
+
currentWidth: number;
|
|
14
|
+
currentHeight: number;
|
|
15
|
+
currentDepth: number;
|
|
16
|
+
meshCount: number;
|
|
17
|
+
vertexCount: number;
|
|
18
|
+
animationCount: number;
|
|
19
|
+
materialCount: number;
|
|
13
20
|
constructor();
|
|
14
21
|
editorTemplate(value: any, spec: PropertySpec): TemplateResult;
|
|
22
|
+
private _formatNumber;
|
|
23
|
+
private _isProportional;
|
|
15
24
|
private _applyAction;
|
|
16
25
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
26
|
+
* W, H 중 짧은 쪽을 기준으로 원래 모델의 비율에 맞게 치수를 조절한다.
|
|
27
|
+
* (contain 방식 — 모델이 컴포넌트 영역 안에 들어감)
|
|
19
28
|
*/
|
|
20
29
|
private _applyProportional;
|
|
30
|
+
private _getRatioLock;
|
|
31
|
+
private _setRatioLock;
|
|
32
|
+
private _refreshCurrentSize;
|
|
21
33
|
updated(changes: PropertyValues<this>): void;
|
|
22
|
-
|
|
34
|
+
private _pollTimer?;
|
|
35
|
+
fetchSourceInfo(component: Component, src: string): Promise<void>;
|
|
36
|
+
private _tryReadFromRealObject;
|
|
37
|
+
disconnectedCallback(): void;
|
|
23
38
|
}
|