@operato/scene-urdf 10.0.0-beta.24 → 10.0.0-beta.31
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/joint-controller.d.ts +1 -1
- package/dist/joint-controller.js.map +1 -1
- package/dist/real-object-urdf.d.ts +4 -4
- package/dist/real-object-urdf.js +33 -31
- package/dist/real-object-urdf.js.map +1 -1
- package/dist/urdf-object.d.ts +19 -8
- package/dist/urdf-object.js.map +1 -1
- package/package.json +2 -2
|
@@ -17,7 +17,7 @@ export interface JointController {
|
|
|
17
17
|
*/
|
|
18
18
|
setTargets(targets: Record<string, number>): void;
|
|
19
19
|
/**
|
|
20
|
-
* 매 프레임 호출 (
|
|
20
|
+
* 매 프레임 호출 (things-scene `frameClock` 'real' 구독). dt는 초 단위.
|
|
21
21
|
* 관절에 값을 적용(joint.setJointValue)하고, 값이 실제로 변경됐다면 true를
|
|
22
22
|
* 반환 (상위가 invalidate를 결정).
|
|
23
23
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"joint-controller.js","sourceRoot":"","sources":["../src/joint-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAqCH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkC,CAAA;AAE1D;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY,EAAE,OAA+B;IACnF,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;AAC7B,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,CAAA;AAC/B,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;AAC7B,CAAC","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * URDF 조인트 제어기 추상화.\n *\n * 배경:\n * state.joints는 조인트의 \"목표값\"을 표현하고, 실제 URDFJoint에 적용되는 값은\n * 어떤 정책을 따를지에 따라 달라질 수 있다.\n * - Direct: 목표값을 즉시 적용 (현재 기본 동작)\n * - Smooth: 스프링-댐퍼로 부드럽게 수렴\n * - Rapier 등 물리: rigid body 시뮬레이션의 결과를 관절에 반영\n *\n * 이들은 모두 \"목표값 → 실제 적용값\" 매핑이라는 공통 추상으로 묶을 수 있다.\n *\n * 확장:\n * 외부 패키지가 `registerJointController('rapier', ...)` 형태로 구현을 등록해\n * urdf 컴포넌트의 state.physics 값을 통해 선택 가능. 예를 들어 미래의\n * `@operato/scene-urdf-rapier` 패키지가 Rapier 기반 controller를 register.\n */\n\nimport type { URDFRobot, URDFJoint } from 'urdf-loader'\nimport type { URDFJointMeta } from './real-object-urdf.js'\n\nexport interface JointControllerContext {\n robot: URDFRobot\n /** non-fixed + fixed 포함 모든 조인트 */\n joints: Map<string, URDFJoint>\n /** non-fixed 조인트의 메타 (type, axis, limit) — fixed는 제외됨 */\n jointMeta: Map<string, URDFJointMeta>\n}\n\nexport interface JointController {\n /** 로봇 로드 직후 1회 호출. 내부 상태(current/target/velocity 등) 초기화. */\n setup(ctx: JointControllerContext): void\n\n /**\n * state.joints 변경 시 호출. controller가 `targets` 맵을 내부 저장하고\n * tick에서 실제 적용을 결정한다. 각 value는 joint의 internal unit\n * (revolute=rad, prismatic=m).\n */\n setTargets(targets: Record<string, number>): void\n\n /**\n * 매 프레임 호출 (
|
|
1
|
+
{"version":3,"file":"joint-controller.js","sourceRoot":"","sources":["../src/joint-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAqCH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkC,CAAA;AAE1D;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY,EAAE,OAA+B;IACnF,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;AAC7B,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,CAAA;AAC/B,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;AAC7B,CAAC","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * URDF 조인트 제어기 추상화.\n *\n * 배경:\n * state.joints는 조인트의 \"목표값\"을 표현하고, 실제 URDFJoint에 적용되는 값은\n * 어떤 정책을 따를지에 따라 달라질 수 있다.\n * - Direct: 목표값을 즉시 적용 (현재 기본 동작)\n * - Smooth: 스프링-댐퍼로 부드럽게 수렴\n * - Rapier 등 물리: rigid body 시뮬레이션의 결과를 관절에 반영\n *\n * 이들은 모두 \"목표값 → 실제 적용값\" 매핑이라는 공통 추상으로 묶을 수 있다.\n *\n * 확장:\n * 외부 패키지가 `registerJointController('rapier', ...)` 형태로 구현을 등록해\n * urdf 컴포넌트의 state.physics 값을 통해 선택 가능. 예를 들어 미래의\n * `@operato/scene-urdf-rapier` 패키지가 Rapier 기반 controller를 register.\n */\n\nimport type { URDFRobot, URDFJoint } from 'urdf-loader'\nimport type { URDFJointMeta } from './real-object-urdf.js'\n\nexport interface JointControllerContext {\n robot: URDFRobot\n /** non-fixed + fixed 포함 모든 조인트 */\n joints: Map<string, URDFJoint>\n /** non-fixed 조인트의 메타 (type, axis, limit) — fixed는 제외됨 */\n jointMeta: Map<string, URDFJointMeta>\n}\n\nexport interface JointController {\n /** 로봇 로드 직후 1회 호출. 내부 상태(current/target/velocity 등) 초기화. */\n setup(ctx: JointControllerContext): void\n\n /**\n * state.joints 변경 시 호출. controller가 `targets` 맵을 내부 저장하고\n * tick에서 실제 적용을 결정한다. 각 value는 joint의 internal unit\n * (revolute=rad, prismatic=m).\n */\n setTargets(targets: Record<string, number>): void\n\n /**\n * 매 프레임 호출 (things-scene `frameClock` 'real' 구독). dt는 초 단위.\n * 관절에 값을 적용(joint.setJointValue)하고, 값이 실제로 변경됐다면 true를\n * 반환 (상위가 invalidate를 결정).\n */\n tick(dt: number): boolean\n\n /** 로봇 해제 시 호출. 내부 자원/캐시 정리. */\n dispose(): void\n}\n\nexport type JointControllerFactory = () => JointController\n\nconst registry = new Map<string, JointControllerFactory>()\n\n/**\n * 외부 패키지가 자신의 controller 구현을 등록한다.\n * 예: `registerJointController('rapier', () => new RapierJointController())`.\n */\nexport function registerJointController(name: string, factory: JointControllerFactory): void {\n registry.set(name, factory)\n}\n\nexport function createJointController(name: string): JointController | undefined {\n return registry.get(name)?.()\n}\n\nexport function listJointControllers(): string[] {\n return [...registry.keys()]\n}\n"]}
|
|
@@ -2,6 +2,7 @@ import * as THREE from 'three';
|
|
|
2
2
|
import type { URDFRobot } from 'urdf-loader';
|
|
3
3
|
import type { Properties } from '@hatiolab/things-scene';
|
|
4
4
|
import { RealObjectExternalModel } from '@hatiolab/things-scene';
|
|
5
|
+
import type { URDFObject } from './urdf-object.js';
|
|
5
6
|
export interface URDFJointState {
|
|
6
7
|
/** revolute/continuous: 라디안. prismatic: 거리(URDF 단위). */
|
|
7
8
|
value?: number;
|
|
@@ -22,6 +23,7 @@ export interface URDFJointMeta {
|
|
|
22
23
|
};
|
|
23
24
|
}
|
|
24
25
|
export declare class RealObjectURDF extends RealObjectExternalModel<URDFRobot> {
|
|
26
|
+
get component(): URDFObject;
|
|
25
27
|
protected get _formatLabel(): string;
|
|
26
28
|
protected get _placeholderColor(): number;
|
|
27
29
|
private static _urdfCache;
|
|
@@ -47,11 +49,9 @@ export declare class RealObjectURDF extends RealObjectExternalModel<URDFRobot> {
|
|
|
47
49
|
private _jointIndex;
|
|
48
50
|
private _jointMeta;
|
|
49
51
|
private _jointOriginals;
|
|
50
|
-
private
|
|
51
|
-
private _animStartTime;
|
|
52
|
+
private _animUnsub;
|
|
52
53
|
private _controller?;
|
|
53
|
-
private
|
|
54
|
-
private _ctrlLastTime;
|
|
54
|
+
private _ctrlUnsub;
|
|
55
55
|
get robot(): URDFRobot | undefined;
|
|
56
56
|
/** 로드된 조인트 메타 리스트 (property panel 등이 참조) */
|
|
57
57
|
get jointMeta(): URDFJointMeta[];
|
package/dist/real-object-urdf.js
CHANGED
|
@@ -15,12 +15,15 @@ import * as THREE from 'three';
|
|
|
15
15
|
import URDFLoader from 'urdf-loader';
|
|
16
16
|
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
|
|
17
17
|
import { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader.js';
|
|
18
|
-
import { warn } from '@hatiolab/things-scene';
|
|
18
|
+
import { warn, frameClock } from '@hatiolab/things-scene';
|
|
19
19
|
import { RealObjectExternalModel } from '@hatiolab/things-scene';
|
|
20
20
|
import { RealObjectGLTF } from '@hatiolab/things-scene';
|
|
21
21
|
import { createJointController } from './joint-controller.js';
|
|
22
22
|
const X_NEG_PI_HALF = -Math.PI / 2;
|
|
23
23
|
export class RealObjectURDF extends RealObjectExternalModel {
|
|
24
|
+
get component() {
|
|
25
|
+
return super.component;
|
|
26
|
+
}
|
|
24
27
|
get _formatLabel() { return 'urdf'; }
|
|
25
28
|
get _placeholderColor() { return 0x1976d2; }
|
|
26
29
|
// URDF 파싱 결과 캐시 — 동일 URL 재파싱 방지. clone()으로 복제 사용.
|
|
@@ -131,12 +134,10 @@ export class RealObjectURDF extends RealObjectExternalModel {
|
|
|
131
134
|
// 원본 조인트 값 — reset을 위해 보존
|
|
132
135
|
_jointOriginals = new Map();
|
|
133
136
|
// 자동 애니메이션 (sine 모드) 상태
|
|
134
|
-
|
|
135
|
-
_animStartTime = 0;
|
|
137
|
+
_animUnsub = null;
|
|
136
138
|
// Joint controller (physics mode) 상태
|
|
137
139
|
_controller;
|
|
138
|
-
|
|
139
|
-
_ctrlLastTime = 0;
|
|
140
|
+
_ctrlUnsub = null;
|
|
140
141
|
get robot() {
|
|
141
142
|
return this._robot;
|
|
142
143
|
}
|
|
@@ -247,7 +248,6 @@ export class RealObjectURDF extends RealObjectExternalModel {
|
|
|
247
248
|
}
|
|
248
249
|
}
|
|
249
250
|
else if (source) {
|
|
250
|
-
;
|
|
251
251
|
this.component._topViewSnapshot = RealObjectURDF._topViewCache.get(source);
|
|
252
252
|
}
|
|
253
253
|
}
|
|
@@ -324,27 +324,25 @@ export class RealObjectURDF extends RealObjectExternalModel {
|
|
|
324
324
|
}
|
|
325
325
|
ctrl.setTargets(targets);
|
|
326
326
|
}
|
|
327
|
-
|
|
328
|
-
|
|
327
|
+
// Joint physics ticks every frame so manual joint manipulation stays responsive.
|
|
328
|
+
// dt is in ms — convert to seconds to match the JointController contract.
|
|
329
|
+
this._ctrlUnsub = frameClock.subscribe((dt) => {
|
|
329
330
|
if (!this._controller) {
|
|
330
|
-
this.
|
|
331
|
+
this._ctrlUnsub?.();
|
|
332
|
+
this._ctrlUnsub = null;
|
|
331
333
|
return;
|
|
332
334
|
}
|
|
333
|
-
const
|
|
334
|
-
const
|
|
335
|
-
this._ctrlLastTime = now;
|
|
336
|
-
const changed = this._controller.tick(dt);
|
|
335
|
+
const dtSec = dt / 1000;
|
|
336
|
+
const changed = this._controller.tick(dtSec);
|
|
337
337
|
if (changed) {
|
|
338
338
|
this.component?.invalidate?.();
|
|
339
339
|
}
|
|
340
|
-
|
|
341
|
-
};
|
|
342
|
-
this._ctrlRaf = requestAnimationFrame(tick);
|
|
340
|
+
}, { description: 'URDF joint physics', component: this.component });
|
|
343
341
|
}
|
|
344
342
|
_disposeController() {
|
|
345
|
-
if (this.
|
|
346
|
-
|
|
347
|
-
this.
|
|
343
|
+
if (this._ctrlUnsub) {
|
|
344
|
+
this._ctrlUnsub();
|
|
345
|
+
this._ctrlUnsub = null;
|
|
348
346
|
}
|
|
349
347
|
if (this._controller) {
|
|
350
348
|
this._controller.dispose();
|
|
@@ -387,18 +385,24 @@ export class RealObjectURDF extends RealObjectExternalModel {
|
|
|
387
385
|
* 분리되어 표시됨 (활성 시 state는 의미 없음).
|
|
388
386
|
*/
|
|
389
387
|
_startAutoAnimate() {
|
|
390
|
-
if (this.
|
|
388
|
+
if (this._animUnsub)
|
|
391
389
|
return;
|
|
392
390
|
if (!this._robot || this._jointMeta.size === 0)
|
|
393
391
|
return;
|
|
394
|
-
this._animStartTime = performance.now();
|
|
395
392
|
const TWO_PI = Math.PI * 2;
|
|
396
|
-
|
|
393
|
+
let startTime;
|
|
394
|
+
// Sine animation is a visual demo / UX feature.
|
|
395
|
+
// `time` arg is sim-clock ms since the FrameClock booted; capture the first
|
|
396
|
+
// sample as t0 to get elapsed seconds.
|
|
397
|
+
this._animUnsub = frameClock.subscribe((_dt, time) => {
|
|
397
398
|
if (!this._robot) {
|
|
398
|
-
this.
|
|
399
|
+
this._animUnsub?.();
|
|
400
|
+
this._animUnsub = null;
|
|
399
401
|
return;
|
|
400
402
|
}
|
|
401
|
-
|
|
403
|
+
if (startTime === undefined)
|
|
404
|
+
startTime = time;
|
|
405
|
+
const t = (time - startTime) / 1000;
|
|
402
406
|
let i = 0;
|
|
403
407
|
let changed = false;
|
|
404
408
|
for (const [name, meta] of this._jointMeta) {
|
|
@@ -418,14 +422,12 @@ export class RealObjectURDF extends RealObjectExternalModel {
|
|
|
418
422
|
if (changed) {
|
|
419
423
|
this.component?.invalidate?.();
|
|
420
424
|
}
|
|
421
|
-
|
|
422
|
-
};
|
|
423
|
-
this._animRaf = requestAnimationFrame(tick);
|
|
425
|
+
}, { description: 'URDF sine demo', component: this.component });
|
|
424
426
|
}
|
|
425
427
|
_stopAutoAnimate() {
|
|
426
|
-
if (this.
|
|
427
|
-
|
|
428
|
-
this.
|
|
428
|
+
if (this._animUnsub) {
|
|
429
|
+
this._animUnsub();
|
|
430
|
+
this._animUnsub = null;
|
|
429
431
|
}
|
|
430
432
|
}
|
|
431
433
|
/**
|
|
@@ -481,7 +483,7 @@ export class RealObjectURDF extends RealObjectExternalModel {
|
|
|
481
483
|
if ('joints' in after) {
|
|
482
484
|
// 사용자가 joint를 직접 조작하면 자동 애니메이션이 이를 즉시 덮어써
|
|
483
485
|
// 의도를 상쇄하므로, 자동 애니메이션을 우선 중지한다.
|
|
484
|
-
if (this.
|
|
486
|
+
if (this._animUnsub) {
|
|
485
487
|
this._stopAutoAnimate();
|
|
486
488
|
this._component.setState({ autoAnimate: 'none' });
|
|
487
489
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"real-object-urdf.js","sourceRoot":"","sources":["../src/real-object-urdf.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * URDF (Unified Robot Description Format) 지원 RealObject.\n * RealObjectGLTF와 동일한 캐시/레이스/placeholder 패턴을 따른다.\n *\n * 차이점: GLTF는 노드/애니메이션이 주된 바인딩 대상이지만, URDF는 \"joint\"가\n * 1급 시민이다. state.joints에 이름→라디안(또는 prismatic의 경우 거리) 맵을\n * 두어 외부 센서값 등을 연결할 수 있도록 한다.\n *\n * 좌표계: URDF는 Z-up, things-scene의 3D는 Y-up. 로드 후 루트 그룹에 1회만\n * -PI/2 X축 회전을 적용하여 변환한다. 내부 조인트 축은 원본 그대로 유지.\n */\n\nimport * as THREE from 'three'\nimport URDFLoader from 'urdf-loader'\nimport type { URDFRobot, URDFJoint } from 'urdf-loader'\nimport { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'\nimport { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader.js'\n\nimport { warn } from '@hatiolab/things-scene'\nimport type { Properties } from '@hatiolab/things-scene'\n\nimport { RealObjectExternalModel } from '@hatiolab/things-scene'\nimport { RealObjectGLTF } from '@hatiolab/things-scene'\n\nimport { createJointController, type JointController } from './joint-controller.js'\n\nexport interface URDFJointState {\n /** revolute/continuous: 라디안. prismatic: 거리(URDF 단위). */\n value?: number\n}\n\nexport interface URDFJointMeta {\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 X_NEG_PI_HALF = -Math.PI / 2\n\nexport class RealObjectURDF extends RealObjectExternalModel<URDFRobot> {\n protected get _formatLabel() { return 'urdf' }\n protected get _placeholderColor() { return 0x1976d2 }\n // URDF 파싱 결과 캐시 — 동일 URL 재파싱 방지. clone()으로 복제 사용.\n private static _urdfCache = new Map<string, Promise<URDFRobot>>()\n\n // 탑뷰 스냅샷 캐시 (2D 렌더 폴백)\n private static _topViewCache = new Map<string, HTMLCanvasElement>()\n\n /**\n * URDF를 로드한다 (동일 URL 캐시).\n * mesh 로더는 STL/DAE(내장) + OBJ(MTL 연동) + GLB(확장)를 지원한다.\n *\n * URDFLoader.loadAsync는 URDF XML 파싱 직후 resolve하지만 mesh(STL/DAE/GLB)는\n * 비동기로 뒤늦게 attach된다. robot.clone() 시점에 mesh가 아직 없으면 클론된\n * 트리엔 visual이 비게 되고 원본에만 mesh가 붙는다. 전용 LoadingManager의\n * onLoad를 사용해 URDF + 모든 mesh가 완료된 후 resolve한다.\n *\n * `packages` 옵션을 통해 ROS `package://<pkg>/<rel>` prefix 해석을 설정할 수\n * 있다 (예: `{ a1_description: 'https://.../robots/a1_description' }`).\n */\n static loadURDF(url: string, packages?: Record<string, string>): Promise<URDFRobot> {\n // 동일 URL이라도 packages 설정이 다르면 다른 캐시 엔트리.\n const cacheKey = packages && Object.keys(packages).length > 0\n ? `${url}|${JSON.stringify(packages)}`\n : url\n let cached = RealObjectURDF._urdfCache.get(cacheKey)\n if (!cached) {\n cached = new Promise<URDFRobot>((resolve, reject) => {\n const manager = new THREE.LoadingManager()\n const loader = new URDFLoader(manager)\n if (packages) {\n loader.packages = packages\n }\n\n loader.loadMeshCb = ((path: string, mgr: THREE.LoadingManager, done: (mesh: any, err?: Error) => void) => {\n if (/\\.(glb|gltf)$/i.test(path)) {\n // GLB는 별도 시스템 — manager 추적에서 벗어나므로 명시적으로 track.\n mgr.itemStart(path)\n RealObjectGLTF.loadGLTF(path).then(\n gltf => { done(gltf.scene.clone()); mgr.itemEnd(path) },\n err => { done(null, err); mgr.itemError(path); mgr.itemEnd(path) }\n )\n } else if (/\\.obj$/i.test(path)) {\n // OBJ는 urdf-loader 내장 defaultMeshLoader가 지원하지 않음.\n // 연관된 .mtl(재질) 파일이 있으면 MTLLoader로 먼저 로드하여 색상/텍스처\n // 적용, 없으면 OBJLoader만 폴백.\n //\n // LoadingManager 정합성: 외부 path로만 itemStart/itemEnd를 1회씩\n // 기록한다. 내부 MTLLoader/OBJLoader는 manager 주입 없이 생성하여\n // 서브-아이템으로 카운트되지 않게 함 — 그렇지 않으면 MTL 404 직후\n // 일시적으로 pending=0이 되어 manager.onLoad가 조기 발화하고\n // 최종 로봇이 빈 상태로 resolve되는 레이스가 발생.\n mgr.itemStart(path)\n const finishOk = (mesh: any) => { done(mesh); mgr.itemEnd(path) }\n const finishErr = (err: Error) => { done(null, err); mgr.itemError(path); mgr.itemEnd(path) }\n\n const lastSlash = path.lastIndexOf('/')\n const baseUrl = lastSlash >= 0 ? path.substring(0, lastSlash + 1) : ''\n const mtlName = path.substring(lastSlash + 1).replace(/\\.obj$/i, '.mtl')\n\n const loadObjOnly = () => {\n new OBJLoader().load(\n path,\n group => finishOk(group),\n undefined,\n err => finishErr(err as Error)\n )\n }\n new MTLLoader().setResourcePath(baseUrl).setPath(baseUrl).load(\n mtlName,\n materials => {\n materials.preload()\n new OBJLoader().setMaterials(materials).load(\n path,\n group => finishOk(group),\n undefined,\n err => finishErr(err as Error)\n )\n },\n undefined,\n () => {\n // MTL 로드 실패 시 OBJ만 로드 (정상 시나리오 — MTL 없는 OBJ도 있음)\n loadObjOnly()\n }\n )\n } else {\n // STL/DAE는 urdf-loader 내장 디폴트로 폴백 (manager 자동 추적)\n ;(loader as any).defaultMeshLoader(path, mgr, done)\n }\n }) as any\n\n let robotResult: URDFRobot | undefined\n\n manager.onLoad = () => {\n if (robotResult) resolve(robotResult)\n else reject(new Error('URDF load completed but no robot result'))\n }\n manager.onError = (failedUrl: string) => {\n // 개별 에러는 로그로만 남김 — 전체 로드는 그래도 완료되도록\n // (일부 mesh 실패 시 나머지라도 보이게)\n warn('URDFLoader: resource load error', failedUrl)\n }\n\n loader.load(\n url,\n robot => { robotResult = robot },\n undefined,\n err => reject(err)\n )\n }).catch(err => {\n RealObjectURDF._urdfCache.delete(cacheKey)\n throw err\n })\n RealObjectURDF._urdfCache.set(cacheKey, cached)\n }\n return cached\n }\n\n /** 모든 캐시 비움 (보드 전환 시 호출) */\n static flushCache() {\n RealObjectURDF._urdfCache.clear()\n RealObjectURDF._topViewCache.clear()\n }\n\n static getTopViewCache(source: string): HTMLCanvasElement | undefined {\n return RealObjectURDF._topViewCache.get(source)\n }\n\n static setTopViewCache(source: string, canvas: HTMLCanvasElement) {\n RealObjectURDF._topViewCache.set(source, canvas)\n }\n\n // --- 인스턴스 상태 ---\n\n private _robot?: URDFRobot\n\n // 조인트 인덱스 + 메타 (로드 후 외부에서 구독 가능)\n private _jointIndex = new Map<string, URDFJoint>()\n private _jointMeta = new Map<string, URDFJointMeta>()\n\n // 원본 조인트 값 — reset을 위해 보존\n private _jointOriginals = new Map<string, number>()\n\n // 자동 애니메이션 (sine 모드) 상태\n private _animRaf?: number\n private _animStartTime = 0\n\n // Joint controller (physics mode) 상태\n private _controller?: JointController\n private _ctrlRaf?: number\n private _ctrlLastTime = 0\n\n get robot(): URDFRobot | undefined {\n return this._robot\n }\n\n /** 로드된 조인트 메타 리스트 (property panel 등이 참조) */\n get jointMeta(): URDFJointMeta[] {\n return Array.from(this._jointMeta.values())\n }\n\n get jointNames(): string[] {\n return Array.from(this._jointIndex.keys())\n }\n\n protected _loadExternal(url: string): Promise<URDFRobot> {\n const packages = this.component.state.packages as Record<string, string> | undefined\n return RealObjectURDF.loadURDF(url, packages)\n }\n\n protected _onLoaded(robot: URDFRobot) {\n if (this.component.state.loadError) {\n this.component.setState({ loadError: undefined })\n }\n\n // 캐시의 robot은 공유 참조이므로 clone하여 독립 인스턴스 구성.\n // urdf-loader의 URDFRobot는 Object3D를 상속하므로 Object3D.clone()이 안전하게 동작.\n const cloned = robot.clone() as URDFRobot\n\n // 섀도우 캐스팅\n cloned.traverse(child => {\n if ((child as THREE.Mesh).isMesh) {\n child.castShadow = true\n }\n })\n\n // clear()는 object3d.userData를 리셋하므로 pending 후 재설정.\n this.clear()\n this.object3d.userData.context = this\n\n this._robot = cloned\n\n // 좌표계 변환: URDF(Z-up) → three(Y-up).\n // 사용자가 state.upAxis = 'y' 로 지정하면 변환 생략.\n this.pivot = new THREE.Object3D()\n const upAxis = this.component.state.upAxis\n if (upAxis !== 'y') {\n this.pivot.rotation.x = X_NEG_PI_HALF\n }\n\n // URDF는 기본 meter 단위. state.unitScale로 에디터 단위로 환산.\n const unitScale = this._resolveUnitScale()\n if (unitScale !== 1) {\n this.pivot.scale.setScalar(unitScale)\n }\n\n this.pivot.add(cloned)\n // URDF의 base_link는 관행상 z=0이 바닥(floor origin). things-scene는 center\n // origin이므로, components3D 그룹(= -geometricOffsetY만큼 shift된 바닥 기준\n // subgroup)에 pivot을 넣어 로봇이 컴포넌트 바닥면에 서도록 한다.\n this.components3D.add(this.pivot)\n\n // 조인트 인덱스/메타 구축\n this._buildJointIndex(cloned)\n\n // bounding box 계산 후 updateDimension으로 크기 반영\n const box = new THREE.Box3().setFromObject(this.pivot)\n this._objectSize = box.getSize(new THREE.Vector3())\n\n this.updateDimension()\n\n // 자동 placement 정렬: URDF의 base_link 원점이 로봇 기하의 최저점이 아닐 때\n // (Nao/minitaur/quadrotor는 torso/중심 기준) 로봇 일부가 컴포넌트 범위를\n // 벗어나는 것을 방지. 씬의 placement 모드에 따라 정렬 기준이 다르다.\n // - floor: 로봇 최저점 → 컴포넌트 바닥면\n // - inverted: 로봇 최상점 → 컴포넌트 천장면 (매달림)\n // - space: 정렬 없음 (중심 원점 유지)\n // state.floorAlign === false 로 비활성화 가능.\n if (this.component.state.floorAlign !== false) {\n const placement = this.scenePlacement\n if (placement !== 'space') {\n this.components3D.updateMatrixWorld(true)\n const worldBox = new THREE.Box3().setFromObject(this.pivot)\n if (isFinite(worldBox.min.y) && isFinite(worldBox.max.y)) {\n if (placement === 'inverted') {\n const localMax = this.components3D.worldToLocal(worldBox.max.clone())\n if (localMax.y > 0) {\n this.pivot.position.y -= localMax.y\n }\n } else {\n // floor (기본)\n const localMin = this.components3D.worldToLocal(worldBox.min.clone())\n if (localMin.y < 0) {\n this.pivot.position.y -= localMin.y\n }\n }\n }\n }\n }\n\n // 컨트롤러 초기화 (state.physics 설정된 경우)\n const physicsMode = this.component.state.physics as string | undefined\n if (physicsMode && physicsMode !== 'none') {\n this._setupController(physicsMode)\n }\n\n // 초기 joint 상태 적용\n const jointStates = this.component.state.joints as Record<string, URDFJointState | number> | undefined\n if (jointStates) {\n this._dispatchJointStates(jointStates)\n }\n\n // 자동 애니메이션 시작 (state.autoAnimate === 'sine'인 경우)\n if (this.component.state.autoAnimate === 'sine') {\n this._startAutoAnimate()\n }\n\n // 탑뷰 스냅샷\n const source = this.component.state.src as string\n if (source && !RealObjectURDF._topViewCache.has(source)) {\n try {\n const canvas = RealObjectGLTF.renderTopView(this.pivot)\n RealObjectURDF._topViewCache.set(source, canvas)\n ;(this.component as any)._topViewSnapshot = canvas\n } catch {\n // 스냅샷 실패는 치명적이지 않음\n }\n } else if (source) {\n ;(this.component as any)._topViewSnapshot = RealObjectURDF._topViewCache.get(source)\n }\n }\n\n private _resolveUnitScale(): number {\n const s = this.component.state.unitScale\n if (typeof s === 'number' && s > 0) return s\n // 기본: URDF meter → 에디터 mm 스케일 1000\n return 1000\n }\n\n /**\n * 조인트 트리를 순회하며 인덱스와 메타를 구축한다.\n * fixed 조인트는 조작 대상이 아니므로 메타에서 제외하되 인덱스에는 남긴다.\n */\n private _buildJointIndex(robot: URDFRobot) {\n this._jointIndex.clear()\n this._jointMeta.clear()\n this._jointOriginals.clear()\n\n const joints = robot.joints || {}\n for (const [name, joint] of Object.entries(joints)) {\n this._jointIndex.set(name, joint)\n this._jointOriginals.set(name, joint.angle || 0)\n\n if (joint.jointType === 'fixed') continue\n\n this._jointMeta.set(name, {\n name,\n type: joint.jointType,\n axis: { x: joint.axis.x, y: joint.axis.y, z: joint.axis.z },\n limit: { ...joint.limit }\n })\n }\n }\n\n /**\n * state.joints 변경을 적절한 경로로 디스패치한다.\n * - controller가 활성이면 setTargets (controller가 스무딩/물리 적용)\n * - 없으면 즉시 적용 (direct 모드, 기본)\n */\n private _dispatchJointStates(states: Record<string, URDFJointState | number>) {\n if (this._controller) {\n const targets: Record<string, number> = {}\n for (const [name, raw] of Object.entries(states)) {\n const v = typeof raw === 'number' ? raw : Number((raw as { value?: number } | null)?.value)\n if (Number.isFinite(v)) targets[name] = v\n }\n this._controller.setTargets(targets)\n return\n }\n this._applyJointStates(states)\n }\n\n /**\n * Controller를 인스턴스화하고 setup + tick 루프 시작.\n * 이미 활성 중이면 먼저 해제.\n */\n private _setupController(mode: string) {\n this._disposeController()\n const ctrl = createJointController(mode)\n if (!ctrl) {\n warn(`URDFObject: unknown physics mode '${mode}'`)\n return\n }\n if (!this._robot) return\n\n ctrl.setup({ robot: this._robot, joints: this._jointIndex, jointMeta: this._jointMeta })\n this._controller = ctrl\n\n // 현재 state.joints를 초기 타겟으로 설정\n const s = this.component.state.joints as Record<string, URDFJointState | number> | undefined\n if (s) {\n const targets: Record<string, number> = {}\n for (const [name, raw] of Object.entries(s)) {\n const v = typeof raw === 'number' ? raw : Number((raw as { value?: number } | null)?.value)\n if (Number.isFinite(v)) targets[name] = v\n }\n ctrl.setTargets(targets)\n }\n\n this._ctrlLastTime = performance.now()\n const tick = () => {\n if (!this._controller) {\n this._ctrlRaf = undefined\n return\n }\n const now = performance.now()\n const dt = (now - this._ctrlLastTime) / 1000\n this._ctrlLastTime = now\n const changed = this._controller.tick(dt)\n if (changed) {\n this.component?.invalidate?.()\n }\n this._ctrlRaf = requestAnimationFrame(tick)\n }\n this._ctrlRaf = requestAnimationFrame(tick)\n }\n\n private _disposeController() {\n if (this._ctrlRaf != null) {\n cancelAnimationFrame(this._ctrlRaf)\n this._ctrlRaf = undefined\n }\n if (this._controller) {\n this._controller.dispose()\n this._controller = undefined\n }\n }\n\n /**\n * state.joints 맵을 순회하며 각 조인트 값을 적용한다 (direct 모드).\n * 값은 숫자 또는 { value: number } 형식 모두 허용 (유연성).\n *\n * joint transform 변경 후 component.invalidate()로 재렌더를 요청한다.\n * 표준 경로: invalidate → Layer.throttle_render → rAF __draw__ → trigger('redraw')\n * → ThreeCapability.animate → renderThreeD.\n */\n private _applyJointStates(states: Record<string, URDFJointState | number>) {\n if (!this._robot) return\n\n let changed = false\n for (const [name, raw] of Object.entries(states)) {\n const joint = this._jointIndex.get(name)\n if (!joint || joint.jointType === 'fixed') continue\n\n const value = typeof raw === 'number' ? raw : raw?.value\n if (typeof value !== 'number' || !isFinite(value)) continue\n\n joint.setJointValue(value)\n changed = true\n }\n\n if (changed) {\n this.component?.invalidate?.()\n }\n }\n\n /**\n * 자동 sine 애니메이션 시작.\n * 각 non-fixed 조인트를 limit 범위 내에서 sine 스윙. 조인트 순서에 따라\n * frequency와 phase를 약간씩 다르게 해 동기화를 피하고 유기적 움직임을 만든다.\n *\n * state.joints를 건드리지 않고 joint.setJointValue만 직접 호출 — state 변경\n * cascade를 피해 성능을 유지하고, 슬라이더가 있는 경우에도 state와 시각이\n * 분리되어 표시됨 (활성 시 state는 의미 없음).\n */\n private _startAutoAnimate() {\n if (this._animRaf != null) return\n if (!this._robot || this._jointMeta.size === 0) return\n\n this._animStartTime = performance.now()\n const TWO_PI = Math.PI * 2\n\n const tick = () => {\n if (!this._robot) {\n this._animRaf = undefined\n return\n }\n const t = (performance.now() - this._animStartTime) / 1000\n\n let i = 0\n let changed = false\n for (const [name, meta] of this._jointMeta) {\n const joint = this._jointIndex.get(name)\n if (!joint) continue\n\n const mid = (meta.limit.upper + meta.limit.lower) / 2\n const amp = (meta.limit.upper - meta.limit.lower) / 2\n // 조인트별로 frequency와 phase 약간씩 다르게 (0.15 ~ 0.4 Hz 범위)\n const freq = 0.15 + (i % 6) * 0.05\n const phase = i * 0.73\n const value = mid + amp * Math.sin(t * freq * TWO_PI + phase)\n joint.setJointValue(value)\n changed = true\n i++\n }\n\n if (changed) {\n this.component?.invalidate?.()\n }\n this._animRaf = requestAnimationFrame(tick)\n }\n this._animRaf = requestAnimationFrame(tick)\n }\n\n private _stopAutoAnimate() {\n if (this._animRaf != null) {\n cancelAnimationFrame(this._animRaf)\n this._animRaf = undefined\n }\n }\n\n /**\n * 모든 조인트를 원본 값으로 복원한다.\n */\n private _resetAllJoints() {\n if (!this._robot) return\n for (const [name, orig] of this._jointOriginals) {\n const joint = this._jointIndex.get(name)\n if (joint && joint.jointType !== 'fixed') {\n joint.setJointValue(orig)\n }\n }\n }\n\n // --- 라이프사이클 ---\n\n clear() {\n this._stopAutoAnimate()\n this._disposeController()\n this._jointIndex.clear()\n this._jointMeta.clear()\n this._jointOriginals.clear()\n this._robot = undefined\n\n return super.clear()\n }\n\n updateDimension() {\n if (!this.pivot) return\n\n const { width = 1, height = 1, depth = 1 } = this.component.state\n const { x = 1, y = 1, z = 1 } = this._objectSize || {}\n\n // URDF는 관절 기반 로봇의 비율을 유지해야 하므로 uniform scale을 적용한다.\n // 세 축 각각의 비율 중 가장 작은 값(best-fit)을 사용해 로봇이 컴포넌트의\n // bounding box 안에 왜곡 없이 맞춰지도록 한다.\n const unit = this._resolveUnitScale()\n const baseX = x || 1\n const baseY = y || 1\n const baseZ = z || 1\n const upAxis = this.component.state.upAxis\n const rotated = upAxis !== 'y'\n\n const rX = width / baseX\n const rY = (rotated ? depth : height) / baseY\n const rZ = (rotated ? height : depth) / baseZ\n const ratio = Math.min(rX, rY, rZ)\n\n this.pivot.scale.setScalar(ratio * unit)\n\n this.component.invalidate()\n }\n\n onchange(after: Properties, before: Properties) {\n super.onchange(after, before)\n\n if ('src' in after) {\n this.updateSource()\n return\n }\n\n if ('joints' in after) {\n // 사용자가 joint를 직접 조작하면 자동 애니메이션이 이를 즉시 덮어써\n // 의도를 상쇄하므로, 자동 애니메이션을 우선 중지한다.\n if (this._animRaf != null) {\n this._stopAutoAnimate()\n this._component.setState({ autoAnimate: 'none' })\n }\n const jointStates = after.joints as Record<string, URDFJointState | number> | undefined\n if (this._controller) {\n // controller 모드: 타겟만 갱신. 이전 값 리셋은 controller가 연속 감쇠로\n // 알아서 수렴하므로 불필요.\n if (jointStates && Object.keys(jointStates).length > 0) {\n this._dispatchJointStates(jointStates)\n }\n } else {\n // direct 모드: 전체 reset 후 명시된 값만 재적용 (sparse state 지원).\n this._resetAllJoints()\n if (jointStates && Object.keys(jointStates).length > 0) {\n this._applyJointStates(jointStates)\n }\n }\n }\n\n if ('autoAnimate' in after) {\n if (after.autoAnimate === 'sine') {\n this._startAutoAnimate()\n } else {\n this._stopAutoAnimate()\n }\n }\n\n if ('physics' in after) {\n const mode = after.physics as string | undefined\n if (mode && mode !== 'none') {\n this._setupController(mode)\n } else {\n this._disposeController()\n }\n }\n\n if ('upAxis' in after || 'unitScale' in after || 'packages' in after) {\n // 좌표/스케일/패키지 매핑 변경은 pivot 재구성이 필요하므로 전체 재빌드\n this.build()\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"real-object-urdf.js","sourceRoot":"","sources":["../src/real-object-urdf.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * URDF (Unified Robot Description Format) 지원 RealObject.\n * RealObjectGLTF와 동일한 캐시/레이스/placeholder 패턴을 따른다.\n *\n * 차이점: GLTF는 노드/애니메이션이 주된 바인딩 대상이지만, URDF는 \"joint\"가\n * 1급 시민이다. state.joints에 이름→라디안(또는 prismatic의 경우 거리) 맵을\n * 두어 외부 센서값 등을 연결할 수 있도록 한다.\n *\n * 좌표계: URDF는 Z-up, things-scene의 3D는 Y-up. 로드 후 루트 그룹에 1회만\n * -PI/2 X축 회전을 적용하여 변환한다. 내부 조인트 축은 원본 그대로 유지.\n */\n\nimport * as THREE from 'three'\nimport URDFLoader from 'urdf-loader'\nimport type { URDFRobot, URDFJoint } from 'urdf-loader'\nimport { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'\nimport { MTLLoader } from 'three/examples/jsm/loaders/MTLLoader.js'\n\nimport { warn, frameClock } from '@hatiolab/things-scene'\nimport type { Properties, Unsubscribe } from '@hatiolab/things-scene'\n\nimport { RealObjectExternalModel } from '@hatiolab/things-scene'\nimport { RealObjectGLTF } from '@hatiolab/things-scene'\nimport type { URDFObject } from './urdf-object.js'\n\nimport { createJointController, type JointController } from './joint-controller.js'\n\nexport interface URDFJointState {\n /** revolute/continuous: 라디안. prismatic: 거리(URDF 단위). */\n value?: number\n}\n\nexport interface URDFJointMeta {\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 X_NEG_PI_HALF = -Math.PI / 2\n\nexport class RealObjectURDF extends RealObjectExternalModel<URDFRobot> {\n override get component(): URDFObject {\n return super.component as URDFObject\n }\n\n protected get _formatLabel() { return 'urdf' }\n protected get _placeholderColor() { return 0x1976d2 }\n // URDF 파싱 결과 캐시 — 동일 URL 재파싱 방지. clone()으로 복제 사용.\n private static _urdfCache = new Map<string, Promise<URDFRobot>>()\n\n // 탑뷰 스냅샷 캐시 (2D 렌더 폴백)\n private static _topViewCache = new Map<string, HTMLCanvasElement>()\n\n /**\n * URDF를 로드한다 (동일 URL 캐시).\n * mesh 로더는 STL/DAE(내장) + OBJ(MTL 연동) + GLB(확장)를 지원한다.\n *\n * URDFLoader.loadAsync는 URDF XML 파싱 직후 resolve하지만 mesh(STL/DAE/GLB)는\n * 비동기로 뒤늦게 attach된다. robot.clone() 시점에 mesh가 아직 없으면 클론된\n * 트리엔 visual이 비게 되고 원본에만 mesh가 붙는다. 전용 LoadingManager의\n * onLoad를 사용해 URDF + 모든 mesh가 완료된 후 resolve한다.\n *\n * `packages` 옵션을 통해 ROS `package://<pkg>/<rel>` prefix 해석을 설정할 수\n * 있다 (예: `{ a1_description: 'https://.../robots/a1_description' }`).\n */\n static loadURDF(url: string, packages?: Record<string, string>): Promise<URDFRobot> {\n // 동일 URL이라도 packages 설정이 다르면 다른 캐시 엔트리.\n const cacheKey = packages && Object.keys(packages).length > 0\n ? `${url}|${JSON.stringify(packages)}`\n : url\n let cached = RealObjectURDF._urdfCache.get(cacheKey)\n if (!cached) {\n cached = new Promise<URDFRobot>((resolve, reject) => {\n const manager = new THREE.LoadingManager()\n const loader = new URDFLoader(manager)\n if (packages) {\n loader.packages = packages\n }\n\n loader.loadMeshCb = ((path: string, mgr: THREE.LoadingManager, done: (mesh: any, err?: Error) => void) => {\n if (/\\.(glb|gltf)$/i.test(path)) {\n // GLB는 별도 시스템 — manager 추적에서 벗어나므로 명시적으로 track.\n mgr.itemStart(path)\n RealObjectGLTF.loadGLTF(path).then(\n gltf => { done(gltf.scene.clone()); mgr.itemEnd(path) },\n err => { done(null, err); mgr.itemError(path); mgr.itemEnd(path) }\n )\n } else if (/\\.obj$/i.test(path)) {\n // OBJ는 urdf-loader 내장 defaultMeshLoader가 지원하지 않음.\n // 연관된 .mtl(재질) 파일이 있으면 MTLLoader로 먼저 로드하여 색상/텍스처\n // 적용, 없으면 OBJLoader만 폴백.\n //\n // LoadingManager 정합성: 외부 path로만 itemStart/itemEnd를 1회씩\n // 기록한다. 내부 MTLLoader/OBJLoader는 manager 주입 없이 생성하여\n // 서브-아이템으로 카운트되지 않게 함 — 그렇지 않으면 MTL 404 직후\n // 일시적으로 pending=0이 되어 manager.onLoad가 조기 발화하고\n // 최종 로봇이 빈 상태로 resolve되는 레이스가 발생.\n mgr.itemStart(path)\n const finishOk = (mesh: any) => { done(mesh); mgr.itemEnd(path) }\n const finishErr = (err: Error) => { done(null, err); mgr.itemError(path); mgr.itemEnd(path) }\n\n const lastSlash = path.lastIndexOf('/')\n const baseUrl = lastSlash >= 0 ? path.substring(0, lastSlash + 1) : ''\n const mtlName = path.substring(lastSlash + 1).replace(/\\.obj$/i, '.mtl')\n\n const loadObjOnly = () => {\n new OBJLoader().load(\n path,\n group => finishOk(group),\n undefined,\n err => finishErr(err as Error)\n )\n }\n new MTLLoader().setResourcePath(baseUrl).setPath(baseUrl).load(\n mtlName,\n materials => {\n materials.preload()\n new OBJLoader().setMaterials(materials).load(\n path,\n group => finishOk(group),\n undefined,\n err => finishErr(err as Error)\n )\n },\n undefined,\n () => {\n // MTL 로드 실패 시 OBJ만 로드 (정상 시나리오 — MTL 없는 OBJ도 있음)\n loadObjOnly()\n }\n )\n } else {\n // STL/DAE는 urdf-loader 내장 디폴트로 폴백 (manager 자동 추적)\n ;(loader as any).defaultMeshLoader(path, mgr, done)\n }\n }) as any\n\n let robotResult: URDFRobot | undefined\n\n manager.onLoad = () => {\n if (robotResult) resolve(robotResult)\n else reject(new Error('URDF load completed but no robot result'))\n }\n manager.onError = (failedUrl: string) => {\n // 개별 에러는 로그로만 남김 — 전체 로드는 그래도 완료되도록\n // (일부 mesh 실패 시 나머지라도 보이게)\n warn('URDFLoader: resource load error', failedUrl)\n }\n\n loader.load(\n url,\n robot => { robotResult = robot },\n undefined,\n err => reject(err)\n )\n }).catch(err => {\n RealObjectURDF._urdfCache.delete(cacheKey)\n throw err\n })\n RealObjectURDF._urdfCache.set(cacheKey, cached)\n }\n return cached\n }\n\n /** 모든 캐시 비움 (보드 전환 시 호출) */\n static flushCache() {\n RealObjectURDF._urdfCache.clear()\n RealObjectURDF._topViewCache.clear()\n }\n\n static getTopViewCache(source: string): HTMLCanvasElement | undefined {\n return RealObjectURDF._topViewCache.get(source)\n }\n\n static setTopViewCache(source: string, canvas: HTMLCanvasElement) {\n RealObjectURDF._topViewCache.set(source, canvas)\n }\n\n // --- 인스턴스 상태 ---\n\n private _robot?: URDFRobot\n\n // 조인트 인덱스 + 메타 (로드 후 외부에서 구독 가능)\n private _jointIndex = new Map<string, URDFJoint>()\n private _jointMeta = new Map<string, URDFJointMeta>()\n\n // 원본 조인트 값 — reset을 위해 보존\n private _jointOriginals = new Map<string, number>()\n\n // 자동 애니메이션 (sine 모드) 상태\n private _animUnsub: Unsubscribe | null = null\n\n // Joint controller (physics mode) 상태\n private _controller?: JointController\n private _ctrlUnsub: Unsubscribe | null = null\n\n get robot(): URDFRobot | undefined {\n return this._robot\n }\n\n /** 로드된 조인트 메타 리스트 (property panel 등이 참조) */\n get jointMeta(): URDFJointMeta[] {\n return Array.from(this._jointMeta.values())\n }\n\n get jointNames(): string[] {\n return Array.from(this._jointIndex.keys())\n }\n\n protected _loadExternal(url: string): Promise<URDFRobot> {\n const packages = this.component.state.packages as Record<string, string> | undefined\n return RealObjectURDF.loadURDF(url, packages)\n }\n\n protected _onLoaded(robot: URDFRobot) {\n if (this.component.state.loadError) {\n this.component.setState({ loadError: undefined })\n }\n\n // 캐시의 robot은 공유 참조이므로 clone하여 독립 인스턴스 구성.\n // urdf-loader의 URDFRobot는 Object3D를 상속하므로 Object3D.clone()이 안전하게 동작.\n const cloned = robot.clone() as URDFRobot\n\n // 섀도우 캐스팅\n cloned.traverse(child => {\n if ((child as THREE.Mesh).isMesh) {\n child.castShadow = true\n }\n })\n\n // clear()는 object3d.userData를 리셋하므로 pending 후 재설정.\n this.clear()\n this.object3d.userData.context = this\n\n this._robot = cloned\n\n // 좌표계 변환: URDF(Z-up) → three(Y-up).\n // 사용자가 state.upAxis = 'y' 로 지정하면 변환 생략.\n this.pivot = new THREE.Object3D()\n const upAxis = this.component.state.upAxis\n if (upAxis !== 'y') {\n this.pivot.rotation.x = X_NEG_PI_HALF\n }\n\n // URDF는 기본 meter 단위. state.unitScale로 에디터 단위로 환산.\n const unitScale = this._resolveUnitScale()\n if (unitScale !== 1) {\n this.pivot.scale.setScalar(unitScale)\n }\n\n this.pivot.add(cloned)\n // URDF의 base_link는 관행상 z=0이 바닥(floor origin). things-scene는 center\n // origin이므로, components3D 그룹(= -geometricOffsetY만큼 shift된 바닥 기준\n // subgroup)에 pivot을 넣어 로봇이 컴포넌트 바닥면에 서도록 한다.\n this.components3D.add(this.pivot)\n\n // 조인트 인덱스/메타 구축\n this._buildJointIndex(cloned)\n\n // bounding box 계산 후 updateDimension으로 크기 반영\n const box = new THREE.Box3().setFromObject(this.pivot)\n this._objectSize = box.getSize(new THREE.Vector3())\n\n this.updateDimension()\n\n // 자동 placement 정렬: URDF의 base_link 원점이 로봇 기하의 최저점이 아닐 때\n // (Nao/minitaur/quadrotor는 torso/중심 기준) 로봇 일부가 컴포넌트 범위를\n // 벗어나는 것을 방지. 씬의 placement 모드에 따라 정렬 기준이 다르다.\n // - floor: 로봇 최저점 → 컴포넌트 바닥면\n // - inverted: 로봇 최상점 → 컴포넌트 천장면 (매달림)\n // - space: 정렬 없음 (중심 원점 유지)\n // state.floorAlign === false 로 비활성화 가능.\n if (this.component.state.floorAlign !== false) {\n const placement = this.scenePlacement\n if (placement !== 'space') {\n this.components3D.updateMatrixWorld(true)\n const worldBox = new THREE.Box3().setFromObject(this.pivot)\n if (isFinite(worldBox.min.y) && isFinite(worldBox.max.y)) {\n if (placement === 'inverted') {\n const localMax = this.components3D.worldToLocal(worldBox.max.clone())\n if (localMax.y > 0) {\n this.pivot.position.y -= localMax.y\n }\n } else {\n // floor (기본)\n const localMin = this.components3D.worldToLocal(worldBox.min.clone())\n if (localMin.y < 0) {\n this.pivot.position.y -= localMin.y\n }\n }\n }\n }\n }\n\n // 컨트롤러 초기화 (state.physics 설정된 경우)\n const physicsMode = this.component.state.physics as string | undefined\n if (physicsMode && physicsMode !== 'none') {\n this._setupController(physicsMode)\n }\n\n // 초기 joint 상태 적용\n const jointStates = this.component.state.joints as Record<string, URDFJointState | number> | undefined\n if (jointStates) {\n this._dispatchJointStates(jointStates)\n }\n\n // 자동 애니메이션 시작 (state.autoAnimate === 'sine'인 경우)\n if (this.component.state.autoAnimate === 'sine') {\n this._startAutoAnimate()\n }\n\n // 탑뷰 스냅샷\n const source = this.component.state.src as string\n if (source && !RealObjectURDF._topViewCache.has(source)) {\n try {\n const canvas = RealObjectGLTF.renderTopView(this.pivot)\n RealObjectURDF._topViewCache.set(source, canvas)\n this.component._topViewSnapshot = canvas\n } catch {\n // 스냅샷 실패는 치명적이지 않음\n }\n } else if (source) {\n this.component._topViewSnapshot = RealObjectURDF._topViewCache.get(source)\n }\n }\n\n private _resolveUnitScale(): number {\n const s = this.component.state.unitScale\n if (typeof s === 'number' && s > 0) return s\n // 기본: URDF meter → 에디터 mm 스케일 1000\n return 1000\n }\n\n /**\n * 조인트 트리를 순회하며 인덱스와 메타를 구축한다.\n * fixed 조인트는 조작 대상이 아니므로 메타에서 제외하되 인덱스에는 남긴다.\n */\n private _buildJointIndex(robot: URDFRobot) {\n this._jointIndex.clear()\n this._jointMeta.clear()\n this._jointOriginals.clear()\n\n const joints = robot.joints || {}\n for (const [name, joint] of Object.entries(joints)) {\n this._jointIndex.set(name, joint)\n this._jointOriginals.set(name, joint.angle || 0)\n\n if (joint.jointType === 'fixed') continue\n\n this._jointMeta.set(name, {\n name,\n type: joint.jointType,\n axis: { x: joint.axis.x, y: joint.axis.y, z: joint.axis.z },\n limit: { ...joint.limit }\n })\n }\n }\n\n /**\n * state.joints 변경을 적절한 경로로 디스패치한다.\n * - controller가 활성이면 setTargets (controller가 스무딩/물리 적용)\n * - 없으면 즉시 적용 (direct 모드, 기본)\n */\n private _dispatchJointStates(states: Record<string, URDFJointState | number>) {\n if (this._controller) {\n const targets: Record<string, number> = {}\n for (const [name, raw] of Object.entries(states)) {\n const v = typeof raw === 'number' ? raw : Number((raw as { value?: number } | null)?.value)\n if (Number.isFinite(v)) targets[name] = v\n }\n this._controller.setTargets(targets)\n return\n }\n this._applyJointStates(states)\n }\n\n /**\n * Controller를 인스턴스화하고 setup + tick 루프 시작.\n * 이미 활성 중이면 먼저 해제.\n */\n private _setupController(mode: string) {\n this._disposeController()\n const ctrl = createJointController(mode)\n if (!ctrl) {\n warn(`URDFObject: unknown physics mode '${mode}'`)\n return\n }\n if (!this._robot) return\n\n ctrl.setup({ robot: this._robot, joints: this._jointIndex, jointMeta: this._jointMeta })\n this._controller = ctrl\n\n // 현재 state.joints를 초기 타겟으로 설정\n const s = this.component.state.joints as Record<string, URDFJointState | number> | undefined\n if (s) {\n const targets: Record<string, number> = {}\n for (const [name, raw] of Object.entries(s)) {\n const v = typeof raw === 'number' ? raw : Number((raw as { value?: number } | null)?.value)\n if (Number.isFinite(v)) targets[name] = v\n }\n ctrl.setTargets(targets)\n }\n\n // Joint physics ticks every frame so manual joint manipulation stays responsive.\n // dt is in ms — convert to seconds to match the JointController contract.\n this._ctrlUnsub = frameClock.subscribe(\n (dt) => {\n if (!this._controller) {\n this._ctrlUnsub?.()\n this._ctrlUnsub = null\n return\n }\n const dtSec = dt / 1000\n const changed = this._controller.tick(dtSec)\n if (changed) {\n this.component?.invalidate?.()\n }\n },\n { description: 'URDF joint physics', component: this.component }\n )\n }\n\n private _disposeController() {\n if (this._ctrlUnsub) {\n this._ctrlUnsub()\n this._ctrlUnsub = null\n }\n if (this._controller) {\n this._controller.dispose()\n this._controller = undefined\n }\n }\n\n /**\n * state.joints 맵을 순회하며 각 조인트 값을 적용한다 (direct 모드).\n * 값은 숫자 또는 { value: number } 형식 모두 허용 (유연성).\n *\n * joint transform 변경 후 component.invalidate()로 재렌더를 요청한다.\n * 표준 경로: invalidate → Layer.throttle_render → rAF __draw__ → trigger('redraw')\n * → ThreeCapability.animate → renderThreeD.\n */\n private _applyJointStates(states: Record<string, URDFJointState | number>) {\n if (!this._robot) return\n\n let changed = false\n for (const [name, raw] of Object.entries(states)) {\n const joint = this._jointIndex.get(name)\n if (!joint || joint.jointType === 'fixed') continue\n\n const value = typeof raw === 'number' ? raw : raw?.value\n if (typeof value !== 'number' || !isFinite(value)) continue\n\n joint.setJointValue(value)\n changed = true\n }\n\n if (changed) {\n this.component?.invalidate?.()\n }\n }\n\n /**\n * 자동 sine 애니메이션 시작.\n * 각 non-fixed 조인트를 limit 범위 내에서 sine 스윙. 조인트 순서에 따라\n * frequency와 phase를 약간씩 다르게 해 동기화를 피하고 유기적 움직임을 만든다.\n *\n * state.joints를 건드리지 않고 joint.setJointValue만 직접 호출 — state 변경\n * cascade를 피해 성능을 유지하고, 슬라이더가 있는 경우에도 state와 시각이\n * 분리되어 표시됨 (활성 시 state는 의미 없음).\n */\n private _startAutoAnimate() {\n if (this._animUnsub) return\n if (!this._robot || this._jointMeta.size === 0) return\n\n const TWO_PI = Math.PI * 2\n let startTime: number | undefined\n\n // Sine animation is a visual demo / UX feature.\n // `time` arg is sim-clock ms since the FrameClock booted; capture the first\n // sample as t0 to get elapsed seconds.\n this._animUnsub = frameClock.subscribe(\n (_dt, time) => {\n if (!this._robot) {\n this._animUnsub?.()\n this._animUnsub = null\n return\n }\n if (startTime === undefined) startTime = time\n const t = (time - startTime) / 1000\n\n let i = 0\n let changed = false\n for (const [name, meta] of this._jointMeta) {\n const joint = this._jointIndex.get(name)\n if (!joint) continue\n\n const mid = (meta.limit.upper + meta.limit.lower) / 2\n const amp = (meta.limit.upper - meta.limit.lower) / 2\n // 조인트별로 frequency와 phase 약간씩 다르게 (0.15 ~ 0.4 Hz 범위)\n const freq = 0.15 + (i % 6) * 0.05\n const phase = i * 0.73\n const value = mid + amp * Math.sin(t * freq * TWO_PI + phase)\n joint.setJointValue(value)\n changed = true\n i++\n }\n\n if (changed) {\n this.component?.invalidate?.()\n }\n },\n { description: 'URDF sine demo', component: this.component }\n )\n }\n\n private _stopAutoAnimate() {\n if (this._animUnsub) {\n this._animUnsub()\n this._animUnsub = null\n }\n }\n\n /**\n * 모든 조인트를 원본 값으로 복원한다.\n */\n private _resetAllJoints() {\n if (!this._robot) return\n for (const [name, orig] of this._jointOriginals) {\n const joint = this._jointIndex.get(name)\n if (joint && joint.jointType !== 'fixed') {\n joint.setJointValue(orig)\n }\n }\n }\n\n // --- 라이프사이클 ---\n\n clear() {\n this._stopAutoAnimate()\n this._disposeController()\n this._jointIndex.clear()\n this._jointMeta.clear()\n this._jointOriginals.clear()\n this._robot = undefined\n\n return super.clear()\n }\n\n updateDimension() {\n if (!this.pivot) return\n\n const { width = 1, height = 1, depth = 1 } = this.component.state\n const { x = 1, y = 1, z = 1 } = this._objectSize || {}\n\n // URDF는 관절 기반 로봇의 비율을 유지해야 하므로 uniform scale을 적용한다.\n // 세 축 각각의 비율 중 가장 작은 값(best-fit)을 사용해 로봇이 컴포넌트의\n // bounding box 안에 왜곡 없이 맞춰지도록 한다.\n const unit = this._resolveUnitScale()\n const baseX = x || 1\n const baseY = y || 1\n const baseZ = z || 1\n const upAxis = this.component.state.upAxis\n const rotated = upAxis !== 'y'\n\n const rX = width / baseX\n const rY = (rotated ? depth : height) / baseY\n const rZ = (rotated ? height : depth) / baseZ\n const ratio = Math.min(rX, rY, rZ)\n\n this.pivot.scale.setScalar(ratio * unit)\n\n this.component.invalidate()\n }\n\n onchange(after: Properties, before: Properties) {\n super.onchange(after, before)\n\n if ('src' in after) {\n this.updateSource()\n return\n }\n\n if ('joints' in after) {\n // 사용자가 joint를 직접 조작하면 자동 애니메이션이 이를 즉시 덮어써\n // 의도를 상쇄하므로, 자동 애니메이션을 우선 중지한다.\n if (this._animUnsub) {\n this._stopAutoAnimate()\n this._component.setState({ autoAnimate: 'none' })\n }\n const jointStates = after.joints as Record<string, URDFJointState | number> | undefined\n if (this._controller) {\n // controller 모드: 타겟만 갱신. 이전 값 리셋은 controller가 연속 감쇠로\n // 알아서 수렴하므로 불필요.\n if (jointStates && Object.keys(jointStates).length > 0) {\n this._dispatchJointStates(jointStates)\n }\n } else {\n // direct 모드: 전체 reset 후 명시된 값만 재적용 (sparse state 지원).\n this._resetAllJoints()\n if (jointStates && Object.keys(jointStates).length > 0) {\n this._applyJointStates(jointStates)\n }\n }\n }\n\n if ('autoAnimate' in after) {\n if (after.autoAnimate === 'sine') {\n this._startAutoAnimate()\n } else {\n this._stopAutoAnimate()\n }\n }\n\n if ('physics' in after) {\n const mode = after.physics as string | undefined\n if (mode && mode !== 'none') {\n this._setupController(mode)\n } else {\n this._disposeController()\n }\n }\n\n if ('upAxis' in after || 'unitScale' in after || 'packages' in after) {\n // 좌표/스케일/패키지 매핑 변경은 pivot 재구성이 필요하므로 전체 재빌드\n this.build()\n }\n }\n}\n"]}
|
package/dist/urdf-object.d.ts
CHANGED
|
@@ -2,12 +2,12 @@ import { Shape, type ComponentNature, type IRealObject } from '@hatiolab/things-
|
|
|
2
2
|
import type { URDFJointState, URDFJointMeta } from './real-object-urdf.js';
|
|
3
3
|
declare const URDFObject_base: (new (...args: any[]) => {
|
|
4
4
|
_topViewSnapshot?: HTMLCanvasElement;
|
|
5
|
+
_snapshotLoading: boolean;
|
|
5
6
|
_ratioLocking: boolean;
|
|
6
7
|
is3dish(): boolean;
|
|
7
8
|
get hasTextProperty(): boolean;
|
|
8
9
|
get controls(): never[];
|
|
9
10
|
render(ctx: CanvasRenderingContext2D): void;
|
|
10
|
-
ready(): Promise<void>;
|
|
11
11
|
onchange(after: Record<string, any>, before: Record<string, any>): void;
|
|
12
12
|
_ensureTopViewSnapshot(): void;
|
|
13
13
|
buildRealObject(): IRealObject | undefined;
|
|
@@ -53,6 +53,7 @@ declare const URDFObject_base: (new (...args: any[]) => {
|
|
|
53
53
|
created(): void;
|
|
54
54
|
added(parent: any): void;
|
|
55
55
|
removed(parent: any): void;
|
|
56
|
+
ready(): Promise<void>;
|
|
56
57
|
touch(): void;
|
|
57
58
|
clearCache(...attrs: any[]): void;
|
|
58
59
|
removeSelf(completely: any): void;
|
|
@@ -62,7 +63,7 @@ declare const URDFObject_base: (new (...args: any[]) => {
|
|
|
62
63
|
get disposed(): boolean;
|
|
63
64
|
isLayer(): boolean;
|
|
64
65
|
isGroup(): boolean;
|
|
65
|
-
isContainer(): this is import("@hatiolab/things-scene
|
|
66
|
+
isContainer(): this is import("@hatiolab/things-scene").Container;
|
|
66
67
|
isLine(): boolean;
|
|
67
68
|
isRoot(): boolean;
|
|
68
69
|
isRootModel(): boolean;
|
|
@@ -77,7 +78,7 @@ declare const URDFObject_base: (new (...args: any[]) => {
|
|
|
77
78
|
get(property: any): any;
|
|
78
79
|
getState(property: any): any;
|
|
79
80
|
get model(): any;
|
|
80
|
-
get state():
|
|
81
|
+
get state(): import("@hatiolab/things-scene").State;
|
|
81
82
|
get hierarchy(): any;
|
|
82
83
|
get volatile(): never[];
|
|
83
84
|
_applyProps(target: any, props: any, options: any): any;
|
|
@@ -168,7 +169,7 @@ declare const URDFObject_base: (new (...args: any[]) => {
|
|
|
168
169
|
findAll(s: string | ((c: import("@hatiolab/things-scene").Component) => boolean), ...others: any[]): any[] | undefined;
|
|
169
170
|
capture(x: number, y: number, except?: (c: import("@hatiolab/things-scene").Component) => boolean): any;
|
|
170
171
|
findAnchor(name: string): any;
|
|
171
|
-
isDescendible(container: import("@hatiolab/things-scene").Component): boolean;
|
|
172
|
+
isDescendible(container: import("@hatiolab/things-scene").Component | any): boolean;
|
|
172
173
|
getContext(component?: unknown): any;
|
|
173
174
|
get root(): import("@hatiolab/things-scene").Component;
|
|
174
175
|
get rootModel(): import("@hatiolab/things-scene").Component;
|
|
@@ -213,9 +214,13 @@ declare const URDFObject_base: (new (...args: any[]) => {
|
|
|
213
214
|
toLocal(x: number, y: number, top?: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
|
|
214
215
|
toGlobal(x: number, y: number, top?: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
|
|
215
216
|
toOther(x: number, y: number, target: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
|
|
217
|
+
on<K extends keyof import("@hatiolab/things-scene").ComponentEventMap>(name: K, callback: (...args: import("@hatiolab/things-scene").ComponentEventMap[K]) => void, context?: any): any;
|
|
216
218
|
on(name: string | object, callback: Function, context?: any): any;
|
|
219
|
+
off<K extends keyof import("@hatiolab/things-scene").ComponentEventMap>(name: K, callback?: ((...args: import("@hatiolab/things-scene").ComponentEventMap[K]) => void) | undefined, context?: any): any;
|
|
217
220
|
off(name?: string | object, callback?: Function, context?: any): any;
|
|
221
|
+
once<K extends keyof import("@hatiolab/things-scene").ComponentEventMap>(name: K, callback: (...args: import("@hatiolab/things-scene").ComponentEventMap[K]) => void, context?: any): any;
|
|
218
222
|
once(name: string | object, callback: Function, context?: any): any;
|
|
223
|
+
trigger<K extends keyof import("@hatiolab/things-scene").ComponentEventMap>(name: K, ...args: import("@hatiolab/things-scene").ComponentEventMap[K]): any;
|
|
219
224
|
trigger(name: string, ...args: any[]): any;
|
|
220
225
|
delegate_on(delegator: any): any;
|
|
221
226
|
delegate_off(delegator: any): any;
|
|
@@ -295,7 +300,7 @@ declare const URDFObject_base: (new (...args: any[]) => {
|
|
|
295
300
|
get disposed(): boolean;
|
|
296
301
|
isLayer(): boolean;
|
|
297
302
|
isGroup(): boolean;
|
|
298
|
-
isContainer(): this is import("@hatiolab/things-scene
|
|
303
|
+
isContainer(): this is import("@hatiolab/things-scene").Container;
|
|
299
304
|
isLine(): boolean;
|
|
300
305
|
isRoot(): boolean;
|
|
301
306
|
isRootModel(): boolean;
|
|
@@ -311,9 +316,9 @@ declare const URDFObject_base: (new (...args: any[]) => {
|
|
|
311
316
|
get(property: any): any;
|
|
312
317
|
set(props: any, propval?: any): any;
|
|
313
318
|
getState(property: any): any;
|
|
314
|
-
setState(props:
|
|
319
|
+
setState(props: Partial<import("@hatiolab/things-scene").State> | string, propval?: any): any;
|
|
315
320
|
get model(): any;
|
|
316
|
-
get state():
|
|
321
|
+
get state(): import("@hatiolab/things-scene").State;
|
|
317
322
|
get hierarchy(): any;
|
|
318
323
|
get volatile(): never[];
|
|
319
324
|
_applyProps(target: any, props: any, options: any): any;
|
|
@@ -404,7 +409,7 @@ declare const URDFObject_base: (new (...args: any[]) => {
|
|
|
404
409
|
findAll(s: string | ((c: import("@hatiolab/things-scene").Component) => boolean), ...others: any[]): any[] | undefined;
|
|
405
410
|
capture(x: number, y: number, except?: (c: import("@hatiolab/things-scene").Component) => boolean): any;
|
|
406
411
|
findAnchor(name: string): any;
|
|
407
|
-
isDescendible(container: import("@hatiolab/things-scene").Component): boolean;
|
|
412
|
+
isDescendible(container: import("@hatiolab/things-scene").Component | any): boolean;
|
|
408
413
|
getContext(component?: unknown): any;
|
|
409
414
|
get root(): import("@hatiolab/things-scene").Component;
|
|
410
415
|
get rootModel(): import("@hatiolab/things-scene").Component;
|
|
@@ -448,9 +453,13 @@ declare const URDFObject_base: (new (...args: any[]) => {
|
|
|
448
453
|
toLocal(x: number, y: number, top?: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
|
|
449
454
|
toGlobal(x: number, y: number, top?: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
|
|
450
455
|
toOther(x: number, y: number, target: import("@hatiolab/things-scene").Component): import("@hatiolab/things-scene").POINT;
|
|
456
|
+
on<K extends keyof import("@hatiolab/things-scene").ComponentEventMap>(name: K, callback: (...args: import("@hatiolab/things-scene").ComponentEventMap[K]) => void, context?: any): any;
|
|
451
457
|
on(name: string | object, callback: Function, context?: any): any;
|
|
458
|
+
off<K extends keyof import("@hatiolab/things-scene").ComponentEventMap>(name: K, callback?: ((...args: import("@hatiolab/things-scene").ComponentEventMap[K]) => void) | undefined, context?: any): any;
|
|
452
459
|
off(name?: string | object, callback?: Function, context?: any): any;
|
|
460
|
+
once<K extends keyof import("@hatiolab/things-scene").ComponentEventMap>(name: K, callback: (...args: import("@hatiolab/things-scene").ComponentEventMap[K]) => void, context?: any): any;
|
|
453
461
|
once(name: string | object, callback: Function, context?: any): any;
|
|
462
|
+
trigger<K extends keyof import("@hatiolab/things-scene").ComponentEventMap>(name: K, ...args: import("@hatiolab/things-scene").ComponentEventMap[K]): any;
|
|
454
463
|
trigger(name: string, ...args: any[]): any;
|
|
455
464
|
delegate_on(delegator: any): any;
|
|
456
465
|
delegate_off(delegator: any): any;
|
|
@@ -483,6 +492,8 @@ declare const URDFObject_base: (new (...args: any[]) => {
|
|
|
483
492
|
set element(_v: HTMLElement | null): any;
|
|
484
493
|
}) & typeof Shape;
|
|
485
494
|
export declare class URDFObject extends URDFObject_base {
|
|
495
|
+
/** URDF 모델의 top-view 스냅샷 (캔버스). RealObjectURDF._onLoaded 가 캐시 hit 시 직접 세팅. */
|
|
496
|
+
_topViewSnapshot?: HTMLCanvasElement;
|
|
486
497
|
/**
|
|
487
498
|
* URDF 캐시 hit 만 처리. miss 시는 RealObjectURDF.\_onLoaded 가
|
|
488
499
|
* 캐시를 채우고 component.\_topViewSnapshot 을 직접 세팅한다 (URDF
|
package/dist/urdf-object.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"urdf-object.js","sourceRoot":"","sources":["../src/urdf-object.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;;AAEH,OAAO,EACL,aAAa,EACb,QAAQ,EACR,KAAK,EACL,cAAc,EAGf,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"urdf-object.js","sourceRoot":"","sources":["../src/urdf-object.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;;AAEH,OAAO,EACL,aAAa,EACb,QAAQ,EACR,KAAK,EACL,cAAc,EAGf,MAAM,wBAAwB,CAAA;AAE/B,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAGtD,MAAM,cAAc,GAAG,WAAW,CAAA;AAElC,MAAM,MAAM,GAAoB;IAC9B,OAAO,EAAE,KAAK;IACd,SAAS,EAAE,IAAI;IACf,SAAS,EAAE,IAAI;IACf,UAAU,EAAE;QACV;YACE,IAAI,EAAE,aAAa;YACnB,KAAK,EAAE,QAAQ;YACf,IAAI,EAAE,QAAQ;SACf;QACD;YACE,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,KAAK;YACZ,IAAI,EAAE,KAAK;YACX,QAAQ,EAAE;gBACR,YAAY,EAAE,IAAI;gBAClB,cAAc,EAAE,IAAI;gBACpB,YAAY,EAAE,cAAc;gBAC5B,cAAc,EAAE,UAAU;gBAC1B,cAAc,EAAE;oBACd,IAAI,EAAE,KAAK;oBACX,KAAK,EAAE;wBACL;4BACE,IAAI,EAAE,UAAU;4BAChB,KAAK,EAAE,MAAM;yBACd;qBACF;iBACF;gBACD,SAAS,EAAE,IAAI;gBACf,QAAQ,EAAE,aAAa;aACxB;SACF;QACD;YACE,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,SAAS;YAChB,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE;gBACR,OAAO,EAAE;oBACP,EAAE,OAAO,EAAE,kBAAkB,EAAE,KAAK,EAAE,GAAG,EAAE;oBAC3C,EAAE,OAAO,EAAE,sBAAsB,EAAE,KAAK,EAAE,GAAG,EAAE;iBAChD;aACF;SACF;QACD;YACE,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,YAAY;YACnB,IAAI,EAAE,WAAW;YACjB,WAAW,EAAE,MAAM;SACpB;QACD;YACE,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,cAAc;YACrB,IAAI,EAAE,aAAa;YACnB,QAAQ,EAAE;gBACR,OAAO,EAAE;oBACP,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE;oBACjC,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE;iBACzC;aACF;SACF;QACD;YACE,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,SAAS;YAChB,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE;gBACR,OAAO,EAAE;oBACP,EAAE,OAAO,EAAE,oBAAoB,EAAE,KAAK,EAAE,MAAM,EAAE;oBAChD,EAAE,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,QAAQ,EAAE;iBAChD;aACF;SACF;QACD;YACE,IAAI,EAAE,aAAa;YACnB,KAAK,EAAE,EAAE;YACT,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE;gBACN,SAAS,EAAE,IAAI;aAChB;SACF;KACF;IACD,gBAAgB,EAAE,QAAQ;IAC1B,IAAI,EAAE,sBAAsB;CAC7B,CAAA;AAGM,IAAM,UAAU,GAAhB,MAAM,UAAW,SAAQ,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAI5D;;;;;OAKG;IACH,sBAAsB;QACpB,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAuB,CAAA;QACtD,IAAI,CAAC,GAAG;YAAE,OAAM;QAEhB,MAAM,MAAM,GAAG,cAAc,CAAC,eAAe,CAAC,GAAG,CAAC,CAAA;QAClD,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAA;YAC9B,IAAI,CAAC,UAAU,EAAE,CAAA;QACnB,CAAC;IACH,CAAC;IAEQ,eAAe;QACtB,OAAO,IAAI,cAAc,CAAC,IAAI,CAAC,CAAA;IACjC,CAAC;IAED,IAAI,MAAM;QACR,OAAO,MAAM,CAAA;IACf,CAAC;IAED,uBAAuB;IAEvB,kDAAkD;IAClD,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAChC,CAAC;IAED,IAAI,MAAM,CAAC,KAA0D;QACnE,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;IAChC,CAAC;IAED,4DAA4D;IAC5D,IAAI,SAAS;QACX,MAAM,EAAE,GAAG,IAAI,CAAC,UAAwC,CAAA;QACxD,OAAO,EAAE,EAAE,SAAS,IAAI,EAAE,CAAA;IAC5B,CAAC;IAED,IAAI,UAAU;QACZ,MAAM,EAAE,GAAG,IAAI,CAAC,UAAwC,CAAA;QACxD,OAAO,EAAE,EAAE,UAAU,IAAI,EAAE,CAAA;IAC7B,CAAC;CACF,CAAA;AAlDY,UAAU;IADtB,cAAc,CAAC,MAAM,CAAC;GACV,UAAU,CAkDtB","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 /** URDF 모델의 top-view 스냅샷 (캔버스). RealObjectURDF._onLoaded 가 캐시 hit 시 직접 세팅. */\n declare _topViewSnapshot?: HTMLCanvasElement\n\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._topViewSnapshot = cached\n this.invalidate()\n }\n }\n\n override buildRealObject(): IRealObject | undefined {\n return new RealObjectURDF(this)\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.
|
|
5
|
+
"version": "10.0.0-beta.31",
|
|
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": "
|
|
69
|
+
"gitHead": "fdafbd04fd083a43690be937230c7d96a3ee5da3"
|
|
70
70
|
}
|