@nebula-spatial/viewer 0.4.2 → 0.4.4
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/CHANGELOG.md +47 -0
- package/FBX-MATERIAL-CONVERSION.md +97 -0
- package/README.md +152 -9
- package/dist/assets/types.d.ts +21 -0
- package/dist/{index-C9CW-EyW.js → index-D-o1ODOX.js} +2084 -1452
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/inspection/types.d.ts +99 -0
- package/dist/mjcf-visual-M1lmyjvw.js +63 -0
- package/dist/mujoco-runtime-C29PXDOl.js +258 -0
- package/dist/plugins.d.ts +5 -1
- package/dist/session-Cz6qHYMc.js +676 -0
- package/dist/session-c7xBLA4J.js +964 -0
- package/dist/{stage-D-Q_zul6.js → stage-D113SHD8.js} +140 -120
- package/dist/types.d.ts +2 -0
- package/dist/ui/types.d.ts +2 -0
- package/dist/urdf-session-AP-cMuwd.js +3001 -0
- package/dist/usd-session-CmSdkRw9.js +1192 -0
- package/package.json +4 -2
- package/dist/mujoco-runtime-s85kwjo7.js +0 -208
- package/dist/session-B-qVl3Yy.js +0 -761
- package/dist/session-B2SfkK9o.js +0 -266
- package/dist/usd-session-DCuV_H-Y.js +0 -538
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export type { ViewerSceneUiOptions, ViewerSceneUiParts } from './ui/types';
|
|
2
2
|
export { createViewer } from './viewer';
|
|
3
|
-
export type { AssetChangeEvent, AssetControlState, AssetControlChangeEvent, AssetDetectionResult, AssetErrorEvent, AssetErrorSnapshot, AssetFileEntry, AssetFormat, AssetLoaderContext, AssetLoaderContribution, AssetPresentationProfile, AssetPreview, AssetPreviewCapabilities, AssetPreviewKind, AssetPreviewPhase, AssetPreviewSnapshot, AssetResourceResolver, AssetSession, AssetSource, AssetUpAxis, NormalizedAssetSource, OpenAssetOptions, ViewerAssetOptions, } from './assets/types';
|
|
3
|
+
export type { AssetChangeEvent, AssetControlState, AssetControlChangeEvent, AssetDetectionResult, AssetErrorEvent, AssetErrorSnapshot, AssetFileEntry, AssetFormat, AssetLoaderContext, AssetLoaderContribution, AssetPhysicsState, AssetPresentationProfile, AssetPreview, AssetPreviewCapabilities, AssetPreviewKind, AssetPreviewPhase, AssetPreviewSnapshot, AssetResourceResolver, AssetSession, AssetSource, AssetUpAxis, NormalizedAssetSource, OpenAssetOptions, ViewerAssetOptions, } from './assets/types';
|
|
4
4
|
export type { Disposable, ToolbarItem, ViewerState, ViewerInteractionHooks, ViewerContributionPoint, ViewerPlugin, ViewerPluginContext, ViewerPluginRegistry, } from './plugins';
|
|
5
5
|
export { configureViewerText } from './cad/text';
|
|
6
6
|
export type { ViewerTextBuilderOptions } from './cad/text';
|
|
7
7
|
export type { CadBlockModel, CadDocumentHandle, CadDocumentUnits, CadFontResolver, CadFontRequest, CadResolvedFont, CadResolvedTextFont, CadTextWarmupOptions, CadHudModel, CadHudSnapshot, CadInspectorModel, CadInspectorSnapshot, CadLayerModel, CadLayerRow, CadBlockUsageRow, CadSessionPhase, CadWorldBounds, CadScaleRulerState, CadInsertDetails, CadInsertSelection, CadInsertSelectionId, CadTextDetails, CadTextSelection, CadTextSelectionId, CadSelection, CadHit, CadViewerSnapshot, CadLoadProgress, CadViewerErrorCode, CadViewerErrorEvent, CadLoadSource, CadOpenOptions, CadSessionLoadTiming, RenderDebugBreakdownEntry, SplitBlockLayerDetail, CadViewerCapability, ResolveTextFont, CadViewerEventMap, CameraStateStore, Viewer, ViewerEventMap, ViewerEnvironment, ViewerFitViewOptions, ViewerHighlightOptions, ViewerOptions, ViewerPickOptions, ViewerPickResult, ViewerPointerEvent, ViewerObjectOptions, ViewerCameraSnapshot, ViewerOrthographicSnapshot, ViewerOrbitSnapshot, ViewerRenderStats, ViewerDocumentRect, ViewerCadUiKeyboardOptions, ViewerUiOptions, ViewerCadUiParts, ViewerCadUiOptions, ViewerUiPreset, ViewerCadUiStateStore, ViewerUiTheme, ViewerViewMode, CadInteractionMode, CameraChangeReason, ViewerUiTokens, } from './types';
|
|
8
|
+
export type { AssetInspection, UrdfEntityKind, UrdfInspection, UrdfInspectionEntity, UrdfInspectionIssue, UrdfInspectionJoint, UrdfInspectionLink, UrdfInspectionMode, UrdfInspectionNode, UrdfInspectionOverlays, UrdfInspectionRobot, UrdfInspectionSnapshot, UrdfSourceRange } from './inspection/types';
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export type UrdfEntityKind = 'robot' | 'link' | 'joint';
|
|
2
|
+
export interface UrdfSourceRange {
|
|
3
|
+
readonly file: string;
|
|
4
|
+
readonly startOffset: number;
|
|
5
|
+
readonly endOffset: number;
|
|
6
|
+
readonly startLine: number;
|
|
7
|
+
readonly startColumn: number;
|
|
8
|
+
readonly endLine: number;
|
|
9
|
+
readonly endColumn: number;
|
|
10
|
+
}
|
|
11
|
+
export interface UrdfInspectionEntity {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly kind: UrdfEntityKind;
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly parentId?: string;
|
|
16
|
+
readonly childIds: readonly string[];
|
|
17
|
+
readonly source?: UrdfSourceRange;
|
|
18
|
+
}
|
|
19
|
+
export interface UrdfInspectionLink extends UrdfInspectionEntity {
|
|
20
|
+
readonly kind: 'link';
|
|
21
|
+
readonly mass?: number;
|
|
22
|
+
readonly centerOfMass?: readonly number[];
|
|
23
|
+
readonly inertiaTensor?: readonly number[];
|
|
24
|
+
readonly inertiaMass?: number;
|
|
25
|
+
readonly visualCount: number;
|
|
26
|
+
readonly collisionCount: number;
|
|
27
|
+
}
|
|
28
|
+
export interface UrdfInspectionJoint extends UrdfInspectionEntity {
|
|
29
|
+
readonly kind: 'joint';
|
|
30
|
+
readonly jointType: string;
|
|
31
|
+
readonly parentLink: string;
|
|
32
|
+
readonly childLink: string;
|
|
33
|
+
readonly axis: readonly number[];
|
|
34
|
+
readonly originPosition: readonly number[];
|
|
35
|
+
readonly originRotationRpy: readonly number[];
|
|
36
|
+
readonly limits?: {
|
|
37
|
+
readonly lower: number;
|
|
38
|
+
readonly upper: number;
|
|
39
|
+
};
|
|
40
|
+
readonly effort?: number;
|
|
41
|
+
readonly velocity?: number;
|
|
42
|
+
readonly movable: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface UrdfInspectionRobot extends UrdfInspectionEntity {
|
|
45
|
+
readonly kind: 'robot';
|
|
46
|
+
readonly rootLinkIds: readonly string[];
|
|
47
|
+
}
|
|
48
|
+
export type UrdfInspectionNode = UrdfInspectionRobot | UrdfInspectionLink | UrdfInspectionJoint;
|
|
49
|
+
export interface UrdfInspectionIssue {
|
|
50
|
+
readonly id: string;
|
|
51
|
+
readonly ruleId: string;
|
|
52
|
+
readonly severity: 'error';
|
|
53
|
+
readonly title: string;
|
|
54
|
+
readonly message: string;
|
|
55
|
+
readonly entityId: string;
|
|
56
|
+
readonly source?: UrdfSourceRange;
|
|
57
|
+
readonly values?: Readonly<Record<string, unknown>>;
|
|
58
|
+
}
|
|
59
|
+
export interface UrdfInspectionOverlays {
|
|
60
|
+
readonly collision: boolean;
|
|
61
|
+
readonly jointAxis: boolean;
|
|
62
|
+
readonly centerOfMass: boolean;
|
|
63
|
+
readonly inertia?: boolean;
|
|
64
|
+
}
|
|
65
|
+
export type UrdfInspectionMode = 'kinematic' | 'dynamics-initializing' | 'dynamics-paused' | 'dynamics-running' | 'dynamics-unavailable';
|
|
66
|
+
export interface UrdfInspectionSnapshot {
|
|
67
|
+
readonly revision: number;
|
|
68
|
+
readonly kind: 'urdf';
|
|
69
|
+
readonly robotName: string;
|
|
70
|
+
readonly sourcePath: string;
|
|
71
|
+
readonly sourceText: string;
|
|
72
|
+
readonly nodes: readonly UrdfInspectionNode[];
|
|
73
|
+
readonly rootEntityId: string;
|
|
74
|
+
readonly selectedEntityId: string | null;
|
|
75
|
+
readonly jointValues: Readonly<Record<string, number>>;
|
|
76
|
+
readonly overlays: UrdfInspectionOverlays;
|
|
77
|
+
readonly issues: readonly UrdfInspectionIssue[];
|
|
78
|
+
readonly mode: UrdfInspectionMode;
|
|
79
|
+
readonly dynamicsAvailable: boolean;
|
|
80
|
+
readonly dynamicsMessage?: string;
|
|
81
|
+
}
|
|
82
|
+
export interface UrdfInspection {
|
|
83
|
+
readonly kind: 'urdf';
|
|
84
|
+
getSnapshot(): UrdfInspectionSnapshot;
|
|
85
|
+
subscribe(listener: (snapshot: UrdfInspectionSnapshot) => void): () => void;
|
|
86
|
+
select(entityId: string | null): void;
|
|
87
|
+
setJointValue(jointId: string, value: number): void;
|
|
88
|
+
resetJoint(jointId: string): void;
|
|
89
|
+
resetPose(): void;
|
|
90
|
+
setOverlay(name: keyof UrdfInspectionOverlays, visible: boolean): void;
|
|
91
|
+
enterKinematicPreview(): void;
|
|
92
|
+
jointWorldFrame?(jointId: string): {
|
|
93
|
+
position: import('three').Vector3;
|
|
94
|
+
axis: import('three').Vector3;
|
|
95
|
+
} | null;
|
|
96
|
+
pickRoots?(): readonly import('three').Object3D[];
|
|
97
|
+
}
|
|
98
|
+
/** Tagged inspection surface. Future formats can extend this union without changing AssetPreview. */
|
|
99
|
+
export type AssetInspection = UrdfInspection;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Group as A, Euler as C, BoxGeometry as E, SphereGeometry as F, CylinderGeometry as x, CapsuleGeometry as J, PlaneGeometry as q, MeshStandardMaterial as G, Mesh as z } from "three";
|
|
2
|
+
import { d as S } from "./stage-D113SHD8.js";
|
|
3
|
+
function v(d) {
|
|
4
|
+
const f = d.extension.documents, l = new A();
|
|
5
|
+
l.name = d.name;
|
|
6
|
+
try {
|
|
7
|
+
if (f.size !== 1) throw new Error("MJCF 独立预览暂不支持 include");
|
|
8
|
+
const c = [...f.values()][0];
|
|
9
|
+
if (c.querySelector("default, include, attach, replicate, composite, flexcomp, skin, hfield, plugin")) throw new Error("MJCF 独立预览包含尚不支持的继承、组合或扩展语义");
|
|
10
|
+
const n = c.querySelector("compiler");
|
|
11
|
+
if ((n == null ? void 0 : n.getAttribute("coordinate")) === "global" || n != null && n.hasAttribute("eulerseq")) throw new Error("MJCF 独立预览不支持此坐标设置");
|
|
12
|
+
const w = (n == null ? void 0 : n.getAttribute("angle")) === "radian" ? 1 : Math.PI / 180, b = (a, u) => {
|
|
13
|
+
const e = a ? a.trim().split(/\s+/).map(Number) : u;
|
|
14
|
+
if (e.some((o) => !Number.isFinite(o))) throw new Error("MJCF 视觉坐标无效");
|
|
15
|
+
return e;
|
|
16
|
+
}, m = (a, u, e) => {
|
|
17
|
+
const o = b(a, u);
|
|
18
|
+
if (o.length !== e) throw new Error(`MJCF 视觉变换需要 ${e} 个分量`);
|
|
19
|
+
return o;
|
|
20
|
+
}, p = (a, u) => {
|
|
21
|
+
for (const e of a.children) {
|
|
22
|
+
if (!["body", "geom"].includes(e.tagName)) continue;
|
|
23
|
+
if (["class", "childclass", "fromto", "axisangle", "xyaxes", "zaxis"].some((r) => e.hasAttribute(r))) throw new Error("MJCF 独立预览不支持此变换或默认类");
|
|
24
|
+
const o = new A();
|
|
25
|
+
if (o.name = e.getAttribute("name") ?? e.tagName, o.position.fromArray(m(e.getAttribute("pos"), [0, 0, 0], 3)), e.hasAttribute("quat")) {
|
|
26
|
+
const r = m(e.getAttribute("quat"), [1, 0, 0, 0], 4);
|
|
27
|
+
if (Math.hypot(...r) === 0) throw new Error("MJCF 视觉四元数不能为零");
|
|
28
|
+
o.quaternion.set(r[1], r[2], r[3], r[0]).normalize();
|
|
29
|
+
} else {
|
|
30
|
+
const r = m(e.getAttribute("euler"), [0, 0, 0], 3);
|
|
31
|
+
o.quaternion.setFromEuler(new C(r[0] * w, r[1] * w, r[2] * w, "XYZ"));
|
|
32
|
+
}
|
|
33
|
+
if (u.add(o), e.tagName === "body") {
|
|
34
|
+
p(e, o);
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const s = e.getAttribute("type") ?? (e.hasAttribute("mesh") ? "mesh" : "sphere"), t = b(e.getAttribute("size"), []);
|
|
38
|
+
if (e.hasAttribute("material") || t.length === 0) throw new Error("MJCF 独立预览缺少可直接解析的几何或材质");
|
|
39
|
+
const y = { box: 3, sphere: 1, cylinder: 2, capsule: 2, plane: 3 }[s];
|
|
40
|
+
if (y && (t.length < y || t.some((r) => s === "plane" ? r < 0 : r <= 0)))
|
|
41
|
+
throw new Error(`MJCF geom ${s} 的尺寸缺失或无效`);
|
|
42
|
+
let i;
|
|
43
|
+
if (s === "box") i = new E(2 * t[0], 2 * t[1], 2 * t[2]);
|
|
44
|
+
else if (s === "sphere") i = new F(t[0]);
|
|
45
|
+
else if (s === "cylinder")
|
|
46
|
+
i = new x(t[0], t[0], 2 * t[1]), i.rotateX(Math.PI / 2);
|
|
47
|
+
else if (s === "capsule")
|
|
48
|
+
i = new J(t[0], 2 * t[1]), i.rotateX(Math.PI / 2);
|
|
49
|
+
else if (s === "plane") i = new q(2 * (t[0] || 5), 2 * (t[1] || 5));
|
|
50
|
+
else throw new Error(`MJCF 独立预览暂不支持 geom ${s}`);
|
|
51
|
+
const h = m(e.getAttribute("rgba"), [0.5, 0.5, 0.5, 1], 4), M = new G({ opacity: h[3], transparent: h[3] < 1 });
|
|
52
|
+
M.color.setRGB(h[0], h[1], h[2]), o.add(new z(i, M));
|
|
53
|
+
}
|
|
54
|
+
}, g = c.querySelector("worldbody");
|
|
55
|
+
if (!g) throw new Error("MJCF 缺少 worldbody");
|
|
56
|
+
return p(g, l), l.updateMatrixWorld(!0), l;
|
|
57
|
+
} catch (c) {
|
|
58
|
+
throw S(l), c;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
v as compileMjcfPreview
|
|
63
|
+
};
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
var M = Object.defineProperty;
|
|
2
|
+
var O = (l, t, i) => t in l ? M(l, t, { enumerable: !0, configurable: !0, writable: !0, value: i }) : l[t] = i;
|
|
3
|
+
var _ = (l, t, i) => O(l, typeof t != "symbol" ? t + "" : t, i);
|
|
4
|
+
import I from "@mujoco/mujoco";
|
|
5
|
+
import T from "@mujoco/mujoco/mujoco.wasm?url";
|
|
6
|
+
let v = 0;
|
|
7
|
+
function G(l, t) {
|
|
8
|
+
const i = `/viewer-simulation-${++v}`;
|
|
9
|
+
l.mkdir(i);
|
|
10
|
+
const n = /* @__PURE__ */ new Set([i]), o = /* @__PURE__ */ new Set(), a = (e, s) => {
|
|
11
|
+
const m = e.replaceAll("\\", "/").split("/");
|
|
12
|
+
if (m[0] === "" || m.some((d) => d === ".." || d.includes(":"))) throw p(`Invalid package path: ${e}`, "E_SIMULATION_PATH");
|
|
13
|
+
const h = m.filter((d) => d && d !== ".");
|
|
14
|
+
if (!h.length) throw p("Package path is empty.", "E_SIMULATION_PATH");
|
|
15
|
+
let c = i;
|
|
16
|
+
for (const d of h.slice(0, -1))
|
|
17
|
+
c += `/${d}`, n.has(c) || (l.mkdir(c), n.add(c));
|
|
18
|
+
const u = `${i}/${h.join("/")}`;
|
|
19
|
+
return l.writeFile(u, s), o.add(u), u;
|
|
20
|
+
}, r = () => {
|
|
21
|
+
for (const e of o) l.unlink(e);
|
|
22
|
+
o.clear();
|
|
23
|
+
for (const e of [...n].reverse()) l.rmdir(e);
|
|
24
|
+
n.clear();
|
|
25
|
+
};
|
|
26
|
+
try {
|
|
27
|
+
for (const [e, s] of t) a(e, s);
|
|
28
|
+
return { writeEntry: (e) => a("__viewer_entry.xml", e), dispose: r };
|
|
29
|
+
} catch (e) {
|
|
30
|
+
throw r(), e;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function p(l, t) {
|
|
34
|
+
return Object.assign(new Error(l), { code: t });
|
|
35
|
+
}
|
|
36
|
+
function w({
|
|
37
|
+
target: l,
|
|
38
|
+
point: t,
|
|
39
|
+
velocity: i,
|
|
40
|
+
stiffness: n,
|
|
41
|
+
damping: o,
|
|
42
|
+
maxForce: a,
|
|
43
|
+
strength: r
|
|
44
|
+
}) {
|
|
45
|
+
const e = [
|
|
46
|
+
n * (l[0] - t[0]) - o * i[0],
|
|
47
|
+
n * (l[1] - t[1]) - o * i[1],
|
|
48
|
+
n * (l[2] - t[2]) - o * i[2]
|
|
49
|
+
], s = Math.hypot(e[0], e[1], e[2]), m = a * r;
|
|
50
|
+
if (s > m && s > 0) {
|
|
51
|
+
const h = m / s;
|
|
52
|
+
e[0] *= h, e[1] *= h, e[2] *= h;
|
|
53
|
+
}
|
|
54
|
+
return e;
|
|
55
|
+
}
|
|
56
|
+
const g = 1 / 240, F = 0.05, x = 80, J = 1.15, b = 3.5, y = 14;
|
|
57
|
+
class S {
|
|
58
|
+
constructor() {
|
|
59
|
+
_(this, "mujoco", null);
|
|
60
|
+
_(this, "model", null);
|
|
61
|
+
_(this, "data", null);
|
|
62
|
+
_(this, "grab", null);
|
|
63
|
+
_(this, "lastGrabForce", [0, 0, 0]);
|
|
64
|
+
_(this, "velocityBuffer", new Float64Array(6));
|
|
65
|
+
_(this, "initialControls", []);
|
|
66
|
+
_(this, "accumulator", 0);
|
|
67
|
+
_(this, "initialGround", null);
|
|
68
|
+
_(this, "initialVelocities", []);
|
|
69
|
+
}
|
|
70
|
+
async initialize(t, i) {
|
|
71
|
+
var e;
|
|
72
|
+
this.mujoco ?? (this.mujoco = await I({ locateFile: (s) => s.endsWith(".wasm") ? i ? new URL(s, i).href : T : s }));
|
|
73
|
+
const n = G(this.mujoco.FS, t.files);
|
|
74
|
+
let o = null, a = null;
|
|
75
|
+
try {
|
|
76
|
+
o = this.mujoco.MjModel.from_xml_path(n.writeEntry(t.xml));
|
|
77
|
+
for (const s of t.massOverrides ?? []) {
|
|
78
|
+
const m = this.mujoco.mj_name2id(o, this.mujoco.mjtObj.mjOBJ_BODY.value, s.body);
|
|
79
|
+
if (m <= 0 || !Number.isFinite(s.mass) || s.mass <= 0 || !(o.body_mass[m] > 0)) throw new Error(`Invalid mass override: ${s.body}`);
|
|
80
|
+
const h = s.mass / o.body_mass[m];
|
|
81
|
+
o.body_mass[m] = s.mass;
|
|
82
|
+
for (let c = 0; c < 3; c++) o.body_inertia[m * 3 + c] *= h;
|
|
83
|
+
}
|
|
84
|
+
a = new this.mujoco.MjData(o), (e = t.massOverrides) != null && e.length && this.mujoco.mj_setConst(o, a), o.opt.timestep = g, this.applyInitialControls(a, t.initialControls), this.applyInitialVelocities(o, a, t.initialVelocities), this.mujoco.mj_forward(o, a), this.seatDefaultGround(o, a);
|
|
85
|
+
} catch (s) {
|
|
86
|
+
throw a == null || a.delete(), o == null || o.delete(), f(s, "E_MUJOCO_INITIALIZE");
|
|
87
|
+
} finally {
|
|
88
|
+
n.dispose();
|
|
89
|
+
}
|
|
90
|
+
this.disposeModel(), this.model = o, this.data = a, this.initialControls = t.initialControls ?? [], this.initialVelocities = t.initialVelocities ?? [], this.accumulator = 0;
|
|
91
|
+
const r = this.mujoco.mj_name2id(o, this.mujoco.mjtObj.mjOBJ_GEOM.value, "__viewer_default_ground__");
|
|
92
|
+
this.initialGround = r < 0 ? null : { id: r, height: o.geom_pos[r * 3 + 2], sameFrame: o.geom_sameframe[r] };
|
|
93
|
+
}
|
|
94
|
+
get defaultGroundHeight() {
|
|
95
|
+
if (!this.mujoco || !this.model) return;
|
|
96
|
+
const t = this.mujoco.mj_name2id(this.model, this.mujoco.mjtObj.mjOBJ_GEOM.value, "__viewer_default_ground__");
|
|
97
|
+
return t < 0 ? void 0 : this.model.geom_pos[t * 3 + 2];
|
|
98
|
+
}
|
|
99
|
+
/** Lower only the viewer-owned plane, never authored bodies or authored floors.
|
|
100
|
+
* Run after forward kinematics, not during stepping (the floor must not follow falling bodies).
|
|
101
|
+
*/
|
|
102
|
+
seatDefaultGround(t, i) {
|
|
103
|
+
const n = this.mujoco, o = n.mj_name2id(t, n.mjtObj.mjOBJ_GEOM.value, "__viewer_default_ground__");
|
|
104
|
+
if (o < 0 || t.geom_bodyid[o] !== 0 || t.geom_type[o] !== n.mjtGeom.mjGEOM_PLANE.value) return;
|
|
105
|
+
let a = t.geom_pos[o * 3 + 2];
|
|
106
|
+
for (let r = 0; r < t.ngeom; r++) {
|
|
107
|
+
if (r === o || t.geom_type[r] === n.mjtGeom.mjGEOM_PLANE.value || !(t.geom_contype[r] & t.geom_conaffinity[o]) && !(t.geom_conaffinity[r] & t.geom_contype[o])) continue;
|
|
108
|
+
const e = r * 6, s = r * 9, m = i.geom_xmat[s + 6], h = i.geom_xmat[s + 7], c = i.geom_xmat[s + 8], u = i.geom_xpos[r * 3 + 2] + m * t.geom_aabb[e] + h * t.geom_aabb[e + 1] + c * t.geom_aabb[e + 2], d = Math.abs(m) * t.geom_aabb[e + 3] + Math.abs(h) * t.geom_aabb[e + 4] + Math.abs(c) * t.geom_aabb[e + 5], j = u - d, E = Math.max(1e-3, t.geom_margin[r] + t.geom_margin[o]);
|
|
109
|
+
Number.isFinite(j) && j < a && (a = Math.min(a, j - E));
|
|
110
|
+
}
|
|
111
|
+
a !== t.geom_pos[o * 3 + 2] && (t.geom_pos[o * 3 + 2] = a, t.geom_sameframe[o] = 0, n.mj_forward(t, i));
|
|
112
|
+
}
|
|
113
|
+
beginGrab({
|
|
114
|
+
bodyId: t,
|
|
115
|
+
localPoint: i,
|
|
116
|
+
target: n,
|
|
117
|
+
stiffness: o = x,
|
|
118
|
+
damping: a,
|
|
119
|
+
maxForce: r,
|
|
120
|
+
dampingRatio: e = J,
|
|
121
|
+
maxTargetSpeed: s = b,
|
|
122
|
+
targetResponse: m = y
|
|
123
|
+
}) {
|
|
124
|
+
var j;
|
|
125
|
+
const h = Math.max(((j = this.model) == null ? void 0 : j.body_mass[t]) ?? 1, 1e-3), c = a ?? 2 * Math.sqrt(o * h) * e, u = r ?? Math.max(120, h * 60), d = [n[0], n[1], n[2]];
|
|
126
|
+
this.grab = {
|
|
127
|
+
bodyId: t,
|
|
128
|
+
localPoint: [i[0], i[1], i[2]],
|
|
129
|
+
target: [...d],
|
|
130
|
+
requestedTarget: d,
|
|
131
|
+
stiffness: o,
|
|
132
|
+
damping: c,
|
|
133
|
+
maxForce: u,
|
|
134
|
+
strength: 1,
|
|
135
|
+
maxTargetSpeed: s,
|
|
136
|
+
targetResponse: m
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
updateGrabTarget(t) {
|
|
140
|
+
this.grab && (this.grab.requestedTarget = [t[0], t[1], t[2]]);
|
|
141
|
+
}
|
|
142
|
+
updateGrabStrength(t) {
|
|
143
|
+
this.grab && (this.grab.strength = t);
|
|
144
|
+
}
|
|
145
|
+
applyGrabForce() {
|
|
146
|
+
if (!this.grab || !this.model || !this.data || !this.mujoco) return;
|
|
147
|
+
const { bodyId: t, localPoint: i, target: n, stiffness: o, damping: a, maxForce: r, strength: e } = this.grab, s = this.data.xmat.subarray(t * 9, t * 9 + 9), m = this.data.xpos.subarray(t * 3, t * 3 + 3), h = [
|
|
148
|
+
m[0] + s[0] * i[0] + s[1] * i[1] + s[2] * i[2],
|
|
149
|
+
m[1] + s[3] * i[0] + s[4] * i[1] + s[5] * i[2],
|
|
150
|
+
m[2] + s[6] * i[0] + s[7] * i[1] + s[8] * i[2]
|
|
151
|
+
];
|
|
152
|
+
this.mujoco.mj_objectVelocity(
|
|
153
|
+
this.model,
|
|
154
|
+
this.data,
|
|
155
|
+
this.mujoco.mjtObj.mjOBJ_BODY.value,
|
|
156
|
+
t,
|
|
157
|
+
this.velocityBuffer,
|
|
158
|
+
0
|
|
159
|
+
);
|
|
160
|
+
const c = this.velocityBuffer, u = [
|
|
161
|
+
h[0] - m[0],
|
|
162
|
+
h[1] - m[1],
|
|
163
|
+
h[2] - m[2]
|
|
164
|
+
], d = [
|
|
165
|
+
c[3] + c[1] * u[2] - c[2] * u[1],
|
|
166
|
+
c[4] + c[2] * u[0] - c[0] * u[2],
|
|
167
|
+
c[5] + c[0] * u[1] - c[1] * u[0]
|
|
168
|
+
], j = w({ target: n, point: h, velocity: d, stiffness: o, damping: a, maxForce: r, strength: e });
|
|
169
|
+
this.lastGrabForce = [...j], this.data.qfrc_applied.fill(0), this.mujoco.mj_applyFT(
|
|
170
|
+
this.model,
|
|
171
|
+
this.data,
|
|
172
|
+
j,
|
|
173
|
+
[0, 0, 0],
|
|
174
|
+
h,
|
|
175
|
+
t,
|
|
176
|
+
this.data.qfrc_applied
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
endGrab() {
|
|
180
|
+
this.data && this.data.qfrc_applied.fill(0), this.lastGrabForce = [0, 0, 0], this.grab = null;
|
|
181
|
+
}
|
|
182
|
+
step(t) {
|
|
183
|
+
if (!this.mujoco || !this.model || !this.data || t <= 0) return !1;
|
|
184
|
+
this.accumulator += Math.min(t, F);
|
|
185
|
+
let i = !1;
|
|
186
|
+
for (; this.accumulator >= g; )
|
|
187
|
+
this.updateGrabTargetForStep(), this.applyGrabForce(), this.mujoco.mj_step(this.model, this.data), this.accumulator -= g, i = !0;
|
|
188
|
+
return i;
|
|
189
|
+
}
|
|
190
|
+
updateGrabTargetForStep() {
|
|
191
|
+
const t = this.grab;
|
|
192
|
+
if (!t) return;
|
|
193
|
+
const i = t.requestedTarget ?? t.target, n = (t.maxTargetSpeed ?? b) * Math.max(1, Math.sqrt(t.strength)), o = t.targetResponse ?? y, a = 1 - Math.exp(-o * g), r = n * g;
|
|
194
|
+
for (let e = 0; e < 3; e++) {
|
|
195
|
+
const s = i[e] - t.target[e], m = Math.sign(s) * Math.min(Math.abs(s) * a, r);
|
|
196
|
+
t.target[e] += m;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
setJointValues(t) {
|
|
200
|
+
if (!(!this.mujoco || !this.model || !this.data)) {
|
|
201
|
+
for (const [i, n] of Object.entries(t)) {
|
|
202
|
+
const o = this.mujoco.mj_name2id(this.model, this.mujoco.mjtObj.mjOBJ_JOINT.value, i);
|
|
203
|
+
if (o < 0 || !Number.isFinite(n)) continue;
|
|
204
|
+
const a = this.model.jnt_type[o], r = this.mujoco.mjtJoint.mjJNT_HINGE.value, e = this.mujoco.mjtJoint.mjJNT_SLIDE.value;
|
|
205
|
+
a !== r && a !== e || (this.data.qpos[this.model.jnt_qposadr[o]] = n);
|
|
206
|
+
}
|
|
207
|
+
this.mujoco.mj_forward(this.model, this.data), this.seatDefaultGround(this.model, this.data), this.accumulator = 0;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
getJointValues(t) {
|
|
211
|
+
const i = {};
|
|
212
|
+
if (!this.mujoco || !this.model || !this.data) return i;
|
|
213
|
+
for (const n of t) {
|
|
214
|
+
const o = this.mujoco.mj_name2id(this.model, this.mujoco.mjtObj.mjOBJ_JOINT.value, n);
|
|
215
|
+
if (o < 0) continue;
|
|
216
|
+
const a = this.model.jnt_type[o], r = this.mujoco.mjtJoint.mjJNT_HINGE.value, e = this.mujoco.mjtJoint.mjJNT_SLIDE.value;
|
|
217
|
+
(a === r || a === e) && (i[n] = this.data.qpos[this.model.jnt_qposadr[o]]);
|
|
218
|
+
}
|
|
219
|
+
return i;
|
|
220
|
+
}
|
|
221
|
+
reset() {
|
|
222
|
+
if (!(!this.mujoco || !this.model || !this.data)) {
|
|
223
|
+
if (this.endGrab(), this.initialGround) {
|
|
224
|
+
const { id: t, height: i, sameFrame: n } = this.initialGround;
|
|
225
|
+
this.model.geom_pos[t * 3 + 2] = i, this.model.geom_sameframe[t] = n;
|
|
226
|
+
}
|
|
227
|
+
this.mujoco.mj_resetData(this.model, this.data), this.applyInitialControls(this.data, this.initialControls), this.applyInitialVelocities(this.model, this.data, this.initialVelocities), this.mujoco.mj_forward(this.model, this.data), this.accumulator = 0;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
dispose() {
|
|
231
|
+
this.disposeModel(), this.mujoco = null;
|
|
232
|
+
}
|
|
233
|
+
applyInitialControls(t, i) {
|
|
234
|
+
if (i != null && i.length) {
|
|
235
|
+
if (i.length !== t.ctrl.length) throw f(new Error(`Initial control count ${i.length} does not match MuJoCo actuator count ${t.ctrl.length}.`), "E_MUJOCO_INITIALIZE");
|
|
236
|
+
t.ctrl.set(i);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
applyInitialVelocities(t, i, n) {
|
|
240
|
+
for (const o of n ?? []) {
|
|
241
|
+
const a = this.mujoco.mj_name2id(t, this.mujoco.mjtObj.mjOBJ_BODY.value, o.body), r = a >= 0 ? t.body_jntadr[a] : -1;
|
|
242
|
+
if (r < 0 || t.jnt_type[r] !== this.mujoco.mjtJoint.mjJNT_FREE.value || o.velocity.length !== 6 || !o.velocity.every(Number.isFinite))
|
|
243
|
+
throw f(new Error(`Invalid free-body initial velocity: ${o.body}`), "E_MUJOCO_INITIALIZE");
|
|
244
|
+
i.qvel.set(o.velocity, t.jnt_dofadr[r]);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
disposeModel() {
|
|
248
|
+
var t, i;
|
|
249
|
+
this.endGrab(), (t = this.data) == null || t.delete(), (i = this.model) == null || i.delete(), this.data = null, this.model = null, this.initialGround = null, this.initialControls = [], this.initialVelocities = [], this.accumulator = 0;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function f(l, t) {
|
|
253
|
+
const i = l instanceof Error ? l : new Error(String(l));
|
|
254
|
+
return Object.assign(i, { code: t });
|
|
255
|
+
}
|
|
256
|
+
export {
|
|
257
|
+
S as MujocoRuntime
|
|
258
|
+
};
|
package/dist/plugins.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Box3, Object3D } from 'three';
|
|
|
2
2
|
import type { CadHit, CadSelection, CadViewerSnapshot, CadViewerEventMap } from './cad/types';
|
|
3
3
|
import type { ViewerEventMap, ViewerViewMode, ViewerUiTheme } from './types';
|
|
4
4
|
import type { AssetControlState, AssetLoaderContribution, AssetPreviewSnapshot, AssetUpAxis } from './assets/types';
|
|
5
|
+
import type { AssetInspection } from './inspection/types';
|
|
5
6
|
export interface Disposable {
|
|
6
7
|
dispose(): void;
|
|
7
8
|
}
|
|
@@ -27,6 +28,7 @@ export interface ViewerAssetReadApi {
|
|
|
27
28
|
$read(): AssetPreviewSnapshot | null;
|
|
28
29
|
/** Authoritative control state, safe to bind button pressed states to. */
|
|
29
30
|
$readControl(): AssetControlState;
|
|
31
|
+
$inspection(): AssetInspection | null;
|
|
30
32
|
}
|
|
31
33
|
export interface ViewerPluginContext {
|
|
32
34
|
readonly cad: {
|
|
@@ -112,6 +114,7 @@ type SnapshotReader = () => CadViewerSnapshot;
|
|
|
112
114
|
type StateReader = () => ViewerState;
|
|
113
115
|
type AssetReader = () => AssetPreviewSnapshot | null;
|
|
114
116
|
type ControlStateReader = () => AssetControlState;
|
|
117
|
+
type InspectionReader = () => AssetInspection | null;
|
|
115
118
|
type PluginEventMap = ViewerEventMap & CadViewerEventMap;
|
|
116
119
|
type EventSubscriber = <K extends keyof PluginEventMap>(type: K, listener: (event: PluginEventMap[K]) => void) => () => void;
|
|
117
120
|
type CommandApi = ViewerPluginContext['commands'];
|
|
@@ -137,6 +140,7 @@ export declare class ViewerPluginManager implements ViewerPluginRegistry {
|
|
|
137
140
|
private readonly commands;
|
|
138
141
|
private readonly hostElement;
|
|
139
142
|
private readonly layout;
|
|
143
|
+
private readonly readInspection;
|
|
140
144
|
private readonly pluginsById;
|
|
141
145
|
private readonly contributions;
|
|
142
146
|
private readonly commandContributions;
|
|
@@ -144,7 +148,7 @@ export declare class ViewerPluginManager implements ViewerPluginRegistry {
|
|
|
144
148
|
private readonly contributionRemovedListeners;
|
|
145
149
|
private refreshingInspectors;
|
|
146
150
|
private disposed;
|
|
147
|
-
constructor(readCad: SnapshotReader, readState: StateReader, readAsset: AssetReader, readControlState: ControlStateReader, subscribe: EventSubscriber, commands: CommandApi, hostElement: HTMLElement, layout?: PluginLayoutPort | null);
|
|
151
|
+
constructor(readCad: SnapshotReader, readState: StateReader, readAsset: AssetReader, readControlState: ControlStateReader, subscribe: EventSubscriber, commands: CommandApi, hostElement: HTMLElement, layout?: PluginLayoutPort | null, readInspection?: InspectionReader);
|
|
148
152
|
register(plugin: ViewerPlugin): Disposable;
|
|
149
153
|
use(plugins: readonly ViewerPlugin[], options?: {
|
|
150
154
|
replace?: boolean;
|