@operato/scene-urdf 10.0.0-beta.22 → 10.0.0-beta.30

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.
@@ -1,6 +1,252 @@
1
- import { Shape, type ComponentNature, type Control, type IRealObject } from '@hatiolab/things-scene';
1
+ import { Shape, type ComponentNature, type IRealObject } from '@hatiolab/things-scene';
2
2
  import type { URDFJointState, URDFJointMeta } from './real-object-urdf.js';
3
3
  declare const URDFObject_base: (new (...args: any[]) => {
4
+ _topViewSnapshot?: HTMLCanvasElement;
5
+ _snapshotLoading: boolean;
6
+ _ratioLocking: boolean;
7
+ is3dish(): boolean;
8
+ get hasTextProperty(): boolean;
9
+ get controls(): never[];
10
+ render(ctx: CanvasRenderingContext2D): void;
11
+ onchange(after: Record<string, any>, before: Record<string, any>): void;
12
+ _ensureTopViewSnapshot(): void;
13
+ buildRealObject(): IRealObject | undefined;
14
+ get source(): string | undefined;
15
+ set source(v: string | undefined): any;
16
+ get play(): boolean;
17
+ set play(value: boolean): any;
18
+ get nodes(): Record<string, import("@hatiolab/things-scene").GLTFNodeState> | undefined;
19
+ set nodes(value: Record<string, import("@hatiolab/things-scene").GLTFNodeState> | undefined): any;
20
+ get animations(): Record<string, import("@hatiolab/things-scene").GLTFAnimationState> | undefined;
21
+ set animations(value: Record<string, import("@hatiolab/things-scene").GLTFAnimationState> | undefined): any;
22
+ get playTargets(): string[] | string | undefined;
23
+ set playTargets(value: string[] | string | undefined): any;
24
+ get fillStyleTargets(): string[] | undefined;
25
+ set fillStyleTargets(value: string[] | undefined): any;
26
+ get nodeNames(): string[];
27
+ get animationNames(): string[];
28
+ set dimension(dimension: {
29
+ width: number;
30
+ height: number;
31
+ depth: number;
32
+ }): any;
33
+ _applyRatioLock(props: any): any;
34
+ set(props: any, propval?: any): any;
35
+ setState(props: any, propval?: any): any;
36
+ _app: any;
37
+ _model: any;
38
+ _state: any;
39
+ _delta: any;
40
+ _animation: any;
41
+ _animate: any;
42
+ _parent: any;
43
+ _disposed: any;
44
+ _textHidden: any;
45
+ _text_substitutor: any;
46
+ _value_substitutor: any;
47
+ _mappings: any;
48
+ _realObject: IRealObject | undefined;
49
+ _cachedState: any;
50
+ updatedAt: any;
51
+ fontSize: any;
52
+ __cache__: any;
53
+ created(): void;
54
+ added(parent: any): void;
55
+ removed(parent: any): void;
56
+ ready(): Promise<void>;
57
+ touch(): void;
58
+ clearCache(...attrs: any[]): void;
59
+ removeSelf(completely: any): void;
60
+ resetAnimation(): void;
61
+ dispose(): void;
62
+ get nature(): ComponentNature;
63
+ get disposed(): boolean;
64
+ isLayer(): boolean;
65
+ isGroup(): boolean;
66
+ isContainer(): this is import("@hatiolab/things-scene").Container;
67
+ isLine(): boolean;
68
+ isRoot(): boolean;
69
+ isRootModel(): boolean;
70
+ get is3dMode(): boolean;
71
+ isIn3DSpace(): boolean;
72
+ isTemplate(): boolean;
73
+ isHTMLElement(): boolean;
74
+ isConnectable(): boolean;
75
+ isIdentifiable(): boolean;
76
+ isPositionable(): boolean;
77
+ replaceRefids(replaceMap: any): void;
78
+ get(property: any): any;
79
+ getState(property: any): any;
80
+ get model(): any;
81
+ get state(): import("@hatiolab/things-scene").State;
82
+ get hierarchy(): any;
83
+ get volatile(): never[];
84
+ _applyProps(target: any, props: any, options: any): any;
85
+ contains(x: number, y: number): boolean;
86
+ move(offset: {
87
+ x: number;
88
+ y: number;
89
+ }, ...args: boolean[]): void;
90
+ symmetryX(x?: number): void;
91
+ symmetryY(y: number): void;
92
+ adjustResize(bounds: import("@hatiolab/things-scene").BOUNDS, origin_bounds: import("@hatiolab/things-scene").BOUNDS, diagonal: boolean): {
93
+ left: any;
94
+ top: any;
95
+ width: any;
96
+ height: any;
97
+ };
98
+ adjustRotation(rotation: number, step: boolean): number;
99
+ outline(progress: number): any;
100
+ get bounds(): import("@hatiolab/things-scene").BOUNDS;
101
+ set bounds(b: import("@hatiolab/things-scene").BOUNDS): any;
102
+ get center(): import("@hatiolab/things-scene").POINT;
103
+ set center(p: import("@hatiolab/things-scene").POINT): any;
104
+ get location(): import("@hatiolab/things-scene").POINT;
105
+ set location(l: import("@hatiolab/things-scene").POINT): any;
106
+ get rotate(): import("@hatiolab/things-scene").POINT;
107
+ set rotate(r: import("@hatiolab/things-scene").POINT): any;
108
+ get path(): import("@hatiolab/things-scene").POINT[];
109
+ set path(p: import("@hatiolab/things-scene").POINT[]): any;
110
+ get drawPath(): import("@hatiolab/things-scene").POINT[];
111
+ get rotatePoint(): import("@hatiolab/things-scene").POINT;
112
+ get mutable(): boolean;
113
+ get resizable(): boolean;
114
+ get rotatable(): boolean;
115
+ get realObject(): IRealObject | undefined;
116
+ draw(context?: import("@hatiolab/things-scene").SceneRenderContext): void;
117
+ prerender(context: import("@hatiolab/things-scene").SceneRenderContext): void;
118
+ postrender(context: import("@hatiolab/things-scene").SceneRenderContext): void;
119
+ prepare(resolve: (component: import("@hatiolab/things-scene").Component) => void, reject: (reason: any) => void): void;
120
+ prepareIf(condition: boolean): void;
121
+ drawText(context: import("@hatiolab/things-scene").SceneRenderContext): void;
122
+ drawStroke(context: import("@hatiolab/things-scene").SceneRenderContext, override?: Record<string, unknown>): void;
123
+ drawFill(context: import("@hatiolab/things-scene").SceneRenderContext, override?: Record<string, unknown>): void;
124
+ get strokeStyle(): any;
125
+ set strokeStyle(v: any): any;
126
+ get fillStyle(): any;
127
+ set fillStyle(v: any): any;
128
+ get fontColor(): string;
129
+ set fontColor(v: string): any;
130
+ get rotation(): number;
131
+ set rotation(v: number): any;
132
+ get decorators(): string[];
133
+ get decotag(): string;
134
+ get hidden(): boolean;
135
+ set hidden(v: boolean): any;
136
+ get tag(): string;
137
+ set tag(v: string): any;
138
+ get appendum(): any;
139
+ set appendum(v: any): any;
140
+ defaultTextSubstitutor(): string;
141
+ textLines(context?: import("@hatiolab/things-scene").SceneRenderContext): any[][];
142
+ get font(): string;
143
+ get lineHeight(): number;
144
+ get textSubstitutor(): () => string;
145
+ get text(): string;
146
+ set text(v: string): any;
147
+ get textBounds(): import("@hatiolab/things-scene").BOUNDS;
148
+ get textRotation(): number;
149
+ get textHidden(): boolean;
150
+ set textHidden(v: boolean): any;
151
+ animate(opts: import("@hatiolab/things-scene").AnimationConfig): any;
152
+ effect(context: import("@hatiolab/things-scene").SceneRenderContext, model: any): void;
153
+ serialize(...others: any[]): string;
154
+ trim(): void;
155
+ closeScene(data: any): void;
156
+ delta(attr?: string | object, value?: any): any;
157
+ invalidate(): void;
158
+ get value(): any;
159
+ set value(v: any): any;
160
+ get data(): any;
161
+ set data(v: any): any;
162
+ set tap(v: any): any;
163
+ get mappings(): any[];
164
+ get retention(): number;
165
+ get animation(): import("@hatiolab/things-scene").AnimationController | undefined;
166
+ get started(): boolean;
167
+ set started(v: boolean): any;
168
+ findFirst(finder: string | ((c: import("@hatiolab/things-scene").Component) => boolean), ...others: any[]): import("@hatiolab/things-scene").Component | undefined;
169
+ findAll(s: string | ((c: import("@hatiolab/things-scene").Component) => boolean), ...others: any[]): any[] | undefined;
170
+ capture(x: number, y: number, except?: (c: import("@hatiolab/things-scene").Component) => boolean): any;
171
+ findAnchor(name: string): any;
172
+ isDescendible(container: import("@hatiolab/things-scene").Component): boolean;
173
+ getContext(component?: unknown): any;
174
+ get root(): import("@hatiolab/things-scene").Component;
175
+ get rootModel(): import("@hatiolab/things-scene").Component;
176
+ get parent(): import("@hatiolab/things-scene").Component;
177
+ set parent(v: import("@hatiolab/things-scene").Component): any;
178
+ get anchors(): import("@hatiolab/things-scene").Anchor[];
179
+ get scalable(): boolean;
180
+ get stuck(): boolean;
181
+ get capturable(): boolean;
182
+ get position(): string;
183
+ get origin(): string;
184
+ get offset(): import("@hatiolab/things-scene").POINT;
185
+ get app(): import("@hatiolab/things-scene").ApplicationContext;
186
+ drawEffect(context: import("@hatiolab/things-scene").SceneRenderContext): void;
187
+ prepareFill(resolve: Function, reject: Function): void;
188
+ prepareFillIf(condition: boolean): void;
189
+ onchangeFill(after: Record<string, any>, before: Record<string, any>): void;
190
+ drawImage(context: import("@hatiolab/things-scene").SceneRenderContext, image: HTMLImageElement, left: number, top: number, width: number, height: number): void;
191
+ mutateBounds(logic: ((bounds: import("@hatiolab/things-scene").BOUNDS) => import("@hatiolab/things-scene").BOUNDS | void) | null, context?: any): void;
192
+ mutatePath(beforeLogic: ((path: import("@hatiolab/things-scene").POINT[]) => import("@hatiolab/things-scene").POINT[] | void) | null, afterLogic: ((path: import("@hatiolab/things-scene").POINT[]) => import("@hatiolab/things-scene").POINT[] | void) | null, context?: any): void;
193
+ access(accessor: string): any;
194
+ substitute(template: string, data: any): string | undefined;
195
+ onchangeMappings(after: Record<string, any>, before: Record<string, any>): void;
196
+ onchangeData(after: Record<string, any>, before: Record<string, any>): void;
197
+ buildMappings(): void;
198
+ executeMappings(force?: boolean): void;
199
+ disposeMappings(): void;
200
+ ondropfile(transfered: FileList, files: string[]): void;
201
+ transcoordS2P(x: number, y: number, rp?: import("@hatiolab/things-scene").POINT): import("@hatiolab/things-scene").POINT;
202
+ transcoordP2S(x: number, y: number, rp?: import("@hatiolab/things-scene").POINT): import("@hatiolab/things-scene").POINT;
203
+ transcoordS2T(x: number, y: number, top?: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
204
+ transcoordT2P(x: number, y: number, top?: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
205
+ transcoordT2S(x: number, y: number, top?: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
206
+ transcoordS2TR(x: number, y: number, top?: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
207
+ transcoordS2O(x: number, y: number, target: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
208
+ transcoordC2S(x: number, y: number, top?: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
209
+ transcoordS2C(x: number, y: number, top?: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
210
+ toParent(x: number, y: number, rp?: import("@hatiolab/things-scene").POINT): import("@hatiolab/things-scene").POINT;
211
+ fromParent(x: number, y: number, rp?: import("@hatiolab/things-scene").POINT): import("@hatiolab/things-scene").POINT;
212
+ toScene(x: number, y: number, top?: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
213
+ fromScene(x: number, y: number, top?: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
214
+ toLocal(x: number, y: number, top?: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
215
+ toGlobal(x: number, y: number, top?: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
216
+ toOther(x: number, y: number, target: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
217
+ on(name: string | object, callback: Function, context?: any): any;
218
+ off(name?: string | object, callback?: Function, context?: any): any;
219
+ once(name: string | object, callback: Function, context?: any): any;
220
+ trigger(name: string, ...args: any[]): any;
221
+ delegate_on(delegator: any): any;
222
+ delegate_off(delegator: any): any;
223
+ calculateBounds?(): void;
224
+ oncreate_element?(element: HTMLElement): void;
225
+ removeComponent(component: import("@hatiolab/things-scene").Component, ghost?: boolean): void;
226
+ addComponent(component: import("@hatiolab/things-scene").Component, ghost?: boolean): void;
227
+ insertComponentAt(component: import("@hatiolab/things-scene").Component, index: number, ghost?: boolean): void;
228
+ getOverlay(component: import("@hatiolab/things-scene").Component): HTMLElement | undefined;
229
+ findById(id: string): import("@hatiolab/things-scene").Component | undefined;
230
+ findByRefid(ref: string | number): import("@hatiolab/things-scene").Component | undefined;
231
+ findAllById(id: string): import("@hatiolab/things-scene").Component[];
232
+ resize(): void;
233
+ fit(type?: string): void;
234
+ get components(): import("@hatiolab/things-scene").Component[] | undefined;
235
+ get layout(): any;
236
+ get auxOverlay(): HTMLElement | undefined;
237
+ get isReady(): boolean;
238
+ get unitScale(): number;
239
+ get selected(): import("@hatiolab/things-scene").Component[];
240
+ set selected(_v: import("@hatiolab/things-scene").Component[]): any;
241
+ get focused(): import("@hatiolab/things-scene").Component | null;
242
+ set focused(_v: import("@hatiolab/things-scene").Component | null): any;
243
+ get hasSameParentForAllSelected(): boolean;
244
+ set hasSameParentForAllSelected(_v: boolean): any;
245
+ get fitMode(): string | undefined;
246
+ set fitMode(_v: string | undefined): any;
247
+ get element(): HTMLElement | null;
248
+ set element(_v: HTMLElement | null): any;
249
+ }) & (new (...args: any[]) => {
4
250
  contains(x: number, y: number): boolean;
5
251
  get path(): {
6
252
  x: any;
@@ -50,7 +296,7 @@ declare const URDFObject_base: (new (...args: any[]) => {
50
296
  get disposed(): boolean;
51
297
  isLayer(): boolean;
52
298
  isGroup(): boolean;
53
- isContainer(): this is import("@hatiolab/things-scene/dist-types/types/component.js").Container;
299
+ isContainer(): this is import("@hatiolab/things-scene").Container;
54
300
  isLine(): boolean;
55
301
  isRoot(): boolean;
56
302
  isRootModel(): boolean;
@@ -66,9 +312,9 @@ declare const URDFObject_base: (new (...args: any[]) => {
66
312
  get(property: any): any;
67
313
  set(props: any, propval?: any): any;
68
314
  getState(property: any): any;
69
- setState(props: any, propval?: any): any;
315
+ setState(props: Partial<import("@hatiolab/things-scene").State> | string, propval?: any): any;
70
316
  get model(): any;
71
- get state(): any;
317
+ get state(): import("@hatiolab/things-scene").State;
72
318
  get hierarchy(): any;
73
319
  get volatile(): never[];
74
320
  _applyProps(target: any, props: any, options: any): any;
@@ -238,22 +484,15 @@ declare const URDFObject_base: (new (...args: any[]) => {
238
484
  set element(_v: HTMLElement | null): any;
239
485
  }) & typeof Shape;
240
486
  export declare class URDFObject extends URDFObject_base {
241
- get hasTextProperty(): boolean;
242
- is3dish(): boolean;
243
- get controls(): Array<Control> | undefined;
244
- render(context: CanvasRenderingContext2D): void;
245
- ready(): Promise<void>;
246
- onchange(after: Record<string, any>, before: Record<string, any>): void;
247
- private _ensureTopViewSnapshot;
487
+ /**
488
+ * URDF 캐시 hit 만 처리. miss 시는 RealObjectURDF.\_onLoaded 가
489
+ * 캐시를 채우고 component.\_topViewSnapshot 을 직접 세팅한다 (URDF
490
+ * 로더는 GLB 로더와 다르므로 mixin 의 디폴트 — RealObjectGLTF.loadGLTF
491
+ * 직접 호출 — 을 쓸 수 없다).
492
+ */
493
+ _ensureTopViewSnapshot(): void;
248
494
  buildRealObject(): IRealObject | undefined;
249
495
  get nature(): ComponentNature;
250
- get source(): any;
251
- set source(source: any);
252
- set dimension(dimension: {
253
- width: number;
254
- height: number;
255
- depth: number;
256
- });
257
496
  /** 조인트 상태 맵. 값은 숫자(라디안/거리) 또는 { value } 형식 허용. */
258
497
  get joints(): Record<string, URDFJointState | number> | undefined;
259
498
  set joints(value: Record<string, URDFJointState | number> | undefined);
@@ -3,9 +3,23 @@
3
3
  *
4
4
  * URDF 로봇을 things-scene 3D 공간에 네이티브로 임베드한다.
5
5
  * GLTFObject와 대칭적인 구조 — 2D 탑뷰는 스냅샷 렌더로 표현한다.
6
+ *
7
+ * GltfComponent mixin 합류 (2026-05): GLB / URDF 양쪽이 공유하는
8
+ * - 2D 탑뷰 fallback render (스냅샷 + 라벨)
9
+ * - src 변경 → 스냅샷 reset → ensure 라이프사이클
10
+ * - is3dish / hasTextProperty / controls / source / dimension 보일러플레이트
11
+ * - ratioLock 인터셉트
12
+ * 를 mixin에서 받음. URDF 고유는:
13
+ * - `_ensureTopViewSnapshot()` override — URDF 로더는 GLB 로더와 다르므로
14
+ * mixin 디폴트(GLB loader 호출)를 쓸 수 없고, 캐시 히트만 처리하고
15
+ * miss 는 RealObjectURDF._onLoaded 가 채우도록 위임.
16
+ * - `buildRealObject()` override — RealObjectURDF 반환.
17
+ * - NATURE — urdf-preset / urdf-joints / upAxis / unitScale / autoAnimate
18
+ * / physics 등 로봇 도메인 전용 properties (GLB의 gltfNatureProperties
19
+ * 와 의미가 다르므로 spread 안 함).
6
20
  */
7
21
  import { __decorate } from "tslib";
8
- import { RectPath, Shape, sceneComponent } from '@hatiolab/things-scene';
22
+ import { GltfComponent, RectPath, Shape, sceneComponent } from '@hatiolab/things-scene';
9
23
  import { RealObjectURDF } from './real-object-urdf.js';
10
24
  const BASE_URL_ALIAS = '$base_url';
11
25
  const NATURE = {
@@ -91,62 +105,23 @@ const NATURE = {
91
105
  'value-property': 'source',
92
106
  help: 'scene/component/urdf'
93
107
  };
94
- let URDFObject = class URDFObject extends RectPath(Shape) {
95
- get hasTextProperty() {
96
- return false;
97
- }
98
- is3dish() {
99
- return true;
100
- }
101
- get controls() {
102
- return [];
103
- }
104
- render(context) {
105
- const { left, top, width, height } = this.bounds;
106
- context.beginPath();
107
- const snapshot = this._topViewSnapshot;
108
- if (snapshot) {
109
- context.drawImage(snapshot, left, top, width, height);
110
- }
111
- else {
112
- // 폴백: 로봇 아이콘 박스 + 중앙 라벨
113
- context.rect(left, top, width, height);
114
- context.fillStyle = '#e3f2fd';
115
- context.fill();
116
- context.strokeStyle = '#1976d2';
117
- context.lineWidth = 1;
118
- context.stroke();
119
- const src = this.getState('src');
120
- const label = src ? src.split('/').pop()?.replace(/\.[^.]+$/, '') || 'URDF' : 'URDF';
121
- const fontSize = Math.max(10, Math.min(width, height) * 0.12);
122
- context.fillStyle = '#1976d2';
123
- context.font = `${fontSize}px sans-serif`;
124
- context.textAlign = 'center';
125
- context.textBaseline = 'middle';
126
- context.fillText(label, left + width / 2, top + height / 2, width * 0.9);
127
- }
128
- }
129
- async ready() {
130
- await super.ready();
131
- this._ensureTopViewSnapshot();
132
- }
133
- onchange(after, before) {
134
- super.onchange(after, before);
135
- if ('src' in after) {
136
- this._topViewSnapshot = undefined;
137
- this._ensureTopViewSnapshot();
138
- }
139
- }
108
+ let URDFObject = class URDFObject extends GltfComponent(RectPath(Shape)) {
109
+ /**
110
+ * URDF 캐시 hit 만 처리. miss 시는 RealObjectURDF.\_onLoaded 가
111
+ * 캐시를 채우고 component.\_topViewSnapshot 을 직접 세팅한다 (URDF
112
+ * 로더는 GLB 로더와 다르므로 mixin 의 디폴트 — RealObjectGLTF.loadGLTF
113
+ * 직접 호출 — 을 쓸 수 없다).
114
+ */
140
115
  _ensureTopViewSnapshot() {
141
116
  const src = this.getState('src');
142
117
  if (!src)
143
118
  return;
144
119
  const cached = RealObjectURDF.getTopViewCache(src);
145
120
  if (cached) {
121
+ ;
146
122
  this._topViewSnapshot = cached;
147
123
  this.invalidate();
148
124
  }
149
- // 캐시 miss인 경우 3D 씬이 활성화되면 _onLoaded에서 자동 생성한다.
150
125
  }
151
126
  buildRealObject() {
152
127
  return new RealObjectURDF(this);
@@ -154,17 +129,6 @@ let URDFObject = class URDFObject extends RectPath(Shape) {
154
129
  get nature() {
155
130
  return NATURE;
156
131
  }
157
- get source() {
158
- return this.getState('src');
159
- }
160
- set source(source) {
161
- this.setState('src', source);
162
- }
163
- set dimension(dimension) {
164
- const { width = 1, height = 1, depth = 1 } = dimension;
165
- this.setState({ width, height, depth });
166
- this.realObject?.updateDimension();
167
- }
168
132
  // --- URDF 바인딩 API ---
169
133
  /** 조인트 상태 맵. 값은 숫자(라디안/거리) 또는 { value } 형식 허용. */
170
134
  get joints() {
@@ -1 +1 @@
1
- {"version":3,"file":"urdf-object.js","sourceRoot":"","sources":["../src/urdf-object.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * URDF 로봇을 things-scene 3D 공간에 네이티브로 임베드한다.\n * GLTFObject와 대칭적인 구조 — 2D 탑뷰는 스냅샷 렌더로 표현한다.\n */\n\nimport {\n RectPath,\n Shape,\n sceneComponent,\n type ComponentNature,\n type Control,\n type IRealObject\n} from '@hatiolab/things-scene'\nimport { RealObjectURDF } from './real-object-urdf.js'\nimport type { URDFJointState, URDFJointMeta } from './real-object-urdf.js'\n\nconst BASE_URL_ALIAS = '$base_url'\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n {\n type: 'urdf-preset',\n label: 'preset',\n name: 'preset'\n },\n {\n type: 'string',\n label: 'url',\n name: 'src',\n property: {\n displayField: 'id',\n displayFullUrl: true,\n baseUrlAlias: BASE_URL_ALIAS,\n defaultStorage: '3d-model',\n storageFilters: {\n type: Array,\n value: [\n {\n name: 'category',\n value: 'urdf'\n }\n ]\n },\n useUpload: true,\n category: 'application'\n }\n },\n {\n type: 'select',\n label: 'up-axis',\n name: 'upAxis',\n property: {\n options: [\n { display: 'Z (URDF default)', value: 'z' },\n { display: 'Y (Three.js default)', value: 'y' }\n ]\n }\n },\n {\n type: 'number',\n label: 'unit-scale',\n name: 'unitScale',\n placeholder: '1000'\n },\n {\n type: 'select',\n label: 'auto-animate',\n name: 'autoAnimate',\n property: {\n options: [\n { display: 'Off', value: 'none' },\n { display: 'Sine swing', value: 'sine' }\n ]\n }\n },\n {\n type: 'select',\n label: 'physics',\n name: 'physics',\n property: {\n options: [\n { display: 'Direct (immediate)', value: 'none' },\n { display: 'Smooth (damped)', value: 'smooth' }\n ]\n }\n },\n {\n type: 'urdf-joints',\n label: '',\n name: 'joints',\n editor: {\n fullwidth: true\n }\n }\n ],\n 'value-property': 'source',\n help: 'scene/component/urdf'\n}\n\n@sceneComponent('urdf')\nexport class URDFObject extends RectPath(Shape) {\n get hasTextProperty() {\n return false\n }\n\n is3dish() {\n return true\n }\n\n get controls(): Array<Control> | undefined {\n return []\n }\n\n render(context: CanvasRenderingContext2D) {\n const { left, top, width, height } = this.bounds\n\n context.beginPath()\n\n const snapshot = (this as any)._topViewSnapshot as HTMLCanvasElement | undefined\n if (snapshot) {\n context.drawImage(snapshot, left, top, width, height)\n } else {\n // 폴백: 로봇 아이콘 박스 + 중앙 라벨\n context.rect(left, top, width, height)\n context.fillStyle = '#e3f2fd'\n context.fill()\n context.strokeStyle = '#1976d2'\n context.lineWidth = 1\n context.stroke()\n\n const src = this.getState('src') as string | undefined\n const label = src ? src.split('/').pop()?.replace(/\\.[^.]+$/, '') || 'URDF' : 'URDF'\n const fontSize = Math.max(10, Math.min(width, height) * 0.12)\n context.fillStyle = '#1976d2'\n context.font = `${fontSize}px sans-serif`\n context.textAlign = 'center'\n context.textBaseline = 'middle'\n context.fillText(label, left + width / 2, top + height / 2, width * 0.9)\n }\n }\n\n async ready() {\n await super.ready()\n this._ensureTopViewSnapshot()\n }\n\n onchange(after: Record<string, any>, before: Record<string, any>) {\n super.onchange(after, before)\n if ('src' in after) {\n (this as any)._topViewSnapshot = undefined\n this._ensureTopViewSnapshot()\n }\n }\n\n private _ensureTopViewSnapshot() {\n const src = this.getState('src') as string | undefined\n if (!src) return\n\n const cached = RealObjectURDF.getTopViewCache(src)\n if (cached) {\n (this as any)._topViewSnapshot = cached\n this.invalidate()\n }\n // 캐시 miss인 경우 3D 씬이 활성화되면 _onLoaded에서 자동 생성한다.\n }\n\n buildRealObject(): IRealObject | undefined {\n return new RealObjectURDF(this as any)\n }\n\n get nature() {\n return NATURE\n }\n\n get source() {\n return this.getState('src')\n }\n\n set source(source) {\n this.setState('src', source)\n }\n\n set dimension(dimension: { width: number; height: number; depth: number }) {\n const { width = 1, height = 1, depth = 1 } = dimension\n this.setState({ width, height, depth })\n this.realObject?.updateDimension()\n }\n\n // --- URDF 바인딩 API ---\n\n /** 조인트 상태 맵. 값은 숫자(라디안/거리) 또는 { value } 형식 허용. */\n get joints(): Record<string, URDFJointState | number> | undefined {\n return this.getState('joints')\n }\n\n set joints(value: Record<string, URDFJointState | number> | undefined) {\n this.setState('joints', value)\n }\n\n /** 로드된 URDF의 조인트 메타 정보. property editor가 슬라이더 UI 생성에 사용. */\n get jointMeta(): URDFJointMeta[] {\n const ro = this.realObject as RealObjectURDF | undefined\n return ro?.jointMeta ?? []\n }\n\n get jointNames(): string[] {\n const ro = this.realObject as RealObjectURDF | undefined\n return ro?.jointNames ?? []\n }\n}\n"]}
1
+ {"version":3,"file":"urdf-object.js","sourceRoot":"","sources":["../src/urdf-object.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * URDF 로봇을 things-scene 3D 공간에 네이티브로 임베드한다.\n * GLTFObject와 대칭적인 구조 — 2D 탑뷰는 스냅샷 렌더로 표현한다.\n *\n * GltfComponent mixin 합류 (2026-05): GLB / URDF 양쪽이 공유하는\n * - 2D 탑뷰 fallback render (스냅샷 + 라벨)\n * - src 변경 → 스냅샷 reset → ensure 라이프사이클\n * - is3dish / hasTextProperty / controls / source / dimension 보일러플레이트\n * - ratioLock 인터셉트\n * 를 mixin에서 받음. URDF 고유는:\n * - `_ensureTopViewSnapshot()` override — URDF 로더는 GLB 로더와 다르므로\n * mixin 디폴트(GLB loader 호출)를 쓸 수 없고, 캐시 히트만 처리하고\n * miss 는 RealObjectURDF._onLoaded 가 채우도록 위임.\n * - `buildRealObject()` override — RealObjectURDF 반환.\n * - NATURE — urdf-preset / urdf-joints / upAxis / unitScale / autoAnimate\n * / physics 등 로봇 도메인 전용 properties (GLB의 gltfNatureProperties\n * 와 의미가 다르므로 spread 안 함).\n */\n\nimport {\n GltfComponent,\n RectPath,\n Shape,\n sceneComponent,\n type ComponentNature,\n type IRealObject\n} from '@hatiolab/things-scene'\n\nimport { RealObjectURDF } from './real-object-urdf.js'\nimport type { URDFJointState, URDFJointMeta } from './real-object-urdf.js'\n\nconst BASE_URL_ALIAS = '$base_url'\n\nconst NATURE: ComponentNature = {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [\n {\n type: 'urdf-preset',\n label: 'preset',\n name: 'preset'\n },\n {\n type: 'string',\n label: 'url',\n name: 'src',\n property: {\n displayField: 'id',\n displayFullUrl: true,\n baseUrlAlias: BASE_URL_ALIAS,\n defaultStorage: '3d-model',\n storageFilters: {\n type: Array,\n value: [\n {\n name: 'category',\n value: 'urdf'\n }\n ]\n },\n useUpload: true,\n category: 'application'\n }\n },\n {\n type: 'select',\n label: 'up-axis',\n name: 'upAxis',\n property: {\n options: [\n { display: 'Z (URDF default)', value: 'z' },\n { display: 'Y (Three.js default)', value: 'y' }\n ]\n }\n },\n {\n type: 'number',\n label: 'unit-scale',\n name: 'unitScale',\n placeholder: '1000'\n },\n {\n type: 'select',\n label: 'auto-animate',\n name: 'autoAnimate',\n property: {\n options: [\n { display: 'Off', value: 'none' },\n { display: 'Sine swing', value: 'sine' }\n ]\n }\n },\n {\n type: 'select',\n label: 'physics',\n name: 'physics',\n property: {\n options: [\n { display: 'Direct (immediate)', value: 'none' },\n { display: 'Smooth (damped)', value: 'smooth' }\n ]\n }\n },\n {\n type: 'urdf-joints',\n label: '',\n name: 'joints',\n editor: {\n fullwidth: true\n }\n }\n ],\n 'value-property': 'source',\n help: 'scene/component/urdf'\n}\n\n@sceneComponent('urdf')\nexport class URDFObject extends GltfComponent(RectPath(Shape)) {\n /**\n * URDF 캐시 hit 만 처리. miss 시는 RealObjectURDF.\\_onLoaded 가\n * 캐시를 채우고 component.\\_topViewSnapshot 을 직접 세팅한다 (URDF\n * 로더는 GLB 로더와 다르므로 mixin 의 디폴트 — RealObjectGLTF.loadGLTF\n * 직접 호출 — 을 쓸 수 없다).\n */\n _ensureTopViewSnapshot() {\n const src = this.getState('src') as string | undefined\n if (!src) return\n\n const cached = RealObjectURDF.getTopViewCache(src)\n if (cached) {\n ;(this as any)._topViewSnapshot = cached\n this.invalidate()\n }\n }\n\n override buildRealObject(): IRealObject | undefined {\n return new RealObjectURDF(this as any)\n }\n\n get nature() {\n return NATURE\n }\n\n // --- URDF 바인딩 API ---\n\n /** 조인트 상태 맵. 값은 숫자(라디안/거리) 또는 { value } 형식 허용. */\n get joints(): Record<string, URDFJointState | number> | undefined {\n return this.getState('joints')\n }\n\n set joints(value: Record<string, URDFJointState | number> | undefined) {\n this.setState('joints', value)\n }\n\n /** 로드된 URDF의 조인트 메타 정보. property editor가 슬라이더 UI 생성에 사용. */\n get jointMeta(): URDFJointMeta[] {\n const ro = this.realObject as RealObjectURDF | undefined\n return ro?.jointMeta ?? []\n }\n\n get jointNames(): string[] {\n const ro = this.realObject as RealObjectURDF | undefined\n return ro?.jointNames ?? []\n }\n}\n"]}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@operato/scene-urdf",
3
3
  "description": "Scene module for manipulating robot simulation through URDF format",
4
4
  "author": "heartyoh",
5
- "version": "10.0.0-beta.22",
5
+ "version": "10.0.0-beta.30",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "module": "dist/index.js",
@@ -66,5 +66,5 @@
66
66
  "prettier --write"
67
67
  ]
68
68
  },
69
- "gitHead": "f48e52f4f5fdc30ec06af9da7cf253f6e29cfb0e"
69
+ "gitHead": "06b35b1726ec4f27ee76657ce341c6c6f3ba1b3a"
70
70
  }