@operato/scene-manufacturing 10.0.0-beta.31 → 10.0.0-beta.32
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/robot-arm.d.ts +42 -5
- package/dist/robot-arm.js +136 -26
- package/dist/robot-arm.js.map +1 -1
- package/package.json +3 -3
package/dist/robot-arm.d.ts
CHANGED
|
@@ -50,6 +50,15 @@ declare const Base: typeof Component & {
|
|
|
50
50
|
export default class RobotArm extends Base {
|
|
51
51
|
get state(): RobotArmState;
|
|
52
52
|
_realObject: RobotArm3D | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* Phase H — pickup contract 호환성 검사용. 보편적 'gripper' type — vacuum,
|
|
55
|
+
* 2-finger, 3-finger 등 다양한 gripper 모두 이 type 으로 매칭. carrier 가
|
|
56
|
+
* 더 세분화된 호환성을 원하면 `pickupFramesFor` override 로 size 등 추가 검증.
|
|
57
|
+
*
|
|
58
|
+
* (override 키워드 미사용 — Base 캐스트가 Mover 의 toolType 을 노출하지 않아
|
|
59
|
+
* TS4113 회피.)
|
|
60
|
+
*/
|
|
61
|
+
get toolType(): string;
|
|
53
62
|
static legends: Record<string, LegendBinding>;
|
|
54
63
|
/**
|
|
55
64
|
* Robot arms sit on the floor (or mount to a ceiling/wall — that's a
|
|
@@ -170,13 +179,20 @@ export default class RobotArm extends Base {
|
|
|
170
179
|
*
|
|
171
180
|
* 1. TCP → APPROACH above place (carrier's xy, +clearance)
|
|
172
181
|
* 2. TCP → place (descend straight down to drop point)
|
|
173
|
-
* 3. open gripper +
|
|
182
|
+
* 3. open gripper + dispatch (carrier → holder)
|
|
174
183
|
*
|
|
175
184
|
* Pose resolution: TCP lands at where the carrier's top WILL BE after
|
|
176
185
|
* the drop, so the carrier lands with its bottom on the holder's attach
|
|
177
186
|
* surface. Computed as `holder.attachFrame.world.y + carrier.depth` (so
|
|
178
187
|
* yOffset = carrierDepth, not halfDepth like pick).
|
|
179
188
|
*
|
|
189
|
+
* Handoff: if this arm has `dispatch` (ContainerCapacity) and the holder
|
|
190
|
+
* exposes `receive`, route via dispatch — slot tracking, `transfer-received`
|
|
191
|
+
* trigger (which downstream conveyance components like InductStation listen
|
|
192
|
+
* to for belt-animation kickoff), and `canReceive` pre-flight all flow
|
|
193
|
+
* through the standard contract. Falls back to raw `holder.reparent()` for
|
|
194
|
+
* holders without ContainerCapacity (plain Spot, etc.). Mirrors Mover.place.
|
|
195
|
+
*
|
|
180
196
|
* @see pick — same option semantics.
|
|
181
197
|
*/
|
|
182
198
|
place(carrier: Component, holder: Component, options?: MoveOptions): Promise<void>;
|
|
@@ -219,10 +235,19 @@ export default class RobotArm extends Base {
|
|
|
219
235
|
/**
|
|
220
236
|
* Resolve a TCP position in this arm's chain-local frame.
|
|
221
237
|
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
238
|
+
* Resolution priority:
|
|
239
|
+
* 1. `attachPointFor` (CarrierHolder) — canonical TCP frame for arms /
|
|
240
|
+
* Spot pads / gripper-on-gripper.
|
|
241
|
+
* 2. `deliveryWorldPosition` (Phase G9 — Holder contract). Holder 가
|
|
242
|
+
* ContainerCapacity 의 default 또는 자체 override 로 정확한 도달
|
|
243
|
+
* world XYZ 를 직접 제공. 이전 SCAFFOLDING 의 cz 공식 역연산이
|
|
244
|
+
* 여기로 이전됨.
|
|
245
|
+
* 3. Component's own `_realObject.object3d` world center.
|
|
246
|
+
* 4. 2D bounds center (only before 3D builds).
|
|
247
|
+
*
|
|
248
|
+
* `carrierForDelivery` 가 주어지면 (place 케이스) `component` 를 holder 로
|
|
249
|
+
* 간주하고 `component.deliveryWorldPosition(carrierForDelivery)` 를 query.
|
|
250
|
+
* 미지정 (pick 케이스 — `component` 자체가 carrier) 시 #3 으로.
|
|
226
251
|
*
|
|
227
252
|
* `worldYOffset` is added to the resolved world Y before projecting
|
|
228
253
|
* back to chain-local — used by pickAndPlace to lift the TCP above
|
|
@@ -238,5 +263,17 @@ export default class RobotArm extends Base {
|
|
|
238
263
|
* moment of reparent.
|
|
239
264
|
*/
|
|
240
265
|
private _yawOf;
|
|
266
|
+
/**
|
|
267
|
+
* Phase G5.2 — Delivery yaw 결정.
|
|
268
|
+
*
|
|
269
|
+
* holder 가 ContainerCapacity contract 의 deliveryWorldRotation 을 노출하면
|
|
270
|
+
* 그 world quaternion 에서 yaw 를 추출 (carrier 가 acceptance pose 와 일치
|
|
271
|
+
* 한 채 release 되도록). InductStation 같은 holder 가 sorter forward 방향을
|
|
272
|
+
* 자기 acceptanceRotation 으로 선언하면 자동으로 그 방향이 TCP yaw 가 됨.
|
|
273
|
+
*
|
|
274
|
+
* holder 가 deliveryWorldRotation 을 안 노출하거나 _realObject 미빌드 시
|
|
275
|
+
* 기존 동작 (holder 자체의 world yaw) 으로 fallback.
|
|
276
|
+
*/
|
|
277
|
+
private _deliveryYawOf;
|
|
241
278
|
}
|
|
242
279
|
export {};
|
package/dist/robot-arm.js
CHANGED
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
*/
|
|
34
34
|
import { __decorate } from "tslib";
|
|
35
35
|
import * as THREE from 'three';
|
|
36
|
-
import { ContainerAbstract, ContainerCapacity, sceneComponent } from '@hatiolab/things-scene';
|
|
36
|
+
import { ContainerAbstract, ContainerCapacity, frameClock, sceneComponent } from '@hatiolab/things-scene';
|
|
37
37
|
import { CarrierHolder, Legendable, Mover, Placeable } from '@operato/scene-base';
|
|
38
38
|
import { RobotArm3D } from './robot-arm-3d.js';
|
|
39
39
|
import { CHAIN_PRESETS } from './robot-arm-styles.js';
|
|
@@ -151,6 +151,17 @@ let RobotArm = class RobotArm extends Base {
|
|
|
151
151
|
get state() {
|
|
152
152
|
return super.state;
|
|
153
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Phase H — pickup contract 호환성 검사용. 보편적 'gripper' type — vacuum,
|
|
156
|
+
* 2-finger, 3-finger 등 다양한 gripper 모두 이 type 으로 매칭. carrier 가
|
|
157
|
+
* 더 세분화된 호환성을 원하면 `pickupFramesFor` override 로 size 등 추가 검증.
|
|
158
|
+
*
|
|
159
|
+
* (override 키워드 미사용 — Base 캐스트가 Mover 의 toolType 을 노출하지 않아
|
|
160
|
+
* TS4113 회피.)
|
|
161
|
+
*/
|
|
162
|
+
get toolType() {
|
|
163
|
+
return 'gripper';
|
|
164
|
+
}
|
|
154
165
|
static legends = {
|
|
155
166
|
bodyColor: { from: 'status', legend: BODY_LEGEND },
|
|
156
167
|
borderColor: { from: 'status', legend: BORDER_LEGEND },
|
|
@@ -401,13 +412,20 @@ let RobotArm = class RobotArm extends Base {
|
|
|
401
412
|
*
|
|
402
413
|
* 1. TCP → APPROACH above place (carrier's xy, +clearance)
|
|
403
414
|
* 2. TCP → place (descend straight down to drop point)
|
|
404
|
-
* 3. open gripper +
|
|
415
|
+
* 3. open gripper + dispatch (carrier → holder)
|
|
405
416
|
*
|
|
406
417
|
* Pose resolution: TCP lands at where the carrier's top WILL BE after
|
|
407
418
|
* the drop, so the carrier lands with its bottom on the holder's attach
|
|
408
419
|
* surface. Computed as `holder.attachFrame.world.y + carrier.depth` (so
|
|
409
420
|
* yOffset = carrierDepth, not halfDepth like pick).
|
|
410
421
|
*
|
|
422
|
+
* Handoff: if this arm has `dispatch` (ContainerCapacity) and the holder
|
|
423
|
+
* exposes `receive`, route via dispatch — slot tracking, `transfer-received`
|
|
424
|
+
* trigger (which downstream conveyance components like InductStation listen
|
|
425
|
+
* to for belt-animation kickoff), and `canReceive` pre-flight all flow
|
|
426
|
+
* through the standard contract. Falls back to raw `holder.reparent()` for
|
|
427
|
+
* holders without ContainerCapacity (plain Spot, etc.). Mirrors Mover.place.
|
|
428
|
+
*
|
|
411
429
|
* @see pick — same option semantics.
|
|
412
430
|
*/
|
|
413
431
|
async place(carrier, holder, options = {}) {
|
|
@@ -415,14 +433,29 @@ let RobotArm = class RobotArm extends Base {
|
|
|
415
433
|
const reparentDuration = options.reparentDuration ?? 800;
|
|
416
434
|
const clearance = options.approachClearance ?? 10;
|
|
417
435
|
const carrierDepth = resolveDepth(carrier);
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
436
|
+
// Phase G5.2 — holder 가 acceptanceRotation 을 선언하면 그 world equivalent
|
|
437
|
+
// (deliveryWorldRotation) 의 yaw 를 TCP yaw 로 사용. 그러면 carrier 가 holder
|
|
438
|
+
// 의 acceptance pose 와 정확히 일치한 채 release 되어 handoff 시 zero-delta
|
|
439
|
+
// (시각적 점프 없음). holder 가 미정의면 holder 자체의 world yaw 사용
|
|
440
|
+
// (이전 동작).
|
|
441
|
+
const yaw = this._deliveryYawOf(holder, carrier);
|
|
442
|
+
// Phase G9 — carrier 도 함께 전달해 holder 의 deliveryWorldPosition 을 정확히
|
|
443
|
+
// query. holder 가 contract 노출 안 하면 fallback (object3d 원점) 으로.
|
|
444
|
+
const target = this._targetFor(holder, carrierDepth, yaw, carrier); // top-of-carrier after drop
|
|
445
|
+
const approach = this._targetFor(holder, carrierDepth + clearance, yaw, carrier);
|
|
421
446
|
this.set('target', approach);
|
|
422
447
|
await waitForTcpReached(this, timeoutMs);
|
|
423
448
|
this.set('target', target);
|
|
424
449
|
await waitForTcpReached(this, timeoutMs);
|
|
425
|
-
|
|
450
|
+
const xferOptions = { animated: true, duration: reparentDuration };
|
|
451
|
+
const canDispatch = typeof this.dispatch === 'function' && typeof holder.receive === 'function';
|
|
452
|
+
if (canDispatch) {
|
|
453
|
+
await this.dispatch(carrier, holder, xferOptions);
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
;
|
|
457
|
+
holder.reparent?.(carrier, xferOptions);
|
|
458
|
+
}
|
|
426
459
|
this.set('gripper', { ...(this.state.gripper ?? {}), state: 0 });
|
|
427
460
|
await sleep(reparentDuration);
|
|
428
461
|
}
|
|
@@ -481,17 +514,26 @@ let RobotArm = class RobotArm extends Base {
|
|
|
481
514
|
/**
|
|
482
515
|
* Resolve a TCP position in this arm's chain-local frame.
|
|
483
516
|
*
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
517
|
+
* Resolution priority:
|
|
518
|
+
* 1. `attachPointFor` (CarrierHolder) — canonical TCP frame for arms /
|
|
519
|
+
* Spot pads / gripper-on-gripper.
|
|
520
|
+
* 2. `deliveryWorldPosition` (Phase G9 — Holder contract). Holder 가
|
|
521
|
+
* ContainerCapacity 의 default 또는 자체 override 로 정확한 도달
|
|
522
|
+
* world XYZ 를 직접 제공. 이전 SCAFFOLDING 의 cz 공식 역연산이
|
|
523
|
+
* 여기로 이전됨.
|
|
524
|
+
* 3. Component's own `_realObject.object3d` world center.
|
|
525
|
+
* 4. 2D bounds center (only before 3D builds).
|
|
526
|
+
*
|
|
527
|
+
* `carrierForDelivery` 가 주어지면 (place 케이스) `component` 를 holder 로
|
|
528
|
+
* 간주하고 `component.deliveryWorldPosition(carrierForDelivery)` 를 query.
|
|
529
|
+
* 미지정 (pick 케이스 — `component` 자체가 carrier) 시 #3 으로.
|
|
488
530
|
*
|
|
489
531
|
* `worldYOffset` is added to the resolved world Y before projecting
|
|
490
532
|
* back to chain-local — used by pickAndPlace to lift the TCP above
|
|
491
533
|
* the target (approach waypoints) and to land it on the carrier's
|
|
492
534
|
* top face rather than its volumetric center.
|
|
493
535
|
*/
|
|
494
|
-
_targetFor(component, worldYOffset = 0, yaw) {
|
|
536
|
+
_targetFor(component, worldYOffset = 0, yaw, carrierForDelivery) {
|
|
495
537
|
const ro3d = this._realObject;
|
|
496
538
|
let baseWp;
|
|
497
539
|
// Prefer holder's attach frame when available.
|
|
@@ -504,10 +546,24 @@ let RobotArm = class RobotArm extends Base {
|
|
|
504
546
|
}
|
|
505
547
|
}
|
|
506
548
|
if (!baseWp) {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
549
|
+
// Phase G9 — Holder.deliveryWorldPosition contract 를 우선 사용 (place
|
|
550
|
+
// 케이스: carrierForDelivery 가 주어진 경우). ContainerCapacity 가 default
|
|
551
|
+
// 로 receivingSurfaceY/geometricOffsetY 보정 + 표면 변환을 제공하므로
|
|
552
|
+
// robot-arm 이 cz 공식 역연산을 직접 알지 않아도 됨.
|
|
553
|
+
if (carrierForDelivery) {
|
|
554
|
+
const deliveryPos = component.deliveryWorldPosition?.(carrierForDelivery);
|
|
555
|
+
if (deliveryPos) {
|
|
556
|
+
baseWp = new THREE.Vector3(deliveryPos.x, deliveryPos.y, deliveryPos.z);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
// Fallback (pick 케이스 또는 holder 가 contract 미노출): component 자체의
|
|
560
|
+
// obj3d world center.
|
|
561
|
+
if (!baseWp) {
|
|
562
|
+
const obj = component._realObject?.object3d;
|
|
563
|
+
if (obj) {
|
|
564
|
+
obj.updateWorldMatrix(true, false);
|
|
565
|
+
baseWp = obj.getWorldPosition(new THREE.Vector3());
|
|
566
|
+
}
|
|
511
567
|
}
|
|
512
568
|
}
|
|
513
569
|
if (!ro3d || !baseWp) {
|
|
@@ -535,33 +591,87 @@ let RobotArm = class RobotArm extends Base {
|
|
|
535
591
|
const e = new THREE.Euler().setFromQuaternion(q, 'YXZ');
|
|
536
592
|
return e.y;
|
|
537
593
|
}
|
|
594
|
+
/**
|
|
595
|
+
* Phase G5.2 — Delivery yaw 결정.
|
|
596
|
+
*
|
|
597
|
+
* holder 가 ContainerCapacity contract 의 deliveryWorldRotation 을 노출하면
|
|
598
|
+
* 그 world quaternion 에서 yaw 를 추출 (carrier 가 acceptance pose 와 일치
|
|
599
|
+
* 한 채 release 되도록). InductStation 같은 holder 가 sorter forward 방향을
|
|
600
|
+
* 자기 acceptanceRotation 으로 선언하면 자동으로 그 방향이 TCP yaw 가 됨.
|
|
601
|
+
*
|
|
602
|
+
* holder 가 deliveryWorldRotation 을 안 노출하거나 _realObject 미빌드 시
|
|
603
|
+
* 기존 동작 (holder 자체의 world yaw) 으로 fallback.
|
|
604
|
+
*/
|
|
605
|
+
_deliveryYawOf(holder, carrier) {
|
|
606
|
+
const deliveryRot = holder.deliveryWorldRotation?.(carrier);
|
|
607
|
+
if (deliveryRot) {
|
|
608
|
+
const q = new THREE.Quaternion(deliveryRot.x, deliveryRot.y, deliveryRot.z, deliveryRot.w);
|
|
609
|
+
const e = new THREE.Euler().setFromQuaternion(q, 'YXZ');
|
|
610
|
+
return e.y;
|
|
611
|
+
}
|
|
612
|
+
return this._yawOf(holder);
|
|
613
|
+
}
|
|
538
614
|
};
|
|
539
615
|
RobotArm = __decorate([
|
|
540
616
|
sceneComponent('robot-arm')
|
|
541
617
|
], RobotArm);
|
|
542
618
|
export default RobotArm;
|
|
543
619
|
/**
|
|
544
|
-
* Resolve when the robot arm fires `tcp-reached`, reject on
|
|
545
|
-
*
|
|
546
|
-
*
|
|
620
|
+
* Resolve when the robot arm fires `tcp-reached`, reject on **simulation-time**
|
|
621
|
+
* timeout. 3D smoothing 은 frameClock 의 simDt 로 진행 (sim-time-driven),
|
|
622
|
+
* 따라서 timeout 도 sim-time 으로 누적해야 sim speed 변경에 일관 동작:
|
|
623
|
+
* - speed=1 → sim time = real time (기존과 동일)
|
|
624
|
+
* - speed=2 → IK 도 2× 빨라지고 timeout 도 2× 빠르게 도달 (real time 기준)
|
|
625
|
+
* - speed=0.5 → IK 가 2× 느려지지만 timeout 도 sim 기준이라 spurious
|
|
626
|
+
* reject 없음 (이전 real-time setTimeout 으로는 1 미만 speed
|
|
627
|
+
* 에서 IK 도달 전에 timeout 발화하던 버그 수정)
|
|
628
|
+
* - paused → simDt=0 누적도 정지 → 일시정지 동안 무한 대기 (의도)
|
|
547
629
|
*/
|
|
548
630
|
function waitForTcpReached(component, timeoutMs) {
|
|
549
631
|
return new Promise((resolve, reject) => {
|
|
550
|
-
let
|
|
551
|
-
|
|
552
|
-
|
|
632
|
+
let elapsedSim = 0;
|
|
633
|
+
let unsub = null;
|
|
634
|
+
const cleanup = () => {
|
|
553
635
|
component.off('tcp-reached', onReached);
|
|
636
|
+
if (unsub) {
|
|
637
|
+
unsub();
|
|
638
|
+
unsub = null;
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
const onReached = () => {
|
|
642
|
+
cleanup();
|
|
554
643
|
resolve();
|
|
555
644
|
};
|
|
556
|
-
timer = setTimeout(() => {
|
|
557
|
-
component.off('tcp-reached', onReached);
|
|
558
|
-
reject(new Error('TCP reach timeout'));
|
|
559
|
-
}, timeoutMs);
|
|
560
645
|
component.on('tcp-reached', onReached);
|
|
646
|
+
unsub = frameClock.subscribe(simDt => {
|
|
647
|
+
elapsedSim += simDt;
|
|
648
|
+
if (elapsedSim >= timeoutMs) {
|
|
649
|
+
cleanup();
|
|
650
|
+
reject(new Error('TCP reach timeout'));
|
|
651
|
+
}
|
|
652
|
+
}, { description: 'robot-arm tcp-reached timeout', component });
|
|
561
653
|
});
|
|
562
654
|
}
|
|
655
|
+
/**
|
|
656
|
+
* sim-time aware sleep — frameClock 의 simDt 를 누적해 ms 도달 시 resolve.
|
|
657
|
+
* speed=0.5 이면 real time 으로 2배 걸리지만 sim time 으로는 동일 — animation
|
|
658
|
+
* 시간과 일관. paused 시 무한 대기 (의도). waitForTcpReached 와 같은 시간축.
|
|
659
|
+
*/
|
|
563
660
|
function sleep(ms) {
|
|
564
|
-
return new Promise(resolve =>
|
|
661
|
+
return new Promise(resolve => {
|
|
662
|
+
let elapsedSim = 0;
|
|
663
|
+
let unsub = null;
|
|
664
|
+
unsub = frameClock.subscribe(simDt => {
|
|
665
|
+
elapsedSim += simDt;
|
|
666
|
+
if (elapsedSim >= ms) {
|
|
667
|
+
if (unsub) {
|
|
668
|
+
unsub();
|
|
669
|
+
unsub = null;
|
|
670
|
+
}
|
|
671
|
+
resolve();
|
|
672
|
+
}
|
|
673
|
+
}, { description: 'robot-arm sim-sleep' });
|
|
674
|
+
});
|
|
565
675
|
}
|
|
566
676
|
function numOr(v, dflt) {
|
|
567
677
|
return typeof v === 'number' && Number.isFinite(v) ? v : dflt;
|
package/dist/robot-arm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"robot-arm.js","sourceRoot":"","sources":["../src/robot-arm.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * Robot Arm — generalized articulated manipulator component.\n *\n * Designed to cover the visualization needs of robot arms WITHOUT a\n * specific URDF model. Configurable kinematic chain, link shape, joint\n * housing, gripper type — same component renders as a sleek cobot, a\n * heavy industrial 6-axis, a SCARA, or a minimal educational arm.\n *\n * State schema (all top-level properties optional except width/height):\n *\n * {\n * // structure\n * style?: 'industrial' | 'cobot' | 'scara' | 'minimal',\n * chainPreset?: '6-axis-industrial' | 'cobot-6-axis' | 'scara-4-axis' | 'pick-and-place-3-axis',\n * chain?: ChainElement[], // overrides preset\n * joints?: number[] | { [i]: number }, // current joint values\n *\n * // styling overrides\n * linkShape?, jointHousing?, linkRadius?, color?, accentColor?,\n *\n * // gripper\n * gripper?: GripperConfig,\n *\n * // IK target (TCP world position relative to component center, in\n * // 3D-z-up convention used by IK module)\n * target?: { x: number, y: number, z: number },\n *\n * // visualization\n * showReachSphere?: boolean\n * }\n */\n\nimport * as THREE from 'three'\nimport { Component, ComponentNature, ContainerAbstract, ContainerCapacity, RealObject, sceneComponent } from '@hatiolab/things-scene'\nimport type { SlotDef, State, Material3D } from '@hatiolab/things-scene'\nimport {\n CarrierHolder,\n Legendable,\n Mover,\n Placeable,\n type Alignment,\n type Heights,\n type LegendBinding,\n type MoveOptions,\n type PlacementArchetype\n} from '@operato/scene-base'\n\nimport { RobotArm3D } from './robot-arm-3d.js'\nimport { CHAIN_PRESETS, type ChainPresetName } from './robot-arm-styles.js'\nimport type { ChainElement } from './robot-arm-ik.js'\nimport type { GripperConfig, LinkShape, JointHousing, StyleName } from './robot-arm-styles.js'\n\n// ── ComponentEventMap 도메인 augmentation ─────────────────────────────────\n// RobotArm 이 발생시키는 도메인 이벤트들. things-scene 의 ComponentEventMap 에\n// 추가 등록하여 component.on/trigger 에서 자동완성 + 페이로드 타입 검증.\ndeclare module '@hatiolab/things-scene' {\n interface ComponentEventMap {\n /** TCP 가 목표 도달 시 발생 (pickAndPlace 시퀀싱용). */\n 'tcp-reached': [info: { component: Component }]\n }\n}\n\nexport type RobotArmStatus = 'idle' | 'moving' | 'gripping' | 'warn' | 'error'\n\n/** RobotArm 컴포넌트 state */\nexport interface RobotArmState extends State {\n // ── 구조 ──\n style?: StyleName\n chainPreset?: ChainPresetName\n chain?: ChainElement[]\n joints?: number[] | Record<string, number>\n linkShape?: LinkShape\n jointHousing?: JointHousing\n linkRadius?: number\n color?: string | number\n accentColor?: string | number\n\n // ── 그리퍼 ──\n gripper?: GripperConfig\n gripperType?: GripperConfig['type']\n\n // ── IK 타겟 ──\n target?: { x: number; y: number; z: number; yaw?: number }\n\n // ── 시각화 ──\n showReachSphere?: boolean\n\n // ── 모션 ──\n jointSpeed?: number\n\n // ── 운영 상태 ──\n status?: RobotArmStatus\n\n // ── 3D 재질 ──\n material3d?: Material3D\n}\n\nconst BODY_LEGEND = {\n idle: '#ccc',\n moving: '#afd0f1',\n gripping: '#cfe6c8',\n warn: '#ffba00',\n error: '#e9746b',\n default: '#ccc'\n}\n\nconst BORDER_LEGEND = {\n idle: '#888',\n moving: '#87b1db',\n gripping: '#7aa274',\n warn: '#d96f21',\n error: '#a73928',\n default: '#888'\n}\n\nconst LAMP_EMISSIVE_LEGEND = {\n idle: '#333333',\n moving: '#44aaff',\n gripping: '#33aa44',\n warn: '#ffaa00',\n error: '#ff3333',\n default: '#333333'\n}\n\nconst STATIC_PROPERTIES = [\n {\n type: 'select',\n label: 'style',\n name: 'style',\n property: {\n options: [\n { display: 'Industrial', value: 'industrial' },\n { display: 'Cobot', value: 'cobot' },\n { display: 'SCARA', value: 'scara' },\n { display: 'Minimal', value: 'minimal' }\n ]\n }\n },\n {\n type: 'select',\n label: 'chain-preset',\n name: 'chainPreset',\n property: {\n options: [\n { display: '6-Axis Industrial', value: '6-axis-industrial' },\n { display: 'Cobot 6-Axis', value: 'cobot-6-axis' },\n { display: 'SCARA 4-Axis', value: 'scara-4-axis' },\n { display: 'Pick & Place 3-Axis', value: 'pick-and-place-3-axis' }\n ]\n }\n },\n { type: 'number', label: 'link-radius', name: 'linkRadius' },\n {\n type: 'select',\n label: 'gripper-type',\n name: 'gripperType',\n property: {\n options: [\n { display: 'Parallel', value: 'parallel' },\n { display: 'Suction', value: 'suction' },\n { display: 'Magnetic', value: 'magnetic' },\n { display: 'Three-Finger', value: 'three-finger' },\n { display: 'None', value: 'none' }\n ]\n }\n },\n { type: 'checkbox', label: 'show-reach-sphere', name: 'showReachSphere' },\n {\n type: 'select',\n label: 'status',\n name: 'status',\n property: {\n options: [\n { display: 'Idle', value: 'idle' },\n { display: 'Moving', value: 'moving' },\n { display: 'Gripping', value: 'gripping' },\n { display: 'Warn', value: 'warn' },\n { display: 'Error', value: 'error' }\n ]\n }\n }\n]\n\n// CarrierHolder + ContainerAbstract: the arm needs the container's child-list\n// machinery (addComponent / reparent / events) AND CarrierHolder's\n// attachPointFor hook. CarrierHolder mixin alone only publishes the attach\n// policy; it doesn't add child management. RectPath(Shape) was wrong here —\n// Shape is a leaf so reparent silently failed. ContainerAbstract provides\n// the missing piece.\n//\n// `ContainerAbstract` (not `Container`) — Container = MixinHTMLElement(ContainerAbstract),\n// which forces `isHTMLElement(): true` and trips the 3D pipeline's\n// addObject DOM-skip gate. Robot arm exists purely in the 3D scene graph\n// (URDF/IK joints, no DOM overlay), so the bare logical container is correct.\n//\n// Mover wraps the chain to publish pick/place/pickAndPlace primitives. It\n// must sit OUTSIDE CarrierHolder because Mover's defaults call this.reparent,\n// which CarrierHolder provides (overrides the base ContainerAbstract\n// version with the attach-frame-aware variant).\n//\n// The instance type cast surfaces Mover's API on `this` so subclass\n// `override` keywords compile and `this.pickAndPlace(...)` resolves —\n// without it, the `as unknown as typeof Component` cast erases all\n// mixin contributions to the static side.\n// ContainerCapacity inserted between CarrierHolder and Legendable — same pattern\n// as Forklift/AGV: receive()/dispatch() for slot-tracking + event emission;\n// CarrierHolder.attachPointFor() (overridden below) provides 3D TCP positioning.\nconst Base = Mover(CarrierHolder(ContainerCapacity(Legendable(Placeable(ContainerAbstract))))) as unknown as typeof Component & {\n new (...args: any[]): Component & {\n isMover: boolean\n pick(carrier: Component, options?: MoveOptions): Promise<void>\n place(carrier: Component, holder: Component, options?: MoveOptions): Promise<void>\n pickAndPlace(carrier: Component, holder: Component, options?: MoveOptions): Promise<void>\n }\n}\n\n@sceneComponent('robot-arm')\nexport default class RobotArm extends Base {\n // `Base` is cast through `typeof Component` so TS sees `state` as an\n // accessor; `declare state: …` would conflict with TS2610. Override the\n // getter instead — runtime behavior is identical (just delegates to super).\n override get state(): RobotArmState {\n return super.state as RobotArmState\n }\n\n declare _realObject: RobotArm3D | undefined\n\n static legends: Record<string, LegendBinding> = {\n bodyColor: { from: 'status', legend: BODY_LEGEND },\n borderColor: { from: 'status', legend: BORDER_LEGEND },\n lampEmissive: { from: 'status', legend: LAMP_EMISSIVE_LEGEND }\n }\n\n /**\n * Robot arms sit on the floor (or mount to a ceiling/wall — that's a\n * v2 feature). Default depth = operation - floor like other floor-\n * mounted equipment, so the chain reach scales with the configured\n * facility height.\n */\n static placement: PlacementArchetype = 'floor'\n static align: Alignment = 'bottom'\n static defaultDepth = (h: Heights) => h.operation - h.floor\n\n // ── ContainerCapacity ─────────────────────────────────────────────────────\n\n /** Single gripper slot — robot arm grips one item at a time. */\n get slots(): SlotDef[] {\n return [{ id: 'gripper', maxCount: 1 }]\n }\n\n /**\n * Dynamic nature — appends one number property per joint in the active\n * chain. Each property uses the joint's range as `min`/`max` so the\n * property panel can render a constrained slider/spinner. The chain\n * preset can change at runtime, and `get nature()` is re-evaluated\n * whenever the property panel queries it, so the joint count stays in\n * sync with the active preset.\n *\n * For revolute joints the value is in radians; for prismatic, scene units.\n */\n get nature(): ComponentNature {\n const chain = this._activeChain\n // Joint values: revolute = degrees (range usually ±180), prismatic =\n // scene length units. CHAIN_PRESETS already encode revolute ranges in\n // degrees; the 3D side converts to radians at the math boundary.\n const jointProps = chain.map((c, i) => ({\n type: 'number',\n label: `joint-${i + 1}`,\n name: `joint${i}`,\n property: {\n min: c.range[0],\n max: c.range[1],\n step: c.type === 'revolute' ? 1 : 1\n }\n }))\n return {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [...STATIC_PROPERTIES, ...jointProps] as any,\n help: 'scene/component/robot-arm'\n }\n }\n\n /**\n * Resolve the active chain for property-panel introspection. Mirrors\n * the resolution in robot-arm-3d.ts (state.chain wins, else preset,\n * else default 6-axis-industrial). Kept simple — the 3D side does the\n * same logic with depth-based scaling, but for property labeling we\n * only need the structure (axis count + ranges), not absolute lengths.\n */\n private get _activeChain(): ChainElement[] {\n const state = this.state\n if (Array.isArray(state.chain) && state.chain.length > 0) return state.chain\n const presetName = (state.chainPreset ?? '6-axis-industrial') as ChainPresetName\n return CHAIN_PRESETS[presetName] ?? CHAIN_PRESETS['6-axis-industrial']\n }\n\n get anchors() {\n return []\n }\n\n /**\n * 2D — base rectangle + a stylized arm silhouette pointing toward the\n * top-right. Just enough to identify the component as a robot arm in\n * plan view; the real visual lives in 3D.\n */\n render(ctx: CanvasRenderingContext2D) {\n const { left = 0, top = 0, width = 100, height = 100 } = this.state\n const bodyFill = pickColor(this.state.fillStyle, this.state.bodyColor, BODY_LEGEND.default)\n const borderColor = pickColor(this.state.strokeStyle, this.state.borderColor, BORDER_LEGEND.default)\n const lineWidth = numOr(this.state.lineWidth, 1)\n\n ctx.beginPath()\n ctx.rect(left, top, width, height)\n ctx.fillStyle = bodyFill\n ctx.fill()\n ctx.strokeStyle = borderColor\n ctx.lineWidth = lineWidth\n ctx.stroke()\n\n // Stylized arm silhouette: base square at center → up → bent right.\n const cx = left + width / 2\n const cy = top + height / 2\n const r = Math.min(width, height) * 0.32\n ctx.save()\n ctx.strokeStyle = borderColor\n ctx.lineWidth = Math.max(lineWidth, Math.min(width, height) * 0.025)\n\n // Base pedestal\n ctx.fillStyle = borderColor\n ctx.fillRect(cx - r * 0.5, cy + r * 0.6, r, r * 0.3)\n // Upper arm\n ctx.beginPath()\n ctx.moveTo(cx, cy + r * 0.6)\n ctx.lineTo(cx, cy - r * 0.2)\n ctx.stroke()\n // Forearm\n ctx.beginPath()\n ctx.moveTo(cx, cy - r * 0.2)\n ctx.lineTo(cx + r * 0.7, cy - r * 0.7)\n ctx.stroke()\n // Joint dots\n ctx.fillStyle = borderColor as string\n ctx.beginPath()\n ctx.arc(cx, cy - r * 0.2, Math.max(2, r * 0.1), 0, Math.PI * 2)\n ctx.fill()\n ctx.beginPath()\n ctx.arc(cx + r * 0.7, cy - r * 0.7, Math.max(1.5, r * 0.08), 0, Math.PI * 2)\n ctx.fill()\n ctx.restore()\n }\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n // Per-joint number properties (joint0 … jointN-1) are dynamic; treat\n // any key matching the pattern as a joint update.\n const hasJoint = Object.keys(after).some(k => /^joint\\d+$/.test(k))\n if (\n hasJoint ||\n 'style' in after ||\n 'chainPreset' in after ||\n 'chain' in after ||\n 'joints' in after ||\n 'linkShape' in after ||\n 'jointHousing' in after ||\n 'linkRadius' in after ||\n 'color' in after ||\n 'accentColor' in after ||\n 'fillStyle' in after ||\n 'strokeStyle' in after ||\n 'lineWidth' in after ||\n 'alpha' in after ||\n 'material3d' in after ||\n 'gripper' in after ||\n 'gripperType' in after ||\n 'target' in after ||\n 'showReachSphere' in after ||\n 'status' in after ||\n 'bodyColor' in after ||\n 'borderColor' in after\n ) {\n this.clearCache('fillStyle')\n this.invalidate()\n }\n super.onchange(after, before)\n }\n\n buildRealObject(): RealObject | undefined {\n return new RobotArm3D(this)\n }\n\n /**\n * CarrierHolder hook — return the gripper TCP frame so any Carriable\n * child (parcel/box/pallet/...) mounts onto the moving end-effector.\n * IK rotation propagates to the carrier through Three.js scene-graph.\n *\n * Pose convention (real-world pick-and-place: gripper pointing toward\n * the work, carrier hanging *beyond* the gripper tip):\n *\n * - Frame: TCP (gripper working point — between finger tips for\n * jaws, contact face for suction/magnetic). Built by\n * `RobotArm3D._buildGripper`. TCP's +Z = gripper-forward direction\n * (away from the wrist).\n *\n * - Rotation: x:-π/2 — maps carrier's local +Y axis (\"up\" in the\n * carrier's authored frame) to TCP's -Z direction. With the gripper\n * pointing down (typical work pose), TCP +Z = world -Y (down), so\n * TCP -Z = world +Y (up). Result: carrier's \"up\" stays world-up\n * during transit, and the carrier's body extends from TCP origin\n * in TCP +Z direction (down in world) — i.e. hanging BELOW the\n * gripper, not buried inside it.\n *\n * - Position: z = +halfDepth — places the carrier's TOP face center\n * at the TCP origin (where the fingers grip). Carrier's local +Y\n * extent (top) = halfDepth, which after x:-π/2 rotation maps to\n * TCP -halfDepth Z. Shifting carrier center to TCP +halfDepth Z\n * puts that top point exactly at TCP origin. Carrier body then\n * extends through TCP +Z, ending at TCP +depth (bottom face).\n *\n * Earlier (x:+π/2, z:-halfDepth) inverted the carrier's body direction\n * — body extended back into the wrist, leaving the box visually inside\n * the finger area. The current convention pushes the body OUT past the\n * fingertips, where a real gripper would hold it.\n */\n attachPointFor(carrier: Component) {\n const ro = this._realObject\n const frame = ro?.getGripperFrame?.()\n if (!frame) return undefined\n const halfDepth = resolveDepth(carrier) / 2\n return {\n attach: frame,\n localPosition: { x: 0, y: 0, z: halfDepth },\n localRotation: { x: -Math.PI / 2, y: 0, z: 0 }\n }\n }\n\n /**\n * Pick: TCP descends vertically onto the carrier's top face, gripper\n * closes, carrier becomes our child.\n *\n * 1. TCP → APPROACH above pick (carrier's xy, +clearance in world Y)\n * 2. TCP → pick (descend straight down to carrier's top face)\n * 3. close gripper + reparent(carrier → this)\n *\n * The approach waypoint exists so the FINAL leg is a clean vertical\n * descent — the gripper enters the carrier from straight above, not a\n * sideways sweep that could clip or grab at a wrong angle.\n *\n * Pose resolution: TCP lands on the CARRIER'S TOP FACE (carrier.world.y +\n * carrier.depth/2). After grip, `attachPointFor`'s `localPosition.z =\n * +halfDepth` keeps the carrier's top at TCP origin during transit —\n * exact, no offset drift.\n *\n * Each waypoint includes the carrier's world yaw so IK can spin joint 5\n * (tool roll) to align the gripper's \"side\" with the carrier's. Without\n * it, reparent at pick instantly snaps the carrier to the gripper's\n * axis, visibly twisting it 0° → up to 360° in one frame.\n *\n * @param options.approachClearance Vertical offset (world units)\n * between approach and pick targets. Default 10.\n * @param options.timeoutMs Per-segment IK timeout (default 15000ms).\n * @param options.reparentDuration Animation duration for reparent (default 800ms).\n */\n override async pick(carrier: Component, options: MoveOptions = {}): Promise<void> {\n const timeoutMs = (options.timeoutMs as number | undefined) ?? 15000\n const reparentDuration = (options.reparentDuration as number | undefined) ?? 800\n const clearance = (options.approachClearance as number | undefined) ?? 10\n // Read carrier height via _realObject.effectiveDepth — `state.depth`\n // is often not explicitly set (carriers like Box rely on\n // `static defaultDepth`), and reading state alone returns 0, which\n // collapses every Y offset and drives the gripper into the carrier's\n // volumetric center instead of its top face.\n const carrierDepth = resolveDepth(carrier)\n const halfDepth = carrierDepth / 2\n const yaw = this._yawOf(carrier)\n const target = this._targetFor(carrier, halfDepth, yaw)\n const approach = this._targetFor(carrier, halfDepth + clearance, yaw)\n\n this.set('target', approach)\n await waitForTcpReached(this, timeoutMs)\n\n this.set('target', target)\n await waitForTcpReached(this, timeoutMs)\n\n ;(this as any).reparent?.(carrier, { animated: true, duration: reparentDuration })\n this.set('gripper', { ...(this.state.gripper ?? {}), state: 1 })\n await sleep(reparentDuration)\n }\n\n /**\n * Place: TCP descends vertically over the holder, gripper opens,\n * carrier becomes the holder's child.\n *\n * 1. TCP → APPROACH above place (carrier's xy, +clearance)\n * 2. TCP → place (descend straight down to drop point)\n * 3. open gripper + reparent(carrier → holder)\n *\n * Pose resolution: TCP lands at where the carrier's top WILL BE after\n * the drop, so the carrier lands with its bottom on the holder's attach\n * surface. Computed as `holder.attachFrame.world.y + carrier.depth` (so\n * yOffset = carrierDepth, not halfDepth like pick).\n *\n * @see pick — same option semantics.\n */\n override async place(carrier: Component, holder: Component, options: MoveOptions = {}): Promise<void> {\n const timeoutMs = (options.timeoutMs as number | undefined) ?? 15000\n const reparentDuration = (options.reparentDuration as number | undefined) ?? 800\n const clearance = (options.approachClearance as number | undefined) ?? 10\n const carrierDepth = resolveDepth(carrier)\n const yaw = this._yawOf(holder)\n const target = this._targetFor(holder, carrierDepth, yaw) // top-of-carrier after drop\n const approach = this._targetFor(holder, carrierDepth + clearance, yaw)\n\n this.set('target', approach)\n await waitForTcpReached(this, timeoutMs)\n\n this.set('target', target)\n await waitForTcpReached(this, timeoutMs)\n\n ;(holder as any).reparent?.(carrier, { animated: true, duration: reparentDuration })\n this.set('gripper', { ...(this.state.gripper ?? {}), state: 0 })\n await sleep(reparentDuration)\n }\n\n /**\n * `pickAndPlace` is inherited from the `Mover` mixin default — runs\n * `pick(carrier)` then `place(carrier, holder)` sequentially. The\n * decomposition is safe: the carrier's world position only matters\n * during pick (carrier-relative waypoints), and after pick the carrier\n * is the TCP's child (so re-querying it would chase its own tail —\n * which we don't, since place uses holder-relative waypoints only).\n *\n * @example\n * ```ts\n * await robotArm.pickAndPlace(box, spotB)\n * await robotArm.pnp(parcel, palletA, { approachClearance: 30 })\n * ```\n */\n /** Short alias for `pickAndPlace`. Same arguments, same behavior. */\n pnp(carrier: Component, placeHolder: Component, options?: MoveOptions): Promise<void> {\n return this.pickAndPlace(carrier, placeHolder, options)\n }\n\n /**\n * Return all joints to the configured home pose (state.joints / state.jointN).\n *\n * IK target 을 클리어하고 forward joint 제어로 부드럽게 휴식 포즈로 복귀.\n * `pickAndPlace` 의 마지막 단계로 자동 호출되며, 사용자가 명시적으로 호출도 가능.\n *\n * **인터럽트 가능**: 집에 도달하기 전에 새 `pick`/`place` 가 호출되면 (`set('target', ...)`)\n * `_retargetIk()` 가 발동되어 smoothing 이 home 으로 가던 걸 멈추고 새 IK target 쪽으로\n * redirect. mid-flight joint 값을 새 IK 의 시작점으로 사용해 끊김 없이 전환. tcp-reached\n * 가 redirect 후의 새 target 에서 발생하면 goHome 의 await 도 함께 resolve (정상).\n * 즉 \"집으로 가다가 다음 pick 으로 자연스럽게 이어진다\".\n *\n * @param options.timeoutMs joint smoothing 의 'tcp-reached' 대기 timeout (default 15000ms).\n */\n async goHome(options: MoveOptions = {}): Promise<void> {\n const timeoutMs = (options.timeoutMs as number | undefined) ?? 15000\n const ro = this._realObject as any\n if (!ro?.moveJointsTo || !ro?.getHomeJoints) return\n // IK target 제거 — 다음 rebuild 시 _smoothing.target 이 IK 로 다시 풀리지 않도록.\n // 외부에서 set('target', ...) 호출하면 다시 IK 모드로 전환되며 redirect.\n this.set('target', undefined)\n ro.moveJointsTo(ro.getHomeJoints())\n await waitForTcpReached(this, timeoutMs)\n }\n\n /**\n * pick → place → goHome 시퀀스. 마지막에 home pose 복귀가 robot-arm 의\n * pickAndPlace 표준 동작. (Mover mixin 의 default 는 pick + place 만)\n */\n override async pickAndPlace(\n carrier: Component,\n holder: Component,\n options: MoveOptions = {}\n ): Promise<void> {\n await this.pick(carrier, options)\n await this.place(carrier, holder, options)\n await this.goHome(options)\n }\n\n /**\n * Resolve a TCP position in this arm's chain-local frame.\n *\n * For a CarrierHolder (has `attachPointFor`), uses the holder's own\n * attach frame as the base — that's the canonical \"where carriers\n * sit on me\" point (top of Spot's pad, gripper TCP for another arm,\n * etc.). For a plain carrier, uses its world center.\n *\n * `worldYOffset` is added to the resolved world Y before projecting\n * back to chain-local — used by pickAndPlace to lift the TCP above\n * the target (approach waypoints) and to land it on the carrier's\n * top face rather than its volumetric center.\n */\n private _targetFor(\n component: Component,\n worldYOffset = 0,\n yaw?: number\n ): { x: number; y: number; z: number; yaw?: number } {\n const ro3d = this._realObject\n\n let baseWp: THREE.Vector3 | undefined\n // Prefer holder's attach frame when available.\n const attachPointFor = (component as any).attachPointFor\n if (typeof attachPointFor === 'function') {\n const point = attachPointFor.call(component, this)\n if (point?.attach) {\n point.attach.updateWorldMatrix?.(true, false)\n baseWp = point.attach.getWorldPosition?.(new THREE.Vector3())\n }\n }\n if (!baseWp) {\n const obj = (component as any)._realObject?.object3d\n if (obj) {\n obj.updateWorldMatrix(true, false)\n baseWp = obj.getWorldPosition(new THREE.Vector3())\n }\n }\n if (!ro3d || !baseWp) {\n // Final fallback: 2D bounds center — only before 3D builds.\n const c = component.center\n return { x: c.x, y: worldYOffset, z: c.y, yaw }\n }\n const local = ro3d.worldToChainLocal(baseWp.x, baseWp.y + worldYOffset, baseWp.z)\n return { x: local.x, y: local.y, z: local.z, yaw }\n }\n\n /**\n * Extract a component's world yaw (rotation around world +Y) from its\n * 3D object's world matrix. Returns 0 if the object isn't built yet.\n * Used by `pickAndPlace` so the gripper can match the carrier's /\n * placeHolder's current orientation — preventing a yaw snap at the\n * moment of reparent.\n */\n private _yawOf(component: Component): number {\n const obj = (component as any)._realObject?.object3d\n if (!obj) return 0\n obj.updateWorldMatrix(true, false)\n const q = new THREE.Quaternion()\n obj.getWorldQuaternion(q)\n const e = new THREE.Euler().setFromQuaternion(q, 'YXZ')\n return e.y\n }\n}\n\n/**\n * Resolve when the robot arm fires `tcp-reached`, reject on timeout.\n * The 3D side fires this event on smoothing completion (last joint\n * value within threshold of its target).\n */\nfunction waitForTcpReached(component: Component, timeoutMs: number): Promise<void> {\n return new Promise((resolve, reject) => {\n let timer: any\n const onReached = () => {\n clearTimeout(timer)\n component.off('tcp-reached', onReached)\n resolve()\n }\n timer = setTimeout(() => {\n component.off('tcp-reached', onReached)\n reject(new Error('TCP reach timeout'))\n }, timeoutMs)\n component.on('tcp-reached', onReached)\n })\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\nfunction numOr(v: unknown, dflt: number): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : dflt\n}\n\n/**\n * Resolve a component's 3D height. things-scene v10 carriers (Box, Pallet,\n * etc.) typically declare `static defaultDepth` rather than setting\n * `state.depth`, so reading state alone returns 0 — wrong for a\n * default-sized box. The framework resolves the actual value into\n * `_realObject.effectiveDepth`. Falls back to `state.depth` for\n * components built before their RealObject exists.\n */\nfunction resolveDepth(c: Component): number {\n const eff = (c as any)?._realObject?.effectiveDepth\n if (typeof eff === 'number' && Number.isFinite(eff)) return eff\n return numOr((c as any)?.state?.depth, 0)\n}\n\nfunction pickColor(...candidates: unknown[]): string {\n for (const c of candidates) {\n if (typeof c === 'string' && c && c !== 'transparent') return c\n }\n return '#888'\n}\n"]}
|
|
1
|
+
{"version":3,"file":"robot-arm.js","sourceRoot":"","sources":["../src/robot-arm.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright © HatioLab Inc. All rights reserved.\n *\n * Robot Arm — generalized articulated manipulator component.\n *\n * Designed to cover the visualization needs of robot arms WITHOUT a\n * specific URDF model. Configurable kinematic chain, link shape, joint\n * housing, gripper type — same component renders as a sleek cobot, a\n * heavy industrial 6-axis, a SCARA, or a minimal educational arm.\n *\n * State schema (all top-level properties optional except width/height):\n *\n * {\n * // structure\n * style?: 'industrial' | 'cobot' | 'scara' | 'minimal',\n * chainPreset?: '6-axis-industrial' | 'cobot-6-axis' | 'scara-4-axis' | 'pick-and-place-3-axis',\n * chain?: ChainElement[], // overrides preset\n * joints?: number[] | { [i]: number }, // current joint values\n *\n * // styling overrides\n * linkShape?, jointHousing?, linkRadius?, color?, accentColor?,\n *\n * // gripper\n * gripper?: GripperConfig,\n *\n * // IK target (TCP world position relative to component center, in\n * // 3D-z-up convention used by IK module)\n * target?: { x: number, y: number, z: number },\n *\n * // visualization\n * showReachSphere?: boolean\n * }\n */\n\nimport * as THREE from 'three'\nimport { Component, ComponentNature, ContainerAbstract, ContainerCapacity, RealObject, frameClock, sceneComponent } from '@hatiolab/things-scene'\nimport type { SlotDef, State, Material3D } from '@hatiolab/things-scene'\nimport {\n CarrierHolder,\n Legendable,\n Mover,\n Placeable,\n type Alignment,\n type Heights,\n type LegendBinding,\n type MoveOptions,\n type PlacementArchetype\n} from '@operato/scene-base'\n\nimport { RobotArm3D } from './robot-arm-3d.js'\nimport { CHAIN_PRESETS, type ChainPresetName } from './robot-arm-styles.js'\nimport type { ChainElement } from './robot-arm-ik.js'\nimport type { GripperConfig, LinkShape, JointHousing, StyleName } from './robot-arm-styles.js'\n\n// ── ComponentEventMap 도메인 augmentation ─────────────────────────────────\n// RobotArm 이 발생시키는 도메인 이벤트들. things-scene 의 ComponentEventMap 에\n// 추가 등록하여 component.on/trigger 에서 자동완성 + 페이로드 타입 검증.\ndeclare module '@hatiolab/things-scene' {\n interface ComponentEventMap {\n /** TCP 가 목표 도달 시 발생 (pickAndPlace 시퀀싱용). */\n 'tcp-reached': [info: { component: Component }]\n }\n}\n\nexport type RobotArmStatus = 'idle' | 'moving' | 'gripping' | 'warn' | 'error'\n\n/** RobotArm 컴포넌트 state */\nexport interface RobotArmState extends State {\n // ── 구조 ──\n style?: StyleName\n chainPreset?: ChainPresetName\n chain?: ChainElement[]\n joints?: number[] | Record<string, number>\n linkShape?: LinkShape\n jointHousing?: JointHousing\n linkRadius?: number\n color?: string | number\n accentColor?: string | number\n\n // ── 그리퍼 ──\n gripper?: GripperConfig\n gripperType?: GripperConfig['type']\n\n // ── IK 타겟 ──\n target?: { x: number; y: number; z: number; yaw?: number }\n\n // ── 시각화 ──\n showReachSphere?: boolean\n\n // ── 모션 ──\n jointSpeed?: number\n\n // ── 운영 상태 ──\n status?: RobotArmStatus\n\n // ── 3D 재질 ──\n material3d?: Material3D\n}\n\nconst BODY_LEGEND = {\n idle: '#ccc',\n moving: '#afd0f1',\n gripping: '#cfe6c8',\n warn: '#ffba00',\n error: '#e9746b',\n default: '#ccc'\n}\n\nconst BORDER_LEGEND = {\n idle: '#888',\n moving: '#87b1db',\n gripping: '#7aa274',\n warn: '#d96f21',\n error: '#a73928',\n default: '#888'\n}\n\nconst LAMP_EMISSIVE_LEGEND = {\n idle: '#333333',\n moving: '#44aaff',\n gripping: '#33aa44',\n warn: '#ffaa00',\n error: '#ff3333',\n default: '#333333'\n}\n\nconst STATIC_PROPERTIES = [\n {\n type: 'select',\n label: 'style',\n name: 'style',\n property: {\n options: [\n { display: 'Industrial', value: 'industrial' },\n { display: 'Cobot', value: 'cobot' },\n { display: 'SCARA', value: 'scara' },\n { display: 'Minimal', value: 'minimal' }\n ]\n }\n },\n {\n type: 'select',\n label: 'chain-preset',\n name: 'chainPreset',\n property: {\n options: [\n { display: '6-Axis Industrial', value: '6-axis-industrial' },\n { display: 'Cobot 6-Axis', value: 'cobot-6-axis' },\n { display: 'SCARA 4-Axis', value: 'scara-4-axis' },\n { display: 'Pick & Place 3-Axis', value: 'pick-and-place-3-axis' }\n ]\n }\n },\n { type: 'number', label: 'link-radius', name: 'linkRadius' },\n {\n type: 'select',\n label: 'gripper-type',\n name: 'gripperType',\n property: {\n options: [\n { display: 'Parallel', value: 'parallel' },\n { display: 'Suction', value: 'suction' },\n { display: 'Magnetic', value: 'magnetic' },\n { display: 'Three-Finger', value: 'three-finger' },\n { display: 'None', value: 'none' }\n ]\n }\n },\n { type: 'checkbox', label: 'show-reach-sphere', name: 'showReachSphere' },\n {\n type: 'select',\n label: 'status',\n name: 'status',\n property: {\n options: [\n { display: 'Idle', value: 'idle' },\n { display: 'Moving', value: 'moving' },\n { display: 'Gripping', value: 'gripping' },\n { display: 'Warn', value: 'warn' },\n { display: 'Error', value: 'error' }\n ]\n }\n }\n]\n\n// CarrierHolder + ContainerAbstract: the arm needs the container's child-list\n// machinery (addComponent / reparent / events) AND CarrierHolder's\n// attachPointFor hook. CarrierHolder mixin alone only publishes the attach\n// policy; it doesn't add child management. RectPath(Shape) was wrong here —\n// Shape is a leaf so reparent silently failed. ContainerAbstract provides\n// the missing piece.\n//\n// `ContainerAbstract` (not `Container`) — Container = MixinHTMLElement(ContainerAbstract),\n// which forces `isHTMLElement(): true` and trips the 3D pipeline's\n// addObject DOM-skip gate. Robot arm exists purely in the 3D scene graph\n// (URDF/IK joints, no DOM overlay), so the bare logical container is correct.\n//\n// Mover wraps the chain to publish pick/place/pickAndPlace primitives. It\n// must sit OUTSIDE CarrierHolder because Mover's defaults call this.reparent,\n// which CarrierHolder provides (overrides the base ContainerAbstract\n// version with the attach-frame-aware variant).\n//\n// The instance type cast surfaces Mover's API on `this` so subclass\n// `override` keywords compile and `this.pickAndPlace(...)` resolves —\n// without it, the `as unknown as typeof Component` cast erases all\n// mixin contributions to the static side.\n// ContainerCapacity inserted between CarrierHolder and Legendable — same pattern\n// as Forklift/AGV: receive()/dispatch() for slot-tracking + event emission;\n// CarrierHolder.attachPointFor() (overridden below) provides 3D TCP positioning.\nconst Base = Mover(CarrierHolder(ContainerCapacity(Legendable(Placeable(ContainerAbstract))))) as unknown as typeof Component & {\n new (...args: any[]): Component & {\n isMover: boolean\n pick(carrier: Component, options?: MoveOptions): Promise<void>\n place(carrier: Component, holder: Component, options?: MoveOptions): Promise<void>\n pickAndPlace(carrier: Component, holder: Component, options?: MoveOptions): Promise<void>\n }\n}\n\n@sceneComponent('robot-arm')\nexport default class RobotArm extends Base {\n // `Base` is cast through `typeof Component` so TS sees `state` as an\n // accessor; `declare state: …` would conflict with TS2610. Override the\n // getter instead — runtime behavior is identical (just delegates to super).\n override get state(): RobotArmState {\n return super.state as RobotArmState\n }\n\n declare _realObject: RobotArm3D | undefined\n\n /**\n * Phase H — pickup contract 호환성 검사용. 보편적 'gripper' type — vacuum,\n * 2-finger, 3-finger 등 다양한 gripper 모두 이 type 으로 매칭. carrier 가\n * 더 세분화된 호환성을 원하면 `pickupFramesFor` override 로 size 등 추가 검증.\n *\n * (override 키워드 미사용 — Base 캐스트가 Mover 의 toolType 을 노출하지 않아\n * TS4113 회피.)\n */\n get toolType(): string {\n return 'gripper'\n }\n\n static legends: Record<string, LegendBinding> = {\n bodyColor: { from: 'status', legend: BODY_LEGEND },\n borderColor: { from: 'status', legend: BORDER_LEGEND },\n lampEmissive: { from: 'status', legend: LAMP_EMISSIVE_LEGEND }\n }\n\n /**\n * Robot arms sit on the floor (or mount to a ceiling/wall — that's a\n * v2 feature). Default depth = operation - floor like other floor-\n * mounted equipment, so the chain reach scales with the configured\n * facility height.\n */\n static placement: PlacementArchetype = 'floor'\n static align: Alignment = 'bottom'\n static defaultDepth = (h: Heights) => h.operation - h.floor\n\n // ── ContainerCapacity ─────────────────────────────────────────────────────\n\n /** Single gripper slot — robot arm grips one item at a time. */\n get slots(): SlotDef[] {\n return [{ id: 'gripper', maxCount: 1 }]\n }\n\n /**\n * Dynamic nature — appends one number property per joint in the active\n * chain. Each property uses the joint's range as `min`/`max` so the\n * property panel can render a constrained slider/spinner. The chain\n * preset can change at runtime, and `get nature()` is re-evaluated\n * whenever the property panel queries it, so the joint count stays in\n * sync with the active preset.\n *\n * For revolute joints the value is in radians; for prismatic, scene units.\n */\n get nature(): ComponentNature {\n const chain = this._activeChain\n // Joint values: revolute = degrees (range usually ±180), prismatic =\n // scene length units. CHAIN_PRESETS already encode revolute ranges in\n // degrees; the 3D side converts to radians at the math boundary.\n const jointProps = chain.map((c, i) => ({\n type: 'number',\n label: `joint-${i + 1}`,\n name: `joint${i}`,\n property: {\n min: c.range[0],\n max: c.range[1],\n step: c.type === 'revolute' ? 1 : 1\n }\n }))\n return {\n mutable: false,\n resizable: true,\n rotatable: true,\n properties: [...STATIC_PROPERTIES, ...jointProps] as any,\n help: 'scene/component/robot-arm'\n }\n }\n\n /**\n * Resolve the active chain for property-panel introspection. Mirrors\n * the resolution in robot-arm-3d.ts (state.chain wins, else preset,\n * else default 6-axis-industrial). Kept simple — the 3D side does the\n * same logic with depth-based scaling, but for property labeling we\n * only need the structure (axis count + ranges), not absolute lengths.\n */\n private get _activeChain(): ChainElement[] {\n const state = this.state\n if (Array.isArray(state.chain) && state.chain.length > 0) return state.chain\n const presetName = (state.chainPreset ?? '6-axis-industrial') as ChainPresetName\n return CHAIN_PRESETS[presetName] ?? CHAIN_PRESETS['6-axis-industrial']\n }\n\n get anchors() {\n return []\n }\n\n /**\n * 2D — base rectangle + a stylized arm silhouette pointing toward the\n * top-right. Just enough to identify the component as a robot arm in\n * plan view; the real visual lives in 3D.\n */\n render(ctx: CanvasRenderingContext2D) {\n const { left = 0, top = 0, width = 100, height = 100 } = this.state\n const bodyFill = pickColor(this.state.fillStyle, this.state.bodyColor, BODY_LEGEND.default)\n const borderColor = pickColor(this.state.strokeStyle, this.state.borderColor, BORDER_LEGEND.default)\n const lineWidth = numOr(this.state.lineWidth, 1)\n\n ctx.beginPath()\n ctx.rect(left, top, width, height)\n ctx.fillStyle = bodyFill\n ctx.fill()\n ctx.strokeStyle = borderColor\n ctx.lineWidth = lineWidth\n ctx.stroke()\n\n // Stylized arm silhouette: base square at center → up → bent right.\n const cx = left + width / 2\n const cy = top + height / 2\n const r = Math.min(width, height) * 0.32\n ctx.save()\n ctx.strokeStyle = borderColor\n ctx.lineWidth = Math.max(lineWidth, Math.min(width, height) * 0.025)\n\n // Base pedestal\n ctx.fillStyle = borderColor\n ctx.fillRect(cx - r * 0.5, cy + r * 0.6, r, r * 0.3)\n // Upper arm\n ctx.beginPath()\n ctx.moveTo(cx, cy + r * 0.6)\n ctx.lineTo(cx, cy - r * 0.2)\n ctx.stroke()\n // Forearm\n ctx.beginPath()\n ctx.moveTo(cx, cy - r * 0.2)\n ctx.lineTo(cx + r * 0.7, cy - r * 0.7)\n ctx.stroke()\n // Joint dots\n ctx.fillStyle = borderColor as string\n ctx.beginPath()\n ctx.arc(cx, cy - r * 0.2, Math.max(2, r * 0.1), 0, Math.PI * 2)\n ctx.fill()\n ctx.beginPath()\n ctx.arc(cx + r * 0.7, cy - r * 0.7, Math.max(1.5, r * 0.08), 0, Math.PI * 2)\n ctx.fill()\n ctx.restore()\n }\n\n onchange(after: Record<string, unknown>, before: Record<string, unknown>) {\n // Per-joint number properties (joint0 … jointN-1) are dynamic; treat\n // any key matching the pattern as a joint update.\n const hasJoint = Object.keys(after).some(k => /^joint\\d+$/.test(k))\n if (\n hasJoint ||\n 'style' in after ||\n 'chainPreset' in after ||\n 'chain' in after ||\n 'joints' in after ||\n 'linkShape' in after ||\n 'jointHousing' in after ||\n 'linkRadius' in after ||\n 'color' in after ||\n 'accentColor' in after ||\n 'fillStyle' in after ||\n 'strokeStyle' in after ||\n 'lineWidth' in after ||\n 'alpha' in after ||\n 'material3d' in after ||\n 'gripper' in after ||\n 'gripperType' in after ||\n 'target' in after ||\n 'showReachSphere' in after ||\n 'status' in after ||\n 'bodyColor' in after ||\n 'borderColor' in after\n ) {\n this.clearCache('fillStyle')\n this.invalidate()\n }\n super.onchange(after, before)\n }\n\n buildRealObject(): RealObject | undefined {\n return new RobotArm3D(this)\n }\n\n /**\n * CarrierHolder hook — return the gripper TCP frame so any Carriable\n * child (parcel/box/pallet/...) mounts onto the moving end-effector.\n * IK rotation propagates to the carrier through Three.js scene-graph.\n *\n * Pose convention (real-world pick-and-place: gripper pointing toward\n * the work, carrier hanging *beyond* the gripper tip):\n *\n * - Frame: TCP (gripper working point — between finger tips for\n * jaws, contact face for suction/magnetic). Built by\n * `RobotArm3D._buildGripper`. TCP's +Z = gripper-forward direction\n * (away from the wrist).\n *\n * - Rotation: x:-π/2 — maps carrier's local +Y axis (\"up\" in the\n * carrier's authored frame) to TCP's -Z direction. With the gripper\n * pointing down (typical work pose), TCP +Z = world -Y (down), so\n * TCP -Z = world +Y (up). Result: carrier's \"up\" stays world-up\n * during transit, and the carrier's body extends from TCP origin\n * in TCP +Z direction (down in world) — i.e. hanging BELOW the\n * gripper, not buried inside it.\n *\n * - Position: z = +halfDepth — places the carrier's TOP face center\n * at the TCP origin (where the fingers grip). Carrier's local +Y\n * extent (top) = halfDepth, which after x:-π/2 rotation maps to\n * TCP -halfDepth Z. Shifting carrier center to TCP +halfDepth Z\n * puts that top point exactly at TCP origin. Carrier body then\n * extends through TCP +Z, ending at TCP +depth (bottom face).\n *\n * Earlier (x:+π/2, z:-halfDepth) inverted the carrier's body direction\n * — body extended back into the wrist, leaving the box visually inside\n * the finger area. The current convention pushes the body OUT past the\n * fingertips, where a real gripper would hold it.\n */\n attachPointFor(carrier: Component) {\n const ro = this._realObject\n const frame = ro?.getGripperFrame?.()\n if (!frame) return undefined\n const halfDepth = resolveDepth(carrier) / 2\n return {\n attach: frame,\n localPosition: { x: 0, y: 0, z: halfDepth },\n localRotation: { x: -Math.PI / 2, y: 0, z: 0 }\n }\n }\n\n /**\n * Pick: TCP descends vertically onto the carrier's top face, gripper\n * closes, carrier becomes our child.\n *\n * 1. TCP → APPROACH above pick (carrier's xy, +clearance in world Y)\n * 2. TCP → pick (descend straight down to carrier's top face)\n * 3. close gripper + reparent(carrier → this)\n *\n * The approach waypoint exists so the FINAL leg is a clean vertical\n * descent — the gripper enters the carrier from straight above, not a\n * sideways sweep that could clip or grab at a wrong angle.\n *\n * Pose resolution: TCP lands on the CARRIER'S TOP FACE (carrier.world.y +\n * carrier.depth/2). After grip, `attachPointFor`'s `localPosition.z =\n * +halfDepth` keeps the carrier's top at TCP origin during transit —\n * exact, no offset drift.\n *\n * Each waypoint includes the carrier's world yaw so IK can spin joint 5\n * (tool roll) to align the gripper's \"side\" with the carrier's. Without\n * it, reparent at pick instantly snaps the carrier to the gripper's\n * axis, visibly twisting it 0° → up to 360° in one frame.\n *\n * @param options.approachClearance Vertical offset (world units)\n * between approach and pick targets. Default 10.\n * @param options.timeoutMs Per-segment IK timeout (default 15000ms).\n * @param options.reparentDuration Animation duration for reparent (default 800ms).\n */\n override async pick(carrier: Component, options: MoveOptions = {}): Promise<void> {\n const timeoutMs = (options.timeoutMs as number | undefined) ?? 15000\n const reparentDuration = (options.reparentDuration as number | undefined) ?? 800\n const clearance = (options.approachClearance as number | undefined) ?? 10\n // Read carrier height via _realObject.effectiveDepth — `state.depth`\n // is often not explicitly set (carriers like Box rely on\n // `static defaultDepth`), and reading state alone returns 0, which\n // collapses every Y offset and drives the gripper into the carrier's\n // volumetric center instead of its top face.\n const carrierDepth = resolveDepth(carrier)\n const halfDepth = carrierDepth / 2\n const yaw = this._yawOf(carrier)\n const target = this._targetFor(carrier, halfDepth, yaw)\n const approach = this._targetFor(carrier, halfDepth + clearance, yaw)\n\n this.set('target', approach)\n await waitForTcpReached(this, timeoutMs)\n\n this.set('target', target)\n await waitForTcpReached(this, timeoutMs)\n\n ;(this as any).reparent?.(carrier, { animated: true, duration: reparentDuration })\n this.set('gripper', { ...(this.state.gripper ?? {}), state: 1 })\n await sleep(reparentDuration)\n }\n\n /**\n * Place: TCP descends vertically over the holder, gripper opens,\n * carrier becomes the holder's child.\n *\n * 1. TCP → APPROACH above place (carrier's xy, +clearance)\n * 2. TCP → place (descend straight down to drop point)\n * 3. open gripper + dispatch (carrier → holder)\n *\n * Pose resolution: TCP lands at where the carrier's top WILL BE after\n * the drop, so the carrier lands with its bottom on the holder's attach\n * surface. Computed as `holder.attachFrame.world.y + carrier.depth` (so\n * yOffset = carrierDepth, not halfDepth like pick).\n *\n * Handoff: if this arm has `dispatch` (ContainerCapacity) and the holder\n * exposes `receive`, route via dispatch — slot tracking, `transfer-received`\n * trigger (which downstream conveyance components like InductStation listen\n * to for belt-animation kickoff), and `canReceive` pre-flight all flow\n * through the standard contract. Falls back to raw `holder.reparent()` for\n * holders without ContainerCapacity (plain Spot, etc.). Mirrors Mover.place.\n *\n * @see pick — same option semantics.\n */\n override async place(carrier: Component, holder: Component, options: MoveOptions = {}): Promise<void> {\n const timeoutMs = (options.timeoutMs as number | undefined) ?? 15000\n const reparentDuration = (options.reparentDuration as number | undefined) ?? 800\n const clearance = (options.approachClearance as number | undefined) ?? 10\n const carrierDepth = resolveDepth(carrier)\n // Phase G5.2 — holder 가 acceptanceRotation 을 선언하면 그 world equivalent\n // (deliveryWorldRotation) 의 yaw 를 TCP yaw 로 사용. 그러면 carrier 가 holder\n // 의 acceptance pose 와 정확히 일치한 채 release 되어 handoff 시 zero-delta\n // (시각적 점프 없음). holder 가 미정의면 holder 자체의 world yaw 사용\n // (이전 동작).\n const yaw = this._deliveryYawOf(holder, carrier)\n // Phase G9 — carrier 도 함께 전달해 holder 의 deliveryWorldPosition 을 정확히\n // query. holder 가 contract 노출 안 하면 fallback (object3d 원점) 으로.\n const target = this._targetFor(holder, carrierDepth, yaw, carrier) // top-of-carrier after drop\n const approach = this._targetFor(holder, carrierDepth + clearance, yaw, carrier)\n\n this.set('target', approach)\n await waitForTcpReached(this, timeoutMs)\n\n this.set('target', target)\n await waitForTcpReached(this, timeoutMs)\n\n const xferOptions = { animated: true, duration: reparentDuration }\n const canDispatch =\n typeof (this as any).dispatch === 'function' && typeof (holder as any).receive === 'function'\n if (canDispatch) {\n await (this as any).dispatch(carrier, holder, xferOptions)\n } else {\n ;(holder as any).reparent?.(carrier, xferOptions)\n }\n this.set('gripper', { ...(this.state.gripper ?? {}), state: 0 })\n await sleep(reparentDuration)\n }\n\n /**\n * `pickAndPlace` is inherited from the `Mover` mixin default — runs\n * `pick(carrier)` then `place(carrier, holder)` sequentially. The\n * decomposition is safe: the carrier's world position only matters\n * during pick (carrier-relative waypoints), and after pick the carrier\n * is the TCP's child (so re-querying it would chase its own tail —\n * which we don't, since place uses holder-relative waypoints only).\n *\n * @example\n * ```ts\n * await robotArm.pickAndPlace(box, spotB)\n * await robotArm.pnp(parcel, palletA, { approachClearance: 30 })\n * ```\n */\n /** Short alias for `pickAndPlace`. Same arguments, same behavior. */\n pnp(carrier: Component, placeHolder: Component, options?: MoveOptions): Promise<void> {\n return this.pickAndPlace(carrier, placeHolder, options)\n }\n\n /**\n * Return all joints to the configured home pose (state.joints / state.jointN).\n *\n * IK target 을 클리어하고 forward joint 제어로 부드럽게 휴식 포즈로 복귀.\n * `pickAndPlace` 의 마지막 단계로 자동 호출되며, 사용자가 명시적으로 호출도 가능.\n *\n * **인터럽트 가능**: 집에 도달하기 전에 새 `pick`/`place` 가 호출되면 (`set('target', ...)`)\n * `_retargetIk()` 가 발동되어 smoothing 이 home 으로 가던 걸 멈추고 새 IK target 쪽으로\n * redirect. mid-flight joint 값을 새 IK 의 시작점으로 사용해 끊김 없이 전환. tcp-reached\n * 가 redirect 후의 새 target 에서 발생하면 goHome 의 await 도 함께 resolve (정상).\n * 즉 \"집으로 가다가 다음 pick 으로 자연스럽게 이어진다\".\n *\n * @param options.timeoutMs joint smoothing 의 'tcp-reached' 대기 timeout (default 15000ms).\n */\n async goHome(options: MoveOptions = {}): Promise<void> {\n const timeoutMs = (options.timeoutMs as number | undefined) ?? 15000\n const ro = this._realObject as any\n if (!ro?.moveJointsTo || !ro?.getHomeJoints) return\n // IK target 제거 — 다음 rebuild 시 _smoothing.target 이 IK 로 다시 풀리지 않도록.\n // 외부에서 set('target', ...) 호출하면 다시 IK 모드로 전환되며 redirect.\n this.set('target', undefined)\n ro.moveJointsTo(ro.getHomeJoints())\n await waitForTcpReached(this, timeoutMs)\n }\n\n /**\n * pick → place → goHome 시퀀스. 마지막에 home pose 복귀가 robot-arm 의\n * pickAndPlace 표준 동작. (Mover mixin 의 default 는 pick + place 만)\n */\n override async pickAndPlace(\n carrier: Component,\n holder: Component,\n options: MoveOptions = {}\n ): Promise<void> {\n await this.pick(carrier, options)\n await this.place(carrier, holder, options)\n await this.goHome(options)\n }\n\n /**\n * Resolve a TCP position in this arm's chain-local frame.\n *\n * Resolution priority:\n * 1. `attachPointFor` (CarrierHolder) — canonical TCP frame for arms /\n * Spot pads / gripper-on-gripper.\n * 2. `deliveryWorldPosition` (Phase G9 — Holder contract). Holder 가\n * ContainerCapacity 의 default 또는 자체 override 로 정확한 도달\n * world XYZ 를 직접 제공. 이전 SCAFFOLDING 의 cz 공식 역연산이\n * 여기로 이전됨.\n * 3. Component's own `_realObject.object3d` world center.\n * 4. 2D bounds center (only before 3D builds).\n *\n * `carrierForDelivery` 가 주어지면 (place 케이스) `component` 를 holder 로\n * 간주하고 `component.deliveryWorldPosition(carrierForDelivery)` 를 query.\n * 미지정 (pick 케이스 — `component` 자체가 carrier) 시 #3 으로.\n *\n * `worldYOffset` is added to the resolved world Y before projecting\n * back to chain-local — used by pickAndPlace to lift the TCP above\n * the target (approach waypoints) and to land it on the carrier's\n * top face rather than its volumetric center.\n */\n private _targetFor(\n component: Component,\n worldYOffset = 0,\n yaw?: number,\n carrierForDelivery?: Component\n ): { x: number; y: number; z: number; yaw?: number } {\n const ro3d = this._realObject\n\n let baseWp: THREE.Vector3 | undefined\n // Prefer holder's attach frame when available.\n const attachPointFor = (component as any).attachPointFor\n if (typeof attachPointFor === 'function') {\n const point = attachPointFor.call(component, this)\n if (point?.attach) {\n point.attach.updateWorldMatrix?.(true, false)\n baseWp = point.attach.getWorldPosition?.(new THREE.Vector3())\n }\n }\n if (!baseWp) {\n // Phase G9 — Holder.deliveryWorldPosition contract 를 우선 사용 (place\n // 케이스: carrierForDelivery 가 주어진 경우). ContainerCapacity 가 default\n // 로 receivingSurfaceY/geometricOffsetY 보정 + 표면 변환을 제공하므로\n // robot-arm 이 cz 공식 역연산을 직접 알지 않아도 됨.\n if (carrierForDelivery) {\n const deliveryPos = (component as any).deliveryWorldPosition?.(carrierForDelivery)\n if (deliveryPos) {\n baseWp = new THREE.Vector3(deliveryPos.x, deliveryPos.y, deliveryPos.z)\n }\n }\n // Fallback (pick 케이스 또는 holder 가 contract 미노출): component 자체의\n // obj3d world center.\n if (!baseWp) {\n const obj = (component as any)._realObject?.object3d\n if (obj) {\n obj.updateWorldMatrix(true, false)\n baseWp = obj.getWorldPosition(new THREE.Vector3())\n }\n }\n }\n if (!ro3d || !baseWp) {\n // Final fallback: 2D bounds center — only before 3D builds.\n const c = component.center\n return { x: c.x, y: worldYOffset, z: c.y, yaw }\n }\n const local = ro3d.worldToChainLocal(baseWp.x, baseWp.y + worldYOffset, baseWp.z)\n return { x: local.x, y: local.y, z: local.z, yaw }\n }\n\n /**\n * Extract a component's world yaw (rotation around world +Y) from its\n * 3D object's world matrix. Returns 0 if the object isn't built yet.\n * Used by `pickAndPlace` so the gripper can match the carrier's /\n * placeHolder's current orientation — preventing a yaw snap at the\n * moment of reparent.\n */\n private _yawOf(component: Component): number {\n const obj = (component as any)._realObject?.object3d\n if (!obj) return 0\n obj.updateWorldMatrix(true, false)\n const q = new THREE.Quaternion()\n obj.getWorldQuaternion(q)\n const e = new THREE.Euler().setFromQuaternion(q, 'YXZ')\n return e.y\n }\n\n /**\n * Phase G5.2 — Delivery yaw 결정.\n *\n * holder 가 ContainerCapacity contract 의 deliveryWorldRotation 을 노출하면\n * 그 world quaternion 에서 yaw 를 추출 (carrier 가 acceptance pose 와 일치\n * 한 채 release 되도록). InductStation 같은 holder 가 sorter forward 방향을\n * 자기 acceptanceRotation 으로 선언하면 자동으로 그 방향이 TCP yaw 가 됨.\n *\n * holder 가 deliveryWorldRotation 을 안 노출하거나 _realObject 미빌드 시\n * 기존 동작 (holder 자체의 world yaw) 으로 fallback.\n */\n private _deliveryYawOf(holder: Component, carrier: Component): number {\n const deliveryRot = (holder as any).deliveryWorldRotation?.(carrier)\n if (deliveryRot) {\n const q = new THREE.Quaternion(deliveryRot.x, deliveryRot.y, deliveryRot.z, deliveryRot.w)\n const e = new THREE.Euler().setFromQuaternion(q, 'YXZ')\n return e.y\n }\n return this._yawOf(holder)\n }\n}\n\n/**\n * Resolve when the robot arm fires `tcp-reached`, reject on **simulation-time**\n * timeout. 3D smoothing 은 frameClock 의 simDt 로 진행 (sim-time-driven),\n * 따라서 timeout 도 sim-time 으로 누적해야 sim speed 변경에 일관 동작:\n * - speed=1 → sim time = real time (기존과 동일)\n * - speed=2 → IK 도 2× 빨라지고 timeout 도 2× 빠르게 도달 (real time 기준)\n * - speed=0.5 → IK 가 2× 느려지지만 timeout 도 sim 기준이라 spurious\n * reject 없음 (이전 real-time setTimeout 으로는 1 미만 speed\n * 에서 IK 도달 전에 timeout 발화하던 버그 수정)\n * - paused → simDt=0 누적도 정지 → 일시정지 동안 무한 대기 (의도)\n */\nfunction waitForTcpReached(component: Component, timeoutMs: number): Promise<void> {\n return new Promise((resolve, reject) => {\n let elapsedSim = 0\n let unsub: (() => void) | null = null\n const cleanup = () => {\n component.off('tcp-reached', onReached)\n if (unsub) {\n unsub()\n unsub = null\n }\n }\n const onReached = () => {\n cleanup()\n resolve()\n }\n component.on('tcp-reached', onReached)\n unsub = frameClock.subscribe(\n simDt => {\n elapsedSim += simDt\n if (elapsedSim >= timeoutMs) {\n cleanup()\n reject(new Error('TCP reach timeout'))\n }\n },\n { description: 'robot-arm tcp-reached timeout', component }\n )\n })\n}\n\n/**\n * sim-time aware sleep — frameClock 의 simDt 를 누적해 ms 도달 시 resolve.\n * speed=0.5 이면 real time 으로 2배 걸리지만 sim time 으로는 동일 — animation\n * 시간과 일관. paused 시 무한 대기 (의도). waitForTcpReached 와 같은 시간축.\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => {\n let elapsedSim = 0\n let unsub: (() => void) | null = null\n unsub = frameClock.subscribe(\n simDt => {\n elapsedSim += simDt\n if (elapsedSim >= ms) {\n if (unsub) {\n unsub()\n unsub = null\n }\n resolve()\n }\n },\n { description: 'robot-arm sim-sleep' }\n )\n })\n}\n\nfunction numOr(v: unknown, dflt: number): number {\n return typeof v === 'number' && Number.isFinite(v) ? v : dflt\n}\n\n/**\n * Resolve a component's 3D height. things-scene v10 carriers (Box, Pallet,\n * etc.) typically declare `static defaultDepth` rather than setting\n * `state.depth`, so reading state alone returns 0 — wrong for a\n * default-sized box. The framework resolves the actual value into\n * `_realObject.effectiveDepth`. Falls back to `state.depth` for\n * components built before their RealObject exists.\n */\nfunction resolveDepth(c: Component): number {\n const eff = (c as any)?._realObject?.effectiveDepth\n if (typeof eff === 'number' && Number.isFinite(eff)) return eff\n return numOr((c as any)?.state?.depth, 0)\n}\n\nfunction pickColor(...candidates: unknown[]): string {\n for (const c of candidates) {\n if (typeof c === 'string' && c && c !== 'transparent') return c\n }\n return '#888'\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@operato/scene-manufacturing",
|
|
3
3
|
"description": "Things scene components for manufacturing industry",
|
|
4
|
-
"version": "10.0.0-beta.
|
|
4
|
+
"version": "10.0.0-beta.32",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"things-scene": true,
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@hatiolab/things-scene": "^10.0.0-beta.1",
|
|
33
|
-
"@operato/scene-base": "^10.0.0-beta.
|
|
33
|
+
"@operato/scene-base": "^10.0.0-beta.32",
|
|
34
34
|
"three": "^0.183.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
"prettier --write"
|
|
67
67
|
]
|
|
68
68
|
},
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "9c9fec2b993ccda805cdd53fe11e15fb4b71254e"
|
|
70
70
|
}
|