@operato/scene-urdf 10.0.0-beta.2 → 10.0.0-beta.24

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.
Files changed (68) hide show
  1. package/README.md +22 -25
  2. package/TODO.md +58 -0
  3. package/dist/editors/index.d.ts +7 -0
  4. package/dist/editors/index.js +12 -1
  5. package/dist/editors/index.js.map +1 -1
  6. package/dist/editors/property-editor-urdf-joints.d.ts +54 -0
  7. package/dist/editors/property-editor-urdf-joints.js +246 -0
  8. package/dist/editors/property-editor-urdf-joints.js.map +1 -0
  9. package/dist/editors/property-editor-urdf-preset.d.ts +8 -0
  10. package/dist/editors/property-editor-urdf-preset.js +114 -0
  11. package/dist/editors/property-editor-urdf-preset.js.map +1 -0
  12. package/dist/index.d.ts +8 -2
  13. package/dist/index.js +22 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/joint-controller.d.ts +35 -0
  16. package/dist/joint-controller.js +34 -0
  17. package/dist/joint-controller.js.map +1 -0
  18. package/dist/real-object-urdf.d.ts +106 -0
  19. package/dist/real-object-urdf.js +527 -0
  20. package/dist/real-object-urdf.js.map +1 -0
  21. package/dist/smoothing-controller.d.ts +15 -0
  22. package/dist/smoothing-controller.js +88 -0
  23. package/dist/smoothing-controller.js.map +1 -0
  24. package/dist/templates/index.d.ts +3 -0
  25. package/dist/templates/index.js +2 -3
  26. package/dist/templates/index.js.map +1 -1
  27. package/dist/templates/{urdf-controller.d.ts → urdf.d.ts} +3 -0
  28. package/dist/templates/urdf.js +19 -0
  29. package/dist/templates/urdf.js.map +1 -0
  30. package/dist/urdf-object.d.ts +502 -0
  31. package/dist/urdf-object.js +153 -0
  32. package/dist/urdf-object.js.map +1 -0
  33. package/dist/urdf-presets.d.ts +22 -0
  34. package/dist/urdf-presets.js +176 -0
  35. package/dist/urdf-presets.js.map +1 -0
  36. package/icons/urdf.png +0 -0
  37. package/package.json +5 -4
  38. package/translations/en.json +10 -16
  39. package/translations/ja.json +10 -16
  40. package/translations/ko.json +10 -16
  41. package/translations/ms.json +10 -16
  42. package/translations/zh.json +10 -16
  43. package/dist/elements/drag-n-drop.d.ts +0 -2
  44. package/dist/elements/drag-n-drop.js +0 -126
  45. package/dist/elements/drag-n-drop.js.map +0 -1
  46. package/dist/elements/urdf-controller-element.d.ts +0 -12
  47. package/dist/elements/urdf-controller-element.js +0 -283
  48. package/dist/elements/urdf-controller-element.js.map +0 -1
  49. package/dist/elements/urdf-drag-controls.d.ts +0 -32
  50. package/dist/elements/urdf-drag-controls.js +0 -197
  51. package/dist/elements/urdf-drag-controls.js.map +0 -1
  52. package/dist/elements/urdf-manipulator-element.d.ts +0 -15
  53. package/dist/elements/urdf-manipulator-element.js +0 -112
  54. package/dist/elements/urdf-manipulator-element.js.map +0 -1
  55. package/dist/elements/urdf-viewer-element.d.ts +0 -53
  56. package/dist/elements/urdf-viewer-element.js +0 -414
  57. package/dist/elements/urdf-viewer-element.js.map +0 -1
  58. package/dist/templates/urdf-controller.js +0 -16
  59. package/dist/templates/urdf-controller.js.map +0 -1
  60. package/dist/templates/urdf-viewer.d.ts +0 -14
  61. package/dist/templates/urdf-viewer.js +0 -16
  62. package/dist/templates/urdf-viewer.js.map +0 -1
  63. package/dist/urdf-controller.d.ts +0 -15
  64. package/dist/urdf-controller.js +0 -70
  65. package/dist/urdf-controller.js.map +0 -1
  66. package/dist/urdf-viewer.d.ts +0 -16
  67. package/dist/urdf-viewer.js +0 -202
  68. package/dist/urdf-viewer.js.map +0 -1
package/README.md CHANGED
@@ -1,41 +1,38 @@
1
- # \<scene-urdf>
1
+ # @operato/scene-urdf
2
2
 
3
- ## Installation
3
+ > Scene module for manipulating robot simulation through URDF format
4
4
 
5
- ```bash
6
- npm i scene-urdf
7
- ```
5
+ <!-- AUTOGEN_BEGIN: do not edit between markers (run scripts/regenerate-readmes.mjs to update) -->
8
6
 
9
- ## Usage
7
+ ## Components
10
8
 
11
- ```html
9
+ - `RealObjectURDF`
10
+ - `URDFObject`
11
+ - `registerJointController`
12
+ - `createJointController`
13
+ - `listJointControllers`
14
+ - `SmoothingController`
12
15
 
13
- ```
14
-
15
- ## Linting and formatting
16
-
17
- To scan the project for linting and formatting errors, run
16
+ ## Install
18
17
 
19
18
  ```bash
20
- npm run lint
19
+ yarn add @operato/scene-urdf
21
20
  ```
22
21
 
23
- To automatically fix linting and formatting errors, run
22
+ ## Usage
24
23
 
25
- ```bash
26
- npm run format
24
+ ```ts
25
+ import { RealObjectURDF, URDFObject, registerJointController, createJointController, listJointControllers } from '@operato/scene-urdf'
27
26
  ```
28
27
 
29
- ## Tooling configs
30
-
31
- For most of the tools, the configuration is in the `package.json` to reduce the amount of files in your project.
32
-
33
- If you customize the configuration a lot, you can consider moving them to individual files.
34
-
35
- ## Local Demo with `web-dev-server`
28
+ ## Build
36
29
 
37
30
  ```bash
38
- npm start
31
+ yarn build
39
32
  ```
40
33
 
41
- To run a local development server that serves the basic demo located in `demo/index.html`
34
+ Output: ESM module at `dist/index.js` (single bundle, no UMD/IE legacy).
35
+
36
+ _Version: 10.0.0-beta.19_
37
+
38
+ <!-- AUTOGEN_END -->
package/TODO.md ADDED
@@ -0,0 +1,58 @@
1
+ # @operato/scene-urdf — TODO
2
+
3
+ ## 물리 시뮬레이션 (Physics)
4
+
5
+ URDF는 `<inertial>`(질량/관성), `<collision>`(충돌 기하), `<joint limit/dynamics>`
6
+ (effort, velocity, friction, damping), `<mimicJoint>` 등 물리 시뮬레이션에 필요한
7
+ 정보를 이미 품고 있다. 현재는 시각(visual) 부분만 사용.
8
+
9
+ ### 레벨별 접근
10
+
11
+ - **Level 1 — 관절 스무딩**
12
+ - 목표값 → 현재값 ODE 기반 감쇠 보간
13
+ - 라이브러리 불필요, 자체 구현
14
+ - 중력/충돌 없음 — 단순 모션 부드러움만 추가
15
+
16
+ - **Level 2 — Rapier.js 리지드바디 (권장 정식 해법)**
17
+ - [Rapier3D](https://rapier.rs/) WASM 통합
18
+ - URDF link/joint/inertia → Rapier world 매핑
19
+ - 관절 타입 1:1 매핑 (revolute/prismatic/fixed/spherical)
20
+ - 중력, 충돌, 마찰 자연스러움
21
+ - 다른 씬 오브젝트와 충돌 (floor/컨테이너/다른 로봇)
22
+
23
+ - **Level 3 — 씬 전역 물리 시뮬레이션**
24
+ - things-scene의 모든 3D 컴포넌트를 물리 월드로 편입
25
+ - 공장 전역 pick & place, 파레트 쌓기, 컨베이어 물류
26
+ - things-scene 엔진 수준의 수정 필요
27
+
28
+ ### 활성화 트리거
29
+
30
+ - `state.physics = 'none' | 'smooth' | 'rigid'` 프로퍼티
31
+ - `'smooth'`: Level 1
32
+ - `'rigid'`: Level 2 (Rapier)
33
+ - `'none'`(기본): 현재처럼 순수 kinematic
34
+
35
+ ### 유스케이스 매핑
36
+
37
+ | 시나리오 | 필요 레벨 |
38
+ |---|---|
39
+ | 센서 → joint 시각화 (현재 Phase B) | Level 0 (현재) |
40
+ | 자연스러운 움직임 (UX 개선) | Level 1 |
41
+ | pick & place 시뮬레이션 | Level 2 |
42
+ | 사전 프로그래밍 검증 | Level 2 ~ 3 |
43
+ | 사고/충돌 예방 시뮬레이션 | Level 3 |
44
+
45
+ ---
46
+
47
+ ## 기타 후속 과제
48
+
49
+ - [ ] xacro-parser 재도입 (ros-industrial 로봇 직접 로드)
50
+ - [ ] `package://` 다중 매핑 preset UI 개선 (URL 편집 가능하게)
51
+ - [ ] 번들 에셋(`assets/urdf-packages/dsr_description2/`)의 `package://` 해석
52
+ 자동 설정 preset 추가 (Doosan DSR 16종)
53
+ - [ ] 조인트별 커스텀 auto-animate 파라미터 (state.animate[joint] = {freq,amp,phase})
54
+ - [ ] 조인트 드래그 gizmo (3D 상에서 직접 관절 조작)
55
+ - [ ] IK (inverse kinematics) — end-effector drag로 관절 역산
56
+ - [ ] Trajectory 녹화/재생
57
+ - [ ] KHR_materials_variants 스타일 상태별 재질 swap
58
+ - [ ] ColladaLoader의 Z-UP 경고 suppress (선택)
@@ -0,0 +1,7 @@
1
+ import './property-editor-urdf-joints.js';
2
+ import './property-editor-urdf-preset.js';
3
+ declare const _default: {
4
+ type: string;
5
+ element: string;
6
+ }[];
7
+ export default _default;
@@ -1,2 +1,13 @@
1
- "use strict";
1
+ import './property-editor-urdf-joints.js';
2
+ import './property-editor-urdf-preset.js';
3
+ export default [
4
+ {
5
+ type: 'urdf-joints',
6
+ element: 'property-editor-urdf-joints'
7
+ },
8
+ {
9
+ type: 'urdf-preset',
10
+ element: 'property-editor-urdf-preset'
11
+ }
12
+ ];
2
13
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/editors/index.ts"],"names":[],"mappings":"","sourcesContent":[""]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/editors/index.ts"],"names":[],"mappings":"AAAA,OAAO,kCAAkC,CAAA;AACzC,OAAO,kCAAkC,CAAA;AAEzC,eAAe;IACb;QACE,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,6BAA6B;KACvC;IACD;QACE,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,6BAA6B;KACvC;CACF,CAAA","sourcesContent":["import './property-editor-urdf-joints.js'\nimport './property-editor-urdf-preset.js'\n\nexport default [\n {\n type: 'urdf-joints',\n element: 'property-editor-urdf-joints'\n },\n {\n type: 'urdf-preset',\n element: 'property-editor-urdf-preset'\n }\n]\n"]}
@@ -0,0 +1,54 @@
1
+ import '@operato/i18n/ox-i18n.js';
2
+ import { TemplateResult } from 'lit';
3
+ import { OxPropertyEditor, PropertySpec } from '@operato/property-editor';
4
+ type JointMeta = {
5
+ name: string;
6
+ type: 'fixed' | 'continuous' | 'revolute' | 'planar' | 'prismatic' | 'floating';
7
+ axis: {
8
+ x: number;
9
+ y: number;
10
+ z: number;
11
+ };
12
+ limit: {
13
+ lower: number;
14
+ upper: number;
15
+ effort: number;
16
+ velocity: number;
17
+ };
18
+ };
19
+ /**
20
+ * URDF 조인트 슬라이더 에디터.
21
+ *
22
+ * 값(value)은 state.joints 객체: `{ [jointName]: number | { value: number } }`.
23
+ * 조인트 메타(이름/타입/axis/limit)는 로드된 RealObjectURDF.jointMeta에서 읽으며,
24
+ * 선택된 컴포넌트 참조를 얻기 위해 `i-need-selected` 커스텀 이벤트를 사용한다.
25
+ *
26
+ * 슬라이더 조작 시 state.joints를 immutable하게 업데이트한 새 객체를 value로
27
+ * 설정 — things-scene setState의 deep-equality 변화 감지를 통과시키기 위함.
28
+ */
29
+ export default class URDFJointsEditor extends OxPropertyEditor {
30
+ static styles: import("lit").CSSResult[];
31
+ private _component;
32
+ private _pollTimer?;
33
+ meta: JointMeta[];
34
+ constructor();
35
+ editorTemplate(value: any, _spec: PropertySpec): TemplateResult;
36
+ private _renderJointSlider;
37
+ private _onSlide;
38
+ /**
39
+ * 슬라이더 드래그 종료 시 브라우저가 네이티브 'change' 이벤트를 발행한다.
40
+ * 이 이벤트가 OxPropertyEditor 베이스의 _valueChanged(renderRoot 리스너)에
41
+ * 도달하면 input.value(문자열)를 this.value에 assign해버려서 joints 객체가
42
+ * "45" 같은 문자열로 덮어쓰여진다. shadow DOM 내부에서 바로 stopPropagation.
43
+ */
44
+ private _stopChange;
45
+ private _onReset;
46
+ connectedCallback(): void;
47
+ disconnectedCallback(): void;
48
+ /**
49
+ * 선택된 URDF 컴포넌트를 얻어 jointMeta를 읽는다. 로드가 아직이면 주기적 폴링.
50
+ */
51
+ private _requestSelectedComponent;
52
+ private _refreshJointMeta;
53
+ }
54
+ export {};
@@ -0,0 +1,246 @@
1
+ import { __decorate } from "tslib";
2
+ import '@operato/i18n/ox-i18n.js';
3
+ import { css, html } from 'lit';
4
+ import { customElement, state } from 'lit/decorators.js';
5
+ import { OxPropertyEditor } from '@operato/property-editor';
6
+ import { ScrollbarStyles } from '@operato/styles/scrollbar-styles.js';
7
+ const RAD2DEG = 180 / Math.PI;
8
+ /**
9
+ * URDF 조인트 슬라이더 에디터.
10
+ *
11
+ * 값(value)은 state.joints 객체: `{ [jointName]: number | { value: number } }`.
12
+ * 조인트 메타(이름/타입/axis/limit)는 로드된 RealObjectURDF.jointMeta에서 읽으며,
13
+ * 선택된 컴포넌트 참조를 얻기 위해 `i-need-selected` 커스텀 이벤트를 사용한다.
14
+ *
15
+ * 슬라이더 조작 시 state.joints를 immutable하게 업데이트한 새 객체를 value로
16
+ * 설정 — things-scene setState의 deep-equality 변화 감지를 통과시키기 위함.
17
+ */
18
+ let URDFJointsEditor = class URDFJointsEditor extends OxPropertyEditor {
19
+ static styles = [
20
+ ...OxPropertyEditor.styles,
21
+ ScrollbarStyles,
22
+ css `
23
+ [joints-panel] {
24
+ max-height: 320px;
25
+ overflow-y: auto;
26
+ padding: 4px 0;
27
+ }
28
+
29
+ .joint-row {
30
+ display: grid;
31
+ grid-template-columns: 80px 1fr 60px;
32
+ gap: 6px;
33
+ align-items: center;
34
+ margin: 3px 0;
35
+ }
36
+
37
+ .joint-row > .joint-name {
38
+ font-size: 11px;
39
+ color: var(--md-sys-color-on-surface, #1c1b1f);
40
+ text-align: right;
41
+ overflow: hidden;
42
+ text-overflow: ellipsis;
43
+ white-space: nowrap;
44
+ }
45
+
46
+ .joint-row > .joint-name[data-fixed] {
47
+ color: var(--md-sys-color-on-surface-variant, #888);
48
+ font-style: italic;
49
+ }
50
+
51
+ .joint-row > input[type='range'] {
52
+ width: 100%;
53
+ accent-color: var(--md-sys-color-primary, #6750a4);
54
+ }
55
+
56
+ .joint-row > .joint-value {
57
+ font: 11px monospace;
58
+ color: var(--md-sys-color-on-surface-variant, #666);
59
+ text-align: right;
60
+ }
61
+
62
+ .joint-empty,
63
+ .joint-waiting {
64
+ font-size: 11px;
65
+ color: var(--md-sys-color-on-surface-variant, #888);
66
+ padding: 8px 10px;
67
+ font-style: italic;
68
+ }
69
+
70
+ .joint-type-badge {
71
+ display: inline-block;
72
+ font-size: 9px;
73
+ padding: 1px 5px;
74
+ margin-left: 4px;
75
+ border-radius: 6px;
76
+ background: var(--md-sys-color-secondary-container, #e8def8);
77
+ color: var(--md-sys-color-on-secondary-container, #1d192b);
78
+ text-transform: uppercase;
79
+ }
80
+
81
+ .reset-btn {
82
+ font-size: 10px;
83
+ padding: 3px 8px;
84
+ margin-top: 4px;
85
+ border: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.2));
86
+ border-radius: 6px;
87
+ background: var(--md-sys-color-surface-container, #f3edf7);
88
+ cursor: pointer;
89
+ color: var(--md-sys-color-on-surface, #1c1b1f);
90
+ }
91
+ .reset-btn:hover {
92
+ background: var(--md-sys-color-surface-container-highest, #e6e0e9);
93
+ }
94
+ `
95
+ ];
96
+ _component = null;
97
+ _pollTimer;
98
+ constructor() {
99
+ super();
100
+ this.meta = [];
101
+ }
102
+ editorTemplate(value, _spec) {
103
+ const joints = (value || {});
104
+ return html `
105
+ <fieldset fullwidth>
106
+ <legend><ox-i18n msgid="label.joints">joints</ox-i18n></legend>
107
+ <div joints-panel>
108
+ ${this.meta.length === 0
109
+ ? html `<div class="joint-waiting">
110
+ <ox-i18n msgid="label.waiting-for-urdf-load">waiting for URDF to load…</ox-i18n>
111
+ </div>`
112
+ : this.meta.map(m => this._renderJointSlider(m, joints))}
113
+ </div>
114
+ ${this.meta.length > 0
115
+ ? html `<button class="reset-btn" @click=${this._onReset}>
116
+ <ox-i18n msgid="label.reset-all">reset all</ox-i18n>
117
+ </button>`
118
+ : null}
119
+ </fieldset>
120
+ `;
121
+ }
122
+ _renderJointSlider(m, joints) {
123
+ const raw = joints[m.name];
124
+ const internal = typeof raw === 'number' ? raw : (raw?.value ?? 0);
125
+ const isRad = m.type === 'revolute' || m.type === 'continuous';
126
+ const scale = isRad ? RAD2DEG : 1;
127
+ const unit = isRad ? '°' : 'm';
128
+ const minDisp = m.limit.lower * scale;
129
+ const maxDisp = m.limit.upper * scale;
130
+ const dispVal = internal * scale;
131
+ const step = isRad ? 1 : Math.max((m.limit.upper - m.limit.lower) / 100, 0.001);
132
+ return html `
133
+ <div class="joint-row">
134
+ <span class="joint-name" title="${m.name} (${m.type})">
135
+ ${m.name}
136
+ <span class="joint-type-badge">${m.type.substring(0, 3)}</span>
137
+ </span>
138
+ <input
139
+ type="range"
140
+ min=${minDisp}
141
+ max=${maxDisp}
142
+ step=${step}
143
+ .value=${String(dispVal)}
144
+ data-joint=${m.name}
145
+ data-scale=${scale}
146
+ @input=${this._onSlide}
147
+ @change=${this._stopChange}
148
+ />
149
+ <span class="joint-value">${dispVal.toFixed(isRad ? 1 : 3)}${unit}</span>
150
+ </div>
151
+ `;
152
+ }
153
+ _onSlide = (e) => {
154
+ e.stopPropagation();
155
+ const input = e.target;
156
+ const jointName = input.dataset.joint;
157
+ const scale = parseFloat(input.dataset.scale || '1');
158
+ const internal = parseFloat(input.value) / scale;
159
+ // 이전 값을 immutable하게 새 객체로 머지. state.joints를 mutate하면
160
+ // setState의 deep-equality 체크에서 before=after로 판정되어 change 감지
161
+ // 실패 → onchange가 fire되지 않음. Phase 1에서 확인된 규칙.
162
+ const prev = (this.value || {});
163
+ const next = { ...prev, [jointName]: internal };
164
+ this.value = next;
165
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
166
+ };
167
+ /**
168
+ * 슬라이더 드래그 종료 시 브라우저가 네이티브 'change' 이벤트를 발행한다.
169
+ * 이 이벤트가 OxPropertyEditor 베이스의 _valueChanged(renderRoot 리스너)에
170
+ * 도달하면 input.value(문자열)를 this.value에 assign해버려서 joints 객체가
171
+ * "45" 같은 문자열로 덮어쓰여진다. shadow DOM 내부에서 바로 stopPropagation.
172
+ */
173
+ _stopChange = (e) => {
174
+ e.stopPropagation();
175
+ };
176
+ _onReset = (e) => {
177
+ e.stopPropagation();
178
+ this.value = {};
179
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
180
+ };
181
+ connectedCallback() {
182
+ super.connectedCallback();
183
+ this._requestSelectedComponent();
184
+ }
185
+ disconnectedCallback() {
186
+ super.disconnectedCallback();
187
+ if (this._pollTimer) {
188
+ clearInterval(this._pollTimer);
189
+ this._pollTimer = undefined;
190
+ }
191
+ }
192
+ /**
193
+ * 선택된 URDF 컴포넌트를 얻어 jointMeta를 읽는다. 로드가 아직이면 주기적 폴링.
194
+ */
195
+ _requestSelectedComponent() {
196
+ this.dispatchEvent(new CustomEvent('i-need-selected', {
197
+ bubbles: true,
198
+ composed: true,
199
+ detail: {
200
+ callback: (selected) => {
201
+ const component = selected?.[0];
202
+ if (!component)
203
+ return;
204
+ this._component = component;
205
+ this._refreshJointMeta();
206
+ }
207
+ }
208
+ }));
209
+ }
210
+ _refreshJointMeta() {
211
+ const ro = this._component?.realObject;
212
+ const meta = ro?.jointMeta;
213
+ if (meta && meta.length > 0) {
214
+ this.meta = meta;
215
+ if (this._pollTimer) {
216
+ clearInterval(this._pollTimer);
217
+ this._pollTimer = undefined;
218
+ }
219
+ return;
220
+ }
221
+ // 아직 로드 전이면 폴링 (최대 20회 = 10초)
222
+ if (this._pollTimer)
223
+ return;
224
+ let retries = 0;
225
+ this._pollTimer = window.setInterval(() => {
226
+ const m = this._component?.realObject?.jointMeta;
227
+ if (m && m.length > 0) {
228
+ this.meta = m;
229
+ clearInterval(this._pollTimer);
230
+ this._pollTimer = undefined;
231
+ }
232
+ else if (++retries >= 20) {
233
+ clearInterval(this._pollTimer);
234
+ this._pollTimer = undefined;
235
+ }
236
+ }, 500);
237
+ }
238
+ };
239
+ __decorate([
240
+ state()
241
+ ], URDFJointsEditor.prototype, "meta", void 0);
242
+ URDFJointsEditor = __decorate([
243
+ customElement('property-editor-urdf-joints')
244
+ ], URDFJointsEditor);
245
+ export default URDFJointsEditor;
246
+ //# sourceMappingURL=property-editor-urdf-joints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"property-editor-urdf-joints.js","sourceRoot":"","sources":["../../src/editors/property-editor-urdf-joints.ts"],"names":[],"mappings":"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","sourcesContent":["import '@operato/i18n/ox-i18n.js'\n\nimport { css, html, TemplateResult } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\n\nimport { OxPropertyEditor, PropertySpec } from '@operato/property-editor'\nimport { ScrollbarStyles } from '@operato/styles/scrollbar-styles.js'\n\ntype JointMeta = {\n name: string\n type: 'fixed' | 'continuous' | 'revolute' | 'planar' | 'prismatic' | 'floating'\n axis: { x: number; y: number; z: number }\n limit: { lower: number; upper: number; effort: number; velocity: number }\n}\n\nconst RAD2DEG = 180 / Math.PI\n\n/**\n * URDF 조인트 슬라이더 에디터.\n *\n * 값(value)은 state.joints 객체: `{ [jointName]: number | { value: number } }`.\n * 조인트 메타(이름/타입/axis/limit)는 로드된 RealObjectURDF.jointMeta에서 읽으며,\n * 선택된 컴포넌트 참조를 얻기 위해 `i-need-selected` 커스텀 이벤트를 사용한다.\n *\n * 슬라이더 조작 시 state.joints를 immutable하게 업데이트한 새 객체를 value로\n * 설정 — things-scene setState의 deep-equality 변화 감지를 통과시키기 위함.\n */\n@customElement('property-editor-urdf-joints')\nexport default class URDFJointsEditor extends OxPropertyEditor {\n static styles = [\n ...OxPropertyEditor.styles,\n ScrollbarStyles,\n css`\n [joints-panel] {\n max-height: 320px;\n overflow-y: auto;\n padding: 4px 0;\n }\n\n .joint-row {\n display: grid;\n grid-template-columns: 80px 1fr 60px;\n gap: 6px;\n align-items: center;\n margin: 3px 0;\n }\n\n .joint-row > .joint-name {\n font-size: 11px;\n color: var(--md-sys-color-on-surface, #1c1b1f);\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .joint-row > .joint-name[data-fixed] {\n color: var(--md-sys-color-on-surface-variant, #888);\n font-style: italic;\n }\n\n .joint-row > input[type='range'] {\n width: 100%;\n accent-color: var(--md-sys-color-primary, #6750a4);\n }\n\n .joint-row > .joint-value {\n font: 11px monospace;\n color: var(--md-sys-color-on-surface-variant, #666);\n text-align: right;\n }\n\n .joint-empty,\n .joint-waiting {\n font-size: 11px;\n color: var(--md-sys-color-on-surface-variant, #888);\n padding: 8px 10px;\n font-style: italic;\n }\n\n .joint-type-badge {\n display: inline-block;\n font-size: 9px;\n padding: 1px 5px;\n margin-left: 4px;\n border-radius: 6px;\n background: var(--md-sys-color-secondary-container, #e8def8);\n color: var(--md-sys-color-on-secondary-container, #1d192b);\n text-transform: uppercase;\n }\n\n .reset-btn {\n font-size: 10px;\n padding: 3px 8px;\n margin-top: 4px;\n border: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.2));\n border-radius: 6px;\n background: var(--md-sys-color-surface-container, #f3edf7);\n cursor: pointer;\n color: var(--md-sys-color-on-surface, #1c1b1f);\n }\n .reset-btn:hover {\n background: var(--md-sys-color-surface-container-highest, #e6e0e9);\n }\n `\n ]\n\n private _component: any = null\n private _pollTimer?: number\n\n @state() declare meta: JointMeta[]\n\n constructor() {\n super()\n this.meta = []\n }\n\n editorTemplate(value: any, _spec: PropertySpec): TemplateResult {\n const joints = (value || {}) as Record<string, number | { value?: number }>\n\n return html`\n <fieldset fullwidth>\n <legend><ox-i18n msgid=\"label.joints\">joints</ox-i18n></legend>\n <div joints-panel>\n ${this.meta.length === 0\n ? html`<div class=\"joint-waiting\">\n <ox-i18n msgid=\"label.waiting-for-urdf-load\">waiting for URDF to load…</ox-i18n>\n </div>`\n : this.meta.map(m => this._renderJointSlider(m, joints))}\n </div>\n ${this.meta.length > 0\n ? html`<button class=\"reset-btn\" @click=${this._onReset}>\n <ox-i18n msgid=\"label.reset-all\">reset all</ox-i18n>\n </button>`\n : null}\n </fieldset>\n `\n }\n\n private _renderJointSlider(m: JointMeta, joints: Record<string, number | { value?: number }>) {\n const raw = joints[m.name]\n const internal = typeof raw === 'number' ? raw : (raw?.value ?? 0)\n const isRad = m.type === 'revolute' || m.type === 'continuous'\n const scale = isRad ? RAD2DEG : 1\n const unit = isRad ? '°' : 'm'\n const minDisp = m.limit.lower * scale\n const maxDisp = m.limit.upper * scale\n const dispVal = internal * scale\n const step = isRad ? 1 : Math.max((m.limit.upper - m.limit.lower) / 100, 0.001)\n\n return html`\n <div class=\"joint-row\">\n <span class=\"joint-name\" title=\"${m.name} (${m.type})\">\n ${m.name}\n <span class=\"joint-type-badge\">${m.type.substring(0, 3)}</span>\n </span>\n <input\n type=\"range\"\n min=${minDisp}\n max=${maxDisp}\n step=${step}\n .value=${String(dispVal)}\n data-joint=${m.name}\n data-scale=${scale}\n @input=${this._onSlide}\n @change=${this._stopChange}\n />\n <span class=\"joint-value\">${dispVal.toFixed(isRad ? 1 : 3)}${unit}</span>\n </div>\n `\n }\n\n private _onSlide = (e: Event) => {\n e.stopPropagation()\n const input = e.target as HTMLInputElement\n const jointName = input.dataset.joint!\n const scale = parseFloat(input.dataset.scale || '1')\n const internal = parseFloat(input.value) / scale\n\n // 이전 값을 immutable하게 새 객체로 머지. state.joints를 mutate하면\n // setState의 deep-equality 체크에서 before=after로 판정되어 change 감지\n // 실패 → onchange가 fire되지 않음. Phase 1에서 확인된 규칙.\n const prev = (this.value || {}) as Record<string, any>\n const next = { ...prev, [jointName]: internal }\n this.value = next\n\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))\n }\n\n /**\n * 슬라이더 드래그 종료 시 브라우저가 네이티브 'change' 이벤트를 발행한다.\n * 이 이벤트가 OxPropertyEditor 베이스의 _valueChanged(renderRoot 리스너)에\n * 도달하면 input.value(문자열)를 this.value에 assign해버려서 joints 객체가\n * \"45\" 같은 문자열로 덮어쓰여진다. shadow DOM 내부에서 바로 stopPropagation.\n */\n private _stopChange = (e: Event) => {\n e.stopPropagation()\n }\n\n private _onReset = (e: MouseEvent) => {\n e.stopPropagation()\n this.value = {}\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))\n }\n\n connectedCallback() {\n super.connectedCallback()\n this._requestSelectedComponent()\n }\n\n disconnectedCallback() {\n super.disconnectedCallback()\n if (this._pollTimer) {\n clearInterval(this._pollTimer)\n this._pollTimer = undefined\n }\n }\n\n /**\n * 선택된 URDF 컴포넌트를 얻어 jointMeta를 읽는다. 로드가 아직이면 주기적 폴링.\n */\n private _requestSelectedComponent() {\n this.dispatchEvent(\n new CustomEvent('i-need-selected', {\n bubbles: true,\n composed: true,\n detail: {\n callback: (selected: any[]) => {\n const component = selected?.[0]\n if (!component) return\n this._component = component\n this._refreshJointMeta()\n }\n }\n })\n )\n }\n\n private _refreshJointMeta() {\n const ro = this._component?.realObject\n const meta: JointMeta[] | undefined = ro?.jointMeta\n if (meta && meta.length > 0) {\n this.meta = meta\n if (this._pollTimer) {\n clearInterval(this._pollTimer)\n this._pollTimer = undefined\n }\n return\n }\n\n // 아직 로드 전이면 폴링 (최대 20회 = 10초)\n if (this._pollTimer) return\n let retries = 0\n this._pollTimer = window.setInterval(() => {\n const m = this._component?.realObject?.jointMeta\n if (m && m.length > 0) {\n this.meta = m\n clearInterval(this._pollTimer!)\n this._pollTimer = undefined\n } else if (++retries >= 20) {\n clearInterval(this._pollTimer!)\n this._pollTimer = undefined\n }\n }, 500)\n }\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import '@operato/i18n/ox-i18n.js';
2
+ import { TemplateResult } from 'lit';
3
+ import { OxPropertyEditor, PropertySpec } from '@operato/property-editor';
4
+ export default class URDFPresetEditor extends OxPropertyEditor {
5
+ static styles: import("lit").CSSResult[];
6
+ editorTemplate(value: any, _spec: PropertySpec): TemplateResult;
7
+ private _onSelect;
8
+ }
@@ -0,0 +1,114 @@
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ *
4
+ * URDF 프리셋 에디터.
5
+ * 드롭다운에서 프리셋 선택 시 컴포넌트의 src + 크기/축/스케일을 일괄 업데이트.
6
+ *
7
+ * 값: preset id ('custom' 또는 URDF_PRESETS[i].id).
8
+ * 변경 이벤트: i-need-selected로 컴포넌트에 set() 호출 → src/width/height/depth/
9
+ * upAxis/unitScale이 한 번에 propagate되어 부모의 undoableChange 한 덩어리로
10
+ * 묶임.
11
+ */
12
+ import { __decorate } from "tslib";
13
+ import '@operato/i18n/ox-i18n.js';
14
+ import { css, html } from 'lit';
15
+ import { customElement } from 'lit/decorators.js';
16
+ import { OxPropertyEditor } from '@operato/property-editor';
17
+ import { URDF_PRESETS, findPreset } from '../urdf-presets.js';
18
+ let URDFPresetEditor = class URDFPresetEditor extends OxPropertyEditor {
19
+ static styles = [
20
+ ...OxPropertyEditor.styles,
21
+ css `
22
+ select {
23
+ width: 100%;
24
+ padding: 4px 8px;
25
+ font-size: 12px;
26
+ border: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.2));
27
+ border-radius: 4px;
28
+ background: var(--md-sys-color-surface, #fff);
29
+ color: var(--md-sys-color-on-surface, #1c1b1f);
30
+ }
31
+ optgroup {
32
+ font-style: normal;
33
+ font-weight: 600;
34
+ }
35
+ `
36
+ ];
37
+ editorTemplate(value, _spec) {
38
+ const current = typeof value === 'string' && value ? value : 'custom';
39
+ // 카테고리별 그룹핑
40
+ const byCategory = {};
41
+ for (const preset of URDF_PRESETS) {
42
+ ;
43
+ (byCategory[preset.category] ||= []).push(preset);
44
+ }
45
+ const categoryOrder = ['arm', 'mobile', 'aerial', 'humanoid', 'quadruped'];
46
+ const categoryLabel = {
47
+ arm: 'Manipulators',
48
+ mobile: 'Mobile Platforms',
49
+ aerial: 'Aerial',
50
+ humanoid: 'Humanoids',
51
+ quadruped: 'Legged'
52
+ };
53
+ return html `
54
+ <select id="editor" @change=${this._onSelect} .value=${current}>
55
+ <option value="custom" ?selected=${current === 'custom'}>Custom URL…</option>
56
+ ${categoryOrder.map(cat => {
57
+ const list = byCategory[cat];
58
+ if (!list || list.length === 0)
59
+ return null;
60
+ return html `
61
+ <optgroup label=${categoryLabel[cat]}>
62
+ ${list.map(p => html `<option value=${p.id} ?selected=${current === p.id}>${p.display}</option>`)}
63
+ </optgroup>
64
+ `;
65
+ })}
66
+ </select>
67
+ `;
68
+ }
69
+ _onSelect = (e) => {
70
+ e.stopPropagation();
71
+ const select = e.target;
72
+ const id = select.value;
73
+ this.value = id;
74
+ if (id === 'custom') {
75
+ // Custom 선택은 현재 src를 건드리지 않음 — 사용자가 직접 src 입력 필드에
76
+ // URL을 넣도록 둔다.
77
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
78
+ return;
79
+ }
80
+ const preset = findPreset(id);
81
+ if (!preset)
82
+ return;
83
+ // i-need-selected로 컴포넌트에 다중 속성 일괄 set.
84
+ // property-change 이벤트도 별도로 발행하여 preset 값 자체도 저장.
85
+ this.dispatchEvent(new CustomEvent('i-need-selected', {
86
+ bubbles: true,
87
+ composed: true,
88
+ detail: {
89
+ callback: (selected) => {
90
+ const component = selected?.[0];
91
+ if (!component)
92
+ return;
93
+ component.set({
94
+ preset: id,
95
+ src: preset.src,
96
+ packages: preset.packages ?? null,
97
+ width: preset.width,
98
+ height: preset.height,
99
+ depth: preset.depth,
100
+ upAxis: preset.upAxis,
101
+ unitScale: preset.unitScale
102
+ });
103
+ }
104
+ }
105
+ }));
106
+ // 자체 change 이벤트도 발행하여 preset 값이 state에 저장되도록
107
+ this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }));
108
+ };
109
+ };
110
+ URDFPresetEditor = __decorate([
111
+ customElement('property-editor-urdf-preset')
112
+ ], URDFPresetEditor);
113
+ export default URDFPresetEditor;
114
+ //# sourceMappingURL=property-editor-urdf-preset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"property-editor-urdf-preset.js","sourceRoot":"","sources":["../../src/editors/property-editor-urdf-preset.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * URDF 프리셋 에디터.\n * 드롭다운에서 프리셋 선택 시 컴포넌트의 src + 크기/축/스케일을 일괄 업데이트.\n *\n * 값: preset id ('custom' 또는 URDF_PRESETS[i].id).\n * 변경 이벤트: i-need-selected로 컴포넌트에 set() 호출 → src/width/height/depth/\n * upAxis/unitScale이 한 번에 propagate되어 부모의 undoableChange 한 덩어리로\n * 묶임.\n */\n\nimport '@operato/i18n/ox-i18n.js'\n\nimport { css, html, TemplateResult } from 'lit'\nimport { customElement } from 'lit/decorators.js'\n\nimport { OxPropertyEditor, PropertySpec } from '@operato/property-editor'\n\nimport { URDF_PRESETS, findPreset } from '../urdf-presets.js'\n\n@customElement('property-editor-urdf-preset')\nexport default class URDFPresetEditor extends OxPropertyEditor {\n static styles = [\n ...OxPropertyEditor.styles,\n css`\n select {\n width: 100%;\n padding: 4px 8px;\n font-size: 12px;\n border: 1px solid var(--md-sys-color-outline-variant, rgba(0, 0, 0, 0.2));\n border-radius: 4px;\n background: var(--md-sys-color-surface, #fff);\n color: var(--md-sys-color-on-surface, #1c1b1f);\n }\n optgroup {\n font-style: normal;\n font-weight: 600;\n }\n `\n ]\n\n editorTemplate(value: any, _spec: PropertySpec): TemplateResult {\n const current = typeof value === 'string' && value ? value : 'custom'\n\n // 카테고리별 그룹핑\n const byCategory: Record<string, typeof URDF_PRESETS> = {}\n for (const preset of URDF_PRESETS) {\n ;(byCategory[preset.category] ||= []).push(preset)\n }\n const categoryOrder: Array<keyof typeof byCategory> = ['arm', 'mobile', 'aerial', 'humanoid', 'quadruped']\n const categoryLabel: Record<string, string> = {\n arm: 'Manipulators',\n mobile: 'Mobile Platforms',\n aerial: 'Aerial',\n humanoid: 'Humanoids',\n quadruped: 'Legged'\n }\n\n return html`\n <select id=\"editor\" @change=${this._onSelect} .value=${current}>\n <option value=\"custom\" ?selected=${current === 'custom'}>Custom URL…</option>\n ${categoryOrder.map(cat => {\n const list = byCategory[cat]\n if (!list || list.length === 0) return null\n return html`\n <optgroup label=${categoryLabel[cat]}>\n ${list.map(\n p => html`<option value=${p.id} ?selected=${current === p.id}>${p.display}</option>`\n )}\n </optgroup>\n `\n })}\n </select>\n `\n }\n\n private _onSelect = (e: Event) => {\n e.stopPropagation()\n const select = e.target as HTMLSelectElement\n const id = select.value\n\n this.value = id\n\n if (id === 'custom') {\n // Custom 선택은 현재 src를 건드리지 않음 — 사용자가 직접 src 입력 필드에\n // URL을 넣도록 둔다.\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))\n return\n }\n\n const preset = findPreset(id)\n if (!preset) return\n\n // i-need-selected로 컴포넌트에 다중 속성 일괄 set.\n // property-change 이벤트도 별도로 발행하여 preset 값 자체도 저장.\n this.dispatchEvent(\n new CustomEvent('i-need-selected', {\n bubbles: true,\n composed: true,\n detail: {\n callback: (selected: any[]) => {\n const component = selected?.[0]\n if (!component) return\n component.set({\n preset: id,\n src: preset.src,\n packages: preset.packages ?? null,\n width: preset.width,\n height: preset.height,\n depth: preset.depth,\n upAxis: preset.upAxis,\n unitScale: preset.unitScale\n })\n }\n }\n })\n )\n\n // 자체 change 이벤트도 발행하여 preset 값이 state에 저장되도록\n this.dispatchEvent(new CustomEvent('change', { bubbles: true, composed: true }))\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,8 @@
1
- export { default as UrdfViewer } from './urdf-viewer.js';
2
- export { default as UrdfController } from './urdf-controller.js';
1
+ import './urdf-object.js';
2
+ import './smoothing-controller.js';
3
+ export { RealObjectURDF } from './real-object-urdf.js';
4
+ export type { URDFJointState, URDFJointMeta } from './real-object-urdf.js';
5
+ export { URDFObject } from './urdf-object.js';
6
+ export { registerJointController, createJointController, listJointControllers } from './joint-controller.js';
7
+ export type { JointController, JointControllerContext, JointControllerFactory } from './joint-controller.js';
8
+ export { SmoothingController } from './smoothing-controller.js';
package/dist/index.js CHANGED
@@ -1,3 +1,23 @@
1
- export { default as UrdfViewer } from './urdf-viewer.js';
2
- export { default as UrdfController } from './urdf-controller.js';
1
+ /*
2
+ * Copyright © HatioLab Inc. All rights reserved.
3
+ *
4
+ * URDF 플러그인 — things-scene의 RealObjectExternalModel 베이스를 상속받아
5
+ * URDF(Unified Robot Description Format) 로봇을 네이티브 3D 씬 오브젝트로
6
+ * 렌더링한다. @sceneComponent('urdf')로 등록되어 things-factory 보드에서
7
+ * 일반 컴포넌트처럼 사용 가능.
8
+ *
9
+ * 기본 loader는 STL/DAE/GLB를 지원하며, GLB는 things-scene의 RealObjectGLTF
10
+ * 캐시와 공유한다. xacro 전처리는 현재 미지원(ros-industrial 로봇 등은 사전
11
+ * 변환된 URDF 필요).
12
+ */
13
+ // @sceneComponent decorator side-effect를 위해 import
14
+ import './urdf-object.js';
15
+ // 기본 'smooth' controller 등록 (side-effect)
16
+ import './smoothing-controller.js';
17
+ export { RealObjectURDF } from './real-object-urdf.js';
18
+ export { URDFObject } from './urdf-object.js';
19
+ // 외부 모듈이 자체 JointController를 등록할 때 사용하는 공개 API.
20
+ // 예: @operato/scene-urdf-rapier가 `registerJointController('rapier', ...)`.
21
+ export { registerJointController, createJointController, listJointControllers } from './joint-controller.js';
22
+ export { SmoothingController } from './smoothing-controller.js';
3
23
  //# sourceMappingURL=index.js.map