Compare commits
249
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be58d589e2 | ||
|
|
5f75afb720 | ||
|
|
99f29218d7 | ||
|
|
db14ee5435 | ||
|
|
81dd44906d | ||
|
|
d45b329de8 | ||
|
|
f164606828 | ||
|
|
556e376a62 | ||
|
|
1a2de8df65 | ||
|
|
b536f0049a | ||
|
|
3b85299fd9 | ||
|
|
505844adc3 | ||
|
|
89b0eeca3f | ||
|
|
1488a46794 | ||
|
|
e4339f4e88 | ||
|
|
7e257275ab | ||
|
|
a3c15e11e9 | ||
|
|
54a85fdf50 | ||
|
|
14f8375713 | ||
|
|
6492ebcdee | ||
|
|
e57511f975 | ||
|
|
f0802d2713 | ||
|
|
d8afb61229 | ||
|
|
421a8c515a | ||
|
|
a23c5b2005 | ||
|
|
1832943558 | ||
|
|
cc90709919 | ||
|
|
ee5d760c30 | ||
|
|
0e9faa6824 | ||
|
|
c6693d6f44 | ||
|
|
ed9a77ba73 | ||
|
|
7dd2c1c836 | ||
|
|
762d77ef95 | ||
|
|
9c4d70b0e9 | ||
|
|
eed53dbc0b | ||
|
|
4f333fc11c | ||
|
|
35a517f921 | ||
|
|
9bba44f7c4 | ||
|
|
9b6534287a | ||
|
|
9e03404f23 | ||
|
|
fa8ac760a1 | ||
|
|
309bdf759e | ||
|
|
ff00851216 | ||
|
|
ad24518b71 | ||
|
|
7ce09eabcd | ||
|
|
92b60de945 | ||
|
|
67398fef10 | ||
|
|
a93f1f1b8b | ||
|
|
102e93796b | ||
|
|
4f715e41d2 | ||
|
|
8f17a7e282 | ||
|
|
7187fd86bd | ||
|
|
e6521e855a | ||
|
|
06d216ac4a | ||
|
|
d7986cb62f | ||
|
|
a0732386f1 | ||
|
|
5c4546a2a6 | ||
|
|
ef1c1f50de | ||
|
|
2f80507bc0 | ||
|
|
d2dbfc9c85 | ||
|
|
3dffe39c89 | ||
|
|
77acb377e2 | ||
|
|
2fdf1d3cc4 | ||
|
|
d8cc5367c4 | ||
|
|
111fe3dcfb | ||
|
|
63ea2bed67 | ||
|
|
020a878915 | ||
|
|
404285419f | ||
|
|
ba0c948ca1 | ||
|
|
429b4681fe | ||
|
|
22d69107ba | ||
|
|
a8647c4d87 | ||
|
|
b1aaa40508 | ||
|
|
3616acc648 | ||
|
|
e82d012907 | ||
|
|
fc545f8440 | ||
|
|
a98ef339de | ||
|
|
9062126913 | ||
|
|
59e14c5bc0 | ||
|
|
15bb793e5e | ||
|
|
79911eb316 | ||
|
|
d696842f5d | ||
|
|
57f2537af3 | ||
|
|
e56eb42507 | ||
|
|
b2a1b23131 | ||
|
|
cada687173 | ||
|
|
eff60e490a | ||
|
|
9db29bcdc6 | ||
|
|
27979854a7 | ||
|
|
3ea8d987a7 | ||
|
|
bd947b49f4 | ||
|
|
54c8d82884 | ||
|
|
bee8552003 | ||
|
|
4e0565eb4b | ||
|
|
44afadb021 | ||
|
|
e436fb56d4 | ||
|
|
c2710b55a7 | ||
|
|
d777f3198b | ||
|
|
1e4ddc2cff | ||
|
|
80fc0058cb | ||
|
|
43f1cdc862 | ||
|
|
0041e9fadb | ||
|
|
cabd3081c7 | ||
|
|
049ed3eed4 | ||
|
|
35c1249631 | ||
|
|
941183d624 | ||
|
|
6af4c208ce | ||
|
|
75a5320756 | ||
|
|
59a74a3677 | ||
|
|
35d28418f3 | ||
|
|
48835a0499 | ||
|
|
93ae1edbb4 | ||
|
|
093df8b38f | ||
|
|
35b6cd9e9e | ||
|
|
d80df61a7e | ||
|
|
d7b8989e43 | ||
|
|
0cc6b96306 | ||
|
|
6469505c41 | ||
|
|
191612282b | ||
|
|
0310592ee3 | ||
|
|
df0158e1ba | ||
|
|
2ef8c08bf2 | ||
|
|
c570f7d938 | ||
|
|
03e8e71bea | ||
|
|
61cbdb30a0 | ||
|
|
e91721fe6a | ||
|
|
70927ea585 | ||
|
|
03a7e730ae | ||
|
|
c31c46caa5 | ||
|
|
06a66a3da8 | ||
|
|
92625bf48b | ||
|
|
b8ba0cae1e | ||
|
|
f39f1ff0b6 | ||
|
|
380b6eaba7 | ||
|
|
f6d73d8bfb | ||
|
|
221e429c0a | ||
|
|
a85aed2e03 | ||
|
|
f102712f0b | ||
|
|
350366bae0 | ||
|
|
c23e86fd39 | ||
|
|
79ce55d197 | ||
|
|
a937400942 | ||
|
|
da60feff90 | ||
|
|
85349f42f9 | ||
|
|
ac69e3b444 | ||
|
|
6acf468b25 | ||
|
|
d264cbce04 | ||
|
|
1f8101e4a5 | ||
|
|
bcacb02a22 | ||
|
|
adcca7aa81 | ||
|
|
097e450a87 | ||
|
|
4e633a662c | ||
|
|
c6c42c29e8 | ||
|
|
ce09d30c1a | ||
|
|
34f13ba59e | ||
|
|
d9ea7c1129 | ||
|
|
a3c67e249c | ||
|
|
294585936f | ||
|
|
380a13688c | ||
|
|
fd8313899b | ||
|
|
05c99efb68 | ||
|
|
ffd6be5606 | ||
|
|
bfe6ef4c77 | ||
|
|
4e04d06191 | ||
|
|
5a78c997ac | ||
|
|
62d5520c7a | ||
|
|
a945d665dd | ||
|
|
d655d6998d | ||
|
|
7025e173a3 | ||
|
|
10fe727561 | ||
|
|
00ee5f163a | ||
|
|
cbcc09afd6 | ||
|
|
e958729e9d | ||
|
|
0fcd037e4f | ||
|
|
11a146011f | ||
|
|
44ebbe7ca2 | ||
|
|
a3299dcfc3 | ||
|
|
2989fba99c | ||
|
|
142481f0d8 | ||
|
|
b2ae29b8d8 | ||
|
|
8eced6c34d | ||
|
|
732cc139fd | ||
|
|
104e54f4dd | ||
|
|
7b021782d3 | ||
|
|
759830b054 | ||
|
|
19c0bd70d0 | ||
|
|
35d99e40d5 | ||
|
|
bfc1f1bbed | ||
|
|
f41428f907 | ||
|
|
3744d79ad3 | ||
|
|
b57c3e94da | ||
|
|
73a7d28065 | ||
|
|
f64fbb1fa6 | ||
|
|
1ff527b264 | ||
|
|
def8c71410 | ||
|
|
7e59176581 | ||
|
|
4cd94b5805 | ||
|
|
1766c0bdb2 | ||
|
|
56bfeaee7b | ||
|
|
892d0085e3 | ||
|
|
e2de8188ab | ||
|
|
9c69d81296 | ||
|
|
2f6e45bc96 | ||
|
|
8a1c578e0e | ||
|
|
9beb534108 | ||
|
|
d1b75efcea | ||
|
|
c9302c78c2 | ||
|
|
d67e86176e | ||
|
|
e72609120f | ||
|
|
177be8869f | ||
|
|
2a5763d3fb | ||
|
|
8d5aeb0533 | ||
|
|
be83283ee0 | ||
|
|
023151c186 | ||
|
|
e6a9846167 | ||
|
|
81fdf6904a | ||
|
|
9c5259dbc9 | ||
|
|
35daf73d5c | ||
|
|
07453142c2 | ||
|
|
f5ee42751d | ||
|
|
e9ffb829c9 | ||
|
|
757d368c86 | ||
|
|
f1cbe0061a | ||
|
|
74da6437e9 | ||
|
|
bd2892140f | ||
|
|
525ab74168 | ||
|
|
c30d77572e | ||
|
|
5179e93f4a | ||
|
|
c06b709fd7 | ||
|
|
856b61be99 | ||
|
|
76694cc869 | ||
|
|
e6df8cb264 | ||
|
|
4b487e367f | ||
|
|
d7d9485724 | ||
|
|
0a0cde7b0e | ||
|
|
9dec37a8a9 | ||
|
|
24b65926fe | ||
|
|
57dacc5a04 | ||
|
|
d9ec8c9cef | ||
|
|
af1e530162 | ||
|
|
e10b96b546 | ||
|
|
a2c3385062 | ||
|
|
7fcfecd063 | ||
|
|
7a3b0b84d0 | ||
|
|
e1c3e7e90a | ||
|
|
14877b6683 | ||
|
|
0848cefc89 | ||
|
|
722e60e5ef | ||
|
|
c4c2392c79 |
@@ -62,6 +62,40 @@ and the boundary between Mission Core and vendor-specific integration code.
|
|||||||
- Synthetic or explicitly redacted fixtures may be committed under
|
- Synthetic or explicitly redacted fixtures may be committed under
|
||||||
`tests/fixtures/`.
|
`tests/fixtures/`.
|
||||||
|
|
||||||
|
## Insta360 and clean-host installation — owner requirement, 2026-09-08
|
||||||
|
|
||||||
|
- The current X4 starting point is USB enumeration only. SDK installation,
|
||||||
|
camera initialization and live video have not been accepted.
|
||||||
|
- Every Ubuntu change needed for Insta360 must execute through the shipped
|
||||||
|
installer or the application's versioned device-preparation workflow from
|
||||||
|
the first board experiment. Do not repair the board with an ad-hoc apt/pip
|
||||||
|
install, copied library, chmod, environment override, service edit or root
|
||||||
|
invocation and promise to package it later. Fix the product artifact first,
|
||||||
|
then rerun that artifact. Read-only engineering inspection remains allowed.
|
||||||
|
- Document every relevant action and its before/after evidence in
|
||||||
|
`docs/node/07_INSTA360_X4_INSTALLATION_LEDGER.md`; record the artifact version,
|
||||||
|
owning installer step, dependencies, idempotency, failure behavior and rollback.
|
||||||
|
Keep private logs/identifiers/media out of Git and redact their public summary.
|
||||||
|
- A working prepared Mini is not clean-Ubuntu acceptance. Qualify dependency
|
||||||
|
closure on the declared clean OS image and actual USB operation separately;
|
||||||
|
installer, preparation and GUI acceptance must use the same shipped code.
|
||||||
|
- X4 is operator live video and camera control (settings, photo, recording
|
||||||
|
start/stop and file access), per the owner’s subsequent scope expansion.
|
||||||
|
Preview and recording are separate operations. No stitching, AI, navigation
|
||||||
|
or vehicle-control integration is admitted. Model support and physical device instances are separate: several
|
||||||
|
cameras of the same model must retain independent identity and lifecycle.
|
||||||
|
- Follow the scoped implementation plan in
|
||||||
|
`docs/node/06_INSTA360_X4_AND_MULTI_CAMERA_PLAN.md`. It is a proposed plan,
|
||||||
|
not evidence that SDK compatibility, hardware capacity or installation passed.
|
||||||
|
- Subsequent owner direction: the existing Ubuntu Mini is the available build
|
||||||
|
and qualification machine; do not depend on Worker 006 or another host for
|
||||||
|
this X4 work. Its existing GCC may compile through a versioned build artifact
|
||||||
|
in bounded temporary staging. This is a build step, not runtime installation:
|
||||||
|
no system changes, SDK execution or camera access during compilation. The
|
||||||
|
final installer must carry the compiled payload so a clean operator Ubuntu
|
||||||
|
does not inherit an undeclared compiler/build-cache prerequisite. All board
|
||||||
|
staging/build/install/test actions remain documented and artifact-owned.
|
||||||
|
|
||||||
## Implementation order
|
## Implementation order
|
||||||
|
|
||||||
Follow the gates in `docs/01_IMPLEMENTATION_PLAN.md`. Do not build heavy
|
Follow the gates in `docs/01_IMPLEMENTATION_PLAN.md`. Do not build heavy
|
||||||
|
|||||||
Generated
+5
-6
@@ -7,7 +7,6 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "@nodedc/mission-core-control-station",
|
"name": "@nodedc/mission-core-control-station",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"hasInstallScript": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/hashes": "^2.2.0",
|
"@noble/hashes": "^2.2.0",
|
||||||
"@nodedc/map-cesium-react": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/map-cesium-react",
|
"@nodedc/map-cesium-react": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/map-cesium-react",
|
||||||
@@ -15,7 +14,8 @@
|
|||||||
"@nodedc/tokens": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/tokens",
|
"@nodedc/tokens": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/tokens",
|
||||||
"@nodedc/ui-core": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/ui-core",
|
"@nodedc/ui-core": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/ui-core",
|
||||||
"@nodedc/ui-react": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/ui-react",
|
"@nodedc/ui-react": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/ui-react",
|
||||||
"@rerun-io/web-viewer": "0.34.1",
|
"@rerun-io/web-viewer": "0.36.3",
|
||||||
|
"meshoptimizer": "1.1.1",
|
||||||
"playcanvas": "2.21.4",
|
"playcanvas": "2.21.4",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
@@ -899,9 +899,9 @@
|
|||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
"node_modules/@rerun-io/web-viewer": {
|
"node_modules/@rerun-io/web-viewer": {
|
||||||
"version": "0.34.1",
|
"version": "0.36.3",
|
||||||
"resolved": "https://registry.npmjs.org/@rerun-io/web-viewer/-/web-viewer-0.34.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rerun-io/web-viewer/-/web-viewer-0.36.3.tgz",
|
||||||
"integrity": "sha512-2Oq9Mw3qOs765XArGq4e/0pAIksPFKlAqkgo+PDsRkPd77/KdnQhb835suRmYQ6tuqlbPeVCFhlNIXT0+TjmTg==",
|
"integrity": "sha512-LMGnsxRmY5UwiGras2dZrMnEYkow5Xr4v+1hAUSspXWPPiilMqoz9G77jo8Ps/deAaX81TnOq123DFO8iX/Ulw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@rolldown/pluginutils": {
|
"node_modules/@rolldown/pluginutils": {
|
||||||
@@ -1661,7 +1661,6 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.1.1.tgz",
|
||||||
"integrity": "sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==",
|
"integrity": "sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "node scripts/patch-rerun-web-viewer.mjs",
|
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc -b && vite build",
|
"build": "tsc -b && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
@@ -18,7 +17,8 @@
|
|||||||
"@nodedc/tokens": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/tokens",
|
"@nodedc/tokens": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/tokens",
|
||||||
"@nodedc/ui-core": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/ui-core",
|
"@nodedc/ui-core": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/ui-core",
|
||||||
"@nodedc/ui-react": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/ui-react",
|
"@nodedc/ui-react": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/ui-react",
|
||||||
"@rerun-io/web-viewer": "0.34.1",
|
"@rerun-io/web-viewer": "0.36.3",
|
||||||
|
"meshoptimizer": "1.1.1",
|
||||||
"playcanvas": "2.21.4",
|
"playcanvas": "2.21.4",
|
||||||
"react": "^19.1.0",
|
"react": "^19.1.0",
|
||||||
"react-dom": "^19.1.0",
|
"react-dom": "^19.1.0",
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# PlayCanvas Ammo runtime
|
||||||
|
|
||||||
|
- Source: `https://developer.playcanvas.com/assets/modules/ammo/ammo.wasm.js`
|
||||||
|
- Source: `https://developer.playcanvas.com/assets/modules/ammo/ammo.wasm.wasm`
|
||||||
|
- Retrieved: 2026-08-27
|
||||||
|
- `ammo.wasm.js`: 442459 bytes, SHA-256 `9b765569bce6993e781cd95d548c3711b119f355b512b25722e86a27d6ea8857`
|
||||||
|
- `ammo.wasm.wasm`: 747690 bytes, SHA-256 `a73b547ed4289aa174bf3424b14b3cb8addc7fe708cd2636d306b72880d6ad13`
|
||||||
|
|
||||||
|
The control station loads these pinned same-origin assets before constructing the PlayCanvas application so the rigid-body systems bind to the expected Ammo instance.
|
||||||
@@ -0,0 +1,921 @@
|
|||||||
|
// This is ammo.js, a port of Bullet Physics to JavaScript. zlib licensed.
|
||||||
|
|
||||||
|
var Ammo = (() => {
|
||||||
|
var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
|
||||||
|
if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename;
|
||||||
|
return (
|
||||||
|
function(Ammo = {}) {
|
||||||
|
|
||||||
|
var b;b||(b=typeof Ammo !== 'undefined' ? Ammo : {});var aa,ba;b.ready=new Promise(function(a,c){aa=a;ba=c});var ca=Object.assign({},b),da="object"==typeof window,ea="function"==typeof importScripts,fa="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,ha="",ia,ja,ka;
|
||||||
|
if(fa){var fs=require("fs"),la=require("path");ha=ea?la.dirname(ha)+"/":__dirname+"/";ia=(a,c)=>{a=a.startsWith("file://")?new URL(a):la.normalize(a);return fs.readFileSync(a,c?void 0:"utf8")};ka=a=>{a=ia(a,!0);a.buffer||(a=new Uint8Array(a));return a};ja=(a,c,d)=>{a=a.startsWith("file://")?new URL(a):la.normalize(a);fs.readFile(a,function(e,g){e?d(e):c(g.buffer)})};1<process.argv.length&&process.argv[1].replace(/\\/g,"/");process.argv.slice(2);b.inspect=function(){return"[Emscripten Module object]"}}else if(da||
|
||||||
|
ea)ea?ha=self.location.href:"undefined"!=typeof document&&document.currentScript&&(ha=document.currentScript.src),_scriptDir&&(ha=_scriptDir),ha=0!==ha.indexOf("blob:")?ha.substr(0,ha.replace(/[?#].*/,"").lastIndexOf("/")+1):"",ia=a=>{var c=new XMLHttpRequest;c.open("GET",a,!1);c.send(null);return c.responseText},ea&&(ka=a=>{var c=new XMLHttpRequest;c.open("GET",a,!1);c.responseType="arraybuffer";c.send(null);return new Uint8Array(c.response)}),ja=(a,c,d)=>{var e=new XMLHttpRequest;e.open("GET",a,
|
||||||
|
!0);e.responseType="arraybuffer";e.onload=()=>{200==e.status||0==e.status&&e.response?c(e.response):d()};e.onerror=d;e.send(null)};b.print||console.log.bind(console);var ma=b.printErr||console.warn.bind(console);Object.assign(b,ca);ca=null;var na;b.wasmBinary&&(na=b.wasmBinary);var noExitRuntime=b.noExitRuntime||!0;"object"!=typeof WebAssembly&&oa("no native wasm support detected");var pa,qa=!1,ra="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0;
|
||||||
|
function sa(a,c){if(a){var d=ta,e=a+c;for(c=a;d[c]&&!(c>=e);)++c;if(16<c-a&&d.buffer&&ra)a=ra.decode(d.subarray(a,c));else{for(e="";a<c;){var g=d[a++];if(g&128){var n=d[a++]&63;if(192==(g&224))e+=String.fromCharCode((g&31)<<6|n);else{var z=d[a++]&63;g=224==(g&240)?(g&15)<<12|n<<6|z:(g&7)<<18|n<<12|z<<6|d[a++]&63;65536>g?e+=String.fromCharCode(g):(g-=65536,e+=String.fromCharCode(55296|g>>10,56320|g&1023))}}else e+=String.fromCharCode(g)}a=e}}else a="";return a}
|
||||||
|
var ua,ta,va,wa,xa,ya,za=[],Aa=[],Ba=[],Ca=!1;function Ea(){var a=b.preRun.shift();za.unshift(a)}var Fa=0,Ga=null,Ha=null;function oa(a){if(b.onAbort)b.onAbort(a);a="Aborted("+a+")";ma(a);qa=!0;a=new WebAssembly.RuntimeError(a+". Build with -sASSERTIONS for more info.");ba(a);throw a;}function Ia(a){return a.startsWith("data:application/octet-stream;base64,")}var Ja;Ja="ammo.wasm.wasm";if(!Ia(Ja)){var Ka=Ja;Ja=b.locateFile?b.locateFile(Ka,ha):ha+Ka}
|
||||||
|
function La(a){try{if(a==Ja&&na)return new Uint8Array(na);if(ka)return ka(a);throw"both async and sync fetching of the wasm failed";}catch(c){oa(c)}}
|
||||||
|
function Ma(a){if(!na&&(da||ea)){if("function"==typeof fetch&&!a.startsWith("file://"))return fetch(a,{credentials:"same-origin"}).then(function(c){if(!c.ok)throw"failed to load wasm binary file at '"+a+"'";return c.arrayBuffer()}).catch(function(){return La(a)});if(ja)return new Promise(function(c,d){ja(a,function(e){c(new Uint8Array(e))},d)})}return Promise.resolve().then(function(){return La(a)})}
|
||||||
|
function Na(a,c,d){return Ma(a).then(function(e){return WebAssembly.instantiate(e,c)}).then(function(e){return e}).then(d,function(e){ma("failed to asynchronously prepare wasm: "+e);oa(e)})}
|
||||||
|
function Oa(a,c){var d=Ja;return na||"function"!=typeof WebAssembly.instantiateStreaming||Ia(d)||d.startsWith("file://")||fa||"function"!=typeof fetch?Na(d,a,c):fetch(d,{credentials:"same-origin"}).then(function(e){return WebAssembly.instantiateStreaming(e,a).then(c,function(g){ma("wasm streaming compile failed: "+g);ma("falling back to ArrayBuffer instantiation");return Na(d,a,c)})})}
|
||||||
|
var Pa={27338:(a,c,d,e)=>{a=b.getCache(b.DebugDrawer)[a];if(!a.hasOwnProperty("drawLine"))throw"a JSImplementation must implement all functions, you forgot DebugDrawer::drawLine.";a.drawLine(c,d,e)},27558:(a,c,d,e,g,n)=>{a=b.getCache(b.DebugDrawer)[a];if(!a.hasOwnProperty("drawContactPoint"))throw"a JSImplementation must implement all functions, you forgot DebugDrawer::drawContactPoint.";a.drawContactPoint(c,d,e,g,n)},27808:(a,c)=>{a=b.getCache(b.DebugDrawer)[a];if(!a.hasOwnProperty("reportErrorWarning"))throw"a JSImplementation must implement all functions, you forgot DebugDrawer::reportErrorWarning.";
|
||||||
|
a.reportErrorWarning(c)},28052:(a,c,d)=>{a=b.getCache(b.DebugDrawer)[a];if(!a.hasOwnProperty("draw3dText"))throw"a JSImplementation must implement all functions, you forgot DebugDrawer::draw3dText.";a.draw3dText(c,d)},28275:(a,c)=>{a=b.getCache(b.DebugDrawer)[a];if(!a.hasOwnProperty("setDebugMode"))throw"a JSImplementation must implement all functions, you forgot DebugDrawer::setDebugMode.";a.setDebugMode(c)},28501:a=>{a=b.getCache(b.DebugDrawer)[a];if(!a.hasOwnProperty("getDebugMode"))throw"a JSImplementation must implement all functions, you forgot DebugDrawer::getDebugMode.";
|
||||||
|
return a.getDebugMode()},28732:(a,c)=>{a=b.getCache(b.MotionState)[a];if(!a.hasOwnProperty("getWorldTransform"))throw"a JSImplementation must implement all functions, you forgot MotionState::getWorldTransform.";a.getWorldTransform(c)},28973:(a,c)=>{a=b.getCache(b.MotionState)[a];if(!a.hasOwnProperty("setWorldTransform"))throw"a JSImplementation must implement all functions, you forgot MotionState::setWorldTransform.";a.setWorldTransform(c)},29214:(a,c,d,e,g,n,z,T)=>{a=b.getCache(b.ConcreteContactResultCallback)[a];
|
||||||
|
if(!a.hasOwnProperty("addSingleResult"))throw"a JSImplementation must implement all functions, you forgot ConcreteContactResultCallback::addSingleResult.";return a.addSingleResult(c,d,e,g,n,z,T)}};function Qa(a){for(;0<a.length;)a.shift()(b)}var Ra=[];function Sa(a,c,d){Ra.length=0;var e;for(d>>=2;e=ta[c++];)d+=105!=e&d,Ra.push(105==e?va[d]:xa[d++>>1]),++d;return Pa[a].apply(null,Ra)}
|
||||||
|
var Ta=[],Ua=void 0,Va=[],Wa={b:function(){oa("")},f:function(a,c,d){return Sa(a,c,d)},a:function(a,c,d){return Sa(a,c,d)},d:function(){return Date.now()},e:function(a,c,d){ta.copyWithin(a,c,c+d)},c:function(){oa("OOM")}};
|
||||||
|
(function(){function a(d){d=d.exports;b.asm=d;pa=b.asm.g;var e=pa.buffer;b.HEAP8=ua=new Int8Array(e);b.HEAP16=new Int16Array(e);b.HEAP32=va=new Int32Array(e);b.HEAPU8=ta=new Uint8Array(e);b.HEAPU16=new Uint16Array(e);b.HEAPU32=new Uint32Array(e);b.HEAPF32=wa=new Float32Array(e);b.HEAPF64=xa=new Float64Array(e);ya=b.asm.iB;Aa.unshift(b.asm.h);Fa--;b.monitorRunDependencies&&b.monitorRunDependencies(Fa);0==Fa&&(null!==Ga&&(clearInterval(Ga),Ga=null),Ha&&(e=Ha,Ha=null,e()));return d}var c={a:Wa};Fa++;
|
||||||
|
b.monitorRunDependencies&&b.monitorRunDependencies(Fa);if(b.instantiateWasm)try{return b.instantiateWasm(c,a)}catch(d){ma("Module.instantiateWasm callback failed with error: "+d),ba(d)}Oa(c,function(d){a(d.instance)}).catch(ba);return{}})();
|
||||||
|
var Xa=b._emscripten_bind_btCollisionShape_setLocalScaling_1=function(){return(Xa=b._emscripten_bind_btCollisionShape_setLocalScaling_1=b.asm.i).apply(null,arguments)},Ya=b._emscripten_bind_btCollisionShape_getLocalScaling_0=function(){return(Ya=b._emscripten_bind_btCollisionShape_getLocalScaling_0=b.asm.j).apply(null,arguments)},Za=b._emscripten_bind_btCollisionShape_calculateLocalInertia_2=function(){return(Za=b._emscripten_bind_btCollisionShape_calculateLocalInertia_2=b.asm.k).apply(null,arguments)},
|
||||||
|
$a=b._emscripten_bind_btCollisionShape_setMargin_1=function(){return($a=b._emscripten_bind_btCollisionShape_setMargin_1=b.asm.l).apply(null,arguments)},ab=b._emscripten_bind_btCollisionShape_getMargin_0=function(){return(ab=b._emscripten_bind_btCollisionShape_getMargin_0=b.asm.m).apply(null,arguments)},bb=b._emscripten_bind_btCollisionShape___destroy___0=function(){return(bb=b._emscripten_bind_btCollisionShape___destroy___0=b.asm.n).apply(null,arguments)},cb=b._emscripten_bind_btCollisionWorld_getDispatcher_0=
|
||||||
|
function(){return(cb=b._emscripten_bind_btCollisionWorld_getDispatcher_0=b.asm.o).apply(null,arguments)},db=b._emscripten_bind_btCollisionWorld_rayTest_3=function(){return(db=b._emscripten_bind_btCollisionWorld_rayTest_3=b.asm.p).apply(null,arguments)},eb=b._emscripten_bind_btCollisionWorld_getPairCache_0=function(){return(eb=b._emscripten_bind_btCollisionWorld_getPairCache_0=b.asm.q).apply(null,arguments)},fb=b._emscripten_bind_btCollisionWorld_getDispatchInfo_0=function(){return(fb=b._emscripten_bind_btCollisionWorld_getDispatchInfo_0=
|
||||||
|
b.asm.r).apply(null,arguments)},gb=b._emscripten_bind_btCollisionWorld_addCollisionObject_1=function(){return(gb=b._emscripten_bind_btCollisionWorld_addCollisionObject_1=b.asm.s).apply(null,arguments)},hb=b._emscripten_bind_btCollisionWorld_addCollisionObject_2=function(){return(hb=b._emscripten_bind_btCollisionWorld_addCollisionObject_2=b.asm.t).apply(null,arguments)},ib=b._emscripten_bind_btCollisionWorld_addCollisionObject_3=function(){return(ib=b._emscripten_bind_btCollisionWorld_addCollisionObject_3=
|
||||||
|
b.asm.u).apply(null,arguments)},jb=b._emscripten_bind_btCollisionWorld_removeCollisionObject_1=function(){return(jb=b._emscripten_bind_btCollisionWorld_removeCollisionObject_1=b.asm.v).apply(null,arguments)},kb=b._emscripten_bind_btCollisionWorld_getBroadphase_0=function(){return(kb=b._emscripten_bind_btCollisionWorld_getBroadphase_0=b.asm.w).apply(null,arguments)},lb=b._emscripten_bind_btCollisionWorld_convexSweepTest_5=function(){return(lb=b._emscripten_bind_btCollisionWorld_convexSweepTest_5=b.asm.x).apply(null,
|
||||||
|
arguments)},mb=b._emscripten_bind_btCollisionWorld_contactPairTest_3=function(){return(mb=b._emscripten_bind_btCollisionWorld_contactPairTest_3=b.asm.y).apply(null,arguments)},nb=b._emscripten_bind_btCollisionWorld_contactTest_2=function(){return(nb=b._emscripten_bind_btCollisionWorld_contactTest_2=b.asm.z).apply(null,arguments)},ob=b._emscripten_bind_btCollisionWorld_updateSingleAabb_1=function(){return(ob=b._emscripten_bind_btCollisionWorld_updateSingleAabb_1=b.asm.A).apply(null,arguments)},pb=
|
||||||
|
b._emscripten_bind_btCollisionWorld_setDebugDrawer_1=function(){return(pb=b._emscripten_bind_btCollisionWorld_setDebugDrawer_1=b.asm.B).apply(null,arguments)},qb=b._emscripten_bind_btCollisionWorld_getDebugDrawer_0=function(){return(qb=b._emscripten_bind_btCollisionWorld_getDebugDrawer_0=b.asm.C).apply(null,arguments)},rb=b._emscripten_bind_btCollisionWorld_debugDrawWorld_0=function(){return(rb=b._emscripten_bind_btCollisionWorld_debugDrawWorld_0=b.asm.D).apply(null,arguments)},sb=b._emscripten_bind_btCollisionWorld_debugDrawObject_3=
|
||||||
|
function(){return(sb=b._emscripten_bind_btCollisionWorld_debugDrawObject_3=b.asm.E).apply(null,arguments)},tb=b._emscripten_bind_btCollisionWorld___destroy___0=function(){return(tb=b._emscripten_bind_btCollisionWorld___destroy___0=b.asm.F).apply(null,arguments)},ub=b._emscripten_bind_btCollisionObject_setAnisotropicFriction_2=function(){return(ub=b._emscripten_bind_btCollisionObject_setAnisotropicFriction_2=b.asm.G).apply(null,arguments)},vb=b._emscripten_bind_btCollisionObject_getCollisionShape_0=
|
||||||
|
function(){return(vb=b._emscripten_bind_btCollisionObject_getCollisionShape_0=b.asm.H).apply(null,arguments)},wb=b._emscripten_bind_btCollisionObject_setContactProcessingThreshold_1=function(){return(wb=b._emscripten_bind_btCollisionObject_setContactProcessingThreshold_1=b.asm.I).apply(null,arguments)},xb=b._emscripten_bind_btCollisionObject_setActivationState_1=function(){return(xb=b._emscripten_bind_btCollisionObject_setActivationState_1=b.asm.J).apply(null,arguments)},yb=b._emscripten_bind_btCollisionObject_forceActivationState_1=
|
||||||
|
function(){return(yb=b._emscripten_bind_btCollisionObject_forceActivationState_1=b.asm.K).apply(null,arguments)},zb=b._emscripten_bind_btCollisionObject_activate_0=function(){return(zb=b._emscripten_bind_btCollisionObject_activate_0=b.asm.L).apply(null,arguments)},Ab=b._emscripten_bind_btCollisionObject_activate_1=function(){return(Ab=b._emscripten_bind_btCollisionObject_activate_1=b.asm.M).apply(null,arguments)},Bb=b._emscripten_bind_btCollisionObject_isActive_0=function(){return(Bb=b._emscripten_bind_btCollisionObject_isActive_0=
|
||||||
|
b.asm.N).apply(null,arguments)},Cb=b._emscripten_bind_btCollisionObject_isKinematicObject_0=function(){return(Cb=b._emscripten_bind_btCollisionObject_isKinematicObject_0=b.asm.O).apply(null,arguments)},Db=b._emscripten_bind_btCollisionObject_isStaticObject_0=function(){return(Db=b._emscripten_bind_btCollisionObject_isStaticObject_0=b.asm.P).apply(null,arguments)},Eb=b._emscripten_bind_btCollisionObject_isStaticOrKinematicObject_0=function(){return(Eb=b._emscripten_bind_btCollisionObject_isStaticOrKinematicObject_0=
|
||||||
|
b.asm.Q).apply(null,arguments)},Fb=b._emscripten_bind_btCollisionObject_getRestitution_0=function(){return(Fb=b._emscripten_bind_btCollisionObject_getRestitution_0=b.asm.R).apply(null,arguments)},Gb=b._emscripten_bind_btCollisionObject_getFriction_0=function(){return(Gb=b._emscripten_bind_btCollisionObject_getFriction_0=b.asm.S).apply(null,arguments)},Hb=b._emscripten_bind_btCollisionObject_getRollingFriction_0=function(){return(Hb=b._emscripten_bind_btCollisionObject_getRollingFriction_0=b.asm.T).apply(null,
|
||||||
|
arguments)},Ib=b._emscripten_bind_btCollisionObject_setRestitution_1=function(){return(Ib=b._emscripten_bind_btCollisionObject_setRestitution_1=b.asm.U).apply(null,arguments)},Jb=b._emscripten_bind_btCollisionObject_setFriction_1=function(){return(Jb=b._emscripten_bind_btCollisionObject_setFriction_1=b.asm.V).apply(null,arguments)},Kb=b._emscripten_bind_btCollisionObject_setRollingFriction_1=function(){return(Kb=b._emscripten_bind_btCollisionObject_setRollingFriction_1=b.asm.W).apply(null,arguments)},
|
||||||
|
Lb=b._emscripten_bind_btCollisionObject_getWorldTransform_0=function(){return(Lb=b._emscripten_bind_btCollisionObject_getWorldTransform_0=b.asm.X).apply(null,arguments)},Mb=b._emscripten_bind_btCollisionObject_getCollisionFlags_0=function(){return(Mb=b._emscripten_bind_btCollisionObject_getCollisionFlags_0=b.asm.Y).apply(null,arguments)},Nb=b._emscripten_bind_btCollisionObject_setCollisionFlags_1=function(){return(Nb=b._emscripten_bind_btCollisionObject_setCollisionFlags_1=b.asm.Z).apply(null,arguments)},
|
||||||
|
Ob=b._emscripten_bind_btCollisionObject_setWorldTransform_1=function(){return(Ob=b._emscripten_bind_btCollisionObject_setWorldTransform_1=b.asm._).apply(null,arguments)},Pb=b._emscripten_bind_btCollisionObject_setCollisionShape_1=function(){return(Pb=b._emscripten_bind_btCollisionObject_setCollisionShape_1=b.asm.$).apply(null,arguments)},Qb=b._emscripten_bind_btCollisionObject_setCcdMotionThreshold_1=function(){return(Qb=b._emscripten_bind_btCollisionObject_setCcdMotionThreshold_1=b.asm.aa).apply(null,
|
||||||
|
arguments)},Rb=b._emscripten_bind_btCollisionObject_setCcdSweptSphereRadius_1=function(){return(Rb=b._emscripten_bind_btCollisionObject_setCcdSweptSphereRadius_1=b.asm.ba).apply(null,arguments)},Sb=b._emscripten_bind_btCollisionObject_getUserIndex_0=function(){return(Sb=b._emscripten_bind_btCollisionObject_getUserIndex_0=b.asm.ca).apply(null,arguments)},Tb=b._emscripten_bind_btCollisionObject_setUserIndex_1=function(){return(Tb=b._emscripten_bind_btCollisionObject_setUserIndex_1=b.asm.da).apply(null,
|
||||||
|
arguments)},Ub=b._emscripten_bind_btCollisionObject_getUserPointer_0=function(){return(Ub=b._emscripten_bind_btCollisionObject_getUserPointer_0=b.asm.ea).apply(null,arguments)},Vb=b._emscripten_bind_btCollisionObject_setUserPointer_1=function(){return(Vb=b._emscripten_bind_btCollisionObject_setUserPointer_1=b.asm.fa).apply(null,arguments)},Wb=b._emscripten_bind_btCollisionObject_getBroadphaseHandle_0=function(){return(Wb=b._emscripten_bind_btCollisionObject_getBroadphaseHandle_0=b.asm.ga).apply(null,
|
||||||
|
arguments)},Xb=b._emscripten_bind_btCollisionObject___destroy___0=function(){return(Xb=b._emscripten_bind_btCollisionObject___destroy___0=b.asm.ha).apply(null,arguments)},Yb=b._emscripten_bind_btConcaveShape_setLocalScaling_1=function(){return(Yb=b._emscripten_bind_btConcaveShape_setLocalScaling_1=b.asm.ia).apply(null,arguments)},Zb=b._emscripten_bind_btConcaveShape_getLocalScaling_0=function(){return(Zb=b._emscripten_bind_btConcaveShape_getLocalScaling_0=b.asm.ja).apply(null,arguments)},$b=b._emscripten_bind_btConcaveShape_calculateLocalInertia_2=
|
||||||
|
function(){return($b=b._emscripten_bind_btConcaveShape_calculateLocalInertia_2=b.asm.ka).apply(null,arguments)},ac=b._emscripten_bind_btConcaveShape___destroy___0=function(){return(ac=b._emscripten_bind_btConcaveShape___destroy___0=b.asm.la).apply(null,arguments)},bc=b._emscripten_bind_btCollisionAlgorithm___destroy___0=function(){return(bc=b._emscripten_bind_btCollisionAlgorithm___destroy___0=b.asm.ma).apply(null,arguments)},cc=b._emscripten_bind_btTypedConstraint_enableFeedback_1=function(){return(cc=
|
||||||
|
b._emscripten_bind_btTypedConstraint_enableFeedback_1=b.asm.na).apply(null,arguments)},ec=b._emscripten_bind_btTypedConstraint_getBreakingImpulseThreshold_0=function(){return(ec=b._emscripten_bind_btTypedConstraint_getBreakingImpulseThreshold_0=b.asm.oa).apply(null,arguments)},fc=b._emscripten_bind_btTypedConstraint_setBreakingImpulseThreshold_1=function(){return(fc=b._emscripten_bind_btTypedConstraint_setBreakingImpulseThreshold_1=b.asm.pa).apply(null,arguments)},gc=b._emscripten_bind_btTypedConstraint_getParam_2=
|
||||||
|
function(){return(gc=b._emscripten_bind_btTypedConstraint_getParam_2=b.asm.qa).apply(null,arguments)},hc=b._emscripten_bind_btTypedConstraint_setParam_3=function(){return(hc=b._emscripten_bind_btTypedConstraint_setParam_3=b.asm.ra).apply(null,arguments)},ic=b._emscripten_bind_btTypedConstraint___destroy___0=function(){return(ic=b._emscripten_bind_btTypedConstraint___destroy___0=b.asm.sa).apply(null,arguments)},jc=b._emscripten_bind_btDynamicsWorld_addAction_1=function(){return(jc=b._emscripten_bind_btDynamicsWorld_addAction_1=
|
||||||
|
b.asm.ta).apply(null,arguments)},kc=b._emscripten_bind_btDynamicsWorld_removeAction_1=function(){return(kc=b._emscripten_bind_btDynamicsWorld_removeAction_1=b.asm.ua).apply(null,arguments)},lc=b._emscripten_bind_btDynamicsWorld_getSolverInfo_0=function(){return(lc=b._emscripten_bind_btDynamicsWorld_getSolverInfo_0=b.asm.va).apply(null,arguments)},mc=b._emscripten_bind_btDynamicsWorld_setInternalTickCallback_1=function(){return(mc=b._emscripten_bind_btDynamicsWorld_setInternalTickCallback_1=b.asm.wa).apply(null,
|
||||||
|
arguments)},nc=b._emscripten_bind_btDynamicsWorld_setInternalTickCallback_2=function(){return(nc=b._emscripten_bind_btDynamicsWorld_setInternalTickCallback_2=b.asm.xa).apply(null,arguments)},oc=b._emscripten_bind_btDynamicsWorld_setInternalTickCallback_3=function(){return(oc=b._emscripten_bind_btDynamicsWorld_setInternalTickCallback_3=b.asm.ya).apply(null,arguments)},pc=b._emscripten_bind_btDynamicsWorld_getDispatcher_0=function(){return(pc=b._emscripten_bind_btDynamicsWorld_getDispatcher_0=b.asm.za).apply(null,
|
||||||
|
arguments)},qc=b._emscripten_bind_btDynamicsWorld_rayTest_3=function(){return(qc=b._emscripten_bind_btDynamicsWorld_rayTest_3=b.asm.Aa).apply(null,arguments)},rc=b._emscripten_bind_btDynamicsWorld_getPairCache_0=function(){return(rc=b._emscripten_bind_btDynamicsWorld_getPairCache_0=b.asm.Ba).apply(null,arguments)},sc=b._emscripten_bind_btDynamicsWorld_getDispatchInfo_0=function(){return(sc=b._emscripten_bind_btDynamicsWorld_getDispatchInfo_0=b.asm.Ca).apply(null,arguments)},tc=b._emscripten_bind_btDynamicsWorld_addCollisionObject_1=
|
||||||
|
function(){return(tc=b._emscripten_bind_btDynamicsWorld_addCollisionObject_1=b.asm.Da).apply(null,arguments)},uc=b._emscripten_bind_btDynamicsWorld_addCollisionObject_2=function(){return(uc=b._emscripten_bind_btDynamicsWorld_addCollisionObject_2=b.asm.Ea).apply(null,arguments)},vc=b._emscripten_bind_btDynamicsWorld_addCollisionObject_3=function(){return(vc=b._emscripten_bind_btDynamicsWorld_addCollisionObject_3=b.asm.Fa).apply(null,arguments)},wc=b._emscripten_bind_btDynamicsWorld_removeCollisionObject_1=
|
||||||
|
function(){return(wc=b._emscripten_bind_btDynamicsWorld_removeCollisionObject_1=b.asm.Ga).apply(null,arguments)},xc=b._emscripten_bind_btDynamicsWorld_getBroadphase_0=function(){return(xc=b._emscripten_bind_btDynamicsWorld_getBroadphase_0=b.asm.Ha).apply(null,arguments)},yc=b._emscripten_bind_btDynamicsWorld_convexSweepTest_5=function(){return(yc=b._emscripten_bind_btDynamicsWorld_convexSweepTest_5=b.asm.Ia).apply(null,arguments)},zc=b._emscripten_bind_btDynamicsWorld_contactPairTest_3=function(){return(zc=
|
||||||
|
b._emscripten_bind_btDynamicsWorld_contactPairTest_3=b.asm.Ja).apply(null,arguments)},Ac=b._emscripten_bind_btDynamicsWorld_contactTest_2=function(){return(Ac=b._emscripten_bind_btDynamicsWorld_contactTest_2=b.asm.Ka).apply(null,arguments)},Bc=b._emscripten_bind_btDynamicsWorld_updateSingleAabb_1=function(){return(Bc=b._emscripten_bind_btDynamicsWorld_updateSingleAabb_1=b.asm.La).apply(null,arguments)},Cc=b._emscripten_bind_btDynamicsWorld_setDebugDrawer_1=function(){return(Cc=b._emscripten_bind_btDynamicsWorld_setDebugDrawer_1=
|
||||||
|
b.asm.Ma).apply(null,arguments)},Dc=b._emscripten_bind_btDynamicsWorld_getDebugDrawer_0=function(){return(Dc=b._emscripten_bind_btDynamicsWorld_getDebugDrawer_0=b.asm.Na).apply(null,arguments)},Ec=b._emscripten_bind_btDynamicsWorld_debugDrawWorld_0=function(){return(Ec=b._emscripten_bind_btDynamicsWorld_debugDrawWorld_0=b.asm.Oa).apply(null,arguments)},Fc=b._emscripten_bind_btDynamicsWorld_debugDrawObject_3=function(){return(Fc=b._emscripten_bind_btDynamicsWorld_debugDrawObject_3=b.asm.Pa).apply(null,
|
||||||
|
arguments)},Gc=b._emscripten_bind_btDynamicsWorld___destroy___0=function(){return(Gc=b._emscripten_bind_btDynamicsWorld___destroy___0=b.asm.Qa).apply(null,arguments)},Hc=b._emscripten_bind_btIDebugDraw_drawLine_3=function(){return(Hc=b._emscripten_bind_btIDebugDraw_drawLine_3=b.asm.Ra).apply(null,arguments)},Ic=b._emscripten_bind_btIDebugDraw_drawContactPoint_5=function(){return(Ic=b._emscripten_bind_btIDebugDraw_drawContactPoint_5=b.asm.Sa).apply(null,arguments)},Jc=b._emscripten_bind_btIDebugDraw_reportErrorWarning_1=
|
||||||
|
function(){return(Jc=b._emscripten_bind_btIDebugDraw_reportErrorWarning_1=b.asm.Ta).apply(null,arguments)},Kc=b._emscripten_bind_btIDebugDraw_draw3dText_2=function(){return(Kc=b._emscripten_bind_btIDebugDraw_draw3dText_2=b.asm.Ua).apply(null,arguments)},Lc=b._emscripten_bind_btIDebugDraw_setDebugMode_1=function(){return(Lc=b._emscripten_bind_btIDebugDraw_setDebugMode_1=b.asm.Va).apply(null,arguments)},Mc=b._emscripten_bind_btIDebugDraw_getDebugMode_0=function(){return(Mc=b._emscripten_bind_btIDebugDraw_getDebugMode_0=
|
||||||
|
b.asm.Wa).apply(null,arguments)},Nc=b._emscripten_bind_btIDebugDraw___destroy___0=function(){return(Nc=b._emscripten_bind_btIDebugDraw___destroy___0=b.asm.Xa).apply(null,arguments)},Oc=b._emscripten_bind_btVector3_btVector3_0=function(){return(Oc=b._emscripten_bind_btVector3_btVector3_0=b.asm.Ya).apply(null,arguments)},Pc=b._emscripten_bind_btVector3_btVector3_3=function(){return(Pc=b._emscripten_bind_btVector3_btVector3_3=b.asm.Za).apply(null,arguments)},Qc=b._emscripten_bind_btVector3_length_0=
|
||||||
|
function(){return(Qc=b._emscripten_bind_btVector3_length_0=b.asm._a).apply(null,arguments)},Rc=b._emscripten_bind_btVector3_x_0=function(){return(Rc=b._emscripten_bind_btVector3_x_0=b.asm.$a).apply(null,arguments)},Sc=b._emscripten_bind_btVector3_y_0=function(){return(Sc=b._emscripten_bind_btVector3_y_0=b.asm.ab).apply(null,arguments)},Tc=b._emscripten_bind_btVector3_z_0=function(){return(Tc=b._emscripten_bind_btVector3_z_0=b.asm.bb).apply(null,arguments)},Uc=b._emscripten_bind_btVector3_setX_1=function(){return(Uc=
|
||||||
|
b._emscripten_bind_btVector3_setX_1=b.asm.cb).apply(null,arguments)},Vc=b._emscripten_bind_btVector3_setY_1=function(){return(Vc=b._emscripten_bind_btVector3_setY_1=b.asm.db).apply(null,arguments)},Wc=b._emscripten_bind_btVector3_setZ_1=function(){return(Wc=b._emscripten_bind_btVector3_setZ_1=b.asm.eb).apply(null,arguments)},Xc=b._emscripten_bind_btVector3_setValue_3=function(){return(Xc=b._emscripten_bind_btVector3_setValue_3=b.asm.fb).apply(null,arguments)},Yc=b._emscripten_bind_btVector3_normalize_0=
|
||||||
|
function(){return(Yc=b._emscripten_bind_btVector3_normalize_0=b.asm.gb).apply(null,arguments)},Zc=b._emscripten_bind_btVector3_rotate_2=function(){return(Zc=b._emscripten_bind_btVector3_rotate_2=b.asm.hb).apply(null,arguments)},$c=b._emscripten_bind_btVector3_dot_1=function(){return($c=b._emscripten_bind_btVector3_dot_1=b.asm.ib).apply(null,arguments)},ad=b._emscripten_bind_btVector3_op_mul_1=function(){return(ad=b._emscripten_bind_btVector3_op_mul_1=b.asm.jb).apply(null,arguments)},bd=b._emscripten_bind_btVector3_op_add_1=
|
||||||
|
function(){return(bd=b._emscripten_bind_btVector3_op_add_1=b.asm.kb).apply(null,arguments)},cd=b._emscripten_bind_btVector3_op_sub_1=function(){return(cd=b._emscripten_bind_btVector3_op_sub_1=b.asm.lb).apply(null,arguments)},dd=b._emscripten_bind_btVector3___destroy___0=function(){return(dd=b._emscripten_bind_btVector3___destroy___0=b.asm.mb).apply(null,arguments)},ed=b._emscripten_bind_btQuadWord_x_0=function(){return(ed=b._emscripten_bind_btQuadWord_x_0=b.asm.nb).apply(null,arguments)},fd=b._emscripten_bind_btQuadWord_y_0=
|
||||||
|
function(){return(fd=b._emscripten_bind_btQuadWord_y_0=b.asm.ob).apply(null,arguments)},gd=b._emscripten_bind_btQuadWord_z_0=function(){return(gd=b._emscripten_bind_btQuadWord_z_0=b.asm.pb).apply(null,arguments)},hd=b._emscripten_bind_btQuadWord_w_0=function(){return(hd=b._emscripten_bind_btQuadWord_w_0=b.asm.qb).apply(null,arguments)},jd=b._emscripten_bind_btQuadWord_setX_1=function(){return(jd=b._emscripten_bind_btQuadWord_setX_1=b.asm.rb).apply(null,arguments)},kd=b._emscripten_bind_btQuadWord_setY_1=
|
||||||
|
function(){return(kd=b._emscripten_bind_btQuadWord_setY_1=b.asm.sb).apply(null,arguments)},ld=b._emscripten_bind_btQuadWord_setZ_1=function(){return(ld=b._emscripten_bind_btQuadWord_setZ_1=b.asm.tb).apply(null,arguments)},md=b._emscripten_bind_btQuadWord_setW_1=function(){return(md=b._emscripten_bind_btQuadWord_setW_1=b.asm.ub).apply(null,arguments)},nd=b._emscripten_bind_btQuadWord___destroy___0=function(){return(nd=b._emscripten_bind_btQuadWord___destroy___0=b.asm.vb).apply(null,arguments)},od=
|
||||||
|
b._emscripten_bind_btMotionState_getWorldTransform_1=function(){return(od=b._emscripten_bind_btMotionState_getWorldTransform_1=b.asm.wb).apply(null,arguments)},pd=b._emscripten_bind_btMotionState_setWorldTransform_1=function(){return(pd=b._emscripten_bind_btMotionState_setWorldTransform_1=b.asm.xb).apply(null,arguments)},qd=b._emscripten_bind_btMotionState___destroy___0=function(){return(qd=b._emscripten_bind_btMotionState___destroy___0=b.asm.yb).apply(null,arguments)},rd=b._emscripten_bind_RayResultCallback_hasHit_0=
|
||||||
|
function(){return(rd=b._emscripten_bind_RayResultCallback_hasHit_0=b.asm.zb).apply(null,arguments)},sd=b._emscripten_bind_RayResultCallback_get_m_collisionFilterGroup_0=function(){return(sd=b._emscripten_bind_RayResultCallback_get_m_collisionFilterGroup_0=b.asm.Ab).apply(null,arguments)},td=b._emscripten_bind_RayResultCallback_set_m_collisionFilterGroup_1=function(){return(td=b._emscripten_bind_RayResultCallback_set_m_collisionFilterGroup_1=b.asm.Bb).apply(null,arguments)},ud=b._emscripten_bind_RayResultCallback_get_m_collisionFilterMask_0=
|
||||||
|
function(){return(ud=b._emscripten_bind_RayResultCallback_get_m_collisionFilterMask_0=b.asm.Cb).apply(null,arguments)},vd=b._emscripten_bind_RayResultCallback_set_m_collisionFilterMask_1=function(){return(vd=b._emscripten_bind_RayResultCallback_set_m_collisionFilterMask_1=b.asm.Db).apply(null,arguments)},wd=b._emscripten_bind_RayResultCallback_get_m_closestHitFraction_0=function(){return(wd=b._emscripten_bind_RayResultCallback_get_m_closestHitFraction_0=b.asm.Eb).apply(null,arguments)},xd=b._emscripten_bind_RayResultCallback_set_m_closestHitFraction_1=
|
||||||
|
function(){return(xd=b._emscripten_bind_RayResultCallback_set_m_closestHitFraction_1=b.asm.Fb).apply(null,arguments)},yd=b._emscripten_bind_RayResultCallback_get_m_collisionObject_0=function(){return(yd=b._emscripten_bind_RayResultCallback_get_m_collisionObject_0=b.asm.Gb).apply(null,arguments)},zd=b._emscripten_bind_RayResultCallback_set_m_collisionObject_1=function(){return(zd=b._emscripten_bind_RayResultCallback_set_m_collisionObject_1=b.asm.Hb).apply(null,arguments)},Ad=b._emscripten_bind_RayResultCallback_get_m_flags_0=
|
||||||
|
function(){return(Ad=b._emscripten_bind_RayResultCallback_get_m_flags_0=b.asm.Ib).apply(null,arguments)},Bd=b._emscripten_bind_RayResultCallback_set_m_flags_1=function(){return(Bd=b._emscripten_bind_RayResultCallback_set_m_flags_1=b.asm.Jb).apply(null,arguments)},Cd=b._emscripten_bind_RayResultCallback___destroy___0=function(){return(Cd=b._emscripten_bind_RayResultCallback___destroy___0=b.asm.Kb).apply(null,arguments)},Dd=b._emscripten_bind_ContactResultCallback_addSingleResult_7=function(){return(Dd=
|
||||||
|
b._emscripten_bind_ContactResultCallback_addSingleResult_7=b.asm.Lb).apply(null,arguments)},Ed=b._emscripten_bind_ContactResultCallback___destroy___0=function(){return(Ed=b._emscripten_bind_ContactResultCallback___destroy___0=b.asm.Mb).apply(null,arguments)},Fd=b._emscripten_bind_ConvexResultCallback_hasHit_0=function(){return(Fd=b._emscripten_bind_ConvexResultCallback_hasHit_0=b.asm.Nb).apply(null,arguments)},Gd=b._emscripten_bind_ConvexResultCallback_get_m_collisionFilterGroup_0=function(){return(Gd=
|
||||||
|
b._emscripten_bind_ConvexResultCallback_get_m_collisionFilterGroup_0=b.asm.Ob).apply(null,arguments)},Hd=b._emscripten_bind_ConvexResultCallback_set_m_collisionFilterGroup_1=function(){return(Hd=b._emscripten_bind_ConvexResultCallback_set_m_collisionFilterGroup_1=b.asm.Pb).apply(null,arguments)},Id=b._emscripten_bind_ConvexResultCallback_get_m_collisionFilterMask_0=function(){return(Id=b._emscripten_bind_ConvexResultCallback_get_m_collisionFilterMask_0=b.asm.Qb).apply(null,arguments)},Jd=b._emscripten_bind_ConvexResultCallback_set_m_collisionFilterMask_1=
|
||||||
|
function(){return(Jd=b._emscripten_bind_ConvexResultCallback_set_m_collisionFilterMask_1=b.asm.Rb).apply(null,arguments)},Kd=b._emscripten_bind_ConvexResultCallback_get_m_closestHitFraction_0=function(){return(Kd=b._emscripten_bind_ConvexResultCallback_get_m_closestHitFraction_0=b.asm.Sb).apply(null,arguments)},Ld=b._emscripten_bind_ConvexResultCallback_set_m_closestHitFraction_1=function(){return(Ld=b._emscripten_bind_ConvexResultCallback_set_m_closestHitFraction_1=b.asm.Tb).apply(null,arguments)},
|
||||||
|
Md=b._emscripten_bind_ConvexResultCallback___destroy___0=function(){return(Md=b._emscripten_bind_ConvexResultCallback___destroy___0=b.asm.Ub).apply(null,arguments)},Nd=b._emscripten_bind_btConvexShape_setLocalScaling_1=function(){return(Nd=b._emscripten_bind_btConvexShape_setLocalScaling_1=b.asm.Vb).apply(null,arguments)},Od=b._emscripten_bind_btConvexShape_getLocalScaling_0=function(){return(Od=b._emscripten_bind_btConvexShape_getLocalScaling_0=b.asm.Wb).apply(null,arguments)},Pd=b._emscripten_bind_btConvexShape_calculateLocalInertia_2=
|
||||||
|
function(){return(Pd=b._emscripten_bind_btConvexShape_calculateLocalInertia_2=b.asm.Xb).apply(null,arguments)},Qd=b._emscripten_bind_btConvexShape_setMargin_1=function(){return(Qd=b._emscripten_bind_btConvexShape_setMargin_1=b.asm.Yb).apply(null,arguments)},Rd=b._emscripten_bind_btConvexShape_getMargin_0=function(){return(Rd=b._emscripten_bind_btConvexShape_getMargin_0=b.asm.Zb).apply(null,arguments)},Sd=b._emscripten_bind_btConvexShape___destroy___0=function(){return(Sd=b._emscripten_bind_btConvexShape___destroy___0=
|
||||||
|
b.asm._b).apply(null,arguments)},Td=b._emscripten_bind_btCapsuleShape_btCapsuleShape_2=function(){return(Td=b._emscripten_bind_btCapsuleShape_btCapsuleShape_2=b.asm.$b).apply(null,arguments)},Ud=b._emscripten_bind_btCapsuleShape_setMargin_1=function(){return(Ud=b._emscripten_bind_btCapsuleShape_setMargin_1=b.asm.ac).apply(null,arguments)},Vd=b._emscripten_bind_btCapsuleShape_getMargin_0=function(){return(Vd=b._emscripten_bind_btCapsuleShape_getMargin_0=b.asm.bc).apply(null,arguments)},Wd=b._emscripten_bind_btCapsuleShape_getUpAxis_0=
|
||||||
|
function(){return(Wd=b._emscripten_bind_btCapsuleShape_getUpAxis_0=b.asm.cc).apply(null,arguments)},Xd=b._emscripten_bind_btCapsuleShape_getRadius_0=function(){return(Xd=b._emscripten_bind_btCapsuleShape_getRadius_0=b.asm.dc).apply(null,arguments)},Yd=b._emscripten_bind_btCapsuleShape_getHalfHeight_0=function(){return(Yd=b._emscripten_bind_btCapsuleShape_getHalfHeight_0=b.asm.ec).apply(null,arguments)},Zd=b._emscripten_bind_btCapsuleShape_setLocalScaling_1=function(){return(Zd=b._emscripten_bind_btCapsuleShape_setLocalScaling_1=
|
||||||
|
b.asm.fc).apply(null,arguments)},$d=b._emscripten_bind_btCapsuleShape_getLocalScaling_0=function(){return($d=b._emscripten_bind_btCapsuleShape_getLocalScaling_0=b.asm.gc).apply(null,arguments)},ae=b._emscripten_bind_btCapsuleShape_calculateLocalInertia_2=function(){return(ae=b._emscripten_bind_btCapsuleShape_calculateLocalInertia_2=b.asm.hc).apply(null,arguments)},be=b._emscripten_bind_btCapsuleShape___destroy___0=function(){return(be=b._emscripten_bind_btCapsuleShape___destroy___0=b.asm.ic).apply(null,
|
||||||
|
arguments)},ce=b._emscripten_bind_btCylinderShape_btCylinderShape_1=function(){return(ce=b._emscripten_bind_btCylinderShape_btCylinderShape_1=b.asm.jc).apply(null,arguments)},de=b._emscripten_bind_btCylinderShape_setMargin_1=function(){return(de=b._emscripten_bind_btCylinderShape_setMargin_1=b.asm.kc).apply(null,arguments)},ee=b._emscripten_bind_btCylinderShape_getMargin_0=function(){return(ee=b._emscripten_bind_btCylinderShape_getMargin_0=b.asm.lc).apply(null,arguments)},fe=b._emscripten_bind_btCylinderShape_setLocalScaling_1=
|
||||||
|
function(){return(fe=b._emscripten_bind_btCylinderShape_setLocalScaling_1=b.asm.mc).apply(null,arguments)},ge=b._emscripten_bind_btCylinderShape_getLocalScaling_0=function(){return(ge=b._emscripten_bind_btCylinderShape_getLocalScaling_0=b.asm.nc).apply(null,arguments)},he=b._emscripten_bind_btCylinderShape_calculateLocalInertia_2=function(){return(he=b._emscripten_bind_btCylinderShape_calculateLocalInertia_2=b.asm.oc).apply(null,arguments)},ie=b._emscripten_bind_btCylinderShape___destroy___0=function(){return(ie=
|
||||||
|
b._emscripten_bind_btCylinderShape___destroy___0=b.asm.pc).apply(null,arguments)},je=b._emscripten_bind_btConeShape_btConeShape_2=function(){return(je=b._emscripten_bind_btConeShape_btConeShape_2=b.asm.qc).apply(null,arguments)},ke=b._emscripten_bind_btConeShape_setLocalScaling_1=function(){return(ke=b._emscripten_bind_btConeShape_setLocalScaling_1=b.asm.rc).apply(null,arguments)},le=b._emscripten_bind_btConeShape_getLocalScaling_0=function(){return(le=b._emscripten_bind_btConeShape_getLocalScaling_0=
|
||||||
|
b.asm.sc).apply(null,arguments)},me=b._emscripten_bind_btConeShape_calculateLocalInertia_2=function(){return(me=b._emscripten_bind_btConeShape_calculateLocalInertia_2=b.asm.tc).apply(null,arguments)},ne=b._emscripten_bind_btConeShape___destroy___0=function(){return(ne=b._emscripten_bind_btConeShape___destroy___0=b.asm.uc).apply(null,arguments)},oe=b._emscripten_bind_btStridingMeshInterface_setScaling_1=function(){return(oe=b._emscripten_bind_btStridingMeshInterface_setScaling_1=b.asm.vc).apply(null,
|
||||||
|
arguments)},pe=b._emscripten_bind_btStridingMeshInterface___destroy___0=function(){return(pe=b._emscripten_bind_btStridingMeshInterface___destroy___0=b.asm.wc).apply(null,arguments)},qe=b._emscripten_bind_btTriangleMeshShape_setLocalScaling_1=function(){return(qe=b._emscripten_bind_btTriangleMeshShape_setLocalScaling_1=b.asm.xc).apply(null,arguments)},re=b._emscripten_bind_btTriangleMeshShape_getLocalScaling_0=function(){return(re=b._emscripten_bind_btTriangleMeshShape_getLocalScaling_0=b.asm.yc).apply(null,
|
||||||
|
arguments)},se=b._emscripten_bind_btTriangleMeshShape_calculateLocalInertia_2=function(){return(se=b._emscripten_bind_btTriangleMeshShape_calculateLocalInertia_2=b.asm.zc).apply(null,arguments)},te=b._emscripten_bind_btTriangleMeshShape___destroy___0=function(){return(te=b._emscripten_bind_btTriangleMeshShape___destroy___0=b.asm.Ac).apply(null,arguments)},ue=b._emscripten_bind_btPrimitiveManagerBase_is_trimesh_0=function(){return(ue=b._emscripten_bind_btPrimitiveManagerBase_is_trimesh_0=b.asm.Bc).apply(null,
|
||||||
|
arguments)},ve=b._emscripten_bind_btPrimitiveManagerBase_get_primitive_count_0=function(){return(ve=b._emscripten_bind_btPrimitiveManagerBase_get_primitive_count_0=b.asm.Cc).apply(null,arguments)},we=b._emscripten_bind_btPrimitiveManagerBase_get_primitive_box_2=function(){return(we=b._emscripten_bind_btPrimitiveManagerBase_get_primitive_box_2=b.asm.Dc).apply(null,arguments)},xe=b._emscripten_bind_btPrimitiveManagerBase_get_primitive_triangle_2=function(){return(xe=b._emscripten_bind_btPrimitiveManagerBase_get_primitive_triangle_2=
|
||||||
|
b.asm.Ec).apply(null,arguments)},ye=b._emscripten_bind_btPrimitiveManagerBase___destroy___0=function(){return(ye=b._emscripten_bind_btPrimitiveManagerBase___destroy___0=b.asm.Fc).apply(null,arguments)},ze=b._emscripten_bind_btGImpactShapeInterface_updateBound_0=function(){return(ze=b._emscripten_bind_btGImpactShapeInterface_updateBound_0=b.asm.Gc).apply(null,arguments)},Ae=b._emscripten_bind_btGImpactShapeInterface_postUpdate_0=function(){return(Ae=b._emscripten_bind_btGImpactShapeInterface_postUpdate_0=
|
||||||
|
b.asm.Hc).apply(null,arguments)},Be=b._emscripten_bind_btGImpactShapeInterface_getShapeType_0=function(){return(Be=b._emscripten_bind_btGImpactShapeInterface_getShapeType_0=b.asm.Ic).apply(null,arguments)},Ce=b._emscripten_bind_btGImpactShapeInterface_getName_0=function(){return(Ce=b._emscripten_bind_btGImpactShapeInterface_getName_0=b.asm.Jc).apply(null,arguments)},De=b._emscripten_bind_btGImpactShapeInterface_getGImpactShapeType_0=function(){return(De=b._emscripten_bind_btGImpactShapeInterface_getGImpactShapeType_0=
|
||||||
|
b.asm.Kc).apply(null,arguments)},Ee=b._emscripten_bind_btGImpactShapeInterface_getPrimitiveManager_0=function(){return(Ee=b._emscripten_bind_btGImpactShapeInterface_getPrimitiveManager_0=b.asm.Lc).apply(null,arguments)},Fe=b._emscripten_bind_btGImpactShapeInterface_getNumChildShapes_0=function(){return(Fe=b._emscripten_bind_btGImpactShapeInterface_getNumChildShapes_0=b.asm.Mc).apply(null,arguments)},Ge=b._emscripten_bind_btGImpactShapeInterface_childrenHasTransform_0=function(){return(Ge=b._emscripten_bind_btGImpactShapeInterface_childrenHasTransform_0=
|
||||||
|
b.asm.Nc).apply(null,arguments)},He=b._emscripten_bind_btGImpactShapeInterface_needsRetrieveTriangles_0=function(){return(He=b._emscripten_bind_btGImpactShapeInterface_needsRetrieveTriangles_0=b.asm.Oc).apply(null,arguments)},Ie=b._emscripten_bind_btGImpactShapeInterface_needsRetrieveTetrahedrons_0=function(){return(Ie=b._emscripten_bind_btGImpactShapeInterface_needsRetrieveTetrahedrons_0=b.asm.Pc).apply(null,arguments)},Je=b._emscripten_bind_btGImpactShapeInterface_getBulletTriangle_2=function(){return(Je=
|
||||||
|
b._emscripten_bind_btGImpactShapeInterface_getBulletTriangle_2=b.asm.Qc).apply(null,arguments)},Ke=b._emscripten_bind_btGImpactShapeInterface_getBulletTetrahedron_2=function(){return(Ke=b._emscripten_bind_btGImpactShapeInterface_getBulletTetrahedron_2=b.asm.Rc).apply(null,arguments)},Le=b._emscripten_bind_btGImpactShapeInterface_getChildShape_1=function(){return(Le=b._emscripten_bind_btGImpactShapeInterface_getChildShape_1=b.asm.Sc).apply(null,arguments)},Me=b._emscripten_bind_btGImpactShapeInterface_getChildTransform_1=
|
||||||
|
function(){return(Me=b._emscripten_bind_btGImpactShapeInterface_getChildTransform_1=b.asm.Tc).apply(null,arguments)},Ne=b._emscripten_bind_btGImpactShapeInterface_setChildTransform_2=function(){return(Ne=b._emscripten_bind_btGImpactShapeInterface_setChildTransform_2=b.asm.Uc).apply(null,arguments)},Oe=b._emscripten_bind_btGImpactShapeInterface_setLocalScaling_1=function(){return(Oe=b._emscripten_bind_btGImpactShapeInterface_setLocalScaling_1=b.asm.Vc).apply(null,arguments)},Pe=b._emscripten_bind_btGImpactShapeInterface_getLocalScaling_0=
|
||||||
|
function(){return(Pe=b._emscripten_bind_btGImpactShapeInterface_getLocalScaling_0=b.asm.Wc).apply(null,arguments)},Qe=b._emscripten_bind_btGImpactShapeInterface_calculateLocalInertia_2=function(){return(Qe=b._emscripten_bind_btGImpactShapeInterface_calculateLocalInertia_2=b.asm.Xc).apply(null,arguments)},Re=b._emscripten_bind_btGImpactShapeInterface___destroy___0=function(){return(Re=b._emscripten_bind_btGImpactShapeInterface___destroy___0=b.asm.Yc).apply(null,arguments)},Se=b._emscripten_bind_btActivatingCollisionAlgorithm___destroy___0=
|
||||||
|
function(){return(Se=b._emscripten_bind_btActivatingCollisionAlgorithm___destroy___0=b.asm.Zc).apply(null,arguments)},Te=b._emscripten_bind_btDefaultCollisionConfiguration_btDefaultCollisionConfiguration_0=function(){return(Te=b._emscripten_bind_btDefaultCollisionConfiguration_btDefaultCollisionConfiguration_0=b.asm._c).apply(null,arguments)},Ue=b._emscripten_bind_btDefaultCollisionConfiguration_btDefaultCollisionConfiguration_1=function(){return(Ue=b._emscripten_bind_btDefaultCollisionConfiguration_btDefaultCollisionConfiguration_1=
|
||||||
|
b.asm.$c).apply(null,arguments)},Ve=b._emscripten_bind_btDefaultCollisionConfiguration___destroy___0=function(){return(Ve=b._emscripten_bind_btDefaultCollisionConfiguration___destroy___0=b.asm.ad).apply(null,arguments)},We=b._emscripten_bind_btDispatcher_getNumManifolds_0=function(){return(We=b._emscripten_bind_btDispatcher_getNumManifolds_0=b.asm.bd).apply(null,arguments)},Xe=b._emscripten_bind_btDispatcher_getManifoldByIndexInternal_1=function(){return(Xe=b._emscripten_bind_btDispatcher_getManifoldByIndexInternal_1=
|
||||||
|
b.asm.cd).apply(null,arguments)},Ye=b._emscripten_bind_btDispatcher___destroy___0=function(){return(Ye=b._emscripten_bind_btDispatcher___destroy___0=b.asm.dd).apply(null,arguments)},Ze=b._emscripten_bind_btGeneric6DofConstraint_btGeneric6DofConstraint_3=function(){return(Ze=b._emscripten_bind_btGeneric6DofConstraint_btGeneric6DofConstraint_3=b.asm.ed).apply(null,arguments)},$e=b._emscripten_bind_btGeneric6DofConstraint_btGeneric6DofConstraint_5=function(){return($e=b._emscripten_bind_btGeneric6DofConstraint_btGeneric6DofConstraint_5=
|
||||||
|
b.asm.fd).apply(null,arguments)},af=b._emscripten_bind_btGeneric6DofConstraint_setLinearLowerLimit_1=function(){return(af=b._emscripten_bind_btGeneric6DofConstraint_setLinearLowerLimit_1=b.asm.gd).apply(null,arguments)},bf=b._emscripten_bind_btGeneric6DofConstraint_setLinearUpperLimit_1=function(){return(bf=b._emscripten_bind_btGeneric6DofConstraint_setLinearUpperLimit_1=b.asm.hd).apply(null,arguments)},cf=b._emscripten_bind_btGeneric6DofConstraint_setAngularLowerLimit_1=function(){return(cf=b._emscripten_bind_btGeneric6DofConstraint_setAngularLowerLimit_1=
|
||||||
|
b.asm.id).apply(null,arguments)},df=b._emscripten_bind_btGeneric6DofConstraint_setAngularUpperLimit_1=function(){return(df=b._emscripten_bind_btGeneric6DofConstraint_setAngularUpperLimit_1=b.asm.jd).apply(null,arguments)},ef=b._emscripten_bind_btGeneric6DofConstraint_getFrameOffsetA_0=function(){return(ef=b._emscripten_bind_btGeneric6DofConstraint_getFrameOffsetA_0=b.asm.kd).apply(null,arguments)},ff=b._emscripten_bind_btGeneric6DofConstraint_enableFeedback_1=function(){return(ff=b._emscripten_bind_btGeneric6DofConstraint_enableFeedback_1=
|
||||||
|
b.asm.ld).apply(null,arguments)},gf=b._emscripten_bind_btGeneric6DofConstraint_getBreakingImpulseThreshold_0=function(){return(gf=b._emscripten_bind_btGeneric6DofConstraint_getBreakingImpulseThreshold_0=b.asm.md).apply(null,arguments)},hf=b._emscripten_bind_btGeneric6DofConstraint_setBreakingImpulseThreshold_1=function(){return(hf=b._emscripten_bind_btGeneric6DofConstraint_setBreakingImpulseThreshold_1=b.asm.nd).apply(null,arguments)},jf=b._emscripten_bind_btGeneric6DofConstraint_getParam_2=function(){return(jf=
|
||||||
|
b._emscripten_bind_btGeneric6DofConstraint_getParam_2=b.asm.od).apply(null,arguments)},kf=b._emscripten_bind_btGeneric6DofConstraint_setParam_3=function(){return(kf=b._emscripten_bind_btGeneric6DofConstraint_setParam_3=b.asm.pd).apply(null,arguments)},lf=b._emscripten_bind_btGeneric6DofConstraint___destroy___0=function(){return(lf=b._emscripten_bind_btGeneric6DofConstraint___destroy___0=b.asm.qd).apply(null,arguments)},mf=b._emscripten_bind_btDiscreteDynamicsWorld_btDiscreteDynamicsWorld_4=function(){return(mf=
|
||||||
|
b._emscripten_bind_btDiscreteDynamicsWorld_btDiscreteDynamicsWorld_4=b.asm.rd).apply(null,arguments)},nf=b._emscripten_bind_btDiscreteDynamicsWorld_setGravity_1=function(){return(nf=b._emscripten_bind_btDiscreteDynamicsWorld_setGravity_1=b.asm.sd).apply(null,arguments)},of=b._emscripten_bind_btDiscreteDynamicsWorld_getGravity_0=function(){return(of=b._emscripten_bind_btDiscreteDynamicsWorld_getGravity_0=b.asm.td).apply(null,arguments)},pf=b._emscripten_bind_btDiscreteDynamicsWorld_addRigidBody_1=
|
||||||
|
function(){return(pf=b._emscripten_bind_btDiscreteDynamicsWorld_addRigidBody_1=b.asm.ud).apply(null,arguments)},qf=b._emscripten_bind_btDiscreteDynamicsWorld_addRigidBody_3=function(){return(qf=b._emscripten_bind_btDiscreteDynamicsWorld_addRigidBody_3=b.asm.vd).apply(null,arguments)},rf=b._emscripten_bind_btDiscreteDynamicsWorld_removeRigidBody_1=function(){return(rf=b._emscripten_bind_btDiscreteDynamicsWorld_removeRigidBody_1=b.asm.wd).apply(null,arguments)},sf=b._emscripten_bind_btDiscreteDynamicsWorld_addConstraint_1=
|
||||||
|
function(){return(sf=b._emscripten_bind_btDiscreteDynamicsWorld_addConstraint_1=b.asm.xd).apply(null,arguments)},tf=b._emscripten_bind_btDiscreteDynamicsWorld_addConstraint_2=function(){return(tf=b._emscripten_bind_btDiscreteDynamicsWorld_addConstraint_2=b.asm.yd).apply(null,arguments)},uf=b._emscripten_bind_btDiscreteDynamicsWorld_removeConstraint_1=function(){return(uf=b._emscripten_bind_btDiscreteDynamicsWorld_removeConstraint_1=b.asm.zd).apply(null,arguments)},vf=b._emscripten_bind_btDiscreteDynamicsWorld_stepSimulation_1=
|
||||||
|
function(){return(vf=b._emscripten_bind_btDiscreteDynamicsWorld_stepSimulation_1=b.asm.Ad).apply(null,arguments)},wf=b._emscripten_bind_btDiscreteDynamicsWorld_stepSimulation_2=function(){return(wf=b._emscripten_bind_btDiscreteDynamicsWorld_stepSimulation_2=b.asm.Bd).apply(null,arguments)},xf=b._emscripten_bind_btDiscreteDynamicsWorld_stepSimulation_3=function(){return(xf=b._emscripten_bind_btDiscreteDynamicsWorld_stepSimulation_3=b.asm.Cd).apply(null,arguments)},yf=b._emscripten_bind_btDiscreteDynamicsWorld_setContactAddedCallback_1=
|
||||||
|
function(){return(yf=b._emscripten_bind_btDiscreteDynamicsWorld_setContactAddedCallback_1=b.asm.Dd).apply(null,arguments)},zf=b._emscripten_bind_btDiscreteDynamicsWorld_setContactProcessedCallback_1=function(){return(zf=b._emscripten_bind_btDiscreteDynamicsWorld_setContactProcessedCallback_1=b.asm.Ed).apply(null,arguments)},Af=b._emscripten_bind_btDiscreteDynamicsWorld_setContactDestroyedCallback_1=function(){return(Af=b._emscripten_bind_btDiscreteDynamicsWorld_setContactDestroyedCallback_1=b.asm.Fd).apply(null,
|
||||||
|
arguments)},Bf=b._emscripten_bind_btDiscreteDynamicsWorld_getDispatcher_0=function(){return(Bf=b._emscripten_bind_btDiscreteDynamicsWorld_getDispatcher_0=b.asm.Gd).apply(null,arguments)},Cf=b._emscripten_bind_btDiscreteDynamicsWorld_rayTest_3=function(){return(Cf=b._emscripten_bind_btDiscreteDynamicsWorld_rayTest_3=b.asm.Hd).apply(null,arguments)},Df=b._emscripten_bind_btDiscreteDynamicsWorld_getPairCache_0=function(){return(Df=b._emscripten_bind_btDiscreteDynamicsWorld_getPairCache_0=b.asm.Id).apply(null,
|
||||||
|
arguments)},Ef=b._emscripten_bind_btDiscreteDynamicsWorld_getDispatchInfo_0=function(){return(Ef=b._emscripten_bind_btDiscreteDynamicsWorld_getDispatchInfo_0=b.asm.Jd).apply(null,arguments)},Ff=b._emscripten_bind_btDiscreteDynamicsWorld_addCollisionObject_1=function(){return(Ff=b._emscripten_bind_btDiscreteDynamicsWorld_addCollisionObject_1=b.asm.Kd).apply(null,arguments)},Gf=b._emscripten_bind_btDiscreteDynamicsWorld_addCollisionObject_2=function(){return(Gf=b._emscripten_bind_btDiscreteDynamicsWorld_addCollisionObject_2=
|
||||||
|
b.asm.Ld).apply(null,arguments)},Hf=b._emscripten_bind_btDiscreteDynamicsWorld_addCollisionObject_3=function(){return(Hf=b._emscripten_bind_btDiscreteDynamicsWorld_addCollisionObject_3=b.asm.Md).apply(null,arguments)},If=b._emscripten_bind_btDiscreteDynamicsWorld_removeCollisionObject_1=function(){return(If=b._emscripten_bind_btDiscreteDynamicsWorld_removeCollisionObject_1=b.asm.Nd).apply(null,arguments)},Jf=b._emscripten_bind_btDiscreteDynamicsWorld_getBroadphase_0=function(){return(Jf=b._emscripten_bind_btDiscreteDynamicsWorld_getBroadphase_0=
|
||||||
|
b.asm.Od).apply(null,arguments)},Kf=b._emscripten_bind_btDiscreteDynamicsWorld_convexSweepTest_5=function(){return(Kf=b._emscripten_bind_btDiscreteDynamicsWorld_convexSweepTest_5=b.asm.Pd).apply(null,arguments)},Lf=b._emscripten_bind_btDiscreteDynamicsWorld_contactPairTest_3=function(){return(Lf=b._emscripten_bind_btDiscreteDynamicsWorld_contactPairTest_3=b.asm.Qd).apply(null,arguments)},Mf=b._emscripten_bind_btDiscreteDynamicsWorld_contactTest_2=function(){return(Mf=b._emscripten_bind_btDiscreteDynamicsWorld_contactTest_2=
|
||||||
|
b.asm.Rd).apply(null,arguments)},Nf=b._emscripten_bind_btDiscreteDynamicsWorld_updateSingleAabb_1=function(){return(Nf=b._emscripten_bind_btDiscreteDynamicsWorld_updateSingleAabb_1=b.asm.Sd).apply(null,arguments)},Of=b._emscripten_bind_btDiscreteDynamicsWorld_setDebugDrawer_1=function(){return(Of=b._emscripten_bind_btDiscreteDynamicsWorld_setDebugDrawer_1=b.asm.Td).apply(null,arguments)},Pf=b._emscripten_bind_btDiscreteDynamicsWorld_getDebugDrawer_0=function(){return(Pf=b._emscripten_bind_btDiscreteDynamicsWorld_getDebugDrawer_0=
|
||||||
|
b.asm.Ud).apply(null,arguments)},Qf=b._emscripten_bind_btDiscreteDynamicsWorld_debugDrawWorld_0=function(){return(Qf=b._emscripten_bind_btDiscreteDynamicsWorld_debugDrawWorld_0=b.asm.Vd).apply(null,arguments)},Rf=b._emscripten_bind_btDiscreteDynamicsWorld_debugDrawObject_3=function(){return(Rf=b._emscripten_bind_btDiscreteDynamicsWorld_debugDrawObject_3=b.asm.Wd).apply(null,arguments)},Sf=b._emscripten_bind_btDiscreteDynamicsWorld_addAction_1=function(){return(Sf=b._emscripten_bind_btDiscreteDynamicsWorld_addAction_1=
|
||||||
|
b.asm.Xd).apply(null,arguments)},Tf=b._emscripten_bind_btDiscreteDynamicsWorld_removeAction_1=function(){return(Tf=b._emscripten_bind_btDiscreteDynamicsWorld_removeAction_1=b.asm.Yd).apply(null,arguments)},Uf=b._emscripten_bind_btDiscreteDynamicsWorld_getSolverInfo_0=function(){return(Uf=b._emscripten_bind_btDiscreteDynamicsWorld_getSolverInfo_0=b.asm.Zd).apply(null,arguments)},Vf=b._emscripten_bind_btDiscreteDynamicsWorld_setInternalTickCallback_1=function(){return(Vf=b._emscripten_bind_btDiscreteDynamicsWorld_setInternalTickCallback_1=
|
||||||
|
b.asm._d).apply(null,arguments)},Wf=b._emscripten_bind_btDiscreteDynamicsWorld_setInternalTickCallback_2=function(){return(Wf=b._emscripten_bind_btDiscreteDynamicsWorld_setInternalTickCallback_2=b.asm.$d).apply(null,arguments)},Xf=b._emscripten_bind_btDiscreteDynamicsWorld_setInternalTickCallback_3=function(){return(Xf=b._emscripten_bind_btDiscreteDynamicsWorld_setInternalTickCallback_3=b.asm.ae).apply(null,arguments)},Yf=b._emscripten_bind_btDiscreteDynamicsWorld___destroy___0=function(){return(Yf=
|
||||||
|
b._emscripten_bind_btDiscreteDynamicsWorld___destroy___0=b.asm.be).apply(null,arguments)},Zf=b._emscripten_bind_btVehicleRaycaster_castRay_3=function(){return(Zf=b._emscripten_bind_btVehicleRaycaster_castRay_3=b.asm.ce).apply(null,arguments)},$f=b._emscripten_bind_btVehicleRaycaster___destroy___0=function(){return($f=b._emscripten_bind_btVehicleRaycaster___destroy___0=b.asm.de).apply(null,arguments)},ag=b._emscripten_bind_btActionInterface_updateAction_2=function(){return(ag=b._emscripten_bind_btActionInterface_updateAction_2=
|
||||||
|
b.asm.ee).apply(null,arguments)},bg=b._emscripten_bind_btActionInterface___destroy___0=function(){return(bg=b._emscripten_bind_btActionInterface___destroy___0=b.asm.fe).apply(null,arguments)},cg=b._emscripten_bind_btGhostObject_btGhostObject_0=function(){return(cg=b._emscripten_bind_btGhostObject_btGhostObject_0=b.asm.ge).apply(null,arguments)},dg=b._emscripten_bind_btGhostObject_getNumOverlappingObjects_0=function(){return(dg=b._emscripten_bind_btGhostObject_getNumOverlappingObjects_0=b.asm.he).apply(null,
|
||||||
|
arguments)},eg=b._emscripten_bind_btGhostObject_getOverlappingObject_1=function(){return(eg=b._emscripten_bind_btGhostObject_getOverlappingObject_1=b.asm.ie).apply(null,arguments)},fg=b._emscripten_bind_btGhostObject_setAnisotropicFriction_2=function(){return(fg=b._emscripten_bind_btGhostObject_setAnisotropicFriction_2=b.asm.je).apply(null,arguments)},gg=b._emscripten_bind_btGhostObject_getCollisionShape_0=function(){return(gg=b._emscripten_bind_btGhostObject_getCollisionShape_0=b.asm.ke).apply(null,
|
||||||
|
arguments)},hg=b._emscripten_bind_btGhostObject_setContactProcessingThreshold_1=function(){return(hg=b._emscripten_bind_btGhostObject_setContactProcessingThreshold_1=b.asm.le).apply(null,arguments)},ig=b._emscripten_bind_btGhostObject_setActivationState_1=function(){return(ig=b._emscripten_bind_btGhostObject_setActivationState_1=b.asm.me).apply(null,arguments)},jg=b._emscripten_bind_btGhostObject_forceActivationState_1=function(){return(jg=b._emscripten_bind_btGhostObject_forceActivationState_1=b.asm.ne).apply(null,
|
||||||
|
arguments)},kg=b._emscripten_bind_btGhostObject_activate_0=function(){return(kg=b._emscripten_bind_btGhostObject_activate_0=b.asm.oe).apply(null,arguments)},lg=b._emscripten_bind_btGhostObject_activate_1=function(){return(lg=b._emscripten_bind_btGhostObject_activate_1=b.asm.pe).apply(null,arguments)},mg=b._emscripten_bind_btGhostObject_isActive_0=function(){return(mg=b._emscripten_bind_btGhostObject_isActive_0=b.asm.qe).apply(null,arguments)},ng=b._emscripten_bind_btGhostObject_isKinematicObject_0=
|
||||||
|
function(){return(ng=b._emscripten_bind_btGhostObject_isKinematicObject_0=b.asm.re).apply(null,arguments)},og=b._emscripten_bind_btGhostObject_isStaticObject_0=function(){return(og=b._emscripten_bind_btGhostObject_isStaticObject_0=b.asm.se).apply(null,arguments)},pg=b._emscripten_bind_btGhostObject_isStaticOrKinematicObject_0=function(){return(pg=b._emscripten_bind_btGhostObject_isStaticOrKinematicObject_0=b.asm.te).apply(null,arguments)},qg=b._emscripten_bind_btGhostObject_getRestitution_0=function(){return(qg=
|
||||||
|
b._emscripten_bind_btGhostObject_getRestitution_0=b.asm.ue).apply(null,arguments)},rg=b._emscripten_bind_btGhostObject_getFriction_0=function(){return(rg=b._emscripten_bind_btGhostObject_getFriction_0=b.asm.ve).apply(null,arguments)},sg=b._emscripten_bind_btGhostObject_getRollingFriction_0=function(){return(sg=b._emscripten_bind_btGhostObject_getRollingFriction_0=b.asm.we).apply(null,arguments)},tg=b._emscripten_bind_btGhostObject_setRestitution_1=function(){return(tg=b._emscripten_bind_btGhostObject_setRestitution_1=
|
||||||
|
b.asm.xe).apply(null,arguments)},ug=b._emscripten_bind_btGhostObject_setFriction_1=function(){return(ug=b._emscripten_bind_btGhostObject_setFriction_1=b.asm.ye).apply(null,arguments)},vg=b._emscripten_bind_btGhostObject_setRollingFriction_1=function(){return(vg=b._emscripten_bind_btGhostObject_setRollingFriction_1=b.asm.ze).apply(null,arguments)},wg=b._emscripten_bind_btGhostObject_getWorldTransform_0=function(){return(wg=b._emscripten_bind_btGhostObject_getWorldTransform_0=b.asm.Ae).apply(null,arguments)},
|
||||||
|
xg=b._emscripten_bind_btGhostObject_getCollisionFlags_0=function(){return(xg=b._emscripten_bind_btGhostObject_getCollisionFlags_0=b.asm.Be).apply(null,arguments)},yg=b._emscripten_bind_btGhostObject_setCollisionFlags_1=function(){return(yg=b._emscripten_bind_btGhostObject_setCollisionFlags_1=b.asm.Ce).apply(null,arguments)},zg=b._emscripten_bind_btGhostObject_setWorldTransform_1=function(){return(zg=b._emscripten_bind_btGhostObject_setWorldTransform_1=b.asm.De).apply(null,arguments)},Ag=b._emscripten_bind_btGhostObject_setCollisionShape_1=
|
||||||
|
function(){return(Ag=b._emscripten_bind_btGhostObject_setCollisionShape_1=b.asm.Ee).apply(null,arguments)},Bg=b._emscripten_bind_btGhostObject_setCcdMotionThreshold_1=function(){return(Bg=b._emscripten_bind_btGhostObject_setCcdMotionThreshold_1=b.asm.Fe).apply(null,arguments)},Cg=b._emscripten_bind_btGhostObject_setCcdSweptSphereRadius_1=function(){return(Cg=b._emscripten_bind_btGhostObject_setCcdSweptSphereRadius_1=b.asm.Ge).apply(null,arguments)},Dg=b._emscripten_bind_btGhostObject_getUserIndex_0=
|
||||||
|
function(){return(Dg=b._emscripten_bind_btGhostObject_getUserIndex_0=b.asm.He).apply(null,arguments)},Eg=b._emscripten_bind_btGhostObject_setUserIndex_1=function(){return(Eg=b._emscripten_bind_btGhostObject_setUserIndex_1=b.asm.Ie).apply(null,arguments)},Fg=b._emscripten_bind_btGhostObject_getUserPointer_0=function(){return(Fg=b._emscripten_bind_btGhostObject_getUserPointer_0=b.asm.Je).apply(null,arguments)},Gg=b._emscripten_bind_btGhostObject_setUserPointer_1=function(){return(Gg=b._emscripten_bind_btGhostObject_setUserPointer_1=
|
||||||
|
b.asm.Ke).apply(null,arguments)},Hg=b._emscripten_bind_btGhostObject_getBroadphaseHandle_0=function(){return(Hg=b._emscripten_bind_btGhostObject_getBroadphaseHandle_0=b.asm.Le).apply(null,arguments)},Ig=b._emscripten_bind_btGhostObject___destroy___0=function(){return(Ig=b._emscripten_bind_btGhostObject___destroy___0=b.asm.Me).apply(null,arguments)},Jg=b._emscripten_bind_btSoftBodySolver___destroy___0=function(){return(Jg=b._emscripten_bind_btSoftBodySolver___destroy___0=b.asm.Ne).apply(null,arguments)},
|
||||||
|
Kg=b._emscripten_bind_VoidPtr___destroy___0=function(){return(Kg=b._emscripten_bind_VoidPtr___destroy___0=b.asm.Oe).apply(null,arguments)},Lg=b._emscripten_bind_DebugDrawer_DebugDrawer_0=function(){return(Lg=b._emscripten_bind_DebugDrawer_DebugDrawer_0=b.asm.Pe).apply(null,arguments)},Mg=b._emscripten_bind_DebugDrawer_drawLine_3=function(){return(Mg=b._emscripten_bind_DebugDrawer_drawLine_3=b.asm.Qe).apply(null,arguments)},Ng=b._emscripten_bind_DebugDrawer_drawContactPoint_5=function(){return(Ng=
|
||||||
|
b._emscripten_bind_DebugDrawer_drawContactPoint_5=b.asm.Re).apply(null,arguments)},Og=b._emscripten_bind_DebugDrawer_reportErrorWarning_1=function(){return(Og=b._emscripten_bind_DebugDrawer_reportErrorWarning_1=b.asm.Se).apply(null,arguments)},Pg=b._emscripten_bind_DebugDrawer_draw3dText_2=function(){return(Pg=b._emscripten_bind_DebugDrawer_draw3dText_2=b.asm.Te).apply(null,arguments)},Qg=b._emscripten_bind_DebugDrawer_setDebugMode_1=function(){return(Qg=b._emscripten_bind_DebugDrawer_setDebugMode_1=
|
||||||
|
b.asm.Ue).apply(null,arguments)},Rg=b._emscripten_bind_DebugDrawer_getDebugMode_0=function(){return(Rg=b._emscripten_bind_DebugDrawer_getDebugMode_0=b.asm.Ve).apply(null,arguments)},Sg=b._emscripten_bind_DebugDrawer___destroy___0=function(){return(Sg=b._emscripten_bind_DebugDrawer___destroy___0=b.asm.We).apply(null,arguments)},Tg=b._emscripten_bind_btVector4_btVector4_0=function(){return(Tg=b._emscripten_bind_btVector4_btVector4_0=b.asm.Xe).apply(null,arguments)},Ug=b._emscripten_bind_btVector4_btVector4_4=
|
||||||
|
function(){return(Ug=b._emscripten_bind_btVector4_btVector4_4=b.asm.Ye).apply(null,arguments)},Vg=b._emscripten_bind_btVector4_w_0=function(){return(Vg=b._emscripten_bind_btVector4_w_0=b.asm.Ze).apply(null,arguments)},Wg=b._emscripten_bind_btVector4_setValue_4=function(){return(Wg=b._emscripten_bind_btVector4_setValue_4=b.asm._e).apply(null,arguments)},Xg=b._emscripten_bind_btVector4_length_0=function(){return(Xg=b._emscripten_bind_btVector4_length_0=b.asm.$e).apply(null,arguments)},Yg=b._emscripten_bind_btVector4_x_0=
|
||||||
|
function(){return(Yg=b._emscripten_bind_btVector4_x_0=b.asm.af).apply(null,arguments)},Zg=b._emscripten_bind_btVector4_y_0=function(){return(Zg=b._emscripten_bind_btVector4_y_0=b.asm.bf).apply(null,arguments)},$g=b._emscripten_bind_btVector4_z_0=function(){return($g=b._emscripten_bind_btVector4_z_0=b.asm.cf).apply(null,arguments)},ah=b._emscripten_bind_btVector4_setX_1=function(){return(ah=b._emscripten_bind_btVector4_setX_1=b.asm.df).apply(null,arguments)},bh=b._emscripten_bind_btVector4_setY_1=
|
||||||
|
function(){return(bh=b._emscripten_bind_btVector4_setY_1=b.asm.ef).apply(null,arguments)},ch=b._emscripten_bind_btVector4_setZ_1=function(){return(ch=b._emscripten_bind_btVector4_setZ_1=b.asm.ff).apply(null,arguments)},dh=b._emscripten_bind_btVector4_normalize_0=function(){return(dh=b._emscripten_bind_btVector4_normalize_0=b.asm.gf).apply(null,arguments)},eh=b._emscripten_bind_btVector4_rotate_2=function(){return(eh=b._emscripten_bind_btVector4_rotate_2=b.asm.hf).apply(null,arguments)},fh=b._emscripten_bind_btVector4_dot_1=
|
||||||
|
function(){return(fh=b._emscripten_bind_btVector4_dot_1=b.asm.jf).apply(null,arguments)},gh=b._emscripten_bind_btVector4_op_mul_1=function(){return(gh=b._emscripten_bind_btVector4_op_mul_1=b.asm.kf).apply(null,arguments)},hh=b._emscripten_bind_btVector4_op_add_1=function(){return(hh=b._emscripten_bind_btVector4_op_add_1=b.asm.lf).apply(null,arguments)},ih=b._emscripten_bind_btVector4_op_sub_1=function(){return(ih=b._emscripten_bind_btVector4_op_sub_1=b.asm.mf).apply(null,arguments)},jh=b._emscripten_bind_btVector4___destroy___0=
|
||||||
|
function(){return(jh=b._emscripten_bind_btVector4___destroy___0=b.asm.nf).apply(null,arguments)},kh=b._emscripten_bind_btQuaternion_btQuaternion_4=function(){return(kh=b._emscripten_bind_btQuaternion_btQuaternion_4=b.asm.of).apply(null,arguments)},lh=b._emscripten_bind_btQuaternion_setValue_4=function(){return(lh=b._emscripten_bind_btQuaternion_setValue_4=b.asm.pf).apply(null,arguments)},mh=b._emscripten_bind_btQuaternion_setEulerZYX_3=function(){return(mh=b._emscripten_bind_btQuaternion_setEulerZYX_3=
|
||||||
|
b.asm.qf).apply(null,arguments)},nh=b._emscripten_bind_btQuaternion_setRotation_2=function(){return(nh=b._emscripten_bind_btQuaternion_setRotation_2=b.asm.rf).apply(null,arguments)},oh=b._emscripten_bind_btQuaternion_normalize_0=function(){return(oh=b._emscripten_bind_btQuaternion_normalize_0=b.asm.sf).apply(null,arguments)},ph=b._emscripten_bind_btQuaternion_length2_0=function(){return(ph=b._emscripten_bind_btQuaternion_length2_0=b.asm.tf).apply(null,arguments)},qh=b._emscripten_bind_btQuaternion_length_0=
|
||||||
|
function(){return(qh=b._emscripten_bind_btQuaternion_length_0=b.asm.uf).apply(null,arguments)},rh=b._emscripten_bind_btQuaternion_dot_1=function(){return(rh=b._emscripten_bind_btQuaternion_dot_1=b.asm.vf).apply(null,arguments)},sh=b._emscripten_bind_btQuaternion_normalized_0=function(){return(sh=b._emscripten_bind_btQuaternion_normalized_0=b.asm.wf).apply(null,arguments)},th=b._emscripten_bind_btQuaternion_getAxis_0=function(){return(th=b._emscripten_bind_btQuaternion_getAxis_0=b.asm.xf).apply(null,
|
||||||
|
arguments)},uh=b._emscripten_bind_btQuaternion_inverse_0=function(){return(uh=b._emscripten_bind_btQuaternion_inverse_0=b.asm.yf).apply(null,arguments)},vh=b._emscripten_bind_btQuaternion_getAngle_0=function(){return(vh=b._emscripten_bind_btQuaternion_getAngle_0=b.asm.zf).apply(null,arguments)},wh=b._emscripten_bind_btQuaternion_getAngleShortestPath_0=function(){return(wh=b._emscripten_bind_btQuaternion_getAngleShortestPath_0=b.asm.Af).apply(null,arguments)},xh=b._emscripten_bind_btQuaternion_angle_1=
|
||||||
|
function(){return(xh=b._emscripten_bind_btQuaternion_angle_1=b.asm.Bf).apply(null,arguments)},yh=b._emscripten_bind_btQuaternion_angleShortestPath_1=function(){return(yh=b._emscripten_bind_btQuaternion_angleShortestPath_1=b.asm.Cf).apply(null,arguments)},zh=b._emscripten_bind_btQuaternion_op_add_1=function(){return(zh=b._emscripten_bind_btQuaternion_op_add_1=b.asm.Df).apply(null,arguments)},Ah=b._emscripten_bind_btQuaternion_op_sub_1=function(){return(Ah=b._emscripten_bind_btQuaternion_op_sub_1=b.asm.Ef).apply(null,
|
||||||
|
arguments)},Bh=b._emscripten_bind_btQuaternion_op_mul_1=function(){return(Bh=b._emscripten_bind_btQuaternion_op_mul_1=b.asm.Ff).apply(null,arguments)},Ch=b._emscripten_bind_btQuaternion_op_mulq_1=function(){return(Ch=b._emscripten_bind_btQuaternion_op_mulq_1=b.asm.Gf).apply(null,arguments)},Dh=b._emscripten_bind_btQuaternion_op_div_1=function(){return(Dh=b._emscripten_bind_btQuaternion_op_div_1=b.asm.Hf).apply(null,arguments)},Eh=b._emscripten_bind_btQuaternion_x_0=function(){return(Eh=b._emscripten_bind_btQuaternion_x_0=
|
||||||
|
b.asm.If).apply(null,arguments)},Fh=b._emscripten_bind_btQuaternion_y_0=function(){return(Fh=b._emscripten_bind_btQuaternion_y_0=b.asm.Jf).apply(null,arguments)},Gh=b._emscripten_bind_btQuaternion_z_0=function(){return(Gh=b._emscripten_bind_btQuaternion_z_0=b.asm.Kf).apply(null,arguments)},Hh=b._emscripten_bind_btQuaternion_w_0=function(){return(Hh=b._emscripten_bind_btQuaternion_w_0=b.asm.Lf).apply(null,arguments)},Ih=b._emscripten_bind_btQuaternion_setX_1=function(){return(Ih=b._emscripten_bind_btQuaternion_setX_1=
|
||||||
|
b.asm.Mf).apply(null,arguments)},Jh=b._emscripten_bind_btQuaternion_setY_1=function(){return(Jh=b._emscripten_bind_btQuaternion_setY_1=b.asm.Nf).apply(null,arguments)},Kh=b._emscripten_bind_btQuaternion_setZ_1=function(){return(Kh=b._emscripten_bind_btQuaternion_setZ_1=b.asm.Of).apply(null,arguments)},Lh=b._emscripten_bind_btQuaternion_setW_1=function(){return(Lh=b._emscripten_bind_btQuaternion_setW_1=b.asm.Pf).apply(null,arguments)},Mh=b._emscripten_bind_btQuaternion___destroy___0=function(){return(Mh=
|
||||||
|
b._emscripten_bind_btQuaternion___destroy___0=b.asm.Qf).apply(null,arguments)},Nh=b._emscripten_bind_btMatrix3x3_setEulerZYX_3=function(){return(Nh=b._emscripten_bind_btMatrix3x3_setEulerZYX_3=b.asm.Rf).apply(null,arguments)},Oh=b._emscripten_bind_btMatrix3x3_getRotation_1=function(){return(Oh=b._emscripten_bind_btMatrix3x3_getRotation_1=b.asm.Sf).apply(null,arguments)},Ph=b._emscripten_bind_btMatrix3x3_getRow_1=function(){return(Ph=b._emscripten_bind_btMatrix3x3_getRow_1=b.asm.Tf).apply(null,arguments)},
|
||||||
|
Qh=b._emscripten_bind_btMatrix3x3___destroy___0=function(){return(Qh=b._emscripten_bind_btMatrix3x3___destroy___0=b.asm.Uf).apply(null,arguments)},Rh=b._emscripten_bind_btTransform_btTransform_0=function(){return(Rh=b._emscripten_bind_btTransform_btTransform_0=b.asm.Vf).apply(null,arguments)},Sh=b._emscripten_bind_btTransform_btTransform_2=function(){return(Sh=b._emscripten_bind_btTransform_btTransform_2=b.asm.Wf).apply(null,arguments)},Th=b._emscripten_bind_btTransform_setIdentity_0=function(){return(Th=
|
||||||
|
b._emscripten_bind_btTransform_setIdentity_0=b.asm.Xf).apply(null,arguments)},Uh=b._emscripten_bind_btTransform_setOrigin_1=function(){return(Uh=b._emscripten_bind_btTransform_setOrigin_1=b.asm.Yf).apply(null,arguments)},Vh=b._emscripten_bind_btTransform_setRotation_1=function(){return(Vh=b._emscripten_bind_btTransform_setRotation_1=b.asm.Zf).apply(null,arguments)},Wh=b._emscripten_bind_btTransform_getOrigin_0=function(){return(Wh=b._emscripten_bind_btTransform_getOrigin_0=b.asm._f).apply(null,arguments)},
|
||||||
|
Xh=b._emscripten_bind_btTransform_getRotation_0=function(){return(Xh=b._emscripten_bind_btTransform_getRotation_0=b.asm.$f).apply(null,arguments)},Yh=b._emscripten_bind_btTransform_getBasis_0=function(){return(Yh=b._emscripten_bind_btTransform_getBasis_0=b.asm.ag).apply(null,arguments)},Zh=b._emscripten_bind_btTransform_setFromOpenGLMatrix_1=function(){return(Zh=b._emscripten_bind_btTransform_setFromOpenGLMatrix_1=b.asm.bg).apply(null,arguments)},$h=b._emscripten_bind_btTransform_inverse_0=function(){return($h=
|
||||||
|
b._emscripten_bind_btTransform_inverse_0=b.asm.cg).apply(null,arguments)},ai=b._emscripten_bind_btTransform_op_mul_1=function(){return(ai=b._emscripten_bind_btTransform_op_mul_1=b.asm.dg).apply(null,arguments)},bi=b._emscripten_bind_btTransform___destroy___0=function(){return(bi=b._emscripten_bind_btTransform___destroy___0=b.asm.eg).apply(null,arguments)},ci=b._emscripten_bind_MotionState_MotionState_0=function(){return(ci=b._emscripten_bind_MotionState_MotionState_0=b.asm.fg).apply(null,arguments)},
|
||||||
|
di=b._emscripten_bind_MotionState_getWorldTransform_1=function(){return(di=b._emscripten_bind_MotionState_getWorldTransform_1=b.asm.gg).apply(null,arguments)},ei=b._emscripten_bind_MotionState_setWorldTransform_1=function(){return(ei=b._emscripten_bind_MotionState_setWorldTransform_1=b.asm.hg).apply(null,arguments)},fi=b._emscripten_bind_MotionState___destroy___0=function(){return(fi=b._emscripten_bind_MotionState___destroy___0=b.asm.ig).apply(null,arguments)},gi=b._emscripten_bind_btDefaultMotionState_btDefaultMotionState_0=
|
||||||
|
function(){return(gi=b._emscripten_bind_btDefaultMotionState_btDefaultMotionState_0=b.asm.jg).apply(null,arguments)},hi=b._emscripten_bind_btDefaultMotionState_btDefaultMotionState_1=function(){return(hi=b._emscripten_bind_btDefaultMotionState_btDefaultMotionState_1=b.asm.kg).apply(null,arguments)},ii=b._emscripten_bind_btDefaultMotionState_btDefaultMotionState_2=function(){return(ii=b._emscripten_bind_btDefaultMotionState_btDefaultMotionState_2=b.asm.lg).apply(null,arguments)},ji=b._emscripten_bind_btDefaultMotionState_getWorldTransform_1=
|
||||||
|
function(){return(ji=b._emscripten_bind_btDefaultMotionState_getWorldTransform_1=b.asm.mg).apply(null,arguments)},ki=b._emscripten_bind_btDefaultMotionState_setWorldTransform_1=function(){return(ki=b._emscripten_bind_btDefaultMotionState_setWorldTransform_1=b.asm.ng).apply(null,arguments)},li=b._emscripten_bind_btDefaultMotionState_get_m_graphicsWorldTrans_0=function(){return(li=b._emscripten_bind_btDefaultMotionState_get_m_graphicsWorldTrans_0=b.asm.og).apply(null,arguments)},mi=b._emscripten_bind_btDefaultMotionState_set_m_graphicsWorldTrans_1=
|
||||||
|
function(){return(mi=b._emscripten_bind_btDefaultMotionState_set_m_graphicsWorldTrans_1=b.asm.pg).apply(null,arguments)},ni=b._emscripten_bind_btDefaultMotionState___destroy___0=function(){return(ni=b._emscripten_bind_btDefaultMotionState___destroy___0=b.asm.qg).apply(null,arguments)},oi=b._emscripten_bind_btCollisionObjectWrapper_getWorldTransform_0=function(){return(oi=b._emscripten_bind_btCollisionObjectWrapper_getWorldTransform_0=b.asm.rg).apply(null,arguments)},pi=b._emscripten_bind_btCollisionObjectWrapper_getCollisionObject_0=
|
||||||
|
function(){return(pi=b._emscripten_bind_btCollisionObjectWrapper_getCollisionObject_0=b.asm.sg).apply(null,arguments)},qi=b._emscripten_bind_btCollisionObjectWrapper_getCollisionShape_0=function(){return(qi=b._emscripten_bind_btCollisionObjectWrapper_getCollisionShape_0=b.asm.tg).apply(null,arguments)},ri=b._emscripten_bind_ClosestRayResultCallback_ClosestRayResultCallback_2=function(){return(ri=b._emscripten_bind_ClosestRayResultCallback_ClosestRayResultCallback_2=b.asm.ug).apply(null,arguments)},
|
||||||
|
si=b._emscripten_bind_ClosestRayResultCallback_hasHit_0=function(){return(si=b._emscripten_bind_ClosestRayResultCallback_hasHit_0=b.asm.vg).apply(null,arguments)},ti=b._emscripten_bind_ClosestRayResultCallback_get_m_rayFromWorld_0=function(){return(ti=b._emscripten_bind_ClosestRayResultCallback_get_m_rayFromWorld_0=b.asm.wg).apply(null,arguments)},ui=b._emscripten_bind_ClosestRayResultCallback_set_m_rayFromWorld_1=function(){return(ui=b._emscripten_bind_ClosestRayResultCallback_set_m_rayFromWorld_1=
|
||||||
|
b.asm.xg).apply(null,arguments)},vi=b._emscripten_bind_ClosestRayResultCallback_get_m_rayToWorld_0=function(){return(vi=b._emscripten_bind_ClosestRayResultCallback_get_m_rayToWorld_0=b.asm.yg).apply(null,arguments)},wi=b._emscripten_bind_ClosestRayResultCallback_set_m_rayToWorld_1=function(){return(wi=b._emscripten_bind_ClosestRayResultCallback_set_m_rayToWorld_1=b.asm.zg).apply(null,arguments)},xi=b._emscripten_bind_ClosestRayResultCallback_get_m_hitNormalWorld_0=function(){return(xi=b._emscripten_bind_ClosestRayResultCallback_get_m_hitNormalWorld_0=
|
||||||
|
b.asm.Ag).apply(null,arguments)},yi=b._emscripten_bind_ClosestRayResultCallback_set_m_hitNormalWorld_1=function(){return(yi=b._emscripten_bind_ClosestRayResultCallback_set_m_hitNormalWorld_1=b.asm.Bg).apply(null,arguments)},zi=b._emscripten_bind_ClosestRayResultCallback_get_m_hitPointWorld_0=function(){return(zi=b._emscripten_bind_ClosestRayResultCallback_get_m_hitPointWorld_0=b.asm.Cg).apply(null,arguments)},Ai=b._emscripten_bind_ClosestRayResultCallback_set_m_hitPointWorld_1=function(){return(Ai=
|
||||||
|
b._emscripten_bind_ClosestRayResultCallback_set_m_hitPointWorld_1=b.asm.Dg).apply(null,arguments)},Bi=b._emscripten_bind_ClosestRayResultCallback_get_m_collisionFilterGroup_0=function(){return(Bi=b._emscripten_bind_ClosestRayResultCallback_get_m_collisionFilterGroup_0=b.asm.Eg).apply(null,arguments)},Ci=b._emscripten_bind_ClosestRayResultCallback_set_m_collisionFilterGroup_1=function(){return(Ci=b._emscripten_bind_ClosestRayResultCallback_set_m_collisionFilterGroup_1=b.asm.Fg).apply(null,arguments)},
|
||||||
|
Di=b._emscripten_bind_ClosestRayResultCallback_get_m_collisionFilterMask_0=function(){return(Di=b._emscripten_bind_ClosestRayResultCallback_get_m_collisionFilterMask_0=b.asm.Gg).apply(null,arguments)},Ei=b._emscripten_bind_ClosestRayResultCallback_set_m_collisionFilterMask_1=function(){return(Ei=b._emscripten_bind_ClosestRayResultCallback_set_m_collisionFilterMask_1=b.asm.Hg).apply(null,arguments)},Fi=b._emscripten_bind_ClosestRayResultCallback_get_m_closestHitFraction_0=function(){return(Fi=b._emscripten_bind_ClosestRayResultCallback_get_m_closestHitFraction_0=
|
||||||
|
b.asm.Ig).apply(null,arguments)},Gi=b._emscripten_bind_ClosestRayResultCallback_set_m_closestHitFraction_1=function(){return(Gi=b._emscripten_bind_ClosestRayResultCallback_set_m_closestHitFraction_1=b.asm.Jg).apply(null,arguments)},Hi=b._emscripten_bind_ClosestRayResultCallback_get_m_collisionObject_0=function(){return(Hi=b._emscripten_bind_ClosestRayResultCallback_get_m_collisionObject_0=b.asm.Kg).apply(null,arguments)},Ii=b._emscripten_bind_ClosestRayResultCallback_set_m_collisionObject_1=function(){return(Ii=
|
||||||
|
b._emscripten_bind_ClosestRayResultCallback_set_m_collisionObject_1=b.asm.Lg).apply(null,arguments)},Ji=b._emscripten_bind_ClosestRayResultCallback_get_m_flags_0=function(){return(Ji=b._emscripten_bind_ClosestRayResultCallback_get_m_flags_0=b.asm.Mg).apply(null,arguments)},Ki=b._emscripten_bind_ClosestRayResultCallback_set_m_flags_1=function(){return(Ki=b._emscripten_bind_ClosestRayResultCallback_set_m_flags_1=b.asm.Ng).apply(null,arguments)},Li=b._emscripten_bind_ClosestRayResultCallback___destroy___0=
|
||||||
|
function(){return(Li=b._emscripten_bind_ClosestRayResultCallback___destroy___0=b.asm.Og).apply(null,arguments)},Mi=b._emscripten_bind_btConstCollisionObjectArray_size_0=function(){return(Mi=b._emscripten_bind_btConstCollisionObjectArray_size_0=b.asm.Pg).apply(null,arguments)},Ni=b._emscripten_bind_btConstCollisionObjectArray_at_1=function(){return(Ni=b._emscripten_bind_btConstCollisionObjectArray_at_1=b.asm.Qg).apply(null,arguments)},Oi=b._emscripten_bind_btConstCollisionObjectArray___destroy___0=
|
||||||
|
function(){return(Oi=b._emscripten_bind_btConstCollisionObjectArray___destroy___0=b.asm.Rg).apply(null,arguments)},Pi=b._emscripten_bind_btScalarArray_size_0=function(){return(Pi=b._emscripten_bind_btScalarArray_size_0=b.asm.Sg).apply(null,arguments)},Qi=b._emscripten_bind_btScalarArray_at_1=function(){return(Qi=b._emscripten_bind_btScalarArray_at_1=b.asm.Tg).apply(null,arguments)},Ri=b._emscripten_bind_btScalarArray___destroy___0=function(){return(Ri=b._emscripten_bind_btScalarArray___destroy___0=
|
||||||
|
b.asm.Ug).apply(null,arguments)},Si=b._emscripten_bind_AllHitsRayResultCallback_AllHitsRayResultCallback_2=function(){return(Si=b._emscripten_bind_AllHitsRayResultCallback_AllHitsRayResultCallback_2=b.asm.Vg).apply(null,arguments)},Ti=b._emscripten_bind_AllHitsRayResultCallback_hasHit_0=function(){return(Ti=b._emscripten_bind_AllHitsRayResultCallback_hasHit_0=b.asm.Wg).apply(null,arguments)},Ui=b._emscripten_bind_AllHitsRayResultCallback_get_m_collisionObjects_0=function(){return(Ui=b._emscripten_bind_AllHitsRayResultCallback_get_m_collisionObjects_0=
|
||||||
|
b.asm.Xg).apply(null,arguments)},Vi=b._emscripten_bind_AllHitsRayResultCallback_set_m_collisionObjects_1=function(){return(Vi=b._emscripten_bind_AllHitsRayResultCallback_set_m_collisionObjects_1=b.asm.Yg).apply(null,arguments)},Wi=b._emscripten_bind_AllHitsRayResultCallback_get_m_rayFromWorld_0=function(){return(Wi=b._emscripten_bind_AllHitsRayResultCallback_get_m_rayFromWorld_0=b.asm.Zg).apply(null,arguments)},Xi=b._emscripten_bind_AllHitsRayResultCallback_set_m_rayFromWorld_1=function(){return(Xi=
|
||||||
|
b._emscripten_bind_AllHitsRayResultCallback_set_m_rayFromWorld_1=b.asm._g).apply(null,arguments)},Yi=b._emscripten_bind_AllHitsRayResultCallback_get_m_rayToWorld_0=function(){return(Yi=b._emscripten_bind_AllHitsRayResultCallback_get_m_rayToWorld_0=b.asm.$g).apply(null,arguments)},Zi=b._emscripten_bind_AllHitsRayResultCallback_set_m_rayToWorld_1=function(){return(Zi=b._emscripten_bind_AllHitsRayResultCallback_set_m_rayToWorld_1=b.asm.ah).apply(null,arguments)},$i=b._emscripten_bind_AllHitsRayResultCallback_get_m_hitNormalWorld_0=
|
||||||
|
function(){return($i=b._emscripten_bind_AllHitsRayResultCallback_get_m_hitNormalWorld_0=b.asm.bh).apply(null,arguments)},aj=b._emscripten_bind_AllHitsRayResultCallback_set_m_hitNormalWorld_1=function(){return(aj=b._emscripten_bind_AllHitsRayResultCallback_set_m_hitNormalWorld_1=b.asm.ch).apply(null,arguments)},bj=b._emscripten_bind_AllHitsRayResultCallback_get_m_hitPointWorld_0=function(){return(bj=b._emscripten_bind_AllHitsRayResultCallback_get_m_hitPointWorld_0=b.asm.dh).apply(null,arguments)},
|
||||||
|
cj=b._emscripten_bind_AllHitsRayResultCallback_set_m_hitPointWorld_1=function(){return(cj=b._emscripten_bind_AllHitsRayResultCallback_set_m_hitPointWorld_1=b.asm.eh).apply(null,arguments)},dj=b._emscripten_bind_AllHitsRayResultCallback_get_m_hitFractions_0=function(){return(dj=b._emscripten_bind_AllHitsRayResultCallback_get_m_hitFractions_0=b.asm.fh).apply(null,arguments)},ej=b._emscripten_bind_AllHitsRayResultCallback_set_m_hitFractions_1=function(){return(ej=b._emscripten_bind_AllHitsRayResultCallback_set_m_hitFractions_1=
|
||||||
|
b.asm.gh).apply(null,arguments)},fj=b._emscripten_bind_AllHitsRayResultCallback_get_m_collisionFilterGroup_0=function(){return(fj=b._emscripten_bind_AllHitsRayResultCallback_get_m_collisionFilterGroup_0=b.asm.hh).apply(null,arguments)},gj=b._emscripten_bind_AllHitsRayResultCallback_set_m_collisionFilterGroup_1=function(){return(gj=b._emscripten_bind_AllHitsRayResultCallback_set_m_collisionFilterGroup_1=b.asm.ih).apply(null,arguments)},hj=b._emscripten_bind_AllHitsRayResultCallback_get_m_collisionFilterMask_0=
|
||||||
|
function(){return(hj=b._emscripten_bind_AllHitsRayResultCallback_get_m_collisionFilterMask_0=b.asm.jh).apply(null,arguments)},ij=b._emscripten_bind_AllHitsRayResultCallback_set_m_collisionFilterMask_1=function(){return(ij=b._emscripten_bind_AllHitsRayResultCallback_set_m_collisionFilterMask_1=b.asm.kh).apply(null,arguments)},jj=b._emscripten_bind_AllHitsRayResultCallback_get_m_closestHitFraction_0=function(){return(jj=b._emscripten_bind_AllHitsRayResultCallback_get_m_closestHitFraction_0=b.asm.lh).apply(null,
|
||||||
|
arguments)},kj=b._emscripten_bind_AllHitsRayResultCallback_set_m_closestHitFraction_1=function(){return(kj=b._emscripten_bind_AllHitsRayResultCallback_set_m_closestHitFraction_1=b.asm.mh).apply(null,arguments)},lj=b._emscripten_bind_AllHitsRayResultCallback_get_m_collisionObject_0=function(){return(lj=b._emscripten_bind_AllHitsRayResultCallback_get_m_collisionObject_0=b.asm.nh).apply(null,arguments)},mj=b._emscripten_bind_AllHitsRayResultCallback_set_m_collisionObject_1=function(){return(mj=b._emscripten_bind_AllHitsRayResultCallback_set_m_collisionObject_1=
|
||||||
|
b.asm.oh).apply(null,arguments)},nj=b._emscripten_bind_AllHitsRayResultCallback_get_m_flags_0=function(){return(nj=b._emscripten_bind_AllHitsRayResultCallback_get_m_flags_0=b.asm.ph).apply(null,arguments)},oj=b._emscripten_bind_AllHitsRayResultCallback_set_m_flags_1=function(){return(oj=b._emscripten_bind_AllHitsRayResultCallback_set_m_flags_1=b.asm.qh).apply(null,arguments)},pj=b._emscripten_bind_AllHitsRayResultCallback___destroy___0=function(){return(pj=b._emscripten_bind_AllHitsRayResultCallback___destroy___0=
|
||||||
|
b.asm.rh).apply(null,arguments)},qj=b._emscripten_bind_btManifoldPoint_getPositionWorldOnA_0=function(){return(qj=b._emscripten_bind_btManifoldPoint_getPositionWorldOnA_0=b.asm.sh).apply(null,arguments)},rj=b._emscripten_bind_btManifoldPoint_getPositionWorldOnB_0=function(){return(rj=b._emscripten_bind_btManifoldPoint_getPositionWorldOnB_0=b.asm.th).apply(null,arguments)},sj=b._emscripten_bind_btManifoldPoint_getAppliedImpulse_0=function(){return(sj=b._emscripten_bind_btManifoldPoint_getAppliedImpulse_0=
|
||||||
|
b.asm.uh).apply(null,arguments)},tj=b._emscripten_bind_btManifoldPoint_getDistance_0=function(){return(tj=b._emscripten_bind_btManifoldPoint_getDistance_0=b.asm.vh).apply(null,arguments)},uj=b._emscripten_bind_btManifoldPoint_get_m_localPointA_0=function(){return(uj=b._emscripten_bind_btManifoldPoint_get_m_localPointA_0=b.asm.wh).apply(null,arguments)},vj=b._emscripten_bind_btManifoldPoint_set_m_localPointA_1=function(){return(vj=b._emscripten_bind_btManifoldPoint_set_m_localPointA_1=b.asm.xh).apply(null,
|
||||||
|
arguments)},wj=b._emscripten_bind_btManifoldPoint_get_m_localPointB_0=function(){return(wj=b._emscripten_bind_btManifoldPoint_get_m_localPointB_0=b.asm.yh).apply(null,arguments)},xj=b._emscripten_bind_btManifoldPoint_set_m_localPointB_1=function(){return(xj=b._emscripten_bind_btManifoldPoint_set_m_localPointB_1=b.asm.zh).apply(null,arguments)},yj=b._emscripten_bind_btManifoldPoint_get_m_positionWorldOnB_0=function(){return(yj=b._emscripten_bind_btManifoldPoint_get_m_positionWorldOnB_0=b.asm.Ah).apply(null,
|
||||||
|
arguments)},zj=b._emscripten_bind_btManifoldPoint_set_m_positionWorldOnB_1=function(){return(zj=b._emscripten_bind_btManifoldPoint_set_m_positionWorldOnB_1=b.asm.Bh).apply(null,arguments)},Aj=b._emscripten_bind_btManifoldPoint_get_m_positionWorldOnA_0=function(){return(Aj=b._emscripten_bind_btManifoldPoint_get_m_positionWorldOnA_0=b.asm.Ch).apply(null,arguments)},Bj=b._emscripten_bind_btManifoldPoint_set_m_positionWorldOnA_1=function(){return(Bj=b._emscripten_bind_btManifoldPoint_set_m_positionWorldOnA_1=
|
||||||
|
b.asm.Dh).apply(null,arguments)},Cj=b._emscripten_bind_btManifoldPoint_get_m_normalWorldOnB_0=function(){return(Cj=b._emscripten_bind_btManifoldPoint_get_m_normalWorldOnB_0=b.asm.Eh).apply(null,arguments)},Dj=b._emscripten_bind_btManifoldPoint_set_m_normalWorldOnB_1=function(){return(Dj=b._emscripten_bind_btManifoldPoint_set_m_normalWorldOnB_1=b.asm.Fh).apply(null,arguments)},Ej=b._emscripten_bind_btManifoldPoint_get_m_userPersistentData_0=function(){return(Ej=b._emscripten_bind_btManifoldPoint_get_m_userPersistentData_0=
|
||||||
|
b.asm.Gh).apply(null,arguments)},Fj=b._emscripten_bind_btManifoldPoint_set_m_userPersistentData_1=function(){return(Fj=b._emscripten_bind_btManifoldPoint_set_m_userPersistentData_1=b.asm.Hh).apply(null,arguments)},Gj=b._emscripten_bind_btManifoldPoint___destroy___0=function(){return(Gj=b._emscripten_bind_btManifoldPoint___destroy___0=b.asm.Ih).apply(null,arguments)},Hj=b._emscripten_bind_ConcreteContactResultCallback_ConcreteContactResultCallback_0=function(){return(Hj=b._emscripten_bind_ConcreteContactResultCallback_ConcreteContactResultCallback_0=
|
||||||
|
b.asm.Jh).apply(null,arguments)},Ij=b._emscripten_bind_ConcreteContactResultCallback_addSingleResult_7=function(){return(Ij=b._emscripten_bind_ConcreteContactResultCallback_addSingleResult_7=b.asm.Kh).apply(null,arguments)},Jj=b._emscripten_bind_ConcreteContactResultCallback___destroy___0=function(){return(Jj=b._emscripten_bind_ConcreteContactResultCallback___destroy___0=b.asm.Lh).apply(null,arguments)},Kj=b._emscripten_bind_LocalShapeInfo_get_m_shapePart_0=function(){return(Kj=b._emscripten_bind_LocalShapeInfo_get_m_shapePart_0=
|
||||||
|
b.asm.Mh).apply(null,arguments)},Lj=b._emscripten_bind_LocalShapeInfo_set_m_shapePart_1=function(){return(Lj=b._emscripten_bind_LocalShapeInfo_set_m_shapePart_1=b.asm.Nh).apply(null,arguments)},Mj=b._emscripten_bind_LocalShapeInfo_get_m_triangleIndex_0=function(){return(Mj=b._emscripten_bind_LocalShapeInfo_get_m_triangleIndex_0=b.asm.Oh).apply(null,arguments)},Nj=b._emscripten_bind_LocalShapeInfo_set_m_triangleIndex_1=function(){return(Nj=b._emscripten_bind_LocalShapeInfo_set_m_triangleIndex_1=b.asm.Ph).apply(null,
|
||||||
|
arguments)},Oj=b._emscripten_bind_LocalShapeInfo___destroy___0=function(){return(Oj=b._emscripten_bind_LocalShapeInfo___destroy___0=b.asm.Qh).apply(null,arguments)},Pj=b._emscripten_bind_LocalConvexResult_LocalConvexResult_5=function(){return(Pj=b._emscripten_bind_LocalConvexResult_LocalConvexResult_5=b.asm.Rh).apply(null,arguments)},Qj=b._emscripten_bind_LocalConvexResult_get_m_hitCollisionObject_0=function(){return(Qj=b._emscripten_bind_LocalConvexResult_get_m_hitCollisionObject_0=b.asm.Sh).apply(null,
|
||||||
|
arguments)},Rj=b._emscripten_bind_LocalConvexResult_set_m_hitCollisionObject_1=function(){return(Rj=b._emscripten_bind_LocalConvexResult_set_m_hitCollisionObject_1=b.asm.Th).apply(null,arguments)},Sj=b._emscripten_bind_LocalConvexResult_get_m_localShapeInfo_0=function(){return(Sj=b._emscripten_bind_LocalConvexResult_get_m_localShapeInfo_0=b.asm.Uh).apply(null,arguments)},Tj=b._emscripten_bind_LocalConvexResult_set_m_localShapeInfo_1=function(){return(Tj=b._emscripten_bind_LocalConvexResult_set_m_localShapeInfo_1=
|
||||||
|
b.asm.Vh).apply(null,arguments)},Uj=b._emscripten_bind_LocalConvexResult_get_m_hitNormalLocal_0=function(){return(Uj=b._emscripten_bind_LocalConvexResult_get_m_hitNormalLocal_0=b.asm.Wh).apply(null,arguments)},Vj=b._emscripten_bind_LocalConvexResult_set_m_hitNormalLocal_1=function(){return(Vj=b._emscripten_bind_LocalConvexResult_set_m_hitNormalLocal_1=b.asm.Xh).apply(null,arguments)},Wj=b._emscripten_bind_LocalConvexResult_get_m_hitPointLocal_0=function(){return(Wj=b._emscripten_bind_LocalConvexResult_get_m_hitPointLocal_0=
|
||||||
|
b.asm.Yh).apply(null,arguments)},Xj=b._emscripten_bind_LocalConvexResult_set_m_hitPointLocal_1=function(){return(Xj=b._emscripten_bind_LocalConvexResult_set_m_hitPointLocal_1=b.asm.Zh).apply(null,arguments)},Yj=b._emscripten_bind_LocalConvexResult_get_m_hitFraction_0=function(){return(Yj=b._emscripten_bind_LocalConvexResult_get_m_hitFraction_0=b.asm._h).apply(null,arguments)},Zj=b._emscripten_bind_LocalConvexResult_set_m_hitFraction_1=function(){return(Zj=b._emscripten_bind_LocalConvexResult_set_m_hitFraction_1=
|
||||||
|
b.asm.$h).apply(null,arguments)},ak=b._emscripten_bind_LocalConvexResult___destroy___0=function(){return(ak=b._emscripten_bind_LocalConvexResult___destroy___0=b.asm.ai).apply(null,arguments)},bk=b._emscripten_bind_ClosestConvexResultCallback_ClosestConvexResultCallback_2=function(){return(bk=b._emscripten_bind_ClosestConvexResultCallback_ClosestConvexResultCallback_2=b.asm.bi).apply(null,arguments)},ck=b._emscripten_bind_ClosestConvexResultCallback_hasHit_0=function(){return(ck=b._emscripten_bind_ClosestConvexResultCallback_hasHit_0=
|
||||||
|
b.asm.ci).apply(null,arguments)},dk=b._emscripten_bind_ClosestConvexResultCallback_get_m_hitCollisionObject_0=function(){return(dk=b._emscripten_bind_ClosestConvexResultCallback_get_m_hitCollisionObject_0=b.asm.di).apply(null,arguments)},ek=b._emscripten_bind_ClosestConvexResultCallback_set_m_hitCollisionObject_1=function(){return(ek=b._emscripten_bind_ClosestConvexResultCallback_set_m_hitCollisionObject_1=b.asm.ei).apply(null,arguments)},fk=b._emscripten_bind_ClosestConvexResultCallback_get_m_convexFromWorld_0=
|
||||||
|
function(){return(fk=b._emscripten_bind_ClosestConvexResultCallback_get_m_convexFromWorld_0=b.asm.fi).apply(null,arguments)},gk=b._emscripten_bind_ClosestConvexResultCallback_set_m_convexFromWorld_1=function(){return(gk=b._emscripten_bind_ClosestConvexResultCallback_set_m_convexFromWorld_1=b.asm.gi).apply(null,arguments)},hk=b._emscripten_bind_ClosestConvexResultCallback_get_m_convexToWorld_0=function(){return(hk=b._emscripten_bind_ClosestConvexResultCallback_get_m_convexToWorld_0=b.asm.hi).apply(null,
|
||||||
|
arguments)},ik=b._emscripten_bind_ClosestConvexResultCallback_set_m_convexToWorld_1=function(){return(ik=b._emscripten_bind_ClosestConvexResultCallback_set_m_convexToWorld_1=b.asm.ii).apply(null,arguments)},jk=b._emscripten_bind_ClosestConvexResultCallback_get_m_hitNormalWorld_0=function(){return(jk=b._emscripten_bind_ClosestConvexResultCallback_get_m_hitNormalWorld_0=b.asm.ji).apply(null,arguments)},kk=b._emscripten_bind_ClosestConvexResultCallback_set_m_hitNormalWorld_1=function(){return(kk=b._emscripten_bind_ClosestConvexResultCallback_set_m_hitNormalWorld_1=
|
||||||
|
b.asm.ki).apply(null,arguments)},lk=b._emscripten_bind_ClosestConvexResultCallback_get_m_hitPointWorld_0=function(){return(lk=b._emscripten_bind_ClosestConvexResultCallback_get_m_hitPointWorld_0=b.asm.li).apply(null,arguments)},mk=b._emscripten_bind_ClosestConvexResultCallback_set_m_hitPointWorld_1=function(){return(mk=b._emscripten_bind_ClosestConvexResultCallback_set_m_hitPointWorld_1=b.asm.mi).apply(null,arguments)},nk=b._emscripten_bind_ClosestConvexResultCallback_get_m_collisionFilterGroup_0=
|
||||||
|
function(){return(nk=b._emscripten_bind_ClosestConvexResultCallback_get_m_collisionFilterGroup_0=b.asm.ni).apply(null,arguments)},ok=b._emscripten_bind_ClosestConvexResultCallback_set_m_collisionFilterGroup_1=function(){return(ok=b._emscripten_bind_ClosestConvexResultCallback_set_m_collisionFilterGroup_1=b.asm.oi).apply(null,arguments)},pk=b._emscripten_bind_ClosestConvexResultCallback_get_m_collisionFilterMask_0=function(){return(pk=b._emscripten_bind_ClosestConvexResultCallback_get_m_collisionFilterMask_0=
|
||||||
|
b.asm.pi).apply(null,arguments)},qk=b._emscripten_bind_ClosestConvexResultCallback_set_m_collisionFilterMask_1=function(){return(qk=b._emscripten_bind_ClosestConvexResultCallback_set_m_collisionFilterMask_1=b.asm.qi).apply(null,arguments)},rk=b._emscripten_bind_ClosestConvexResultCallback_get_m_closestHitFraction_0=function(){return(rk=b._emscripten_bind_ClosestConvexResultCallback_get_m_closestHitFraction_0=b.asm.ri).apply(null,arguments)},sk=b._emscripten_bind_ClosestConvexResultCallback_set_m_closestHitFraction_1=
|
||||||
|
function(){return(sk=b._emscripten_bind_ClosestConvexResultCallback_set_m_closestHitFraction_1=b.asm.si).apply(null,arguments)},tk=b._emscripten_bind_ClosestConvexResultCallback___destroy___0=function(){return(tk=b._emscripten_bind_ClosestConvexResultCallback___destroy___0=b.asm.ti).apply(null,arguments)},uk=b._emscripten_bind_btConvexTriangleMeshShape_btConvexTriangleMeshShape_1=function(){return(uk=b._emscripten_bind_btConvexTriangleMeshShape_btConvexTriangleMeshShape_1=b.asm.ui).apply(null,arguments)},
|
||||||
|
vk=b._emscripten_bind_btConvexTriangleMeshShape_btConvexTriangleMeshShape_2=function(){return(vk=b._emscripten_bind_btConvexTriangleMeshShape_btConvexTriangleMeshShape_2=b.asm.vi).apply(null,arguments)},wk=b._emscripten_bind_btConvexTriangleMeshShape_setLocalScaling_1=function(){return(wk=b._emscripten_bind_btConvexTriangleMeshShape_setLocalScaling_1=b.asm.wi).apply(null,arguments)},xk=b._emscripten_bind_btConvexTriangleMeshShape_getLocalScaling_0=function(){return(xk=b._emscripten_bind_btConvexTriangleMeshShape_getLocalScaling_0=
|
||||||
|
b.asm.xi).apply(null,arguments)},yk=b._emscripten_bind_btConvexTriangleMeshShape_calculateLocalInertia_2=function(){return(yk=b._emscripten_bind_btConvexTriangleMeshShape_calculateLocalInertia_2=b.asm.yi).apply(null,arguments)},zk=b._emscripten_bind_btConvexTriangleMeshShape_setMargin_1=function(){return(zk=b._emscripten_bind_btConvexTriangleMeshShape_setMargin_1=b.asm.zi).apply(null,arguments)},Ak=b._emscripten_bind_btConvexTriangleMeshShape_getMargin_0=function(){return(Ak=b._emscripten_bind_btConvexTriangleMeshShape_getMargin_0=
|
||||||
|
b.asm.Ai).apply(null,arguments)},Bk=b._emscripten_bind_btConvexTriangleMeshShape___destroy___0=function(){return(Bk=b._emscripten_bind_btConvexTriangleMeshShape___destroy___0=b.asm.Bi).apply(null,arguments)},Ck=b._emscripten_bind_btBoxShape_btBoxShape_1=function(){return(Ck=b._emscripten_bind_btBoxShape_btBoxShape_1=b.asm.Ci).apply(null,arguments)},Dk=b._emscripten_bind_btBoxShape_setMargin_1=function(){return(Dk=b._emscripten_bind_btBoxShape_setMargin_1=b.asm.Di).apply(null,arguments)},Ek=b._emscripten_bind_btBoxShape_getMargin_0=
|
||||||
|
function(){return(Ek=b._emscripten_bind_btBoxShape_getMargin_0=b.asm.Ei).apply(null,arguments)},Fk=b._emscripten_bind_btBoxShape_setLocalScaling_1=function(){return(Fk=b._emscripten_bind_btBoxShape_setLocalScaling_1=b.asm.Fi).apply(null,arguments)},Gk=b._emscripten_bind_btBoxShape_getLocalScaling_0=function(){return(Gk=b._emscripten_bind_btBoxShape_getLocalScaling_0=b.asm.Gi).apply(null,arguments)},Hk=b._emscripten_bind_btBoxShape_calculateLocalInertia_2=function(){return(Hk=b._emscripten_bind_btBoxShape_calculateLocalInertia_2=
|
||||||
|
b.asm.Hi).apply(null,arguments)},Ik=b._emscripten_bind_btBoxShape___destroy___0=function(){return(Ik=b._emscripten_bind_btBoxShape___destroy___0=b.asm.Ii).apply(null,arguments)},Jk=b._emscripten_bind_btCapsuleShapeX_btCapsuleShapeX_2=function(){return(Jk=b._emscripten_bind_btCapsuleShapeX_btCapsuleShapeX_2=b.asm.Ji).apply(null,arguments)},Kk=b._emscripten_bind_btCapsuleShapeX_setMargin_1=function(){return(Kk=b._emscripten_bind_btCapsuleShapeX_setMargin_1=b.asm.Ki).apply(null,arguments)},Lk=b._emscripten_bind_btCapsuleShapeX_getMargin_0=
|
||||||
|
function(){return(Lk=b._emscripten_bind_btCapsuleShapeX_getMargin_0=b.asm.Li).apply(null,arguments)},Mk=b._emscripten_bind_btCapsuleShapeX_getUpAxis_0=function(){return(Mk=b._emscripten_bind_btCapsuleShapeX_getUpAxis_0=b.asm.Mi).apply(null,arguments)},Nk=b._emscripten_bind_btCapsuleShapeX_getRadius_0=function(){return(Nk=b._emscripten_bind_btCapsuleShapeX_getRadius_0=b.asm.Ni).apply(null,arguments)},Ok=b._emscripten_bind_btCapsuleShapeX_getHalfHeight_0=function(){return(Ok=b._emscripten_bind_btCapsuleShapeX_getHalfHeight_0=
|
||||||
|
b.asm.Oi).apply(null,arguments)},Pk=b._emscripten_bind_btCapsuleShapeX_setLocalScaling_1=function(){return(Pk=b._emscripten_bind_btCapsuleShapeX_setLocalScaling_1=b.asm.Pi).apply(null,arguments)},Qk=b._emscripten_bind_btCapsuleShapeX_getLocalScaling_0=function(){return(Qk=b._emscripten_bind_btCapsuleShapeX_getLocalScaling_0=b.asm.Qi).apply(null,arguments)},Rk=b._emscripten_bind_btCapsuleShapeX_calculateLocalInertia_2=function(){return(Rk=b._emscripten_bind_btCapsuleShapeX_calculateLocalInertia_2=
|
||||||
|
b.asm.Ri).apply(null,arguments)},Sk=b._emscripten_bind_btCapsuleShapeX___destroy___0=function(){return(Sk=b._emscripten_bind_btCapsuleShapeX___destroy___0=b.asm.Si).apply(null,arguments)},Tk=b._emscripten_bind_btCapsuleShapeZ_btCapsuleShapeZ_2=function(){return(Tk=b._emscripten_bind_btCapsuleShapeZ_btCapsuleShapeZ_2=b.asm.Ti).apply(null,arguments)},Uk=b._emscripten_bind_btCapsuleShapeZ_setMargin_1=function(){return(Uk=b._emscripten_bind_btCapsuleShapeZ_setMargin_1=b.asm.Ui).apply(null,arguments)},
|
||||||
|
Vk=b._emscripten_bind_btCapsuleShapeZ_getMargin_0=function(){return(Vk=b._emscripten_bind_btCapsuleShapeZ_getMargin_0=b.asm.Vi).apply(null,arguments)},Wk=b._emscripten_bind_btCapsuleShapeZ_getUpAxis_0=function(){return(Wk=b._emscripten_bind_btCapsuleShapeZ_getUpAxis_0=b.asm.Wi).apply(null,arguments)},Xk=b._emscripten_bind_btCapsuleShapeZ_getRadius_0=function(){return(Xk=b._emscripten_bind_btCapsuleShapeZ_getRadius_0=b.asm.Xi).apply(null,arguments)},Yk=b._emscripten_bind_btCapsuleShapeZ_getHalfHeight_0=
|
||||||
|
function(){return(Yk=b._emscripten_bind_btCapsuleShapeZ_getHalfHeight_0=b.asm.Yi).apply(null,arguments)},Zk=b._emscripten_bind_btCapsuleShapeZ_setLocalScaling_1=function(){return(Zk=b._emscripten_bind_btCapsuleShapeZ_setLocalScaling_1=b.asm.Zi).apply(null,arguments)},$k=b._emscripten_bind_btCapsuleShapeZ_getLocalScaling_0=function(){return($k=b._emscripten_bind_btCapsuleShapeZ_getLocalScaling_0=b.asm._i).apply(null,arguments)},al=b._emscripten_bind_btCapsuleShapeZ_calculateLocalInertia_2=function(){return(al=
|
||||||
|
b._emscripten_bind_btCapsuleShapeZ_calculateLocalInertia_2=b.asm.$i).apply(null,arguments)},bl=b._emscripten_bind_btCapsuleShapeZ___destroy___0=function(){return(bl=b._emscripten_bind_btCapsuleShapeZ___destroy___0=b.asm.aj).apply(null,arguments)},cl=b._emscripten_bind_btCylinderShapeX_btCylinderShapeX_1=function(){return(cl=b._emscripten_bind_btCylinderShapeX_btCylinderShapeX_1=b.asm.bj).apply(null,arguments)},dl=b._emscripten_bind_btCylinderShapeX_setMargin_1=function(){return(dl=b._emscripten_bind_btCylinderShapeX_setMargin_1=
|
||||||
|
b.asm.cj).apply(null,arguments)},el=b._emscripten_bind_btCylinderShapeX_getMargin_0=function(){return(el=b._emscripten_bind_btCylinderShapeX_getMargin_0=b.asm.dj).apply(null,arguments)},fl=b._emscripten_bind_btCylinderShapeX_setLocalScaling_1=function(){return(fl=b._emscripten_bind_btCylinderShapeX_setLocalScaling_1=b.asm.ej).apply(null,arguments)},gl=b._emscripten_bind_btCylinderShapeX_getLocalScaling_0=function(){return(gl=b._emscripten_bind_btCylinderShapeX_getLocalScaling_0=b.asm.fj).apply(null,
|
||||||
|
arguments)},hl=b._emscripten_bind_btCylinderShapeX_calculateLocalInertia_2=function(){return(hl=b._emscripten_bind_btCylinderShapeX_calculateLocalInertia_2=b.asm.gj).apply(null,arguments)},il=b._emscripten_bind_btCylinderShapeX___destroy___0=function(){return(il=b._emscripten_bind_btCylinderShapeX___destroy___0=b.asm.hj).apply(null,arguments)},jl=b._emscripten_bind_btCylinderShapeZ_btCylinderShapeZ_1=function(){return(jl=b._emscripten_bind_btCylinderShapeZ_btCylinderShapeZ_1=b.asm.ij).apply(null,
|
||||||
|
arguments)},kl=b._emscripten_bind_btCylinderShapeZ_setMargin_1=function(){return(kl=b._emscripten_bind_btCylinderShapeZ_setMargin_1=b.asm.jj).apply(null,arguments)},ll=b._emscripten_bind_btCylinderShapeZ_getMargin_0=function(){return(ll=b._emscripten_bind_btCylinderShapeZ_getMargin_0=b.asm.kj).apply(null,arguments)},ml=b._emscripten_bind_btCylinderShapeZ_setLocalScaling_1=function(){return(ml=b._emscripten_bind_btCylinderShapeZ_setLocalScaling_1=b.asm.lj).apply(null,arguments)},nl=b._emscripten_bind_btCylinderShapeZ_getLocalScaling_0=
|
||||||
|
function(){return(nl=b._emscripten_bind_btCylinderShapeZ_getLocalScaling_0=b.asm.mj).apply(null,arguments)},ol=b._emscripten_bind_btCylinderShapeZ_calculateLocalInertia_2=function(){return(ol=b._emscripten_bind_btCylinderShapeZ_calculateLocalInertia_2=b.asm.nj).apply(null,arguments)},pl=b._emscripten_bind_btCylinderShapeZ___destroy___0=function(){return(pl=b._emscripten_bind_btCylinderShapeZ___destroy___0=b.asm.oj).apply(null,arguments)},ql=b._emscripten_bind_btSphereShape_btSphereShape_1=function(){return(ql=
|
||||||
|
b._emscripten_bind_btSphereShape_btSphereShape_1=b.asm.pj).apply(null,arguments)},rl=b._emscripten_bind_btSphereShape_setMargin_1=function(){return(rl=b._emscripten_bind_btSphereShape_setMargin_1=b.asm.qj).apply(null,arguments)},sl=b._emscripten_bind_btSphereShape_getMargin_0=function(){return(sl=b._emscripten_bind_btSphereShape_getMargin_0=b.asm.rj).apply(null,arguments)},tl=b._emscripten_bind_btSphereShape_setLocalScaling_1=function(){return(tl=b._emscripten_bind_btSphereShape_setLocalScaling_1=
|
||||||
|
b.asm.sj).apply(null,arguments)},ul=b._emscripten_bind_btSphereShape_getLocalScaling_0=function(){return(ul=b._emscripten_bind_btSphereShape_getLocalScaling_0=b.asm.tj).apply(null,arguments)},vl=b._emscripten_bind_btSphereShape_calculateLocalInertia_2=function(){return(vl=b._emscripten_bind_btSphereShape_calculateLocalInertia_2=b.asm.uj).apply(null,arguments)},wl=b._emscripten_bind_btSphereShape___destroy___0=function(){return(wl=b._emscripten_bind_btSphereShape___destroy___0=b.asm.vj).apply(null,
|
||||||
|
arguments)},xl=b._emscripten_bind_btMultiSphereShape_btMultiSphereShape_3=function(){return(xl=b._emscripten_bind_btMultiSphereShape_btMultiSphereShape_3=b.asm.wj).apply(null,arguments)},yl=b._emscripten_bind_btMultiSphereShape_setLocalScaling_1=function(){return(yl=b._emscripten_bind_btMultiSphereShape_setLocalScaling_1=b.asm.xj).apply(null,arguments)},zl=b._emscripten_bind_btMultiSphereShape_getLocalScaling_0=function(){return(zl=b._emscripten_bind_btMultiSphereShape_getLocalScaling_0=b.asm.yj).apply(null,
|
||||||
|
arguments)},Al=b._emscripten_bind_btMultiSphereShape_calculateLocalInertia_2=function(){return(Al=b._emscripten_bind_btMultiSphereShape_calculateLocalInertia_2=b.asm.zj).apply(null,arguments)},Bl=b._emscripten_bind_btMultiSphereShape___destroy___0=function(){return(Bl=b._emscripten_bind_btMultiSphereShape___destroy___0=b.asm.Aj).apply(null,arguments)},Cl=b._emscripten_bind_btConeShapeX_btConeShapeX_2=function(){return(Cl=b._emscripten_bind_btConeShapeX_btConeShapeX_2=b.asm.Bj).apply(null,arguments)},
|
||||||
|
Dl=b._emscripten_bind_btConeShapeX_setLocalScaling_1=function(){return(Dl=b._emscripten_bind_btConeShapeX_setLocalScaling_1=b.asm.Cj).apply(null,arguments)},El=b._emscripten_bind_btConeShapeX_getLocalScaling_0=function(){return(El=b._emscripten_bind_btConeShapeX_getLocalScaling_0=b.asm.Dj).apply(null,arguments)},Fl=b._emscripten_bind_btConeShapeX_calculateLocalInertia_2=function(){return(Fl=b._emscripten_bind_btConeShapeX_calculateLocalInertia_2=b.asm.Ej).apply(null,arguments)},Gl=b._emscripten_bind_btConeShapeX___destroy___0=
|
||||||
|
function(){return(Gl=b._emscripten_bind_btConeShapeX___destroy___0=b.asm.Fj).apply(null,arguments)},Hl=b._emscripten_bind_btConeShapeZ_btConeShapeZ_2=function(){return(Hl=b._emscripten_bind_btConeShapeZ_btConeShapeZ_2=b.asm.Gj).apply(null,arguments)},Il=b._emscripten_bind_btConeShapeZ_setLocalScaling_1=function(){return(Il=b._emscripten_bind_btConeShapeZ_setLocalScaling_1=b.asm.Hj).apply(null,arguments)},Jl=b._emscripten_bind_btConeShapeZ_getLocalScaling_0=function(){return(Jl=b._emscripten_bind_btConeShapeZ_getLocalScaling_0=
|
||||||
|
b.asm.Ij).apply(null,arguments)},Kl=b._emscripten_bind_btConeShapeZ_calculateLocalInertia_2=function(){return(Kl=b._emscripten_bind_btConeShapeZ_calculateLocalInertia_2=b.asm.Jj).apply(null,arguments)},Ll=b._emscripten_bind_btConeShapeZ___destroy___0=function(){return(Ll=b._emscripten_bind_btConeShapeZ___destroy___0=b.asm.Kj).apply(null,arguments)},Ml=b._emscripten_bind_btIntArray_size_0=function(){return(Ml=b._emscripten_bind_btIntArray_size_0=b.asm.Lj).apply(null,arguments)},Nl=b._emscripten_bind_btIntArray_at_1=
|
||||||
|
function(){return(Nl=b._emscripten_bind_btIntArray_at_1=b.asm.Mj).apply(null,arguments)},Ol=b._emscripten_bind_btIntArray___destroy___0=function(){return(Ol=b._emscripten_bind_btIntArray___destroy___0=b.asm.Nj).apply(null,arguments)},Pl=b._emscripten_bind_btFace_get_m_indices_0=function(){return(Pl=b._emscripten_bind_btFace_get_m_indices_0=b.asm.Oj).apply(null,arguments)},Ql=b._emscripten_bind_btFace_set_m_indices_1=function(){return(Ql=b._emscripten_bind_btFace_set_m_indices_1=b.asm.Pj).apply(null,
|
||||||
|
arguments)},Rl=b._emscripten_bind_btFace_get_m_plane_1=function(){return(Rl=b._emscripten_bind_btFace_get_m_plane_1=b.asm.Qj).apply(null,arguments)},Sl=b._emscripten_bind_btFace_set_m_plane_2=function(){return(Sl=b._emscripten_bind_btFace_set_m_plane_2=b.asm.Rj).apply(null,arguments)},Tl=b._emscripten_bind_btFace___destroy___0=function(){return(Tl=b._emscripten_bind_btFace___destroy___0=b.asm.Sj).apply(null,arguments)},Ul=b._emscripten_bind_btVector3Array_size_0=function(){return(Ul=b._emscripten_bind_btVector3Array_size_0=
|
||||||
|
b.asm.Tj).apply(null,arguments)},Vl=b._emscripten_bind_btVector3Array_at_1=function(){return(Vl=b._emscripten_bind_btVector3Array_at_1=b.asm.Uj).apply(null,arguments)},Wl=b._emscripten_bind_btVector3Array___destroy___0=function(){return(Wl=b._emscripten_bind_btVector3Array___destroy___0=b.asm.Vj).apply(null,arguments)},Xl=b._emscripten_bind_btFaceArray_size_0=function(){return(Xl=b._emscripten_bind_btFaceArray_size_0=b.asm.Wj).apply(null,arguments)},Yl=b._emscripten_bind_btFaceArray_at_1=function(){return(Yl=
|
||||||
|
b._emscripten_bind_btFaceArray_at_1=b.asm.Xj).apply(null,arguments)},Zl=b._emscripten_bind_btFaceArray___destroy___0=function(){return(Zl=b._emscripten_bind_btFaceArray___destroy___0=b.asm.Yj).apply(null,arguments)},$l=b._emscripten_bind_btConvexPolyhedron_get_m_vertices_0=function(){return($l=b._emscripten_bind_btConvexPolyhedron_get_m_vertices_0=b.asm.Zj).apply(null,arguments)},am=b._emscripten_bind_btConvexPolyhedron_set_m_vertices_1=function(){return(am=b._emscripten_bind_btConvexPolyhedron_set_m_vertices_1=
|
||||||
|
b.asm._j).apply(null,arguments)},bm=b._emscripten_bind_btConvexPolyhedron_get_m_faces_0=function(){return(bm=b._emscripten_bind_btConvexPolyhedron_get_m_faces_0=b.asm.$j).apply(null,arguments)},cm=b._emscripten_bind_btConvexPolyhedron_set_m_faces_1=function(){return(cm=b._emscripten_bind_btConvexPolyhedron_set_m_faces_1=b.asm.ak).apply(null,arguments)},dm=b._emscripten_bind_btConvexPolyhedron___destroy___0=function(){return(dm=b._emscripten_bind_btConvexPolyhedron___destroy___0=b.asm.bk).apply(null,
|
||||||
|
arguments)},em=b._emscripten_bind_btConvexHullShape_btConvexHullShape_0=function(){return(em=b._emscripten_bind_btConvexHullShape_btConvexHullShape_0=b.asm.ck).apply(null,arguments)},fm=b._emscripten_bind_btConvexHullShape_btConvexHullShape_1=function(){return(fm=b._emscripten_bind_btConvexHullShape_btConvexHullShape_1=b.asm.dk).apply(null,arguments)},gm=b._emscripten_bind_btConvexHullShape_btConvexHullShape_2=function(){return(gm=b._emscripten_bind_btConvexHullShape_btConvexHullShape_2=b.asm.ek).apply(null,
|
||||||
|
arguments)},hm=b._emscripten_bind_btConvexHullShape_addPoint_1=function(){return(hm=b._emscripten_bind_btConvexHullShape_addPoint_1=b.asm.fk).apply(null,arguments)},im=b._emscripten_bind_btConvexHullShape_addPoint_2=function(){return(im=b._emscripten_bind_btConvexHullShape_addPoint_2=b.asm.gk).apply(null,arguments)},jm=b._emscripten_bind_btConvexHullShape_setMargin_1=function(){return(jm=b._emscripten_bind_btConvexHullShape_setMargin_1=b.asm.hk).apply(null,arguments)},km=b._emscripten_bind_btConvexHullShape_getMargin_0=
|
||||||
|
function(){return(km=b._emscripten_bind_btConvexHullShape_getMargin_0=b.asm.ik).apply(null,arguments)},lm=b._emscripten_bind_btConvexHullShape_getNumVertices_0=function(){return(lm=b._emscripten_bind_btConvexHullShape_getNumVertices_0=b.asm.jk).apply(null,arguments)},mm=b._emscripten_bind_btConvexHullShape_initializePolyhedralFeatures_1=function(){return(mm=b._emscripten_bind_btConvexHullShape_initializePolyhedralFeatures_1=b.asm.kk).apply(null,arguments)},nm=b._emscripten_bind_btConvexHullShape_recalcLocalAabb_0=
|
||||||
|
function(){return(nm=b._emscripten_bind_btConvexHullShape_recalcLocalAabb_0=b.asm.lk).apply(null,arguments)},om=b._emscripten_bind_btConvexHullShape_getConvexPolyhedron_0=function(){return(om=b._emscripten_bind_btConvexHullShape_getConvexPolyhedron_0=b.asm.mk).apply(null,arguments)},pm=b._emscripten_bind_btConvexHullShape_setLocalScaling_1=function(){return(pm=b._emscripten_bind_btConvexHullShape_setLocalScaling_1=b.asm.nk).apply(null,arguments)},qm=b._emscripten_bind_btConvexHullShape_getLocalScaling_0=
|
||||||
|
function(){return(qm=b._emscripten_bind_btConvexHullShape_getLocalScaling_0=b.asm.ok).apply(null,arguments)},rm=b._emscripten_bind_btConvexHullShape_calculateLocalInertia_2=function(){return(rm=b._emscripten_bind_btConvexHullShape_calculateLocalInertia_2=b.asm.pk).apply(null,arguments)},sm=b._emscripten_bind_btConvexHullShape___destroy___0=function(){return(sm=b._emscripten_bind_btConvexHullShape___destroy___0=b.asm.qk).apply(null,arguments)},tm=b._emscripten_bind_btShapeHull_btShapeHull_1=function(){return(tm=
|
||||||
|
b._emscripten_bind_btShapeHull_btShapeHull_1=b.asm.rk).apply(null,arguments)},um=b._emscripten_bind_btShapeHull_buildHull_1=function(){return(um=b._emscripten_bind_btShapeHull_buildHull_1=b.asm.sk).apply(null,arguments)},wm=b._emscripten_bind_btShapeHull_numVertices_0=function(){return(wm=b._emscripten_bind_btShapeHull_numVertices_0=b.asm.tk).apply(null,arguments)},xm=b._emscripten_bind_btShapeHull_getVertexPointer_0=function(){return(xm=b._emscripten_bind_btShapeHull_getVertexPointer_0=b.asm.uk).apply(null,
|
||||||
|
arguments)},ym=b._emscripten_bind_btShapeHull___destroy___0=function(){return(ym=b._emscripten_bind_btShapeHull___destroy___0=b.asm.vk).apply(null,arguments)},zm=b._emscripten_bind_btCompoundShape_btCompoundShape_0=function(){return(zm=b._emscripten_bind_btCompoundShape_btCompoundShape_0=b.asm.wk).apply(null,arguments)},Am=b._emscripten_bind_btCompoundShape_btCompoundShape_1=function(){return(Am=b._emscripten_bind_btCompoundShape_btCompoundShape_1=b.asm.xk).apply(null,arguments)},Bm=b._emscripten_bind_btCompoundShape_addChildShape_2=
|
||||||
|
function(){return(Bm=b._emscripten_bind_btCompoundShape_addChildShape_2=b.asm.yk).apply(null,arguments)},Cm=b._emscripten_bind_btCompoundShape_removeChildShape_1=function(){return(Cm=b._emscripten_bind_btCompoundShape_removeChildShape_1=b.asm.zk).apply(null,arguments)},Dm=b._emscripten_bind_btCompoundShape_removeChildShapeByIndex_1=function(){return(Dm=b._emscripten_bind_btCompoundShape_removeChildShapeByIndex_1=b.asm.Ak).apply(null,arguments)},Em=b._emscripten_bind_btCompoundShape_getNumChildShapes_0=
|
||||||
|
function(){return(Em=b._emscripten_bind_btCompoundShape_getNumChildShapes_0=b.asm.Bk).apply(null,arguments)},Fm=b._emscripten_bind_btCompoundShape_getChildShape_1=function(){return(Fm=b._emscripten_bind_btCompoundShape_getChildShape_1=b.asm.Ck).apply(null,arguments)},Gm=b._emscripten_bind_btCompoundShape_updateChildTransform_2=function(){return(Gm=b._emscripten_bind_btCompoundShape_updateChildTransform_2=b.asm.Dk).apply(null,arguments)},Hm=b._emscripten_bind_btCompoundShape_updateChildTransform_3=
|
||||||
|
function(){return(Hm=b._emscripten_bind_btCompoundShape_updateChildTransform_3=b.asm.Ek).apply(null,arguments)},Im=b._emscripten_bind_btCompoundShape_setMargin_1=function(){return(Im=b._emscripten_bind_btCompoundShape_setMargin_1=b.asm.Fk).apply(null,arguments)},Jm=b._emscripten_bind_btCompoundShape_getMargin_0=function(){return(Jm=b._emscripten_bind_btCompoundShape_getMargin_0=b.asm.Gk).apply(null,arguments)},Km=b._emscripten_bind_btCompoundShape_setLocalScaling_1=function(){return(Km=b._emscripten_bind_btCompoundShape_setLocalScaling_1=
|
||||||
|
b.asm.Hk).apply(null,arguments)},Lm=b._emscripten_bind_btCompoundShape_getLocalScaling_0=function(){return(Lm=b._emscripten_bind_btCompoundShape_getLocalScaling_0=b.asm.Ik).apply(null,arguments)},Mm=b._emscripten_bind_btCompoundShape_calculateLocalInertia_2=function(){return(Mm=b._emscripten_bind_btCompoundShape_calculateLocalInertia_2=b.asm.Jk).apply(null,arguments)},Nm=b._emscripten_bind_btCompoundShape___destroy___0=function(){return(Nm=b._emscripten_bind_btCompoundShape___destroy___0=b.asm.Kk).apply(null,
|
||||||
|
arguments)},Om=b._emscripten_bind_btIndexedMesh_get_m_numTriangles_0=function(){return(Om=b._emscripten_bind_btIndexedMesh_get_m_numTriangles_0=b.asm.Lk).apply(null,arguments)},Pm=b._emscripten_bind_btIndexedMesh_set_m_numTriangles_1=function(){return(Pm=b._emscripten_bind_btIndexedMesh_set_m_numTriangles_1=b.asm.Mk).apply(null,arguments)},Qm=b._emscripten_bind_btIndexedMesh___destroy___0=function(){return(Qm=b._emscripten_bind_btIndexedMesh___destroy___0=b.asm.Nk).apply(null,arguments)},Rm=b._emscripten_bind_btIndexedMeshArray_size_0=
|
||||||
|
function(){return(Rm=b._emscripten_bind_btIndexedMeshArray_size_0=b.asm.Ok).apply(null,arguments)},Sm=b._emscripten_bind_btIndexedMeshArray_at_1=function(){return(Sm=b._emscripten_bind_btIndexedMeshArray_at_1=b.asm.Pk).apply(null,arguments)},Tm=b._emscripten_bind_btIndexedMeshArray___destroy___0=function(){return(Tm=b._emscripten_bind_btIndexedMeshArray___destroy___0=b.asm.Qk).apply(null,arguments)},Um=b._emscripten_bind_btTriangleMesh_btTriangleMesh_0=function(){return(Um=b._emscripten_bind_btTriangleMesh_btTriangleMesh_0=
|
||||||
|
b.asm.Rk).apply(null,arguments)},Vm=b._emscripten_bind_btTriangleMesh_btTriangleMesh_1=function(){return(Vm=b._emscripten_bind_btTriangleMesh_btTriangleMesh_1=b.asm.Sk).apply(null,arguments)},Wm=b._emscripten_bind_btTriangleMesh_btTriangleMesh_2=function(){return(Wm=b._emscripten_bind_btTriangleMesh_btTriangleMesh_2=b.asm.Tk).apply(null,arguments)},Xm=b._emscripten_bind_btTriangleMesh_addTriangle_3=function(){return(Xm=b._emscripten_bind_btTriangleMesh_addTriangle_3=b.asm.Uk).apply(null,arguments)},
|
||||||
|
Ym=b._emscripten_bind_btTriangleMesh_addTriangle_4=function(){return(Ym=b._emscripten_bind_btTriangleMesh_addTriangle_4=b.asm.Vk).apply(null,arguments)},Zm=b._emscripten_bind_btTriangleMesh_findOrAddVertex_2=function(){return(Zm=b._emscripten_bind_btTriangleMesh_findOrAddVertex_2=b.asm.Wk).apply(null,arguments)},$m=b._emscripten_bind_btTriangleMesh_addIndex_1=function(){return($m=b._emscripten_bind_btTriangleMesh_addIndex_1=b.asm.Xk).apply(null,arguments)},an=b._emscripten_bind_btTriangleMesh_getIndexedMeshArray_0=
|
||||||
|
function(){return(an=b._emscripten_bind_btTriangleMesh_getIndexedMeshArray_0=b.asm.Yk).apply(null,arguments)},bn=b._emscripten_bind_btTriangleMesh_setScaling_1=function(){return(bn=b._emscripten_bind_btTriangleMesh_setScaling_1=b.asm.Zk).apply(null,arguments)},cn=b._emscripten_bind_btTriangleMesh___destroy___0=function(){return(cn=b._emscripten_bind_btTriangleMesh___destroy___0=b.asm._k).apply(null,arguments)},dn=b._emscripten_bind_btEmptyShape_btEmptyShape_0=function(){return(dn=b._emscripten_bind_btEmptyShape_btEmptyShape_0=
|
||||||
|
b.asm.$k).apply(null,arguments)},en=b._emscripten_bind_btEmptyShape_setLocalScaling_1=function(){return(en=b._emscripten_bind_btEmptyShape_setLocalScaling_1=b.asm.al).apply(null,arguments)},fn=b._emscripten_bind_btEmptyShape_getLocalScaling_0=function(){return(fn=b._emscripten_bind_btEmptyShape_getLocalScaling_0=b.asm.bl).apply(null,arguments)},gn=b._emscripten_bind_btEmptyShape_calculateLocalInertia_2=function(){return(gn=b._emscripten_bind_btEmptyShape_calculateLocalInertia_2=b.asm.cl).apply(null,
|
||||||
|
arguments)},hn=b._emscripten_bind_btEmptyShape___destroy___0=function(){return(hn=b._emscripten_bind_btEmptyShape___destroy___0=b.asm.dl).apply(null,arguments)},jn=b._emscripten_bind_btStaticPlaneShape_btStaticPlaneShape_2=function(){return(jn=b._emscripten_bind_btStaticPlaneShape_btStaticPlaneShape_2=b.asm.el).apply(null,arguments)},kn=b._emscripten_bind_btStaticPlaneShape_setLocalScaling_1=function(){return(kn=b._emscripten_bind_btStaticPlaneShape_setLocalScaling_1=b.asm.fl).apply(null,arguments)},
|
||||||
|
ln=b._emscripten_bind_btStaticPlaneShape_getLocalScaling_0=function(){return(ln=b._emscripten_bind_btStaticPlaneShape_getLocalScaling_0=b.asm.gl).apply(null,arguments)},mn=b._emscripten_bind_btStaticPlaneShape_calculateLocalInertia_2=function(){return(mn=b._emscripten_bind_btStaticPlaneShape_calculateLocalInertia_2=b.asm.hl).apply(null,arguments)},nn=b._emscripten_bind_btStaticPlaneShape___destroy___0=function(){return(nn=b._emscripten_bind_btStaticPlaneShape___destroy___0=b.asm.il).apply(null,arguments)},
|
||||||
|
on=b._emscripten_bind_btBvhTriangleMeshShape_btBvhTriangleMeshShape_2=function(){return(on=b._emscripten_bind_btBvhTriangleMeshShape_btBvhTriangleMeshShape_2=b.asm.jl).apply(null,arguments)},pn=b._emscripten_bind_btBvhTriangleMeshShape_btBvhTriangleMeshShape_3=function(){return(pn=b._emscripten_bind_btBvhTriangleMeshShape_btBvhTriangleMeshShape_3=b.asm.kl).apply(null,arguments)},qn=b._emscripten_bind_btBvhTriangleMeshShape_setLocalScaling_1=function(){return(qn=b._emscripten_bind_btBvhTriangleMeshShape_setLocalScaling_1=
|
||||||
|
b.asm.ll).apply(null,arguments)},rn=b._emscripten_bind_btBvhTriangleMeshShape_getLocalScaling_0=function(){return(rn=b._emscripten_bind_btBvhTriangleMeshShape_getLocalScaling_0=b.asm.ml).apply(null,arguments)},sn=b._emscripten_bind_btBvhTriangleMeshShape_calculateLocalInertia_2=function(){return(sn=b._emscripten_bind_btBvhTriangleMeshShape_calculateLocalInertia_2=b.asm.nl).apply(null,arguments)},tn=b._emscripten_bind_btBvhTriangleMeshShape___destroy___0=function(){return(tn=b._emscripten_bind_btBvhTriangleMeshShape___destroy___0=
|
||||||
|
b.asm.ol).apply(null,arguments)},un=b._emscripten_bind_btHeightfieldTerrainShape_btHeightfieldTerrainShape_9=function(){return(un=b._emscripten_bind_btHeightfieldTerrainShape_btHeightfieldTerrainShape_9=b.asm.pl).apply(null,arguments)},vn=b._emscripten_bind_btHeightfieldTerrainShape_setMargin_1=function(){return(vn=b._emscripten_bind_btHeightfieldTerrainShape_setMargin_1=b.asm.ql).apply(null,arguments)},wn=b._emscripten_bind_btHeightfieldTerrainShape_getMargin_0=function(){return(wn=b._emscripten_bind_btHeightfieldTerrainShape_getMargin_0=
|
||||||
|
b.asm.rl).apply(null,arguments)},xn=b._emscripten_bind_btHeightfieldTerrainShape_setLocalScaling_1=function(){return(xn=b._emscripten_bind_btHeightfieldTerrainShape_setLocalScaling_1=b.asm.sl).apply(null,arguments)},yn=b._emscripten_bind_btHeightfieldTerrainShape_getLocalScaling_0=function(){return(yn=b._emscripten_bind_btHeightfieldTerrainShape_getLocalScaling_0=b.asm.tl).apply(null,arguments)},zn=b._emscripten_bind_btHeightfieldTerrainShape_calculateLocalInertia_2=function(){return(zn=b._emscripten_bind_btHeightfieldTerrainShape_calculateLocalInertia_2=
|
||||||
|
b.asm.ul).apply(null,arguments)},An=b._emscripten_bind_btHeightfieldTerrainShape___destroy___0=function(){return(An=b._emscripten_bind_btHeightfieldTerrainShape___destroy___0=b.asm.vl).apply(null,arguments)},Bn=b._emscripten_bind_btAABB_btAABB_4=function(){return(Bn=b._emscripten_bind_btAABB_btAABB_4=b.asm.wl).apply(null,arguments)},Cn=b._emscripten_bind_btAABB_invalidate_0=function(){return(Cn=b._emscripten_bind_btAABB_invalidate_0=b.asm.xl).apply(null,arguments)},Dn=b._emscripten_bind_btAABB_increment_margin_1=
|
||||||
|
function(){return(Dn=b._emscripten_bind_btAABB_increment_margin_1=b.asm.yl).apply(null,arguments)},En=b._emscripten_bind_btAABB_copy_with_margin_2=function(){return(En=b._emscripten_bind_btAABB_copy_with_margin_2=b.asm.zl).apply(null,arguments)},Fn=b._emscripten_bind_btAABB___destroy___0=function(){return(Fn=b._emscripten_bind_btAABB___destroy___0=b.asm.Al).apply(null,arguments)},Gn=b._emscripten_bind_btPrimitiveTriangle_btPrimitiveTriangle_0=function(){return(Gn=b._emscripten_bind_btPrimitiveTriangle_btPrimitiveTriangle_0=
|
||||||
|
b.asm.Bl).apply(null,arguments)},Hn=b._emscripten_bind_btPrimitiveTriangle___destroy___0=function(){return(Hn=b._emscripten_bind_btPrimitiveTriangle___destroy___0=b.asm.Cl).apply(null,arguments)},In=b._emscripten_bind_btTriangleShapeEx_btTriangleShapeEx_3=function(){return(In=b._emscripten_bind_btTriangleShapeEx_btTriangleShapeEx_3=b.asm.Dl).apply(null,arguments)},Jn=b._emscripten_bind_btTriangleShapeEx_getAabb_3=function(){return(Jn=b._emscripten_bind_btTriangleShapeEx_getAabb_3=b.asm.El).apply(null,
|
||||||
|
arguments)},Kn=b._emscripten_bind_btTriangleShapeEx_applyTransform_1=function(){return(Kn=b._emscripten_bind_btTriangleShapeEx_applyTransform_1=b.asm.Fl).apply(null,arguments)},Ln=b._emscripten_bind_btTriangleShapeEx_buildTriPlane_1=function(){return(Ln=b._emscripten_bind_btTriangleShapeEx_buildTriPlane_1=b.asm.Gl).apply(null,arguments)},Mn=b._emscripten_bind_btTriangleShapeEx___destroy___0=function(){return(Mn=b._emscripten_bind_btTriangleShapeEx___destroy___0=b.asm.Hl).apply(null,arguments)},Nn=
|
||||||
|
b._emscripten_bind_btTetrahedronShapeEx_btTetrahedronShapeEx_0=function(){return(Nn=b._emscripten_bind_btTetrahedronShapeEx_btTetrahedronShapeEx_0=b.asm.Il).apply(null,arguments)},On=b._emscripten_bind_btTetrahedronShapeEx_setVertices_4=function(){return(On=b._emscripten_bind_btTetrahedronShapeEx_setVertices_4=b.asm.Jl).apply(null,arguments)},Pn=b._emscripten_bind_btTetrahedronShapeEx___destroy___0=function(){return(Pn=b._emscripten_bind_btTetrahedronShapeEx___destroy___0=b.asm.Kl).apply(null,arguments)},
|
||||||
|
Qn=b._emscripten_bind_CompoundPrimitiveManager_get_primitive_count_0=function(){return(Qn=b._emscripten_bind_CompoundPrimitiveManager_get_primitive_count_0=b.asm.Ll).apply(null,arguments)},Rn=b._emscripten_bind_CompoundPrimitiveManager_get_primitive_box_2=function(){return(Rn=b._emscripten_bind_CompoundPrimitiveManager_get_primitive_box_2=b.asm.Ml).apply(null,arguments)},Sn=b._emscripten_bind_CompoundPrimitiveManager_get_primitive_triangle_2=function(){return(Sn=b._emscripten_bind_CompoundPrimitiveManager_get_primitive_triangle_2=
|
||||||
|
b.asm.Nl).apply(null,arguments)},Tn=b._emscripten_bind_CompoundPrimitiveManager_is_trimesh_0=function(){return(Tn=b._emscripten_bind_CompoundPrimitiveManager_is_trimesh_0=b.asm.Ol).apply(null,arguments)},Un=b._emscripten_bind_CompoundPrimitiveManager_get_m_compoundShape_0=function(){return(Un=b._emscripten_bind_CompoundPrimitiveManager_get_m_compoundShape_0=b.asm.Pl).apply(null,arguments)},Vn=b._emscripten_bind_CompoundPrimitiveManager_set_m_compoundShape_1=function(){return(Vn=b._emscripten_bind_CompoundPrimitiveManager_set_m_compoundShape_1=
|
||||||
|
b.asm.Ql).apply(null,arguments)},Wn=b._emscripten_bind_CompoundPrimitiveManager___destroy___0=function(){return(Wn=b._emscripten_bind_CompoundPrimitiveManager___destroy___0=b.asm.Rl).apply(null,arguments)},Xn=b._emscripten_bind_btGImpactCompoundShape_btGImpactCompoundShape_0=function(){return(Xn=b._emscripten_bind_btGImpactCompoundShape_btGImpactCompoundShape_0=b.asm.Sl).apply(null,arguments)},Yn=b._emscripten_bind_btGImpactCompoundShape_btGImpactCompoundShape_1=function(){return(Yn=b._emscripten_bind_btGImpactCompoundShape_btGImpactCompoundShape_1=
|
||||||
|
b.asm.Tl).apply(null,arguments)},Zn=b._emscripten_bind_btGImpactCompoundShape_childrenHasTransform_0=function(){return(Zn=b._emscripten_bind_btGImpactCompoundShape_childrenHasTransform_0=b.asm.Ul).apply(null,arguments)},$n=b._emscripten_bind_btGImpactCompoundShape_getPrimitiveManager_0=function(){return($n=b._emscripten_bind_btGImpactCompoundShape_getPrimitiveManager_0=b.asm.Vl).apply(null,arguments)},ao=b._emscripten_bind_btGImpactCompoundShape_getCompoundPrimitiveManager_0=function(){return(ao=
|
||||||
|
b._emscripten_bind_btGImpactCompoundShape_getCompoundPrimitiveManager_0=b.asm.Wl).apply(null,arguments)},bo=b._emscripten_bind_btGImpactCompoundShape_getNumChildShapes_0=function(){return(bo=b._emscripten_bind_btGImpactCompoundShape_getNumChildShapes_0=b.asm.Xl).apply(null,arguments)},co=b._emscripten_bind_btGImpactCompoundShape_addChildShape_2=function(){return(co=b._emscripten_bind_btGImpactCompoundShape_addChildShape_2=b.asm.Yl).apply(null,arguments)},eo=b._emscripten_bind_btGImpactCompoundShape_getChildShape_1=
|
||||||
|
function(){return(eo=b._emscripten_bind_btGImpactCompoundShape_getChildShape_1=b.asm.Zl).apply(null,arguments)},fo=b._emscripten_bind_btGImpactCompoundShape_getChildAabb_4=function(){return(fo=b._emscripten_bind_btGImpactCompoundShape_getChildAabb_4=b.asm._l).apply(null,arguments)},go=b._emscripten_bind_btGImpactCompoundShape_getChildTransform_1=function(){return(go=b._emscripten_bind_btGImpactCompoundShape_getChildTransform_1=b.asm.$l).apply(null,arguments)},ho=b._emscripten_bind_btGImpactCompoundShape_setChildTransform_2=
|
||||||
|
function(){return(ho=b._emscripten_bind_btGImpactCompoundShape_setChildTransform_2=b.asm.am).apply(null,arguments)},io=b._emscripten_bind_btGImpactCompoundShape_calculateLocalInertia_2=function(){return(io=b._emscripten_bind_btGImpactCompoundShape_calculateLocalInertia_2=b.asm.bm).apply(null,arguments)},jo=b._emscripten_bind_btGImpactCompoundShape_getName_0=function(){return(jo=b._emscripten_bind_btGImpactCompoundShape_getName_0=b.asm.cm).apply(null,arguments)},ko=b._emscripten_bind_btGImpactCompoundShape_getGImpactShapeType_0=
|
||||||
|
function(){return(ko=b._emscripten_bind_btGImpactCompoundShape_getGImpactShapeType_0=b.asm.dm).apply(null,arguments)},lo=b._emscripten_bind_btGImpactCompoundShape_setLocalScaling_1=function(){return(lo=b._emscripten_bind_btGImpactCompoundShape_setLocalScaling_1=b.asm.em).apply(null,arguments)},mo=b._emscripten_bind_btGImpactCompoundShape_getLocalScaling_0=function(){return(mo=b._emscripten_bind_btGImpactCompoundShape_getLocalScaling_0=b.asm.fm).apply(null,arguments)},no=b._emscripten_bind_btGImpactCompoundShape_updateBound_0=
|
||||||
|
function(){return(no=b._emscripten_bind_btGImpactCompoundShape_updateBound_0=b.asm.gm).apply(null,arguments)},oo=b._emscripten_bind_btGImpactCompoundShape_postUpdate_0=function(){return(oo=b._emscripten_bind_btGImpactCompoundShape_postUpdate_0=b.asm.hm).apply(null,arguments)},po=b._emscripten_bind_btGImpactCompoundShape_getShapeType_0=function(){return(po=b._emscripten_bind_btGImpactCompoundShape_getShapeType_0=b.asm.im).apply(null,arguments)},qo=b._emscripten_bind_btGImpactCompoundShape_needsRetrieveTriangles_0=
|
||||||
|
function(){return(qo=b._emscripten_bind_btGImpactCompoundShape_needsRetrieveTriangles_0=b.asm.jm).apply(null,arguments)},ro=b._emscripten_bind_btGImpactCompoundShape_needsRetrieveTetrahedrons_0=function(){return(ro=b._emscripten_bind_btGImpactCompoundShape_needsRetrieveTetrahedrons_0=b.asm.km).apply(null,arguments)},so=b._emscripten_bind_btGImpactCompoundShape_getBulletTriangle_2=function(){return(so=b._emscripten_bind_btGImpactCompoundShape_getBulletTriangle_2=b.asm.lm).apply(null,arguments)},to=
|
||||||
|
b._emscripten_bind_btGImpactCompoundShape_getBulletTetrahedron_2=function(){return(to=b._emscripten_bind_btGImpactCompoundShape_getBulletTetrahedron_2=b.asm.mm).apply(null,arguments)},uo=b._emscripten_bind_btGImpactCompoundShape___destroy___0=function(){return(uo=b._emscripten_bind_btGImpactCompoundShape___destroy___0=b.asm.nm).apply(null,arguments)},vo=b._emscripten_bind_TrimeshPrimitiveManager_TrimeshPrimitiveManager_0=function(){return(vo=b._emscripten_bind_TrimeshPrimitiveManager_TrimeshPrimitiveManager_0=
|
||||||
|
b.asm.om).apply(null,arguments)},wo=b._emscripten_bind_TrimeshPrimitiveManager_TrimeshPrimitiveManager_1=function(){return(wo=b._emscripten_bind_TrimeshPrimitiveManager_TrimeshPrimitiveManager_1=b.asm.pm).apply(null,arguments)},xo=b._emscripten_bind_TrimeshPrimitiveManager_lock_0=function(){return(xo=b._emscripten_bind_TrimeshPrimitiveManager_lock_0=b.asm.qm).apply(null,arguments)},yo=b._emscripten_bind_TrimeshPrimitiveManager_unlock_0=function(){return(yo=b._emscripten_bind_TrimeshPrimitiveManager_unlock_0=
|
||||||
|
b.asm.rm).apply(null,arguments)},zo=b._emscripten_bind_TrimeshPrimitiveManager_is_trimesh_0=function(){return(zo=b._emscripten_bind_TrimeshPrimitiveManager_is_trimesh_0=b.asm.sm).apply(null,arguments)},Ao=b._emscripten_bind_TrimeshPrimitiveManager_get_vertex_count_0=function(){return(Ao=b._emscripten_bind_TrimeshPrimitiveManager_get_vertex_count_0=b.asm.tm).apply(null,arguments)},Bo=b._emscripten_bind_TrimeshPrimitiveManager_get_indices_4=function(){return(Bo=b._emscripten_bind_TrimeshPrimitiveManager_get_indices_4=
|
||||||
|
b.asm.um).apply(null,arguments)},Co=b._emscripten_bind_TrimeshPrimitiveManager_get_vertex_2=function(){return(Co=b._emscripten_bind_TrimeshPrimitiveManager_get_vertex_2=b.asm.vm).apply(null,arguments)},Do=b._emscripten_bind_TrimeshPrimitiveManager_get_bullet_triangle_2=function(){return(Do=b._emscripten_bind_TrimeshPrimitiveManager_get_bullet_triangle_2=b.asm.wm).apply(null,arguments)},Eo=b._emscripten_bind_TrimeshPrimitiveManager_get_m_margin_0=function(){return(Eo=b._emscripten_bind_TrimeshPrimitiveManager_get_m_margin_0=
|
||||||
|
b.asm.xm).apply(null,arguments)},Fo=b._emscripten_bind_TrimeshPrimitiveManager_set_m_margin_1=function(){return(Fo=b._emscripten_bind_TrimeshPrimitiveManager_set_m_margin_1=b.asm.ym).apply(null,arguments)},Go=b._emscripten_bind_TrimeshPrimitiveManager_get_m_meshInterface_0=function(){return(Go=b._emscripten_bind_TrimeshPrimitiveManager_get_m_meshInterface_0=b.asm.zm).apply(null,arguments)},Ho=b._emscripten_bind_TrimeshPrimitiveManager_set_m_meshInterface_1=function(){return(Ho=b._emscripten_bind_TrimeshPrimitiveManager_set_m_meshInterface_1=
|
||||||
|
b.asm.Am).apply(null,arguments)},Io=b._emscripten_bind_TrimeshPrimitiveManager_get_m_part_0=function(){return(Io=b._emscripten_bind_TrimeshPrimitiveManager_get_m_part_0=b.asm.Bm).apply(null,arguments)},Jo=b._emscripten_bind_TrimeshPrimitiveManager_set_m_part_1=function(){return(Jo=b._emscripten_bind_TrimeshPrimitiveManager_set_m_part_1=b.asm.Cm).apply(null,arguments)},Ko=b._emscripten_bind_TrimeshPrimitiveManager_get_m_lock_count_0=function(){return(Ko=b._emscripten_bind_TrimeshPrimitiveManager_get_m_lock_count_0=
|
||||||
|
b.asm.Dm).apply(null,arguments)},Lo=b._emscripten_bind_TrimeshPrimitiveManager_set_m_lock_count_1=function(){return(Lo=b._emscripten_bind_TrimeshPrimitiveManager_set_m_lock_count_1=b.asm.Em).apply(null,arguments)},Mo=b._emscripten_bind_TrimeshPrimitiveManager_get_numverts_0=function(){return(Mo=b._emscripten_bind_TrimeshPrimitiveManager_get_numverts_0=b.asm.Fm).apply(null,arguments)},No=b._emscripten_bind_TrimeshPrimitiveManager_set_numverts_1=function(){return(No=b._emscripten_bind_TrimeshPrimitiveManager_set_numverts_1=
|
||||||
|
b.asm.Gm).apply(null,arguments)},Oo=b._emscripten_bind_TrimeshPrimitiveManager_get_type_0=function(){return(Oo=b._emscripten_bind_TrimeshPrimitiveManager_get_type_0=b.asm.Hm).apply(null,arguments)},Po=b._emscripten_bind_TrimeshPrimitiveManager_set_type_1=function(){return(Po=b._emscripten_bind_TrimeshPrimitiveManager_set_type_1=b.asm.Im).apply(null,arguments)},Qo=b._emscripten_bind_TrimeshPrimitiveManager_get_stride_0=function(){return(Qo=b._emscripten_bind_TrimeshPrimitiveManager_get_stride_0=b.asm.Jm).apply(null,
|
||||||
|
arguments)},Ro=b._emscripten_bind_TrimeshPrimitiveManager_set_stride_1=function(){return(Ro=b._emscripten_bind_TrimeshPrimitiveManager_set_stride_1=b.asm.Km).apply(null,arguments)},So=b._emscripten_bind_TrimeshPrimitiveManager_get_indexstride_0=function(){return(So=b._emscripten_bind_TrimeshPrimitiveManager_get_indexstride_0=b.asm.Lm).apply(null,arguments)},To=b._emscripten_bind_TrimeshPrimitiveManager_set_indexstride_1=function(){return(To=b._emscripten_bind_TrimeshPrimitiveManager_set_indexstride_1=
|
||||||
|
b.asm.Mm).apply(null,arguments)},Uo=b._emscripten_bind_TrimeshPrimitiveManager_get_numfaces_0=function(){return(Uo=b._emscripten_bind_TrimeshPrimitiveManager_get_numfaces_0=b.asm.Nm).apply(null,arguments)},Vo=b._emscripten_bind_TrimeshPrimitiveManager_set_numfaces_1=function(){return(Vo=b._emscripten_bind_TrimeshPrimitiveManager_set_numfaces_1=b.asm.Om).apply(null,arguments)},Wo=b._emscripten_bind_TrimeshPrimitiveManager_get_indicestype_0=function(){return(Wo=b._emscripten_bind_TrimeshPrimitiveManager_get_indicestype_0=
|
||||||
|
b.asm.Pm).apply(null,arguments)},Xo=b._emscripten_bind_TrimeshPrimitiveManager_set_indicestype_1=function(){return(Xo=b._emscripten_bind_TrimeshPrimitiveManager_set_indicestype_1=b.asm.Qm).apply(null,arguments)},Yo=b._emscripten_bind_TrimeshPrimitiveManager___destroy___0=function(){return(Yo=b._emscripten_bind_TrimeshPrimitiveManager___destroy___0=b.asm.Rm).apply(null,arguments)},Zo=b._emscripten_bind_btGImpactMeshShapePart_btGImpactMeshShapePart_2=function(){return(Zo=b._emscripten_bind_btGImpactMeshShapePart_btGImpactMeshShapePart_2=
|
||||||
|
b.asm.Sm).apply(null,arguments)},$o=b._emscripten_bind_btGImpactMeshShapePart_getTrimeshPrimitiveManager_0=function(){return($o=b._emscripten_bind_btGImpactMeshShapePart_getTrimeshPrimitiveManager_0=b.asm.Tm).apply(null,arguments)},ap=b._emscripten_bind_btGImpactMeshShapePart_getVertexCount_0=function(){return(ap=b._emscripten_bind_btGImpactMeshShapePart_getVertexCount_0=b.asm.Um).apply(null,arguments)},bp=b._emscripten_bind_btGImpactMeshShapePart_getVertex_2=function(){return(bp=b._emscripten_bind_btGImpactMeshShapePart_getVertex_2=
|
||||||
|
b.asm.Vm).apply(null,arguments)},cp=b._emscripten_bind_btGImpactMeshShapePart_getPart_0=function(){return(cp=b._emscripten_bind_btGImpactMeshShapePart_getPart_0=b.asm.Wm).apply(null,arguments)},dp=b._emscripten_bind_btGImpactMeshShapePart_setLocalScaling_1=function(){return(dp=b._emscripten_bind_btGImpactMeshShapePart_setLocalScaling_1=b.asm.Xm).apply(null,arguments)},ep=b._emscripten_bind_btGImpactMeshShapePart_getLocalScaling_0=function(){return(ep=b._emscripten_bind_btGImpactMeshShapePart_getLocalScaling_0=
|
||||||
|
b.asm.Ym).apply(null,arguments)},fp=b._emscripten_bind_btGImpactMeshShapePart_updateBound_0=function(){return(fp=b._emscripten_bind_btGImpactMeshShapePart_updateBound_0=b.asm.Zm).apply(null,arguments)},gp=b._emscripten_bind_btGImpactMeshShapePart_postUpdate_0=function(){return(gp=b._emscripten_bind_btGImpactMeshShapePart_postUpdate_0=b.asm._m).apply(null,arguments)},hp=b._emscripten_bind_btGImpactMeshShapePart_getShapeType_0=function(){return(hp=b._emscripten_bind_btGImpactMeshShapePart_getShapeType_0=
|
||||||
|
b.asm.$m).apply(null,arguments)},ip=b._emscripten_bind_btGImpactMeshShapePart_needsRetrieveTriangles_0=function(){return(ip=b._emscripten_bind_btGImpactMeshShapePart_needsRetrieveTriangles_0=b.asm.an).apply(null,arguments)},jp=b._emscripten_bind_btGImpactMeshShapePart_needsRetrieveTetrahedrons_0=function(){return(jp=b._emscripten_bind_btGImpactMeshShapePart_needsRetrieveTetrahedrons_0=b.asm.bn).apply(null,arguments)},kp=b._emscripten_bind_btGImpactMeshShapePart_getBulletTriangle_2=function(){return(kp=
|
||||||
|
b._emscripten_bind_btGImpactMeshShapePart_getBulletTriangle_2=b.asm.cn).apply(null,arguments)},lp=b._emscripten_bind_btGImpactMeshShapePart_getBulletTetrahedron_2=function(){return(lp=b._emscripten_bind_btGImpactMeshShapePart_getBulletTetrahedron_2=b.asm.dn).apply(null,arguments)},mp=b._emscripten_bind_btGImpactMeshShapePart___destroy___0=function(){return(mp=b._emscripten_bind_btGImpactMeshShapePart___destroy___0=b.asm.en).apply(null,arguments)},np=b._emscripten_bind_btGImpactMeshShape_btGImpactMeshShape_1=
|
||||||
|
function(){return(np=b._emscripten_bind_btGImpactMeshShape_btGImpactMeshShape_1=b.asm.fn).apply(null,arguments)},op=b._emscripten_bind_btGImpactMeshShape_getMeshInterface_0=function(){return(op=b._emscripten_bind_btGImpactMeshShape_getMeshInterface_0=b.asm.gn).apply(null,arguments)},pp=b._emscripten_bind_btGImpactMeshShape_getMeshPartCount_0=function(){return(pp=b._emscripten_bind_btGImpactMeshShape_getMeshPartCount_0=b.asm.hn).apply(null,arguments)},qp=b._emscripten_bind_btGImpactMeshShape_getMeshPart_1=
|
||||||
|
function(){return(qp=b._emscripten_bind_btGImpactMeshShape_getMeshPart_1=b.asm.jn).apply(null,arguments)},rp=b._emscripten_bind_btGImpactMeshShape_calculateSerializeBufferSize_0=function(){return(rp=b._emscripten_bind_btGImpactMeshShape_calculateSerializeBufferSize_0=b.asm.kn).apply(null,arguments)},sp=b._emscripten_bind_btGImpactMeshShape_setLocalScaling_1=function(){return(sp=b._emscripten_bind_btGImpactMeshShape_setLocalScaling_1=b.asm.ln).apply(null,arguments)},tp=b._emscripten_bind_btGImpactMeshShape_getLocalScaling_0=
|
||||||
|
function(){return(tp=b._emscripten_bind_btGImpactMeshShape_getLocalScaling_0=b.asm.mn).apply(null,arguments)},up=b._emscripten_bind_btGImpactMeshShape_updateBound_0=function(){return(up=b._emscripten_bind_btGImpactMeshShape_updateBound_0=b.asm.nn).apply(null,arguments)},vp=b._emscripten_bind_btGImpactMeshShape_postUpdate_0=function(){return(vp=b._emscripten_bind_btGImpactMeshShape_postUpdate_0=b.asm.on).apply(null,arguments)},wp=b._emscripten_bind_btGImpactMeshShape_getShapeType_0=function(){return(wp=
|
||||||
|
b._emscripten_bind_btGImpactMeshShape_getShapeType_0=b.asm.pn).apply(null,arguments)},xp=b._emscripten_bind_btGImpactMeshShape_needsRetrieveTriangles_0=function(){return(xp=b._emscripten_bind_btGImpactMeshShape_needsRetrieveTriangles_0=b.asm.qn).apply(null,arguments)},yp=b._emscripten_bind_btGImpactMeshShape_needsRetrieveTetrahedrons_0=function(){return(yp=b._emscripten_bind_btGImpactMeshShape_needsRetrieveTetrahedrons_0=b.asm.rn).apply(null,arguments)},zp=b._emscripten_bind_btGImpactMeshShape_getBulletTriangle_2=
|
||||||
|
function(){return(zp=b._emscripten_bind_btGImpactMeshShape_getBulletTriangle_2=b.asm.sn).apply(null,arguments)},Ap=b._emscripten_bind_btGImpactMeshShape_getBulletTetrahedron_2=function(){return(Ap=b._emscripten_bind_btGImpactMeshShape_getBulletTetrahedron_2=b.asm.tn).apply(null,arguments)},Bp=b._emscripten_bind_btGImpactMeshShape___destroy___0=function(){return(Bp=b._emscripten_bind_btGImpactMeshShape___destroy___0=b.asm.un).apply(null,arguments)},Cp=b._emscripten_bind_btCollisionAlgorithmConstructionInfo_btCollisionAlgorithmConstructionInfo_0=
|
||||||
|
function(){return(Cp=b._emscripten_bind_btCollisionAlgorithmConstructionInfo_btCollisionAlgorithmConstructionInfo_0=b.asm.vn).apply(null,arguments)},Dp=b._emscripten_bind_btCollisionAlgorithmConstructionInfo_btCollisionAlgorithmConstructionInfo_2=function(){return(Dp=b._emscripten_bind_btCollisionAlgorithmConstructionInfo_btCollisionAlgorithmConstructionInfo_2=b.asm.wn).apply(null,arguments)},Ep=b._emscripten_bind_btCollisionAlgorithmConstructionInfo_get_m_dispatcher1_0=function(){return(Ep=b._emscripten_bind_btCollisionAlgorithmConstructionInfo_get_m_dispatcher1_0=
|
||||||
|
b.asm.xn).apply(null,arguments)},Fp=b._emscripten_bind_btCollisionAlgorithmConstructionInfo_set_m_dispatcher1_1=function(){return(Fp=b._emscripten_bind_btCollisionAlgorithmConstructionInfo_set_m_dispatcher1_1=b.asm.yn).apply(null,arguments)},Gp=b._emscripten_bind_btCollisionAlgorithmConstructionInfo_get_m_manifold_0=function(){return(Gp=b._emscripten_bind_btCollisionAlgorithmConstructionInfo_get_m_manifold_0=b.asm.zn).apply(null,arguments)},Hp=b._emscripten_bind_btCollisionAlgorithmConstructionInfo_set_m_manifold_1=
|
||||||
|
function(){return(Hp=b._emscripten_bind_btCollisionAlgorithmConstructionInfo_set_m_manifold_1=b.asm.An).apply(null,arguments)},Ip=b._emscripten_bind_btCollisionAlgorithmConstructionInfo___destroy___0=function(){return(Ip=b._emscripten_bind_btCollisionAlgorithmConstructionInfo___destroy___0=b.asm.Bn).apply(null,arguments)},Jp=b._emscripten_bind_btGImpactCollisionAlgorithm_btGImpactCollisionAlgorithm_3=function(){return(Jp=b._emscripten_bind_btGImpactCollisionAlgorithm_btGImpactCollisionAlgorithm_3=
|
||||||
|
b.asm.Cn).apply(null,arguments)},Kp=b._emscripten_bind_btGImpactCollisionAlgorithm_registerAlgorithm_1=function(){return(Kp=b._emscripten_bind_btGImpactCollisionAlgorithm_registerAlgorithm_1=b.asm.Dn).apply(null,arguments)},Lp=b._emscripten_bind_btGImpactCollisionAlgorithm___destroy___0=function(){return(Lp=b._emscripten_bind_btGImpactCollisionAlgorithm___destroy___0=b.asm.En).apply(null,arguments)},Mp=b._emscripten_bind_btDefaultCollisionConstructionInfo_btDefaultCollisionConstructionInfo_0=function(){return(Mp=
|
||||||
|
b._emscripten_bind_btDefaultCollisionConstructionInfo_btDefaultCollisionConstructionInfo_0=b.asm.Fn).apply(null,arguments)},Np=b._emscripten_bind_btDefaultCollisionConstructionInfo___destroy___0=function(){return(Np=b._emscripten_bind_btDefaultCollisionConstructionInfo___destroy___0=b.asm.Gn).apply(null,arguments)},Op=b._emscripten_bind_btPersistentManifold_btPersistentManifold_0=function(){return(Op=b._emscripten_bind_btPersistentManifold_btPersistentManifold_0=b.asm.Hn).apply(null,arguments)},Pp=
|
||||||
|
b._emscripten_bind_btPersistentManifold_getBody0_0=function(){return(Pp=b._emscripten_bind_btPersistentManifold_getBody0_0=b.asm.In).apply(null,arguments)},Qp=b._emscripten_bind_btPersistentManifold_getBody1_0=function(){return(Qp=b._emscripten_bind_btPersistentManifold_getBody1_0=b.asm.Jn).apply(null,arguments)},Rp=b._emscripten_bind_btPersistentManifold_getNumContacts_0=function(){return(Rp=b._emscripten_bind_btPersistentManifold_getNumContacts_0=b.asm.Kn).apply(null,arguments)},Sp=b._emscripten_bind_btPersistentManifold_getContactPoint_1=
|
||||||
|
function(){return(Sp=b._emscripten_bind_btPersistentManifold_getContactPoint_1=b.asm.Ln).apply(null,arguments)},Tp=b._emscripten_bind_btPersistentManifold___destroy___0=function(){return(Tp=b._emscripten_bind_btPersistentManifold___destroy___0=b.asm.Mn).apply(null,arguments)},Up=b._emscripten_bind_btCollisionDispatcher_btCollisionDispatcher_1=function(){return(Up=b._emscripten_bind_btCollisionDispatcher_btCollisionDispatcher_1=b.asm.Nn).apply(null,arguments)},Vp=b._emscripten_bind_btCollisionDispatcher_getNumManifolds_0=
|
||||||
|
function(){return(Vp=b._emscripten_bind_btCollisionDispatcher_getNumManifolds_0=b.asm.On).apply(null,arguments)},Wp=b._emscripten_bind_btCollisionDispatcher_getManifoldByIndexInternal_1=function(){return(Wp=b._emscripten_bind_btCollisionDispatcher_getManifoldByIndexInternal_1=b.asm.Pn).apply(null,arguments)},Xp=b._emscripten_bind_btCollisionDispatcher___destroy___0=function(){return(Xp=b._emscripten_bind_btCollisionDispatcher___destroy___0=b.asm.Qn).apply(null,arguments)},Yp=b._emscripten_bind_btOverlappingPairCallback___destroy___0=
|
||||||
|
function(){return(Yp=b._emscripten_bind_btOverlappingPairCallback___destroy___0=b.asm.Rn).apply(null,arguments)},Zp=b._emscripten_bind_btOverlappingPairCache_setInternalGhostPairCallback_1=function(){return(Zp=b._emscripten_bind_btOverlappingPairCache_setInternalGhostPairCallback_1=b.asm.Sn).apply(null,arguments)},$p=b._emscripten_bind_btOverlappingPairCache_getNumOverlappingPairs_0=function(){return($p=b._emscripten_bind_btOverlappingPairCache_getNumOverlappingPairs_0=b.asm.Tn).apply(null,arguments)},
|
||||||
|
aq=b._emscripten_bind_btOverlappingPairCache___destroy___0=function(){return(aq=b._emscripten_bind_btOverlappingPairCache___destroy___0=b.asm.Un).apply(null,arguments)},bq=b._emscripten_bind_btAxisSweep3_btAxisSweep3_2=function(){return(bq=b._emscripten_bind_btAxisSweep3_btAxisSweep3_2=b.asm.Vn).apply(null,arguments)},cq=b._emscripten_bind_btAxisSweep3_btAxisSweep3_3=function(){return(cq=b._emscripten_bind_btAxisSweep3_btAxisSweep3_3=b.asm.Wn).apply(null,arguments)},dq=b._emscripten_bind_btAxisSweep3_btAxisSweep3_4=
|
||||||
|
function(){return(dq=b._emscripten_bind_btAxisSweep3_btAxisSweep3_4=b.asm.Xn).apply(null,arguments)},eq=b._emscripten_bind_btAxisSweep3_btAxisSweep3_5=function(){return(eq=b._emscripten_bind_btAxisSweep3_btAxisSweep3_5=b.asm.Yn).apply(null,arguments)},fq=b._emscripten_bind_btAxisSweep3___destroy___0=function(){return(fq=b._emscripten_bind_btAxisSweep3___destroy___0=b.asm.Zn).apply(null,arguments)},gq=b._emscripten_bind_btBroadphaseInterface_getOverlappingPairCache_0=function(){return(gq=b._emscripten_bind_btBroadphaseInterface_getOverlappingPairCache_0=
|
||||||
|
b.asm._n).apply(null,arguments)},hq=b._emscripten_bind_btBroadphaseInterface___destroy___0=function(){return(hq=b._emscripten_bind_btBroadphaseInterface___destroy___0=b.asm.$n).apply(null,arguments)},iq=b._emscripten_bind_btCollisionConfiguration___destroy___0=function(){return(iq=b._emscripten_bind_btCollisionConfiguration___destroy___0=b.asm.ao).apply(null,arguments)},jq=b._emscripten_bind_btDbvtBroadphase_btDbvtBroadphase_0=function(){return(jq=b._emscripten_bind_btDbvtBroadphase_btDbvtBroadphase_0=
|
||||||
|
b.asm.bo).apply(null,arguments)},kq=b._emscripten_bind_btDbvtBroadphase___destroy___0=function(){return(kq=b._emscripten_bind_btDbvtBroadphase___destroy___0=b.asm.co).apply(null,arguments)},lq=b._emscripten_bind_btBroadphaseProxy_get_m_collisionFilterGroup_0=function(){return(lq=b._emscripten_bind_btBroadphaseProxy_get_m_collisionFilterGroup_0=b.asm.eo).apply(null,arguments)},mq=b._emscripten_bind_btBroadphaseProxy_set_m_collisionFilterGroup_1=function(){return(mq=b._emscripten_bind_btBroadphaseProxy_set_m_collisionFilterGroup_1=
|
||||||
|
b.asm.fo).apply(null,arguments)},nq=b._emscripten_bind_btBroadphaseProxy_get_m_collisionFilterMask_0=function(){return(nq=b._emscripten_bind_btBroadphaseProxy_get_m_collisionFilterMask_0=b.asm.go).apply(null,arguments)},oq=b._emscripten_bind_btBroadphaseProxy_set_m_collisionFilterMask_1=function(){return(oq=b._emscripten_bind_btBroadphaseProxy_set_m_collisionFilterMask_1=b.asm.ho).apply(null,arguments)},pq=b._emscripten_bind_btBroadphaseProxy___destroy___0=function(){return(pq=b._emscripten_bind_btBroadphaseProxy___destroy___0=
|
||||||
|
b.asm.io).apply(null,arguments)},qq=b._emscripten_bind_btRigidBodyConstructionInfo_btRigidBodyConstructionInfo_3=function(){return(qq=b._emscripten_bind_btRigidBodyConstructionInfo_btRigidBodyConstructionInfo_3=b.asm.jo).apply(null,arguments)},rq=b._emscripten_bind_btRigidBodyConstructionInfo_btRigidBodyConstructionInfo_4=function(){return(rq=b._emscripten_bind_btRigidBodyConstructionInfo_btRigidBodyConstructionInfo_4=b.asm.ko).apply(null,arguments)},sq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_linearDamping_0=
|
||||||
|
function(){return(sq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_linearDamping_0=b.asm.lo).apply(null,arguments)},tq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_linearDamping_1=function(){return(tq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_linearDamping_1=b.asm.mo).apply(null,arguments)},uq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_angularDamping_0=function(){return(uq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_angularDamping_0=b.asm.no).apply(null,
|
||||||
|
arguments)},vq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_angularDamping_1=function(){return(vq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_angularDamping_1=b.asm.oo).apply(null,arguments)},wq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_friction_0=function(){return(wq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_friction_0=b.asm.po).apply(null,arguments)},xq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_friction_1=function(){return(xq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_friction_1=
|
||||||
|
b.asm.qo).apply(null,arguments)},yq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_rollingFriction_0=function(){return(yq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_rollingFriction_0=b.asm.ro).apply(null,arguments)},zq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_rollingFriction_1=function(){return(zq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_rollingFriction_1=b.asm.so).apply(null,arguments)},Aq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_restitution_0=
|
||||||
|
function(){return(Aq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_restitution_0=b.asm.to).apply(null,arguments)},Bq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_restitution_1=function(){return(Bq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_restitution_1=b.asm.uo).apply(null,arguments)},Cq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_linearSleepingThreshold_0=function(){return(Cq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_linearSleepingThreshold_0=b.asm.vo).apply(null,
|
||||||
|
arguments)},Dq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_linearSleepingThreshold_1=function(){return(Dq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_linearSleepingThreshold_1=b.asm.wo).apply(null,arguments)},Eq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_angularSleepingThreshold_0=function(){return(Eq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_angularSleepingThreshold_0=b.asm.xo).apply(null,arguments)},Fq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_angularSleepingThreshold_1=
|
||||||
|
function(){return(Fq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_angularSleepingThreshold_1=b.asm.yo).apply(null,arguments)},Gq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_additionalDamping_0=function(){return(Gq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_additionalDamping_0=b.asm.zo).apply(null,arguments)},Hq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_additionalDamping_1=function(){return(Hq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_additionalDamping_1=
|
||||||
|
b.asm.Ao).apply(null,arguments)},Iq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_additionalDampingFactor_0=function(){return(Iq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_additionalDampingFactor_0=b.asm.Bo).apply(null,arguments)},Jq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_additionalDampingFactor_1=function(){return(Jq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_additionalDampingFactor_1=b.asm.Co).apply(null,arguments)},Kq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_additionalLinearDampingThresholdSqr_0=
|
||||||
|
function(){return(Kq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_additionalLinearDampingThresholdSqr_0=b.asm.Do).apply(null,arguments)},Lq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_additionalLinearDampingThresholdSqr_1=function(){return(Lq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_additionalLinearDampingThresholdSqr_1=b.asm.Eo).apply(null,arguments)},Mq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_additionalAngularDampingThresholdSqr_0=function(){return(Mq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_additionalAngularDampingThresholdSqr_0=
|
||||||
|
b.asm.Fo).apply(null,arguments)},Nq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_additionalAngularDampingThresholdSqr_1=function(){return(Nq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_additionalAngularDampingThresholdSqr_1=b.asm.Go).apply(null,arguments)},Oq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_additionalAngularDampingFactor_0=function(){return(Oq=b._emscripten_bind_btRigidBodyConstructionInfo_get_m_additionalAngularDampingFactor_0=b.asm.Ho).apply(null,arguments)},
|
||||||
|
Pq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_additionalAngularDampingFactor_1=function(){return(Pq=b._emscripten_bind_btRigidBodyConstructionInfo_set_m_additionalAngularDampingFactor_1=b.asm.Io).apply(null,arguments)},Qq=b._emscripten_bind_btRigidBodyConstructionInfo___destroy___0=function(){return(Qq=b._emscripten_bind_btRigidBodyConstructionInfo___destroy___0=b.asm.Jo).apply(null,arguments)},Rq=b._emscripten_bind_btRigidBody_btRigidBody_1=function(){return(Rq=b._emscripten_bind_btRigidBody_btRigidBody_1=
|
||||||
|
b.asm.Ko).apply(null,arguments)},Sq=b._emscripten_bind_btRigidBody_getCenterOfMassTransform_0=function(){return(Sq=b._emscripten_bind_btRigidBody_getCenterOfMassTransform_0=b.asm.Lo).apply(null,arguments)},Tq=b._emscripten_bind_btRigidBody_setCenterOfMassTransform_1=function(){return(Tq=b._emscripten_bind_btRigidBody_setCenterOfMassTransform_1=b.asm.Mo).apply(null,arguments)},Uq=b._emscripten_bind_btRigidBody_setSleepingThresholds_2=function(){return(Uq=b._emscripten_bind_btRigidBody_setSleepingThresholds_2=
|
||||||
|
b.asm.No).apply(null,arguments)},Vq=b._emscripten_bind_btRigidBody_getLinearDamping_0=function(){return(Vq=b._emscripten_bind_btRigidBody_getLinearDamping_0=b.asm.Oo).apply(null,arguments)},Wq=b._emscripten_bind_btRigidBody_getAngularDamping_0=function(){return(Wq=b._emscripten_bind_btRigidBody_getAngularDamping_0=b.asm.Po).apply(null,arguments)},Xq=b._emscripten_bind_btRigidBody_setDamping_2=function(){return(Xq=b._emscripten_bind_btRigidBody_setDamping_2=b.asm.Qo).apply(null,arguments)},Yq=b._emscripten_bind_btRigidBody_setMassProps_2=
|
||||||
|
function(){return(Yq=b._emscripten_bind_btRigidBody_setMassProps_2=b.asm.Ro).apply(null,arguments)},Zq=b._emscripten_bind_btRigidBody_getLinearFactor_0=function(){return(Zq=b._emscripten_bind_btRigidBody_getLinearFactor_0=b.asm.So).apply(null,arguments)},$q=b._emscripten_bind_btRigidBody_setLinearFactor_1=function(){return($q=b._emscripten_bind_btRigidBody_setLinearFactor_1=b.asm.To).apply(null,arguments)},ar=b._emscripten_bind_btRigidBody_applyTorque_1=function(){return(ar=b._emscripten_bind_btRigidBody_applyTorque_1=
|
||||||
|
b.asm.Uo).apply(null,arguments)},br=b._emscripten_bind_btRigidBody_applyLocalTorque_1=function(){return(br=b._emscripten_bind_btRigidBody_applyLocalTorque_1=b.asm.Vo).apply(null,arguments)},cr=b._emscripten_bind_btRigidBody_applyForce_2=function(){return(cr=b._emscripten_bind_btRigidBody_applyForce_2=b.asm.Wo).apply(null,arguments)},dr=b._emscripten_bind_btRigidBody_applyCentralForce_1=function(){return(dr=b._emscripten_bind_btRigidBody_applyCentralForce_1=b.asm.Xo).apply(null,arguments)},er=b._emscripten_bind_btRigidBody_applyCentralLocalForce_1=
|
||||||
|
function(){return(er=b._emscripten_bind_btRigidBody_applyCentralLocalForce_1=b.asm.Yo).apply(null,arguments)},fr=b._emscripten_bind_btRigidBody_applyTorqueImpulse_1=function(){return(fr=b._emscripten_bind_btRigidBody_applyTorqueImpulse_1=b.asm.Zo).apply(null,arguments)},gr=b._emscripten_bind_btRigidBody_applyImpulse_2=function(){return(gr=b._emscripten_bind_btRigidBody_applyImpulse_2=b.asm._o).apply(null,arguments)},hr=b._emscripten_bind_btRigidBody_applyCentralImpulse_1=function(){return(hr=b._emscripten_bind_btRigidBody_applyCentralImpulse_1=
|
||||||
|
b.asm.$o).apply(null,arguments)},ir=b._emscripten_bind_btRigidBody_updateInertiaTensor_0=function(){return(ir=b._emscripten_bind_btRigidBody_updateInertiaTensor_0=b.asm.ap).apply(null,arguments)},jr=b._emscripten_bind_btRigidBody_getLinearVelocity_0=function(){return(jr=b._emscripten_bind_btRigidBody_getLinearVelocity_0=b.asm.bp).apply(null,arguments)},kr=b._emscripten_bind_btRigidBody_getAngularVelocity_0=function(){return(kr=b._emscripten_bind_btRigidBody_getAngularVelocity_0=b.asm.cp).apply(null,
|
||||||
|
arguments)},lr=b._emscripten_bind_btRigidBody_setLinearVelocity_1=function(){return(lr=b._emscripten_bind_btRigidBody_setLinearVelocity_1=b.asm.dp).apply(null,arguments)},mr=b._emscripten_bind_btRigidBody_setAngularVelocity_1=function(){return(mr=b._emscripten_bind_btRigidBody_setAngularVelocity_1=b.asm.ep).apply(null,arguments)},nr=b._emscripten_bind_btRigidBody_getMotionState_0=function(){return(nr=b._emscripten_bind_btRigidBody_getMotionState_0=b.asm.fp).apply(null,arguments)},or=b._emscripten_bind_btRigidBody_setMotionState_1=
|
||||||
|
function(){return(or=b._emscripten_bind_btRigidBody_setMotionState_1=b.asm.gp).apply(null,arguments)},pr=b._emscripten_bind_btRigidBody_getAngularFactor_0=function(){return(pr=b._emscripten_bind_btRigidBody_getAngularFactor_0=b.asm.hp).apply(null,arguments)},qr=b._emscripten_bind_btRigidBody_setAngularFactor_1=function(){return(qr=b._emscripten_bind_btRigidBody_setAngularFactor_1=b.asm.ip).apply(null,arguments)},rr=b._emscripten_bind_btRigidBody_upcast_1=function(){return(rr=b._emscripten_bind_btRigidBody_upcast_1=
|
||||||
|
b.asm.jp).apply(null,arguments)},sr=b._emscripten_bind_btRigidBody_getAabb_2=function(){return(sr=b._emscripten_bind_btRigidBody_getAabb_2=b.asm.kp).apply(null,arguments)},tr=b._emscripten_bind_btRigidBody_applyGravity_0=function(){return(tr=b._emscripten_bind_btRigidBody_applyGravity_0=b.asm.lp).apply(null,arguments)},ur=b._emscripten_bind_btRigidBody_getGravity_0=function(){return(ur=b._emscripten_bind_btRigidBody_getGravity_0=b.asm.mp).apply(null,arguments)},vr=b._emscripten_bind_btRigidBody_setGravity_1=
|
||||||
|
function(){return(vr=b._emscripten_bind_btRigidBody_setGravity_1=b.asm.np).apply(null,arguments)},wr=b._emscripten_bind_btRigidBody_getBroadphaseProxy_0=function(){return(wr=b._emscripten_bind_btRigidBody_getBroadphaseProxy_0=b.asm.op).apply(null,arguments)},xr=b._emscripten_bind_btRigidBody_clearForces_0=function(){return(xr=b._emscripten_bind_btRigidBody_clearForces_0=b.asm.pp).apply(null,arguments)},yr=b._emscripten_bind_btRigidBody_setFlags_1=function(){return(yr=b._emscripten_bind_btRigidBody_setFlags_1=
|
||||||
|
b.asm.qp).apply(null,arguments)},zr=b._emscripten_bind_btRigidBody_getFlags_0=function(){return(zr=b._emscripten_bind_btRigidBody_getFlags_0=b.asm.rp).apply(null,arguments)},Ar=b._emscripten_bind_btRigidBody_setAnisotropicFriction_2=function(){return(Ar=b._emscripten_bind_btRigidBody_setAnisotropicFriction_2=b.asm.sp).apply(null,arguments)},Br=b._emscripten_bind_btRigidBody_getCollisionShape_0=function(){return(Br=b._emscripten_bind_btRigidBody_getCollisionShape_0=b.asm.tp).apply(null,arguments)},
|
||||||
|
Cr=b._emscripten_bind_btRigidBody_setContactProcessingThreshold_1=function(){return(Cr=b._emscripten_bind_btRigidBody_setContactProcessingThreshold_1=b.asm.up).apply(null,arguments)},Dr=b._emscripten_bind_btRigidBody_setActivationState_1=function(){return(Dr=b._emscripten_bind_btRigidBody_setActivationState_1=b.asm.vp).apply(null,arguments)},Er=b._emscripten_bind_btRigidBody_forceActivationState_1=function(){return(Er=b._emscripten_bind_btRigidBody_forceActivationState_1=b.asm.wp).apply(null,arguments)},
|
||||||
|
Fr=b._emscripten_bind_btRigidBody_activate_0=function(){return(Fr=b._emscripten_bind_btRigidBody_activate_0=b.asm.xp).apply(null,arguments)},Gr=b._emscripten_bind_btRigidBody_activate_1=function(){return(Gr=b._emscripten_bind_btRigidBody_activate_1=b.asm.yp).apply(null,arguments)},Hr=b._emscripten_bind_btRigidBody_isActive_0=function(){return(Hr=b._emscripten_bind_btRigidBody_isActive_0=b.asm.zp).apply(null,arguments)},Ir=b._emscripten_bind_btRigidBody_isKinematicObject_0=function(){return(Ir=b._emscripten_bind_btRigidBody_isKinematicObject_0=
|
||||||
|
b.asm.Ap).apply(null,arguments)},Jr=b._emscripten_bind_btRigidBody_isStaticObject_0=function(){return(Jr=b._emscripten_bind_btRigidBody_isStaticObject_0=b.asm.Bp).apply(null,arguments)},Kr=b._emscripten_bind_btRigidBody_isStaticOrKinematicObject_0=function(){return(Kr=b._emscripten_bind_btRigidBody_isStaticOrKinematicObject_0=b.asm.Cp).apply(null,arguments)},Lr=b._emscripten_bind_btRigidBody_getRestitution_0=function(){return(Lr=b._emscripten_bind_btRigidBody_getRestitution_0=b.asm.Dp).apply(null,
|
||||||
|
arguments)},Mr=b._emscripten_bind_btRigidBody_getFriction_0=function(){return(Mr=b._emscripten_bind_btRigidBody_getFriction_0=b.asm.Ep).apply(null,arguments)},Nr=b._emscripten_bind_btRigidBody_getRollingFriction_0=function(){return(Nr=b._emscripten_bind_btRigidBody_getRollingFriction_0=b.asm.Fp).apply(null,arguments)},Or=b._emscripten_bind_btRigidBody_setRestitution_1=function(){return(Or=b._emscripten_bind_btRigidBody_setRestitution_1=b.asm.Gp).apply(null,arguments)},Pr=b._emscripten_bind_btRigidBody_setFriction_1=
|
||||||
|
function(){return(Pr=b._emscripten_bind_btRigidBody_setFriction_1=b.asm.Hp).apply(null,arguments)},Qr=b._emscripten_bind_btRigidBody_setRollingFriction_1=function(){return(Qr=b._emscripten_bind_btRigidBody_setRollingFriction_1=b.asm.Ip).apply(null,arguments)},Rr=b._emscripten_bind_btRigidBody_getWorldTransform_0=function(){return(Rr=b._emscripten_bind_btRigidBody_getWorldTransform_0=b.asm.Jp).apply(null,arguments)},Sr=b._emscripten_bind_btRigidBody_getCollisionFlags_0=function(){return(Sr=b._emscripten_bind_btRigidBody_getCollisionFlags_0=
|
||||||
|
b.asm.Kp).apply(null,arguments)},Tr=b._emscripten_bind_btRigidBody_setCollisionFlags_1=function(){return(Tr=b._emscripten_bind_btRigidBody_setCollisionFlags_1=b.asm.Lp).apply(null,arguments)},Ur=b._emscripten_bind_btRigidBody_setWorldTransform_1=function(){return(Ur=b._emscripten_bind_btRigidBody_setWorldTransform_1=b.asm.Mp).apply(null,arguments)},Vr=b._emscripten_bind_btRigidBody_setCollisionShape_1=function(){return(Vr=b._emscripten_bind_btRigidBody_setCollisionShape_1=b.asm.Np).apply(null,arguments)},
|
||||||
|
Wr=b._emscripten_bind_btRigidBody_setCcdMotionThreshold_1=function(){return(Wr=b._emscripten_bind_btRigidBody_setCcdMotionThreshold_1=b.asm.Op).apply(null,arguments)},Xr=b._emscripten_bind_btRigidBody_setCcdSweptSphereRadius_1=function(){return(Xr=b._emscripten_bind_btRigidBody_setCcdSweptSphereRadius_1=b.asm.Pp).apply(null,arguments)},Yr=b._emscripten_bind_btRigidBody_getUserIndex_0=function(){return(Yr=b._emscripten_bind_btRigidBody_getUserIndex_0=b.asm.Qp).apply(null,arguments)},Zr=b._emscripten_bind_btRigidBody_setUserIndex_1=
|
||||||
|
function(){return(Zr=b._emscripten_bind_btRigidBody_setUserIndex_1=b.asm.Rp).apply(null,arguments)},$r=b._emscripten_bind_btRigidBody_getUserPointer_0=function(){return($r=b._emscripten_bind_btRigidBody_getUserPointer_0=b.asm.Sp).apply(null,arguments)},as=b._emscripten_bind_btRigidBody_setUserPointer_1=function(){return(as=b._emscripten_bind_btRigidBody_setUserPointer_1=b.asm.Tp).apply(null,arguments)},bs=b._emscripten_bind_btRigidBody_getBroadphaseHandle_0=function(){return(bs=b._emscripten_bind_btRigidBody_getBroadphaseHandle_0=
|
||||||
|
b.asm.Up).apply(null,arguments)},cs=b._emscripten_bind_btRigidBody___destroy___0=function(){return(cs=b._emscripten_bind_btRigidBody___destroy___0=b.asm.Vp).apply(null,arguments)},ds=b._emscripten_bind_btConstraintSetting_btConstraintSetting_0=function(){return(ds=b._emscripten_bind_btConstraintSetting_btConstraintSetting_0=b.asm.Wp).apply(null,arguments)},es=b._emscripten_bind_btConstraintSetting_get_m_tau_0=function(){return(es=b._emscripten_bind_btConstraintSetting_get_m_tau_0=b.asm.Xp).apply(null,
|
||||||
|
arguments)},gs=b._emscripten_bind_btConstraintSetting_set_m_tau_1=function(){return(gs=b._emscripten_bind_btConstraintSetting_set_m_tau_1=b.asm.Yp).apply(null,arguments)},hs=b._emscripten_bind_btConstraintSetting_get_m_damping_0=function(){return(hs=b._emscripten_bind_btConstraintSetting_get_m_damping_0=b.asm.Zp).apply(null,arguments)},is=b._emscripten_bind_btConstraintSetting_set_m_damping_1=function(){return(is=b._emscripten_bind_btConstraintSetting_set_m_damping_1=b.asm._p).apply(null,arguments)},
|
||||||
|
js=b._emscripten_bind_btConstraintSetting_get_m_impulseClamp_0=function(){return(js=b._emscripten_bind_btConstraintSetting_get_m_impulseClamp_0=b.asm.$p).apply(null,arguments)},ks=b._emscripten_bind_btConstraintSetting_set_m_impulseClamp_1=function(){return(ks=b._emscripten_bind_btConstraintSetting_set_m_impulseClamp_1=b.asm.aq).apply(null,arguments)},ls=b._emscripten_bind_btConstraintSetting___destroy___0=function(){return(ls=b._emscripten_bind_btConstraintSetting___destroy___0=b.asm.bq).apply(null,
|
||||||
|
arguments)},ms=b._emscripten_bind_btPoint2PointConstraint_btPoint2PointConstraint_2=function(){return(ms=b._emscripten_bind_btPoint2PointConstraint_btPoint2PointConstraint_2=b.asm.cq).apply(null,arguments)},ns=b._emscripten_bind_btPoint2PointConstraint_btPoint2PointConstraint_4=function(){return(ns=b._emscripten_bind_btPoint2PointConstraint_btPoint2PointConstraint_4=b.asm.dq).apply(null,arguments)},ps=b._emscripten_bind_btPoint2PointConstraint_setPivotA_1=function(){return(ps=b._emscripten_bind_btPoint2PointConstraint_setPivotA_1=
|
||||||
|
b.asm.eq).apply(null,arguments)},qs=b._emscripten_bind_btPoint2PointConstraint_setPivotB_1=function(){return(qs=b._emscripten_bind_btPoint2PointConstraint_setPivotB_1=b.asm.fq).apply(null,arguments)},rs=b._emscripten_bind_btPoint2PointConstraint_getPivotInA_0=function(){return(rs=b._emscripten_bind_btPoint2PointConstraint_getPivotInA_0=b.asm.gq).apply(null,arguments)},ss=b._emscripten_bind_btPoint2PointConstraint_getPivotInB_0=function(){return(ss=b._emscripten_bind_btPoint2PointConstraint_getPivotInB_0=
|
||||||
|
b.asm.hq).apply(null,arguments)},ts=b._emscripten_bind_btPoint2PointConstraint_enableFeedback_1=function(){return(ts=b._emscripten_bind_btPoint2PointConstraint_enableFeedback_1=b.asm.iq).apply(null,arguments)},us=b._emscripten_bind_btPoint2PointConstraint_getBreakingImpulseThreshold_0=function(){return(us=b._emscripten_bind_btPoint2PointConstraint_getBreakingImpulseThreshold_0=b.asm.jq).apply(null,arguments)},vs=b._emscripten_bind_btPoint2PointConstraint_setBreakingImpulseThreshold_1=function(){return(vs=
|
||||||
|
b._emscripten_bind_btPoint2PointConstraint_setBreakingImpulseThreshold_1=b.asm.kq).apply(null,arguments)},xs=b._emscripten_bind_btPoint2PointConstraint_getParam_2=function(){return(xs=b._emscripten_bind_btPoint2PointConstraint_getParam_2=b.asm.lq).apply(null,arguments)},ys=b._emscripten_bind_btPoint2PointConstraint_setParam_3=function(){return(ys=b._emscripten_bind_btPoint2PointConstraint_setParam_3=b.asm.mq).apply(null,arguments)},zs=b._emscripten_bind_btPoint2PointConstraint_get_m_setting_0=function(){return(zs=
|
||||||
|
b._emscripten_bind_btPoint2PointConstraint_get_m_setting_0=b.asm.nq).apply(null,arguments)},As=b._emscripten_bind_btPoint2PointConstraint_set_m_setting_1=function(){return(As=b._emscripten_bind_btPoint2PointConstraint_set_m_setting_1=b.asm.oq).apply(null,arguments)},Bs=b._emscripten_bind_btPoint2PointConstraint___destroy___0=function(){return(Bs=b._emscripten_bind_btPoint2PointConstraint___destroy___0=b.asm.pq).apply(null,arguments)},Cs=b._emscripten_bind_btGeneric6DofSpringConstraint_btGeneric6DofSpringConstraint_3=
|
||||||
|
function(){return(Cs=b._emscripten_bind_btGeneric6DofSpringConstraint_btGeneric6DofSpringConstraint_3=b.asm.qq).apply(null,arguments)},Ds=b._emscripten_bind_btGeneric6DofSpringConstraint_btGeneric6DofSpringConstraint_5=function(){return(Ds=b._emscripten_bind_btGeneric6DofSpringConstraint_btGeneric6DofSpringConstraint_5=b.asm.rq).apply(null,arguments)},Es=b._emscripten_bind_btGeneric6DofSpringConstraint_enableSpring_2=function(){return(Es=b._emscripten_bind_btGeneric6DofSpringConstraint_enableSpring_2=
|
||||||
|
b.asm.sq).apply(null,arguments)},Fs=b._emscripten_bind_btGeneric6DofSpringConstraint_setStiffness_2=function(){return(Fs=b._emscripten_bind_btGeneric6DofSpringConstraint_setStiffness_2=b.asm.tq).apply(null,arguments)},Gs=b._emscripten_bind_btGeneric6DofSpringConstraint_setDamping_2=function(){return(Gs=b._emscripten_bind_btGeneric6DofSpringConstraint_setDamping_2=b.asm.uq).apply(null,arguments)},Hs=b._emscripten_bind_btGeneric6DofSpringConstraint_setEquilibriumPoint_0=function(){return(Hs=b._emscripten_bind_btGeneric6DofSpringConstraint_setEquilibriumPoint_0=
|
||||||
|
b.asm.vq).apply(null,arguments)},Is=b._emscripten_bind_btGeneric6DofSpringConstraint_setEquilibriumPoint_1=function(){return(Is=b._emscripten_bind_btGeneric6DofSpringConstraint_setEquilibriumPoint_1=b.asm.wq).apply(null,arguments)},Js=b._emscripten_bind_btGeneric6DofSpringConstraint_setEquilibriumPoint_2=function(){return(Js=b._emscripten_bind_btGeneric6DofSpringConstraint_setEquilibriumPoint_2=b.asm.xq).apply(null,arguments)},Ks=b._emscripten_bind_btGeneric6DofSpringConstraint_setLinearLowerLimit_1=
|
||||||
|
function(){return(Ks=b._emscripten_bind_btGeneric6DofSpringConstraint_setLinearLowerLimit_1=b.asm.yq).apply(null,arguments)},Ls=b._emscripten_bind_btGeneric6DofSpringConstraint_setLinearUpperLimit_1=function(){return(Ls=b._emscripten_bind_btGeneric6DofSpringConstraint_setLinearUpperLimit_1=b.asm.zq).apply(null,arguments)},Ms=b._emscripten_bind_btGeneric6DofSpringConstraint_setAngularLowerLimit_1=function(){return(Ms=b._emscripten_bind_btGeneric6DofSpringConstraint_setAngularLowerLimit_1=b.asm.Aq).apply(null,
|
||||||
|
arguments)},Ns=b._emscripten_bind_btGeneric6DofSpringConstraint_setAngularUpperLimit_1=function(){return(Ns=b._emscripten_bind_btGeneric6DofSpringConstraint_setAngularUpperLimit_1=b.asm.Bq).apply(null,arguments)},Os=b._emscripten_bind_btGeneric6DofSpringConstraint_getFrameOffsetA_0=function(){return(Os=b._emscripten_bind_btGeneric6DofSpringConstraint_getFrameOffsetA_0=b.asm.Cq).apply(null,arguments)},Ps=b._emscripten_bind_btGeneric6DofSpringConstraint_enableFeedback_1=function(){return(Ps=b._emscripten_bind_btGeneric6DofSpringConstraint_enableFeedback_1=
|
||||||
|
b.asm.Dq).apply(null,arguments)},Qs=b._emscripten_bind_btGeneric6DofSpringConstraint_getBreakingImpulseThreshold_0=function(){return(Qs=b._emscripten_bind_btGeneric6DofSpringConstraint_getBreakingImpulseThreshold_0=b.asm.Eq).apply(null,arguments)},Rs=b._emscripten_bind_btGeneric6DofSpringConstraint_setBreakingImpulseThreshold_1=function(){return(Rs=b._emscripten_bind_btGeneric6DofSpringConstraint_setBreakingImpulseThreshold_1=b.asm.Fq).apply(null,arguments)},Ss=b._emscripten_bind_btGeneric6DofSpringConstraint_getParam_2=
|
||||||
|
function(){return(Ss=b._emscripten_bind_btGeneric6DofSpringConstraint_getParam_2=b.asm.Gq).apply(null,arguments)},Ts=b._emscripten_bind_btGeneric6DofSpringConstraint_setParam_3=function(){return(Ts=b._emscripten_bind_btGeneric6DofSpringConstraint_setParam_3=b.asm.Hq).apply(null,arguments)},Us=b._emscripten_bind_btGeneric6DofSpringConstraint___destroy___0=function(){return(Us=b._emscripten_bind_btGeneric6DofSpringConstraint___destroy___0=b.asm.Iq).apply(null,arguments)},Vs=b._emscripten_bind_btSequentialImpulseConstraintSolver_btSequentialImpulseConstraintSolver_0=
|
||||||
|
function(){return(Vs=b._emscripten_bind_btSequentialImpulseConstraintSolver_btSequentialImpulseConstraintSolver_0=b.asm.Jq).apply(null,arguments)},Ws=b._emscripten_bind_btSequentialImpulseConstraintSolver___destroy___0=function(){return(Ws=b._emscripten_bind_btSequentialImpulseConstraintSolver___destroy___0=b.asm.Kq).apply(null,arguments)},Xs=b._emscripten_bind_btConeTwistConstraint_btConeTwistConstraint_2=function(){return(Xs=b._emscripten_bind_btConeTwistConstraint_btConeTwistConstraint_2=b.asm.Lq).apply(null,
|
||||||
|
arguments)},Ys=b._emscripten_bind_btConeTwistConstraint_btConeTwistConstraint_4=function(){return(Ys=b._emscripten_bind_btConeTwistConstraint_btConeTwistConstraint_4=b.asm.Mq).apply(null,arguments)},Zs=b._emscripten_bind_btConeTwistConstraint_setLimit_2=function(){return(Zs=b._emscripten_bind_btConeTwistConstraint_setLimit_2=b.asm.Nq).apply(null,arguments)},$s=b._emscripten_bind_btConeTwistConstraint_setAngularOnly_1=function(){return($s=b._emscripten_bind_btConeTwistConstraint_setAngularOnly_1=b.asm.Oq).apply(null,
|
||||||
|
arguments)},at=b._emscripten_bind_btConeTwistConstraint_setDamping_1=function(){return(at=b._emscripten_bind_btConeTwistConstraint_setDamping_1=b.asm.Pq).apply(null,arguments)},bt=b._emscripten_bind_btConeTwistConstraint_enableMotor_1=function(){return(bt=b._emscripten_bind_btConeTwistConstraint_enableMotor_1=b.asm.Qq).apply(null,arguments)},ct=b._emscripten_bind_btConeTwistConstraint_setMaxMotorImpulse_1=function(){return(ct=b._emscripten_bind_btConeTwistConstraint_setMaxMotorImpulse_1=b.asm.Rq).apply(null,
|
||||||
|
arguments)},dt=b._emscripten_bind_btConeTwistConstraint_setMaxMotorImpulseNormalized_1=function(){return(dt=b._emscripten_bind_btConeTwistConstraint_setMaxMotorImpulseNormalized_1=b.asm.Sq).apply(null,arguments)},et=b._emscripten_bind_btConeTwistConstraint_setMotorTarget_1=function(){return(et=b._emscripten_bind_btConeTwistConstraint_setMotorTarget_1=b.asm.Tq).apply(null,arguments)},ft=b._emscripten_bind_btConeTwistConstraint_setMotorTargetInConstraintSpace_1=function(){return(ft=b._emscripten_bind_btConeTwistConstraint_setMotorTargetInConstraintSpace_1=
|
||||||
|
b.asm.Uq).apply(null,arguments)},gt=b._emscripten_bind_btConeTwistConstraint_enableFeedback_1=function(){return(gt=b._emscripten_bind_btConeTwistConstraint_enableFeedback_1=b.asm.Vq).apply(null,arguments)},ht=b._emscripten_bind_btConeTwistConstraint_getBreakingImpulseThreshold_0=function(){return(ht=b._emscripten_bind_btConeTwistConstraint_getBreakingImpulseThreshold_0=b.asm.Wq).apply(null,arguments)},it=b._emscripten_bind_btConeTwistConstraint_setBreakingImpulseThreshold_1=function(){return(it=b._emscripten_bind_btConeTwistConstraint_setBreakingImpulseThreshold_1=
|
||||||
|
b.asm.Xq).apply(null,arguments)},jt=b._emscripten_bind_btConeTwistConstraint_getParam_2=function(){return(jt=b._emscripten_bind_btConeTwistConstraint_getParam_2=b.asm.Yq).apply(null,arguments)},kt=b._emscripten_bind_btConeTwistConstraint_setParam_3=function(){return(kt=b._emscripten_bind_btConeTwistConstraint_setParam_3=b.asm.Zq).apply(null,arguments)},lt=b._emscripten_bind_btConeTwistConstraint___destroy___0=function(){return(lt=b._emscripten_bind_btConeTwistConstraint___destroy___0=b.asm._q).apply(null,
|
||||||
|
arguments)},mt=b._emscripten_bind_btHingeConstraint_btHingeConstraint_2=function(){return(mt=b._emscripten_bind_btHingeConstraint_btHingeConstraint_2=b.asm.$q).apply(null,arguments)},nt=b._emscripten_bind_btHingeConstraint_btHingeConstraint_3=function(){return(nt=b._emscripten_bind_btHingeConstraint_btHingeConstraint_3=b.asm.ar).apply(null,arguments)},ot=b._emscripten_bind_btHingeConstraint_btHingeConstraint_4=function(){return(ot=b._emscripten_bind_btHingeConstraint_btHingeConstraint_4=b.asm.br).apply(null,
|
||||||
|
arguments)},pt=b._emscripten_bind_btHingeConstraint_btHingeConstraint_5=function(){return(pt=b._emscripten_bind_btHingeConstraint_btHingeConstraint_5=b.asm.cr).apply(null,arguments)},qt=b._emscripten_bind_btHingeConstraint_btHingeConstraint_6=function(){return(qt=b._emscripten_bind_btHingeConstraint_btHingeConstraint_6=b.asm.dr).apply(null,arguments)},rt=b._emscripten_bind_btHingeConstraint_btHingeConstraint_7=function(){return(rt=b._emscripten_bind_btHingeConstraint_btHingeConstraint_7=b.asm.er).apply(null,
|
||||||
|
arguments)},st=b._emscripten_bind_btHingeConstraint_getHingeAngle_0=function(){return(st=b._emscripten_bind_btHingeConstraint_getHingeAngle_0=b.asm.fr).apply(null,arguments)},tt=b._emscripten_bind_btHingeConstraint_setLimit_4=function(){return(tt=b._emscripten_bind_btHingeConstraint_setLimit_4=b.asm.gr).apply(null,arguments)},ut=b._emscripten_bind_btHingeConstraint_setLimit_5=function(){return(ut=b._emscripten_bind_btHingeConstraint_setLimit_5=b.asm.hr).apply(null,arguments)},vt=b._emscripten_bind_btHingeConstraint_enableAngularMotor_3=
|
||||||
|
function(){return(vt=b._emscripten_bind_btHingeConstraint_enableAngularMotor_3=b.asm.ir).apply(null,arguments)},wt=b._emscripten_bind_btHingeConstraint_setAngularOnly_1=function(){return(wt=b._emscripten_bind_btHingeConstraint_setAngularOnly_1=b.asm.jr).apply(null,arguments)},xt=b._emscripten_bind_btHingeConstraint_enableMotor_1=function(){return(xt=b._emscripten_bind_btHingeConstraint_enableMotor_1=b.asm.kr).apply(null,arguments)},yt=b._emscripten_bind_btHingeConstraint_setMaxMotorImpulse_1=function(){return(yt=
|
||||||
|
b._emscripten_bind_btHingeConstraint_setMaxMotorImpulse_1=b.asm.lr).apply(null,arguments)},zt=b._emscripten_bind_btHingeConstraint_setMotorTarget_2=function(){return(zt=b._emscripten_bind_btHingeConstraint_setMotorTarget_2=b.asm.mr).apply(null,arguments)},At=b._emscripten_bind_btHingeConstraint_enableFeedback_1=function(){return(At=b._emscripten_bind_btHingeConstraint_enableFeedback_1=b.asm.nr).apply(null,arguments)},Bt=b._emscripten_bind_btHingeConstraint_getBreakingImpulseThreshold_0=function(){return(Bt=
|
||||||
|
b._emscripten_bind_btHingeConstraint_getBreakingImpulseThreshold_0=b.asm.or).apply(null,arguments)},Ct=b._emscripten_bind_btHingeConstraint_setBreakingImpulseThreshold_1=function(){return(Ct=b._emscripten_bind_btHingeConstraint_setBreakingImpulseThreshold_1=b.asm.pr).apply(null,arguments)},Dt=b._emscripten_bind_btHingeConstraint_getParam_2=function(){return(Dt=b._emscripten_bind_btHingeConstraint_getParam_2=b.asm.qr).apply(null,arguments)},Et=b._emscripten_bind_btHingeConstraint_setParam_3=function(){return(Et=
|
||||||
|
b._emscripten_bind_btHingeConstraint_setParam_3=b.asm.rr).apply(null,arguments)},Ft=b._emscripten_bind_btHingeConstraint___destroy___0=function(){return(Ft=b._emscripten_bind_btHingeConstraint___destroy___0=b.asm.sr).apply(null,arguments)},Gt=b._emscripten_bind_btSliderConstraint_btSliderConstraint_3=function(){return(Gt=b._emscripten_bind_btSliderConstraint_btSliderConstraint_3=b.asm.tr).apply(null,arguments)},Ht=b._emscripten_bind_btSliderConstraint_btSliderConstraint_5=function(){return(Ht=b._emscripten_bind_btSliderConstraint_btSliderConstraint_5=
|
||||||
|
b.asm.ur).apply(null,arguments)},It=b._emscripten_bind_btSliderConstraint_getLinearPos_0=function(){return(It=b._emscripten_bind_btSliderConstraint_getLinearPos_0=b.asm.vr).apply(null,arguments)},Jt=b._emscripten_bind_btSliderConstraint_getAngularPos_0=function(){return(Jt=b._emscripten_bind_btSliderConstraint_getAngularPos_0=b.asm.wr).apply(null,arguments)},Kt=b._emscripten_bind_btSliderConstraint_setLowerLinLimit_1=function(){return(Kt=b._emscripten_bind_btSliderConstraint_setLowerLinLimit_1=b.asm.xr).apply(null,
|
||||||
|
arguments)},Lt=b._emscripten_bind_btSliderConstraint_setUpperLinLimit_1=function(){return(Lt=b._emscripten_bind_btSliderConstraint_setUpperLinLimit_1=b.asm.yr).apply(null,arguments)},Mt=b._emscripten_bind_btSliderConstraint_setLowerAngLimit_1=function(){return(Mt=b._emscripten_bind_btSliderConstraint_setLowerAngLimit_1=b.asm.zr).apply(null,arguments)},Nt=b._emscripten_bind_btSliderConstraint_setUpperAngLimit_1=function(){return(Nt=b._emscripten_bind_btSliderConstraint_setUpperAngLimit_1=b.asm.Ar).apply(null,
|
||||||
|
arguments)},Ot=b._emscripten_bind_btSliderConstraint_setPoweredLinMotor_1=function(){return(Ot=b._emscripten_bind_btSliderConstraint_setPoweredLinMotor_1=b.asm.Br).apply(null,arguments)},Pt=b._emscripten_bind_btSliderConstraint_setMaxLinMotorForce_1=function(){return(Pt=b._emscripten_bind_btSliderConstraint_setMaxLinMotorForce_1=b.asm.Cr).apply(null,arguments)},Qt=b._emscripten_bind_btSliderConstraint_setTargetLinMotorVelocity_1=function(){return(Qt=b._emscripten_bind_btSliderConstraint_setTargetLinMotorVelocity_1=
|
||||||
|
b.asm.Dr).apply(null,arguments)},Rt=b._emscripten_bind_btSliderConstraint_enableFeedback_1=function(){return(Rt=b._emscripten_bind_btSliderConstraint_enableFeedback_1=b.asm.Er).apply(null,arguments)},St=b._emscripten_bind_btSliderConstraint_getBreakingImpulseThreshold_0=function(){return(St=b._emscripten_bind_btSliderConstraint_getBreakingImpulseThreshold_0=b.asm.Fr).apply(null,arguments)},Tt=b._emscripten_bind_btSliderConstraint_setBreakingImpulseThreshold_1=function(){return(Tt=b._emscripten_bind_btSliderConstraint_setBreakingImpulseThreshold_1=
|
||||||
|
b.asm.Gr).apply(null,arguments)},Ut=b._emscripten_bind_btSliderConstraint_getParam_2=function(){return(Ut=b._emscripten_bind_btSliderConstraint_getParam_2=b.asm.Hr).apply(null,arguments)},Vt=b._emscripten_bind_btSliderConstraint_setParam_3=function(){return(Vt=b._emscripten_bind_btSliderConstraint_setParam_3=b.asm.Ir).apply(null,arguments)},Wt=b._emscripten_bind_btSliderConstraint___destroy___0=function(){return(Wt=b._emscripten_bind_btSliderConstraint___destroy___0=b.asm.Jr).apply(null,arguments)},
|
||||||
|
Xt=b._emscripten_bind_btFixedConstraint_btFixedConstraint_4=function(){return(Xt=b._emscripten_bind_btFixedConstraint_btFixedConstraint_4=b.asm.Kr).apply(null,arguments)},Yt=b._emscripten_bind_btFixedConstraint_enableFeedback_1=function(){return(Yt=b._emscripten_bind_btFixedConstraint_enableFeedback_1=b.asm.Lr).apply(null,arguments)},Zt=b._emscripten_bind_btFixedConstraint_getBreakingImpulseThreshold_0=function(){return(Zt=b._emscripten_bind_btFixedConstraint_getBreakingImpulseThreshold_0=b.asm.Mr).apply(null,
|
||||||
|
arguments)},$t=b._emscripten_bind_btFixedConstraint_setBreakingImpulseThreshold_1=function(){return($t=b._emscripten_bind_btFixedConstraint_setBreakingImpulseThreshold_1=b.asm.Nr).apply(null,arguments)},au=b._emscripten_bind_btFixedConstraint_getParam_2=function(){return(au=b._emscripten_bind_btFixedConstraint_getParam_2=b.asm.Or).apply(null,arguments)},bu=b._emscripten_bind_btFixedConstraint_setParam_3=function(){return(bu=b._emscripten_bind_btFixedConstraint_setParam_3=b.asm.Pr).apply(null,arguments)},
|
||||||
|
cu=b._emscripten_bind_btFixedConstraint___destroy___0=function(){return(cu=b._emscripten_bind_btFixedConstraint___destroy___0=b.asm.Qr).apply(null,arguments)},du=b._emscripten_bind_btConstraintSolver___destroy___0=function(){return(du=b._emscripten_bind_btConstraintSolver___destroy___0=b.asm.Rr).apply(null,arguments)},eu=b._emscripten_bind_btDispatcherInfo_get_m_timeStep_0=function(){return(eu=b._emscripten_bind_btDispatcherInfo_get_m_timeStep_0=b.asm.Sr).apply(null,arguments)},fu=b._emscripten_bind_btDispatcherInfo_set_m_timeStep_1=
|
||||||
|
function(){return(fu=b._emscripten_bind_btDispatcherInfo_set_m_timeStep_1=b.asm.Tr).apply(null,arguments)},gu=b._emscripten_bind_btDispatcherInfo_get_m_stepCount_0=function(){return(gu=b._emscripten_bind_btDispatcherInfo_get_m_stepCount_0=b.asm.Ur).apply(null,arguments)},hu=b._emscripten_bind_btDispatcherInfo_set_m_stepCount_1=function(){return(hu=b._emscripten_bind_btDispatcherInfo_set_m_stepCount_1=b.asm.Vr).apply(null,arguments)},iu=b._emscripten_bind_btDispatcherInfo_get_m_dispatchFunc_0=function(){return(iu=
|
||||||
|
b._emscripten_bind_btDispatcherInfo_get_m_dispatchFunc_0=b.asm.Wr).apply(null,arguments)},ju=b._emscripten_bind_btDispatcherInfo_set_m_dispatchFunc_1=function(){return(ju=b._emscripten_bind_btDispatcherInfo_set_m_dispatchFunc_1=b.asm.Xr).apply(null,arguments)},ku=b._emscripten_bind_btDispatcherInfo_get_m_timeOfImpact_0=function(){return(ku=b._emscripten_bind_btDispatcherInfo_get_m_timeOfImpact_0=b.asm.Yr).apply(null,arguments)},lu=b._emscripten_bind_btDispatcherInfo_set_m_timeOfImpact_1=function(){return(lu=
|
||||||
|
b._emscripten_bind_btDispatcherInfo_set_m_timeOfImpact_1=b.asm.Zr).apply(null,arguments)},mu=b._emscripten_bind_btDispatcherInfo_get_m_useContinuous_0=function(){return(mu=b._emscripten_bind_btDispatcherInfo_get_m_useContinuous_0=b.asm._r).apply(null,arguments)},nu=b._emscripten_bind_btDispatcherInfo_set_m_useContinuous_1=function(){return(nu=b._emscripten_bind_btDispatcherInfo_set_m_useContinuous_1=b.asm.$r).apply(null,arguments)},ou=b._emscripten_bind_btDispatcherInfo_get_m_enableSatConvex_0=function(){return(ou=
|
||||||
|
b._emscripten_bind_btDispatcherInfo_get_m_enableSatConvex_0=b.asm.as).apply(null,arguments)},pu=b._emscripten_bind_btDispatcherInfo_set_m_enableSatConvex_1=function(){return(pu=b._emscripten_bind_btDispatcherInfo_set_m_enableSatConvex_1=b.asm.bs).apply(null,arguments)},qu=b._emscripten_bind_btDispatcherInfo_get_m_enableSPU_0=function(){return(qu=b._emscripten_bind_btDispatcherInfo_get_m_enableSPU_0=b.asm.cs).apply(null,arguments)},ru=b._emscripten_bind_btDispatcherInfo_set_m_enableSPU_1=function(){return(ru=
|
||||||
|
b._emscripten_bind_btDispatcherInfo_set_m_enableSPU_1=b.asm.ds).apply(null,arguments)},su=b._emscripten_bind_btDispatcherInfo_get_m_useEpa_0=function(){return(su=b._emscripten_bind_btDispatcherInfo_get_m_useEpa_0=b.asm.es).apply(null,arguments)},tu=b._emscripten_bind_btDispatcherInfo_set_m_useEpa_1=function(){return(tu=b._emscripten_bind_btDispatcherInfo_set_m_useEpa_1=b.asm.fs).apply(null,arguments)},uu=b._emscripten_bind_btDispatcherInfo_get_m_allowedCcdPenetration_0=function(){return(uu=b._emscripten_bind_btDispatcherInfo_get_m_allowedCcdPenetration_0=
|
||||||
|
b.asm.gs).apply(null,arguments)},vu=b._emscripten_bind_btDispatcherInfo_set_m_allowedCcdPenetration_1=function(){return(vu=b._emscripten_bind_btDispatcherInfo_set_m_allowedCcdPenetration_1=b.asm.hs).apply(null,arguments)},wu=b._emscripten_bind_btDispatcherInfo_get_m_useConvexConservativeDistanceUtil_0=function(){return(wu=b._emscripten_bind_btDispatcherInfo_get_m_useConvexConservativeDistanceUtil_0=b.asm.is).apply(null,arguments)},xu=b._emscripten_bind_btDispatcherInfo_set_m_useConvexConservativeDistanceUtil_1=
|
||||||
|
function(){return(xu=b._emscripten_bind_btDispatcherInfo_set_m_useConvexConservativeDistanceUtil_1=b.asm.js).apply(null,arguments)},yu=b._emscripten_bind_btDispatcherInfo_get_m_convexConservativeDistanceThreshold_0=function(){return(yu=b._emscripten_bind_btDispatcherInfo_get_m_convexConservativeDistanceThreshold_0=b.asm.ks).apply(null,arguments)},zu=b._emscripten_bind_btDispatcherInfo_set_m_convexConservativeDistanceThreshold_1=function(){return(zu=b._emscripten_bind_btDispatcherInfo_set_m_convexConservativeDistanceThreshold_1=
|
||||||
|
b.asm.ls).apply(null,arguments)},Au=b._emscripten_bind_btDispatcherInfo___destroy___0=function(){return(Au=b._emscripten_bind_btDispatcherInfo___destroy___0=b.asm.ms).apply(null,arguments)},Bu=b._emscripten_bind_btContactSolverInfo_get_m_splitImpulse_0=function(){return(Bu=b._emscripten_bind_btContactSolverInfo_get_m_splitImpulse_0=b.asm.ns).apply(null,arguments)},Cu=b._emscripten_bind_btContactSolverInfo_set_m_splitImpulse_1=function(){return(Cu=b._emscripten_bind_btContactSolverInfo_set_m_splitImpulse_1=
|
||||||
|
b.asm.os).apply(null,arguments)},Du=b._emscripten_bind_btContactSolverInfo_get_m_splitImpulsePenetrationThreshold_0=function(){return(Du=b._emscripten_bind_btContactSolverInfo_get_m_splitImpulsePenetrationThreshold_0=b.asm.ps).apply(null,arguments)},Eu=b._emscripten_bind_btContactSolverInfo_set_m_splitImpulsePenetrationThreshold_1=function(){return(Eu=b._emscripten_bind_btContactSolverInfo_set_m_splitImpulsePenetrationThreshold_1=b.asm.qs).apply(null,arguments)},Fu=b._emscripten_bind_btContactSolverInfo_get_m_numIterations_0=
|
||||||
|
function(){return(Fu=b._emscripten_bind_btContactSolverInfo_get_m_numIterations_0=b.asm.rs).apply(null,arguments)},Gu=b._emscripten_bind_btContactSolverInfo_set_m_numIterations_1=function(){return(Gu=b._emscripten_bind_btContactSolverInfo_set_m_numIterations_1=b.asm.ss).apply(null,arguments)},Hu=b._emscripten_bind_btContactSolverInfo___destroy___0=function(){return(Hu=b._emscripten_bind_btContactSolverInfo___destroy___0=b.asm.ts).apply(null,arguments)},Iu=b._emscripten_bind_btVehicleTuning_btVehicleTuning_0=
|
||||||
|
function(){return(Iu=b._emscripten_bind_btVehicleTuning_btVehicleTuning_0=b.asm.us).apply(null,arguments)},Ju=b._emscripten_bind_btVehicleTuning_get_m_suspensionStiffness_0=function(){return(Ju=b._emscripten_bind_btVehicleTuning_get_m_suspensionStiffness_0=b.asm.vs).apply(null,arguments)},Ku=b._emscripten_bind_btVehicleTuning_set_m_suspensionStiffness_1=function(){return(Ku=b._emscripten_bind_btVehicleTuning_set_m_suspensionStiffness_1=b.asm.ws).apply(null,arguments)},Lu=b._emscripten_bind_btVehicleTuning_get_m_suspensionCompression_0=
|
||||||
|
function(){return(Lu=b._emscripten_bind_btVehicleTuning_get_m_suspensionCompression_0=b.asm.xs).apply(null,arguments)},Mu=b._emscripten_bind_btVehicleTuning_set_m_suspensionCompression_1=function(){return(Mu=b._emscripten_bind_btVehicleTuning_set_m_suspensionCompression_1=b.asm.ys).apply(null,arguments)},Nu=b._emscripten_bind_btVehicleTuning_get_m_suspensionDamping_0=function(){return(Nu=b._emscripten_bind_btVehicleTuning_get_m_suspensionDamping_0=b.asm.zs).apply(null,arguments)},Ou=b._emscripten_bind_btVehicleTuning_set_m_suspensionDamping_1=
|
||||||
|
function(){return(Ou=b._emscripten_bind_btVehicleTuning_set_m_suspensionDamping_1=b.asm.As).apply(null,arguments)},Pu=b._emscripten_bind_btVehicleTuning_get_m_maxSuspensionTravelCm_0=function(){return(Pu=b._emscripten_bind_btVehicleTuning_get_m_maxSuspensionTravelCm_0=b.asm.Bs).apply(null,arguments)},Qu=b._emscripten_bind_btVehicleTuning_set_m_maxSuspensionTravelCm_1=function(){return(Qu=b._emscripten_bind_btVehicleTuning_set_m_maxSuspensionTravelCm_1=b.asm.Cs).apply(null,arguments)},Ru=b._emscripten_bind_btVehicleTuning_get_m_frictionSlip_0=
|
||||||
|
function(){return(Ru=b._emscripten_bind_btVehicleTuning_get_m_frictionSlip_0=b.asm.Ds).apply(null,arguments)},Su=b._emscripten_bind_btVehicleTuning_set_m_frictionSlip_1=function(){return(Su=b._emscripten_bind_btVehicleTuning_set_m_frictionSlip_1=b.asm.Es).apply(null,arguments)},Tu=b._emscripten_bind_btVehicleTuning_get_m_maxSuspensionForce_0=function(){return(Tu=b._emscripten_bind_btVehicleTuning_get_m_maxSuspensionForce_0=b.asm.Fs).apply(null,arguments)},Uu=b._emscripten_bind_btVehicleTuning_set_m_maxSuspensionForce_1=
|
||||||
|
function(){return(Uu=b._emscripten_bind_btVehicleTuning_set_m_maxSuspensionForce_1=b.asm.Gs).apply(null,arguments)},Vu=b._emscripten_bind_btVehicleRaycasterResult_get_m_hitPointInWorld_0=function(){return(Vu=b._emscripten_bind_btVehicleRaycasterResult_get_m_hitPointInWorld_0=b.asm.Hs).apply(null,arguments)},Wu=b._emscripten_bind_btVehicleRaycasterResult_set_m_hitPointInWorld_1=function(){return(Wu=b._emscripten_bind_btVehicleRaycasterResult_set_m_hitPointInWorld_1=b.asm.Is).apply(null,arguments)},
|
||||||
|
Xu=b._emscripten_bind_btVehicleRaycasterResult_get_m_hitNormalInWorld_0=function(){return(Xu=b._emscripten_bind_btVehicleRaycasterResult_get_m_hitNormalInWorld_0=b.asm.Js).apply(null,arguments)},Yu=b._emscripten_bind_btVehicleRaycasterResult_set_m_hitNormalInWorld_1=function(){return(Yu=b._emscripten_bind_btVehicleRaycasterResult_set_m_hitNormalInWorld_1=b.asm.Ks).apply(null,arguments)},Zu=b._emscripten_bind_btVehicleRaycasterResult_get_m_distFraction_0=function(){return(Zu=b._emscripten_bind_btVehicleRaycasterResult_get_m_distFraction_0=
|
||||||
|
b.asm.Ls).apply(null,arguments)},$u=b._emscripten_bind_btVehicleRaycasterResult_set_m_distFraction_1=function(){return($u=b._emscripten_bind_btVehicleRaycasterResult_set_m_distFraction_1=b.asm.Ms).apply(null,arguments)},av=b._emscripten_bind_btVehicleRaycasterResult___destroy___0=function(){return(av=b._emscripten_bind_btVehicleRaycasterResult___destroy___0=b.asm.Ns).apply(null,arguments)},bv=b._emscripten_bind_btDefaultVehicleRaycaster_btDefaultVehicleRaycaster_1=function(){return(bv=b._emscripten_bind_btDefaultVehicleRaycaster_btDefaultVehicleRaycaster_1=
|
||||||
|
b.asm.Os).apply(null,arguments)},cv=b._emscripten_bind_btDefaultVehicleRaycaster_castRay_3=function(){return(cv=b._emscripten_bind_btDefaultVehicleRaycaster_castRay_3=b.asm.Ps).apply(null,arguments)},dv=b._emscripten_bind_btDefaultVehicleRaycaster___destroy___0=function(){return(dv=b._emscripten_bind_btDefaultVehicleRaycaster___destroy___0=b.asm.Qs).apply(null,arguments)},ev=b._emscripten_bind_RaycastInfo_get_m_contactNormalWS_0=function(){return(ev=b._emscripten_bind_RaycastInfo_get_m_contactNormalWS_0=
|
||||||
|
b.asm.Rs).apply(null,arguments)},fv=b._emscripten_bind_RaycastInfo_set_m_contactNormalWS_1=function(){return(fv=b._emscripten_bind_RaycastInfo_set_m_contactNormalWS_1=b.asm.Ss).apply(null,arguments)},gv=b._emscripten_bind_RaycastInfo_get_m_contactPointWS_0=function(){return(gv=b._emscripten_bind_RaycastInfo_get_m_contactPointWS_0=b.asm.Ts).apply(null,arguments)},hv=b._emscripten_bind_RaycastInfo_set_m_contactPointWS_1=function(){return(hv=b._emscripten_bind_RaycastInfo_set_m_contactPointWS_1=b.asm.Us).apply(null,
|
||||||
|
arguments)},iv=b._emscripten_bind_RaycastInfo_get_m_suspensionLength_0=function(){return(iv=b._emscripten_bind_RaycastInfo_get_m_suspensionLength_0=b.asm.Vs).apply(null,arguments)},jv=b._emscripten_bind_RaycastInfo_set_m_suspensionLength_1=function(){return(jv=b._emscripten_bind_RaycastInfo_set_m_suspensionLength_1=b.asm.Ws).apply(null,arguments)},kv=b._emscripten_bind_RaycastInfo_get_m_hardPointWS_0=function(){return(kv=b._emscripten_bind_RaycastInfo_get_m_hardPointWS_0=b.asm.Xs).apply(null,arguments)},
|
||||||
|
lv=b._emscripten_bind_RaycastInfo_set_m_hardPointWS_1=function(){return(lv=b._emscripten_bind_RaycastInfo_set_m_hardPointWS_1=b.asm.Ys).apply(null,arguments)},mv=b._emscripten_bind_RaycastInfo_get_m_wheelDirectionWS_0=function(){return(mv=b._emscripten_bind_RaycastInfo_get_m_wheelDirectionWS_0=b.asm.Zs).apply(null,arguments)},nv=b._emscripten_bind_RaycastInfo_set_m_wheelDirectionWS_1=function(){return(nv=b._emscripten_bind_RaycastInfo_set_m_wheelDirectionWS_1=b.asm._s).apply(null,arguments)},ov=b._emscripten_bind_RaycastInfo_get_m_wheelAxleWS_0=
|
||||||
|
function(){return(ov=b._emscripten_bind_RaycastInfo_get_m_wheelAxleWS_0=b.asm.$s).apply(null,arguments)},pv=b._emscripten_bind_RaycastInfo_set_m_wheelAxleWS_1=function(){return(pv=b._emscripten_bind_RaycastInfo_set_m_wheelAxleWS_1=b.asm.at).apply(null,arguments)},qv=b._emscripten_bind_RaycastInfo_get_m_isInContact_0=function(){return(qv=b._emscripten_bind_RaycastInfo_get_m_isInContact_0=b.asm.bt).apply(null,arguments)},rv=b._emscripten_bind_RaycastInfo_set_m_isInContact_1=function(){return(rv=b._emscripten_bind_RaycastInfo_set_m_isInContact_1=
|
||||||
|
b.asm.ct).apply(null,arguments)},sv=b._emscripten_bind_RaycastInfo_get_m_groundObject_0=function(){return(sv=b._emscripten_bind_RaycastInfo_get_m_groundObject_0=b.asm.dt).apply(null,arguments)},tv=b._emscripten_bind_RaycastInfo_set_m_groundObject_1=function(){return(tv=b._emscripten_bind_RaycastInfo_set_m_groundObject_1=b.asm.et).apply(null,arguments)},uv=b._emscripten_bind_RaycastInfo___destroy___0=function(){return(uv=b._emscripten_bind_RaycastInfo___destroy___0=b.asm.ft).apply(null,arguments)},
|
||||||
|
vv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_chassisConnectionCS_0=function(){return(vv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_chassisConnectionCS_0=b.asm.gt).apply(null,arguments)},wv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_chassisConnectionCS_1=function(){return(wv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_chassisConnectionCS_1=b.asm.ht).apply(null,arguments)},xv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_wheelDirectionCS_0=function(){return(xv=
|
||||||
|
b._emscripten_bind_btWheelInfoConstructionInfo_get_m_wheelDirectionCS_0=b.asm.it).apply(null,arguments)},yv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_wheelDirectionCS_1=function(){return(yv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_wheelDirectionCS_1=b.asm.jt).apply(null,arguments)},zv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_wheelAxleCS_0=function(){return(zv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_wheelAxleCS_0=b.asm.kt).apply(null,arguments)},Av=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_wheelAxleCS_1=
|
||||||
|
function(){return(Av=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_wheelAxleCS_1=b.asm.lt).apply(null,arguments)},Bv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_suspensionRestLength_0=function(){return(Bv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_suspensionRestLength_0=b.asm.mt).apply(null,arguments)},Cv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_suspensionRestLength_1=function(){return(Cv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_suspensionRestLength_1=
|
||||||
|
b.asm.nt).apply(null,arguments)},Dv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_maxSuspensionTravelCm_0=function(){return(Dv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_maxSuspensionTravelCm_0=b.asm.ot).apply(null,arguments)},Ev=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_maxSuspensionTravelCm_1=function(){return(Ev=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_maxSuspensionTravelCm_1=b.asm.pt).apply(null,arguments)},Fv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_wheelRadius_0=
|
||||||
|
function(){return(Fv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_wheelRadius_0=b.asm.qt).apply(null,arguments)},Gv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_wheelRadius_1=function(){return(Gv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_wheelRadius_1=b.asm.rt).apply(null,arguments)},Hv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_suspensionStiffness_0=function(){return(Hv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_suspensionStiffness_0=b.asm.st).apply(null,
|
||||||
|
arguments)},Iv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_suspensionStiffness_1=function(){return(Iv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_suspensionStiffness_1=b.asm.tt).apply(null,arguments)},Jv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_wheelsDampingCompression_0=function(){return(Jv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_wheelsDampingCompression_0=b.asm.ut).apply(null,arguments)},Kv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_wheelsDampingCompression_1=
|
||||||
|
function(){return(Kv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_wheelsDampingCompression_1=b.asm.vt).apply(null,arguments)},Lv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_wheelsDampingRelaxation_0=function(){return(Lv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_wheelsDampingRelaxation_0=b.asm.wt).apply(null,arguments)},Mv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_wheelsDampingRelaxation_1=function(){return(Mv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_wheelsDampingRelaxation_1=
|
||||||
|
b.asm.xt).apply(null,arguments)},Nv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_frictionSlip_0=function(){return(Nv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_frictionSlip_0=b.asm.yt).apply(null,arguments)},Ov=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_frictionSlip_1=function(){return(Ov=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_frictionSlip_1=b.asm.zt).apply(null,arguments)},Pv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_maxSuspensionForce_0=function(){return(Pv=
|
||||||
|
b._emscripten_bind_btWheelInfoConstructionInfo_get_m_maxSuspensionForce_0=b.asm.At).apply(null,arguments)},Qv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_maxSuspensionForce_1=function(){return(Qv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_maxSuspensionForce_1=b.asm.Bt).apply(null,arguments)},Rv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_bIsFrontWheel_0=function(){return(Rv=b._emscripten_bind_btWheelInfoConstructionInfo_get_m_bIsFrontWheel_0=b.asm.Ct).apply(null,arguments)},
|
||||||
|
Sv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_bIsFrontWheel_1=function(){return(Sv=b._emscripten_bind_btWheelInfoConstructionInfo_set_m_bIsFrontWheel_1=b.asm.Dt).apply(null,arguments)},Tv=b._emscripten_bind_btWheelInfoConstructionInfo___destroy___0=function(){return(Tv=b._emscripten_bind_btWheelInfoConstructionInfo___destroy___0=b.asm.Et).apply(null,arguments)},Uv=b._emscripten_bind_btWheelInfo_btWheelInfo_1=function(){return(Uv=b._emscripten_bind_btWheelInfo_btWheelInfo_1=b.asm.Ft).apply(null,
|
||||||
|
arguments)},Vv=b._emscripten_bind_btWheelInfo_getSuspensionRestLength_0=function(){return(Vv=b._emscripten_bind_btWheelInfo_getSuspensionRestLength_0=b.asm.Gt).apply(null,arguments)},Wv=b._emscripten_bind_btWheelInfo_updateWheel_2=function(){return(Wv=b._emscripten_bind_btWheelInfo_updateWheel_2=b.asm.Ht).apply(null,arguments)},Xv=b._emscripten_bind_btWheelInfo_get_m_suspensionStiffness_0=function(){return(Xv=b._emscripten_bind_btWheelInfo_get_m_suspensionStiffness_0=b.asm.It).apply(null,arguments)},
|
||||||
|
Yv=b._emscripten_bind_btWheelInfo_set_m_suspensionStiffness_1=function(){return(Yv=b._emscripten_bind_btWheelInfo_set_m_suspensionStiffness_1=b.asm.Jt).apply(null,arguments)},Zv=b._emscripten_bind_btWheelInfo_get_m_frictionSlip_0=function(){return(Zv=b._emscripten_bind_btWheelInfo_get_m_frictionSlip_0=b.asm.Kt).apply(null,arguments)},$v=b._emscripten_bind_btWheelInfo_set_m_frictionSlip_1=function(){return($v=b._emscripten_bind_btWheelInfo_set_m_frictionSlip_1=b.asm.Lt).apply(null,arguments)},aw=b._emscripten_bind_btWheelInfo_get_m_engineForce_0=
|
||||||
|
function(){return(aw=b._emscripten_bind_btWheelInfo_get_m_engineForce_0=b.asm.Mt).apply(null,arguments)},bw=b._emscripten_bind_btWheelInfo_set_m_engineForce_1=function(){return(bw=b._emscripten_bind_btWheelInfo_set_m_engineForce_1=b.asm.Nt).apply(null,arguments)},cw=b._emscripten_bind_btWheelInfo_get_m_rollInfluence_0=function(){return(cw=b._emscripten_bind_btWheelInfo_get_m_rollInfluence_0=b.asm.Ot).apply(null,arguments)},dw=b._emscripten_bind_btWheelInfo_set_m_rollInfluence_1=function(){return(dw=
|
||||||
|
b._emscripten_bind_btWheelInfo_set_m_rollInfluence_1=b.asm.Pt).apply(null,arguments)},ew=b._emscripten_bind_btWheelInfo_get_m_suspensionRestLength1_0=function(){return(ew=b._emscripten_bind_btWheelInfo_get_m_suspensionRestLength1_0=b.asm.Qt).apply(null,arguments)},fw=b._emscripten_bind_btWheelInfo_set_m_suspensionRestLength1_1=function(){return(fw=b._emscripten_bind_btWheelInfo_set_m_suspensionRestLength1_1=b.asm.Rt).apply(null,arguments)},gw=b._emscripten_bind_btWheelInfo_get_m_wheelsRadius_0=function(){return(gw=
|
||||||
|
b._emscripten_bind_btWheelInfo_get_m_wheelsRadius_0=b.asm.St).apply(null,arguments)},hw=b._emscripten_bind_btWheelInfo_set_m_wheelsRadius_1=function(){return(hw=b._emscripten_bind_btWheelInfo_set_m_wheelsRadius_1=b.asm.Tt).apply(null,arguments)},iw=b._emscripten_bind_btWheelInfo_get_m_wheelsDampingCompression_0=function(){return(iw=b._emscripten_bind_btWheelInfo_get_m_wheelsDampingCompression_0=b.asm.Ut).apply(null,arguments)},jw=b._emscripten_bind_btWheelInfo_set_m_wheelsDampingCompression_1=function(){return(jw=
|
||||||
|
b._emscripten_bind_btWheelInfo_set_m_wheelsDampingCompression_1=b.asm.Vt).apply(null,arguments)},kw=b._emscripten_bind_btWheelInfo_get_m_wheelsDampingRelaxation_0=function(){return(kw=b._emscripten_bind_btWheelInfo_get_m_wheelsDampingRelaxation_0=b.asm.Wt).apply(null,arguments)},lw=b._emscripten_bind_btWheelInfo_set_m_wheelsDampingRelaxation_1=function(){return(lw=b._emscripten_bind_btWheelInfo_set_m_wheelsDampingRelaxation_1=b.asm.Xt).apply(null,arguments)},mw=b._emscripten_bind_btWheelInfo_get_m_steering_0=
|
||||||
|
function(){return(mw=b._emscripten_bind_btWheelInfo_get_m_steering_0=b.asm.Yt).apply(null,arguments)},nw=b._emscripten_bind_btWheelInfo_set_m_steering_1=function(){return(nw=b._emscripten_bind_btWheelInfo_set_m_steering_1=b.asm.Zt).apply(null,arguments)},ow=b._emscripten_bind_btWheelInfo_get_m_maxSuspensionForce_0=function(){return(ow=b._emscripten_bind_btWheelInfo_get_m_maxSuspensionForce_0=b.asm._t).apply(null,arguments)},pw=b._emscripten_bind_btWheelInfo_set_m_maxSuspensionForce_1=function(){return(pw=
|
||||||
|
b._emscripten_bind_btWheelInfo_set_m_maxSuspensionForce_1=b.asm.$t).apply(null,arguments)},qw=b._emscripten_bind_btWheelInfo_get_m_maxSuspensionTravelCm_0=function(){return(qw=b._emscripten_bind_btWheelInfo_get_m_maxSuspensionTravelCm_0=b.asm.au).apply(null,arguments)},rw=b._emscripten_bind_btWheelInfo_set_m_maxSuspensionTravelCm_1=function(){return(rw=b._emscripten_bind_btWheelInfo_set_m_maxSuspensionTravelCm_1=b.asm.bu).apply(null,arguments)},sw=b._emscripten_bind_btWheelInfo_get_m_wheelsSuspensionForce_0=
|
||||||
|
function(){return(sw=b._emscripten_bind_btWheelInfo_get_m_wheelsSuspensionForce_0=b.asm.cu).apply(null,arguments)},tw=b._emscripten_bind_btWheelInfo_set_m_wheelsSuspensionForce_1=function(){return(tw=b._emscripten_bind_btWheelInfo_set_m_wheelsSuspensionForce_1=b.asm.du).apply(null,arguments)},uw=b._emscripten_bind_btWheelInfo_get_m_bIsFrontWheel_0=function(){return(uw=b._emscripten_bind_btWheelInfo_get_m_bIsFrontWheel_0=b.asm.eu).apply(null,arguments)},vw=b._emscripten_bind_btWheelInfo_set_m_bIsFrontWheel_1=
|
||||||
|
function(){return(vw=b._emscripten_bind_btWheelInfo_set_m_bIsFrontWheel_1=b.asm.fu).apply(null,arguments)},ww=b._emscripten_bind_btWheelInfo_get_m_raycastInfo_0=function(){return(ww=b._emscripten_bind_btWheelInfo_get_m_raycastInfo_0=b.asm.gu).apply(null,arguments)},xw=b._emscripten_bind_btWheelInfo_set_m_raycastInfo_1=function(){return(xw=b._emscripten_bind_btWheelInfo_set_m_raycastInfo_1=b.asm.hu).apply(null,arguments)},yw=b._emscripten_bind_btWheelInfo_get_m_chassisConnectionPointCS_0=function(){return(yw=
|
||||||
|
b._emscripten_bind_btWheelInfo_get_m_chassisConnectionPointCS_0=b.asm.iu).apply(null,arguments)},zw=b._emscripten_bind_btWheelInfo_set_m_chassisConnectionPointCS_1=function(){return(zw=b._emscripten_bind_btWheelInfo_set_m_chassisConnectionPointCS_1=b.asm.ju).apply(null,arguments)},Aw=b._emscripten_bind_btWheelInfo_get_m_worldTransform_0=function(){return(Aw=b._emscripten_bind_btWheelInfo_get_m_worldTransform_0=b.asm.ku).apply(null,arguments)},Bw=b._emscripten_bind_btWheelInfo_set_m_worldTransform_1=
|
||||||
|
function(){return(Bw=b._emscripten_bind_btWheelInfo_set_m_worldTransform_1=b.asm.lu).apply(null,arguments)},Cw=b._emscripten_bind_btWheelInfo_get_m_wheelDirectionCS_0=function(){return(Cw=b._emscripten_bind_btWheelInfo_get_m_wheelDirectionCS_0=b.asm.mu).apply(null,arguments)},Dw=b._emscripten_bind_btWheelInfo_set_m_wheelDirectionCS_1=function(){return(Dw=b._emscripten_bind_btWheelInfo_set_m_wheelDirectionCS_1=b.asm.nu).apply(null,arguments)},Ew=b._emscripten_bind_btWheelInfo_get_m_wheelAxleCS_0=function(){return(Ew=
|
||||||
|
b._emscripten_bind_btWheelInfo_get_m_wheelAxleCS_0=b.asm.ou).apply(null,arguments)},Fw=b._emscripten_bind_btWheelInfo_set_m_wheelAxleCS_1=function(){return(Fw=b._emscripten_bind_btWheelInfo_set_m_wheelAxleCS_1=b.asm.pu).apply(null,arguments)},Gw=b._emscripten_bind_btWheelInfo_get_m_rotation_0=function(){return(Gw=b._emscripten_bind_btWheelInfo_get_m_rotation_0=b.asm.qu).apply(null,arguments)},Hw=b._emscripten_bind_btWheelInfo_set_m_rotation_1=function(){return(Hw=b._emscripten_bind_btWheelInfo_set_m_rotation_1=
|
||||||
|
b.asm.ru).apply(null,arguments)},Iw=b._emscripten_bind_btWheelInfo_get_m_deltaRotation_0=function(){return(Iw=b._emscripten_bind_btWheelInfo_get_m_deltaRotation_0=b.asm.su).apply(null,arguments)},Jw=b._emscripten_bind_btWheelInfo_set_m_deltaRotation_1=function(){return(Jw=b._emscripten_bind_btWheelInfo_set_m_deltaRotation_1=b.asm.tu).apply(null,arguments)},Kw=b._emscripten_bind_btWheelInfo_get_m_brake_0=function(){return(Kw=b._emscripten_bind_btWheelInfo_get_m_brake_0=b.asm.uu).apply(null,arguments)},
|
||||||
|
Lw=b._emscripten_bind_btWheelInfo_set_m_brake_1=function(){return(Lw=b._emscripten_bind_btWheelInfo_set_m_brake_1=b.asm.vu).apply(null,arguments)},Mw=b._emscripten_bind_btWheelInfo_get_m_clippedInvContactDotSuspension_0=function(){return(Mw=b._emscripten_bind_btWheelInfo_get_m_clippedInvContactDotSuspension_0=b.asm.wu).apply(null,arguments)},Nw=b._emscripten_bind_btWheelInfo_set_m_clippedInvContactDotSuspension_1=function(){return(Nw=b._emscripten_bind_btWheelInfo_set_m_clippedInvContactDotSuspension_1=
|
||||||
|
b.asm.xu).apply(null,arguments)},Ow=b._emscripten_bind_btWheelInfo_get_m_suspensionRelativeVelocity_0=function(){return(Ow=b._emscripten_bind_btWheelInfo_get_m_suspensionRelativeVelocity_0=b.asm.yu).apply(null,arguments)},Pw=b._emscripten_bind_btWheelInfo_set_m_suspensionRelativeVelocity_1=function(){return(Pw=b._emscripten_bind_btWheelInfo_set_m_suspensionRelativeVelocity_1=b.asm.zu).apply(null,arguments)},Qw=b._emscripten_bind_btWheelInfo_get_m_skidInfo_0=function(){return(Qw=b._emscripten_bind_btWheelInfo_get_m_skidInfo_0=
|
||||||
|
b.asm.Au).apply(null,arguments)},Rw=b._emscripten_bind_btWheelInfo_set_m_skidInfo_1=function(){return(Rw=b._emscripten_bind_btWheelInfo_set_m_skidInfo_1=b.asm.Bu).apply(null,arguments)},Sw=b._emscripten_bind_btWheelInfo___destroy___0=function(){return(Sw=b._emscripten_bind_btWheelInfo___destroy___0=b.asm.Cu).apply(null,arguments)},Tw=b._emscripten_bind_btKinematicCharacterController_btKinematicCharacterController_3=function(){return(Tw=b._emscripten_bind_btKinematicCharacterController_btKinematicCharacterController_3=
|
||||||
|
b.asm.Du).apply(null,arguments)},Uw=b._emscripten_bind_btKinematicCharacterController_btKinematicCharacterController_4=function(){return(Uw=b._emscripten_bind_btKinematicCharacterController_btKinematicCharacterController_4=b.asm.Eu).apply(null,arguments)},Vw=b._emscripten_bind_btKinematicCharacterController_setUpAxis_1=function(){return(Vw=b._emscripten_bind_btKinematicCharacterController_setUpAxis_1=b.asm.Fu).apply(null,arguments)},Ww=b._emscripten_bind_btKinematicCharacterController_setWalkDirection_1=
|
||||||
|
function(){return(Ww=b._emscripten_bind_btKinematicCharacterController_setWalkDirection_1=b.asm.Gu).apply(null,arguments)},Xw=b._emscripten_bind_btKinematicCharacterController_setVelocityForTimeInterval_2=function(){return(Xw=b._emscripten_bind_btKinematicCharacterController_setVelocityForTimeInterval_2=b.asm.Hu).apply(null,arguments)},Yw=b._emscripten_bind_btKinematicCharacterController_warp_1=function(){return(Yw=b._emscripten_bind_btKinematicCharacterController_warp_1=b.asm.Iu).apply(null,arguments)},
|
||||||
|
Zw=b._emscripten_bind_btKinematicCharacterController_preStep_1=function(){return(Zw=b._emscripten_bind_btKinematicCharacterController_preStep_1=b.asm.Ju).apply(null,arguments)},$w=b._emscripten_bind_btKinematicCharacterController_playerStep_2=function(){return($w=b._emscripten_bind_btKinematicCharacterController_playerStep_2=b.asm.Ku).apply(null,arguments)},ax=b._emscripten_bind_btKinematicCharacterController_setFallSpeed_1=function(){return(ax=b._emscripten_bind_btKinematicCharacterController_setFallSpeed_1=
|
||||||
|
b.asm.Lu).apply(null,arguments)},bx=b._emscripten_bind_btKinematicCharacterController_setJumpSpeed_1=function(){return(bx=b._emscripten_bind_btKinematicCharacterController_setJumpSpeed_1=b.asm.Mu).apply(null,arguments)},cx=b._emscripten_bind_btKinematicCharacterController_setMaxJumpHeight_1=function(){return(cx=b._emscripten_bind_btKinematicCharacterController_setMaxJumpHeight_1=b.asm.Nu).apply(null,arguments)},dx=b._emscripten_bind_btKinematicCharacterController_canJump_0=function(){return(dx=b._emscripten_bind_btKinematicCharacterController_canJump_0=
|
||||||
|
b.asm.Ou).apply(null,arguments)},ex=b._emscripten_bind_btKinematicCharacterController_jump_0=function(){return(ex=b._emscripten_bind_btKinematicCharacterController_jump_0=b.asm.Pu).apply(null,arguments)},fx=b._emscripten_bind_btKinematicCharacterController_setGravity_1=function(){return(fx=b._emscripten_bind_btKinematicCharacterController_setGravity_1=b.asm.Qu).apply(null,arguments)},gx=b._emscripten_bind_btKinematicCharacterController_getGravity_0=function(){return(gx=b._emscripten_bind_btKinematicCharacterController_getGravity_0=
|
||||||
|
b.asm.Ru).apply(null,arguments)},hx=b._emscripten_bind_btKinematicCharacterController_setMaxSlope_1=function(){return(hx=b._emscripten_bind_btKinematicCharacterController_setMaxSlope_1=b.asm.Su).apply(null,arguments)},ix=b._emscripten_bind_btKinematicCharacterController_getMaxSlope_0=function(){return(ix=b._emscripten_bind_btKinematicCharacterController_getMaxSlope_0=b.asm.Tu).apply(null,arguments)},jx=b._emscripten_bind_btKinematicCharacterController_getGhostObject_0=function(){return(jx=b._emscripten_bind_btKinematicCharacterController_getGhostObject_0=
|
||||||
|
b.asm.Uu).apply(null,arguments)},kx=b._emscripten_bind_btKinematicCharacterController_setUseGhostSweepTest_1=function(){return(kx=b._emscripten_bind_btKinematicCharacterController_setUseGhostSweepTest_1=b.asm.Vu).apply(null,arguments)},lx=b._emscripten_bind_btKinematicCharacterController_onGround_0=function(){return(lx=b._emscripten_bind_btKinematicCharacterController_onGround_0=b.asm.Wu).apply(null,arguments)},mx=b._emscripten_bind_btKinematicCharacterController_setUpInterpolate_1=function(){return(mx=
|
||||||
|
b._emscripten_bind_btKinematicCharacterController_setUpInterpolate_1=b.asm.Xu).apply(null,arguments)},nx=b._emscripten_bind_btKinematicCharacterController_updateAction_2=function(){return(nx=b._emscripten_bind_btKinematicCharacterController_updateAction_2=b.asm.Yu).apply(null,arguments)},ox=b._emscripten_bind_btKinematicCharacterController___destroy___0=function(){return(ox=b._emscripten_bind_btKinematicCharacterController___destroy___0=b.asm.Zu).apply(null,arguments)},px=b._emscripten_bind_btRaycastVehicle_btRaycastVehicle_3=
|
||||||
|
function(){return(px=b._emscripten_bind_btRaycastVehicle_btRaycastVehicle_3=b.asm._u).apply(null,arguments)},qx=b._emscripten_bind_btRaycastVehicle_applyEngineForce_2=function(){return(qx=b._emscripten_bind_btRaycastVehicle_applyEngineForce_2=b.asm.$u).apply(null,arguments)},rx=b._emscripten_bind_btRaycastVehicle_setSteeringValue_2=function(){return(rx=b._emscripten_bind_btRaycastVehicle_setSteeringValue_2=b.asm.av).apply(null,arguments)},sx=b._emscripten_bind_btRaycastVehicle_getWheelTransformWS_1=
|
||||||
|
function(){return(sx=b._emscripten_bind_btRaycastVehicle_getWheelTransformWS_1=b.asm.bv).apply(null,arguments)},tx=b._emscripten_bind_btRaycastVehicle_updateWheelTransform_2=function(){return(tx=b._emscripten_bind_btRaycastVehicle_updateWheelTransform_2=b.asm.cv).apply(null,arguments)},ux=b._emscripten_bind_btRaycastVehicle_addWheel_7=function(){return(ux=b._emscripten_bind_btRaycastVehicle_addWheel_7=b.asm.dv).apply(null,arguments)},vx=b._emscripten_bind_btRaycastVehicle_getNumWheels_0=function(){return(vx=
|
||||||
|
b._emscripten_bind_btRaycastVehicle_getNumWheels_0=b.asm.ev).apply(null,arguments)},wx=b._emscripten_bind_btRaycastVehicle_getRigidBody_0=function(){return(wx=b._emscripten_bind_btRaycastVehicle_getRigidBody_0=b.asm.fv).apply(null,arguments)},xx=b._emscripten_bind_btRaycastVehicle_getWheelInfo_1=function(){return(xx=b._emscripten_bind_btRaycastVehicle_getWheelInfo_1=b.asm.gv).apply(null,arguments)},yx=b._emscripten_bind_btRaycastVehicle_setBrake_2=function(){return(yx=b._emscripten_bind_btRaycastVehicle_setBrake_2=
|
||||||
|
b.asm.hv).apply(null,arguments)},zx=b._emscripten_bind_btRaycastVehicle_setCoordinateSystem_3=function(){return(zx=b._emscripten_bind_btRaycastVehicle_setCoordinateSystem_3=b.asm.iv).apply(null,arguments)},Ax=b._emscripten_bind_btRaycastVehicle_getCurrentSpeedKmHour_0=function(){return(Ax=b._emscripten_bind_btRaycastVehicle_getCurrentSpeedKmHour_0=b.asm.jv).apply(null,arguments)},Bx=b._emscripten_bind_btRaycastVehicle_getChassisWorldTransform_0=function(){return(Bx=b._emscripten_bind_btRaycastVehicle_getChassisWorldTransform_0=
|
||||||
|
b.asm.kv).apply(null,arguments)},Cx=b._emscripten_bind_btRaycastVehicle_rayCast_1=function(){return(Cx=b._emscripten_bind_btRaycastVehicle_rayCast_1=b.asm.lv).apply(null,arguments)},Dx=b._emscripten_bind_btRaycastVehicle_updateVehicle_1=function(){return(Dx=b._emscripten_bind_btRaycastVehicle_updateVehicle_1=b.asm.mv).apply(null,arguments)},Ex=b._emscripten_bind_btRaycastVehicle_resetSuspension_0=function(){return(Ex=b._emscripten_bind_btRaycastVehicle_resetSuspension_0=b.asm.nv).apply(null,arguments)},
|
||||||
|
Fx=b._emscripten_bind_btRaycastVehicle_getSteeringValue_1=function(){return(Fx=b._emscripten_bind_btRaycastVehicle_getSteeringValue_1=b.asm.ov).apply(null,arguments)},Gx=b._emscripten_bind_btRaycastVehicle_updateWheelTransformsWS_1=function(){return(Gx=b._emscripten_bind_btRaycastVehicle_updateWheelTransformsWS_1=b.asm.pv).apply(null,arguments)},Hx=b._emscripten_bind_btRaycastVehicle_updateWheelTransformsWS_2=function(){return(Hx=b._emscripten_bind_btRaycastVehicle_updateWheelTransformsWS_2=b.asm.qv).apply(null,
|
||||||
|
arguments)},Ix=b._emscripten_bind_btRaycastVehicle_setPitchControl_1=function(){return(Ix=b._emscripten_bind_btRaycastVehicle_setPitchControl_1=b.asm.rv).apply(null,arguments)},Jx=b._emscripten_bind_btRaycastVehicle_updateSuspension_1=function(){return(Jx=b._emscripten_bind_btRaycastVehicle_updateSuspension_1=b.asm.sv).apply(null,arguments)},Kx=b._emscripten_bind_btRaycastVehicle_updateFriction_1=function(){return(Kx=b._emscripten_bind_btRaycastVehicle_updateFriction_1=b.asm.tv).apply(null,arguments)},
|
||||||
|
Lx=b._emscripten_bind_btRaycastVehicle_getRightAxis_0=function(){return(Lx=b._emscripten_bind_btRaycastVehicle_getRightAxis_0=b.asm.uv).apply(null,arguments)},Mx=b._emscripten_bind_btRaycastVehicle_getUpAxis_0=function(){return(Mx=b._emscripten_bind_btRaycastVehicle_getUpAxis_0=b.asm.vv).apply(null,arguments)},Nx=b._emscripten_bind_btRaycastVehicle_getForwardAxis_0=function(){return(Nx=b._emscripten_bind_btRaycastVehicle_getForwardAxis_0=b.asm.wv).apply(null,arguments)},Ox=b._emscripten_bind_btRaycastVehicle_getForwardVector_0=
|
||||||
|
function(){return(Ox=b._emscripten_bind_btRaycastVehicle_getForwardVector_0=b.asm.xv).apply(null,arguments)},Px=b._emscripten_bind_btRaycastVehicle_getUserConstraintType_0=function(){return(Px=b._emscripten_bind_btRaycastVehicle_getUserConstraintType_0=b.asm.yv).apply(null,arguments)},Qx=b._emscripten_bind_btRaycastVehicle_setUserConstraintType_1=function(){return(Qx=b._emscripten_bind_btRaycastVehicle_setUserConstraintType_1=b.asm.zv).apply(null,arguments)},Rx=b._emscripten_bind_btRaycastVehicle_setUserConstraintId_1=
|
||||||
|
function(){return(Rx=b._emscripten_bind_btRaycastVehicle_setUserConstraintId_1=b.asm.Av).apply(null,arguments)},Sx=b._emscripten_bind_btRaycastVehicle_getUserConstraintId_0=function(){return(Sx=b._emscripten_bind_btRaycastVehicle_getUserConstraintId_0=b.asm.Bv).apply(null,arguments)},Tx=b._emscripten_bind_btRaycastVehicle_updateAction_2=function(){return(Tx=b._emscripten_bind_btRaycastVehicle_updateAction_2=b.asm.Cv).apply(null,arguments)},Ux=b._emscripten_bind_btRaycastVehicle___destroy___0=function(){return(Ux=
|
||||||
|
b._emscripten_bind_btRaycastVehicle___destroy___0=b.asm.Dv).apply(null,arguments)},Vx=b._emscripten_bind_btPairCachingGhostObject_btPairCachingGhostObject_0=function(){return(Vx=b._emscripten_bind_btPairCachingGhostObject_btPairCachingGhostObject_0=b.asm.Ev).apply(null,arguments)},Wx=b._emscripten_bind_btPairCachingGhostObject_setAnisotropicFriction_2=function(){return(Wx=b._emscripten_bind_btPairCachingGhostObject_setAnisotropicFriction_2=b.asm.Fv).apply(null,arguments)},Xx=b._emscripten_bind_btPairCachingGhostObject_getCollisionShape_0=
|
||||||
|
function(){return(Xx=b._emscripten_bind_btPairCachingGhostObject_getCollisionShape_0=b.asm.Gv).apply(null,arguments)},Yx=b._emscripten_bind_btPairCachingGhostObject_setContactProcessingThreshold_1=function(){return(Yx=b._emscripten_bind_btPairCachingGhostObject_setContactProcessingThreshold_1=b.asm.Hv).apply(null,arguments)},Zx=b._emscripten_bind_btPairCachingGhostObject_setActivationState_1=function(){return(Zx=b._emscripten_bind_btPairCachingGhostObject_setActivationState_1=b.asm.Iv).apply(null,
|
||||||
|
arguments)},$x=b._emscripten_bind_btPairCachingGhostObject_forceActivationState_1=function(){return($x=b._emscripten_bind_btPairCachingGhostObject_forceActivationState_1=b.asm.Jv).apply(null,arguments)},ay=b._emscripten_bind_btPairCachingGhostObject_activate_0=function(){return(ay=b._emscripten_bind_btPairCachingGhostObject_activate_0=b.asm.Kv).apply(null,arguments)},by=b._emscripten_bind_btPairCachingGhostObject_activate_1=function(){return(by=b._emscripten_bind_btPairCachingGhostObject_activate_1=
|
||||||
|
b.asm.Lv).apply(null,arguments)},cy=b._emscripten_bind_btPairCachingGhostObject_isActive_0=function(){return(cy=b._emscripten_bind_btPairCachingGhostObject_isActive_0=b.asm.Mv).apply(null,arguments)},dy=b._emscripten_bind_btPairCachingGhostObject_isKinematicObject_0=function(){return(dy=b._emscripten_bind_btPairCachingGhostObject_isKinematicObject_0=b.asm.Nv).apply(null,arguments)},ey=b._emscripten_bind_btPairCachingGhostObject_isStaticObject_0=function(){return(ey=b._emscripten_bind_btPairCachingGhostObject_isStaticObject_0=
|
||||||
|
b.asm.Ov).apply(null,arguments)},fy=b._emscripten_bind_btPairCachingGhostObject_isStaticOrKinematicObject_0=function(){return(fy=b._emscripten_bind_btPairCachingGhostObject_isStaticOrKinematicObject_0=b.asm.Pv).apply(null,arguments)},gy=b._emscripten_bind_btPairCachingGhostObject_getRestitution_0=function(){return(gy=b._emscripten_bind_btPairCachingGhostObject_getRestitution_0=b.asm.Qv).apply(null,arguments)},hy=b._emscripten_bind_btPairCachingGhostObject_getFriction_0=function(){return(hy=b._emscripten_bind_btPairCachingGhostObject_getFriction_0=
|
||||||
|
b.asm.Rv).apply(null,arguments)},iy=b._emscripten_bind_btPairCachingGhostObject_getRollingFriction_0=function(){return(iy=b._emscripten_bind_btPairCachingGhostObject_getRollingFriction_0=b.asm.Sv).apply(null,arguments)},jy=b._emscripten_bind_btPairCachingGhostObject_setRestitution_1=function(){return(jy=b._emscripten_bind_btPairCachingGhostObject_setRestitution_1=b.asm.Tv).apply(null,arguments)},ky=b._emscripten_bind_btPairCachingGhostObject_setFriction_1=function(){return(ky=b._emscripten_bind_btPairCachingGhostObject_setFriction_1=
|
||||||
|
b.asm.Uv).apply(null,arguments)},ly=b._emscripten_bind_btPairCachingGhostObject_setRollingFriction_1=function(){return(ly=b._emscripten_bind_btPairCachingGhostObject_setRollingFriction_1=b.asm.Vv).apply(null,arguments)},my=b._emscripten_bind_btPairCachingGhostObject_getWorldTransform_0=function(){return(my=b._emscripten_bind_btPairCachingGhostObject_getWorldTransform_0=b.asm.Wv).apply(null,arguments)},ny=b._emscripten_bind_btPairCachingGhostObject_getCollisionFlags_0=function(){return(ny=b._emscripten_bind_btPairCachingGhostObject_getCollisionFlags_0=
|
||||||
|
b.asm.Xv).apply(null,arguments)},oy=b._emscripten_bind_btPairCachingGhostObject_setCollisionFlags_1=function(){return(oy=b._emscripten_bind_btPairCachingGhostObject_setCollisionFlags_1=b.asm.Yv).apply(null,arguments)},py=b._emscripten_bind_btPairCachingGhostObject_setWorldTransform_1=function(){return(py=b._emscripten_bind_btPairCachingGhostObject_setWorldTransform_1=b.asm.Zv).apply(null,arguments)},qy=b._emscripten_bind_btPairCachingGhostObject_setCollisionShape_1=function(){return(qy=b._emscripten_bind_btPairCachingGhostObject_setCollisionShape_1=
|
||||||
|
b.asm._v).apply(null,arguments)},ry=b._emscripten_bind_btPairCachingGhostObject_setCcdMotionThreshold_1=function(){return(ry=b._emscripten_bind_btPairCachingGhostObject_setCcdMotionThreshold_1=b.asm.$v).apply(null,arguments)},sy=b._emscripten_bind_btPairCachingGhostObject_setCcdSweptSphereRadius_1=function(){return(sy=b._emscripten_bind_btPairCachingGhostObject_setCcdSweptSphereRadius_1=b.asm.aw).apply(null,arguments)},ty=b._emscripten_bind_btPairCachingGhostObject_getUserIndex_0=function(){return(ty=
|
||||||
|
b._emscripten_bind_btPairCachingGhostObject_getUserIndex_0=b.asm.bw).apply(null,arguments)},uy=b._emscripten_bind_btPairCachingGhostObject_setUserIndex_1=function(){return(uy=b._emscripten_bind_btPairCachingGhostObject_setUserIndex_1=b.asm.cw).apply(null,arguments)},vy=b._emscripten_bind_btPairCachingGhostObject_getUserPointer_0=function(){return(vy=b._emscripten_bind_btPairCachingGhostObject_getUserPointer_0=b.asm.dw).apply(null,arguments)},wy=b._emscripten_bind_btPairCachingGhostObject_setUserPointer_1=
|
||||||
|
function(){return(wy=b._emscripten_bind_btPairCachingGhostObject_setUserPointer_1=b.asm.ew).apply(null,arguments)},xy=b._emscripten_bind_btPairCachingGhostObject_getBroadphaseHandle_0=function(){return(xy=b._emscripten_bind_btPairCachingGhostObject_getBroadphaseHandle_0=b.asm.fw).apply(null,arguments)},yy=b._emscripten_bind_btPairCachingGhostObject_getNumOverlappingObjects_0=function(){return(yy=b._emscripten_bind_btPairCachingGhostObject_getNumOverlappingObjects_0=b.asm.gw).apply(null,arguments)},
|
||||||
|
zy=b._emscripten_bind_btPairCachingGhostObject_getOverlappingObject_1=function(){return(zy=b._emscripten_bind_btPairCachingGhostObject_getOverlappingObject_1=b.asm.hw).apply(null,arguments)},Ay=b._emscripten_bind_btPairCachingGhostObject___destroy___0=function(){return(Ay=b._emscripten_bind_btPairCachingGhostObject___destroy___0=b.asm.iw).apply(null,arguments)},By=b._emscripten_bind_btGhostPairCallback_btGhostPairCallback_0=function(){return(By=b._emscripten_bind_btGhostPairCallback_btGhostPairCallback_0=
|
||||||
|
b.asm.jw).apply(null,arguments)},Cy=b._emscripten_bind_btGhostPairCallback___destroy___0=function(){return(Cy=b._emscripten_bind_btGhostPairCallback___destroy___0=b.asm.kw).apply(null,arguments)},Dy=b._emscripten_bind_btSoftBodyWorldInfo_btSoftBodyWorldInfo_0=function(){return(Dy=b._emscripten_bind_btSoftBodyWorldInfo_btSoftBodyWorldInfo_0=b.asm.lw).apply(null,arguments)},Ey=b._emscripten_bind_btSoftBodyWorldInfo_get_air_density_0=function(){return(Ey=b._emscripten_bind_btSoftBodyWorldInfo_get_air_density_0=
|
||||||
|
b.asm.mw).apply(null,arguments)},Fy=b._emscripten_bind_btSoftBodyWorldInfo_set_air_density_1=function(){return(Fy=b._emscripten_bind_btSoftBodyWorldInfo_set_air_density_1=b.asm.nw).apply(null,arguments)},Gy=b._emscripten_bind_btSoftBodyWorldInfo_get_water_density_0=function(){return(Gy=b._emscripten_bind_btSoftBodyWorldInfo_get_water_density_0=b.asm.ow).apply(null,arguments)},Hy=b._emscripten_bind_btSoftBodyWorldInfo_set_water_density_1=function(){return(Hy=b._emscripten_bind_btSoftBodyWorldInfo_set_water_density_1=
|
||||||
|
b.asm.pw).apply(null,arguments)},Iy=b._emscripten_bind_btSoftBodyWorldInfo_get_water_offset_0=function(){return(Iy=b._emscripten_bind_btSoftBodyWorldInfo_get_water_offset_0=b.asm.qw).apply(null,arguments)},Jy=b._emscripten_bind_btSoftBodyWorldInfo_set_water_offset_1=function(){return(Jy=b._emscripten_bind_btSoftBodyWorldInfo_set_water_offset_1=b.asm.rw).apply(null,arguments)},Ky=b._emscripten_bind_btSoftBodyWorldInfo_get_m_maxDisplacement_0=function(){return(Ky=b._emscripten_bind_btSoftBodyWorldInfo_get_m_maxDisplacement_0=
|
||||||
|
b.asm.sw).apply(null,arguments)},Ly=b._emscripten_bind_btSoftBodyWorldInfo_set_m_maxDisplacement_1=function(){return(Ly=b._emscripten_bind_btSoftBodyWorldInfo_set_m_maxDisplacement_1=b.asm.tw).apply(null,arguments)},My=b._emscripten_bind_btSoftBodyWorldInfo_get_water_normal_0=function(){return(My=b._emscripten_bind_btSoftBodyWorldInfo_get_water_normal_0=b.asm.uw).apply(null,arguments)},Ny=b._emscripten_bind_btSoftBodyWorldInfo_set_water_normal_1=function(){return(Ny=b._emscripten_bind_btSoftBodyWorldInfo_set_water_normal_1=
|
||||||
|
b.asm.vw).apply(null,arguments)},Oy=b._emscripten_bind_btSoftBodyWorldInfo_get_m_broadphase_0=function(){return(Oy=b._emscripten_bind_btSoftBodyWorldInfo_get_m_broadphase_0=b.asm.ww).apply(null,arguments)},Py=b._emscripten_bind_btSoftBodyWorldInfo_set_m_broadphase_1=function(){return(Py=b._emscripten_bind_btSoftBodyWorldInfo_set_m_broadphase_1=b.asm.xw).apply(null,arguments)},Qy=b._emscripten_bind_btSoftBodyWorldInfo_get_m_dispatcher_0=function(){return(Qy=b._emscripten_bind_btSoftBodyWorldInfo_get_m_dispatcher_0=
|
||||||
|
b.asm.yw).apply(null,arguments)},Ry=b._emscripten_bind_btSoftBodyWorldInfo_set_m_dispatcher_1=function(){return(Ry=b._emscripten_bind_btSoftBodyWorldInfo_set_m_dispatcher_1=b.asm.zw).apply(null,arguments)},Sy=b._emscripten_bind_btSoftBodyWorldInfo_get_m_gravity_0=function(){return(Sy=b._emscripten_bind_btSoftBodyWorldInfo_get_m_gravity_0=b.asm.Aw).apply(null,arguments)},Ty=b._emscripten_bind_btSoftBodyWorldInfo_set_m_gravity_1=function(){return(Ty=b._emscripten_bind_btSoftBodyWorldInfo_set_m_gravity_1=
|
||||||
|
b.asm.Bw).apply(null,arguments)},Uy=b._emscripten_bind_btSoftBodyWorldInfo___destroy___0=function(){return(Uy=b._emscripten_bind_btSoftBodyWorldInfo___destroy___0=b.asm.Cw).apply(null,arguments)},Vy=b._emscripten_bind_Face_get_m_n_1=function(){return(Vy=b._emscripten_bind_Face_get_m_n_1=b.asm.Dw).apply(null,arguments)},Wy=b._emscripten_bind_Face_set_m_n_2=function(){return(Wy=b._emscripten_bind_Face_set_m_n_2=b.asm.Ew).apply(null,arguments)},Xy=b._emscripten_bind_Face_get_m_normal_0=function(){return(Xy=
|
||||||
|
b._emscripten_bind_Face_get_m_normal_0=b.asm.Fw).apply(null,arguments)},Yy=b._emscripten_bind_Face_set_m_normal_1=function(){return(Yy=b._emscripten_bind_Face_set_m_normal_1=b.asm.Gw).apply(null,arguments)},Zy=b._emscripten_bind_Face_get_m_ra_0=function(){return(Zy=b._emscripten_bind_Face_get_m_ra_0=b.asm.Hw).apply(null,arguments)},$y=b._emscripten_bind_Face_set_m_ra_1=function(){return($y=b._emscripten_bind_Face_set_m_ra_1=b.asm.Iw).apply(null,arguments)},az=b._emscripten_bind_Face___destroy___0=
|
||||||
|
function(){return(az=b._emscripten_bind_Face___destroy___0=b.asm.Jw).apply(null,arguments)},bz=b._emscripten_bind_tFaceArray_size_0=function(){return(bz=b._emscripten_bind_tFaceArray_size_0=b.asm.Kw).apply(null,arguments)},cz=b._emscripten_bind_tFaceArray_at_1=function(){return(cz=b._emscripten_bind_tFaceArray_at_1=b.asm.Lw).apply(null,arguments)},dz=b._emscripten_bind_tFaceArray___destroy___0=function(){return(dz=b._emscripten_bind_tFaceArray___destroy___0=b.asm.Mw).apply(null,arguments)},ez=b._emscripten_bind_Node_get_m_x_0=
|
||||||
|
function(){return(ez=b._emscripten_bind_Node_get_m_x_0=b.asm.Nw).apply(null,arguments)},fz=b._emscripten_bind_Node_set_m_x_1=function(){return(fz=b._emscripten_bind_Node_set_m_x_1=b.asm.Ow).apply(null,arguments)},gz=b._emscripten_bind_Node_get_m_q_0=function(){return(gz=b._emscripten_bind_Node_get_m_q_0=b.asm.Pw).apply(null,arguments)},hz=b._emscripten_bind_Node_set_m_q_1=function(){return(hz=b._emscripten_bind_Node_set_m_q_1=b.asm.Qw).apply(null,arguments)},iz=b._emscripten_bind_Node_get_m_v_0=function(){return(iz=
|
||||||
|
b._emscripten_bind_Node_get_m_v_0=b.asm.Rw).apply(null,arguments)},jz=b._emscripten_bind_Node_set_m_v_1=function(){return(jz=b._emscripten_bind_Node_set_m_v_1=b.asm.Sw).apply(null,arguments)},kz=b._emscripten_bind_Node_get_m_f_0=function(){return(kz=b._emscripten_bind_Node_get_m_f_0=b.asm.Tw).apply(null,arguments)},lz=b._emscripten_bind_Node_set_m_f_1=function(){return(lz=b._emscripten_bind_Node_set_m_f_1=b.asm.Uw).apply(null,arguments)},mz=b._emscripten_bind_Node_get_m_n_0=function(){return(mz=b._emscripten_bind_Node_get_m_n_0=
|
||||||
|
b.asm.Vw).apply(null,arguments)},nz=b._emscripten_bind_Node_set_m_n_1=function(){return(nz=b._emscripten_bind_Node_set_m_n_1=b.asm.Ww).apply(null,arguments)},oz=b._emscripten_bind_Node_get_m_im_0=function(){return(oz=b._emscripten_bind_Node_get_m_im_0=b.asm.Xw).apply(null,arguments)},pz=b._emscripten_bind_Node_set_m_im_1=function(){return(pz=b._emscripten_bind_Node_set_m_im_1=b.asm.Yw).apply(null,arguments)},qz=b._emscripten_bind_Node_get_m_area_0=function(){return(qz=b._emscripten_bind_Node_get_m_area_0=
|
||||||
|
b.asm.Zw).apply(null,arguments)},rz=b._emscripten_bind_Node_set_m_area_1=function(){return(rz=b._emscripten_bind_Node_set_m_area_1=b.asm._w).apply(null,arguments)},sz=b._emscripten_bind_Node___destroy___0=function(){return(sz=b._emscripten_bind_Node___destroy___0=b.asm.$w).apply(null,arguments)},tz=b._emscripten_bind_tNodeArray_size_0=function(){return(tz=b._emscripten_bind_tNodeArray_size_0=b.asm.ax).apply(null,arguments)},uz=b._emscripten_bind_tNodeArray_at_1=function(){return(uz=b._emscripten_bind_tNodeArray_at_1=
|
||||||
|
b.asm.bx).apply(null,arguments)},vz=b._emscripten_bind_tNodeArray___destroy___0=function(){return(vz=b._emscripten_bind_tNodeArray___destroy___0=b.asm.cx).apply(null,arguments)},wz=b._emscripten_bind_Material_get_m_kLST_0=function(){return(wz=b._emscripten_bind_Material_get_m_kLST_0=b.asm.dx).apply(null,arguments)},xz=b._emscripten_bind_Material_set_m_kLST_1=function(){return(xz=b._emscripten_bind_Material_set_m_kLST_1=b.asm.ex).apply(null,arguments)},yz=b._emscripten_bind_Material_get_m_kAST_0=function(){return(yz=
|
||||||
|
b._emscripten_bind_Material_get_m_kAST_0=b.asm.fx).apply(null,arguments)},zz=b._emscripten_bind_Material_set_m_kAST_1=function(){return(zz=b._emscripten_bind_Material_set_m_kAST_1=b.asm.gx).apply(null,arguments)},Az=b._emscripten_bind_Material_get_m_kVST_0=function(){return(Az=b._emscripten_bind_Material_get_m_kVST_0=b.asm.hx).apply(null,arguments)},Bz=b._emscripten_bind_Material_set_m_kVST_1=function(){return(Bz=b._emscripten_bind_Material_set_m_kVST_1=b.asm.ix).apply(null,arguments)},Cz=b._emscripten_bind_Material_get_m_flags_0=
|
||||||
|
function(){return(Cz=b._emscripten_bind_Material_get_m_flags_0=b.asm.jx).apply(null,arguments)},Dz=b._emscripten_bind_Material_set_m_flags_1=function(){return(Dz=b._emscripten_bind_Material_set_m_flags_1=b.asm.kx).apply(null,arguments)},Ez=b._emscripten_bind_Material___destroy___0=function(){return(Ez=b._emscripten_bind_Material___destroy___0=b.asm.lx).apply(null,arguments)},Fz=b._emscripten_bind_tMaterialArray_size_0=function(){return(Fz=b._emscripten_bind_tMaterialArray_size_0=b.asm.mx).apply(null,
|
||||||
|
arguments)},Gz=b._emscripten_bind_tMaterialArray_at_1=function(){return(Gz=b._emscripten_bind_tMaterialArray_at_1=b.asm.nx).apply(null,arguments)},Hz=b._emscripten_bind_tMaterialArray___destroy___0=function(){return(Hz=b._emscripten_bind_tMaterialArray___destroy___0=b.asm.ox).apply(null,arguments)},Iz=b._emscripten_bind_Anchor_get_m_node_0=function(){return(Iz=b._emscripten_bind_Anchor_get_m_node_0=b.asm.px).apply(null,arguments)},Jz=b._emscripten_bind_Anchor_set_m_node_1=function(){return(Jz=b._emscripten_bind_Anchor_set_m_node_1=
|
||||||
|
b.asm.qx).apply(null,arguments)},Kz=b._emscripten_bind_Anchor_get_m_local_0=function(){return(Kz=b._emscripten_bind_Anchor_get_m_local_0=b.asm.rx).apply(null,arguments)},Lz=b._emscripten_bind_Anchor_set_m_local_1=function(){return(Lz=b._emscripten_bind_Anchor_set_m_local_1=b.asm.sx).apply(null,arguments)},Mz=b._emscripten_bind_Anchor_get_m_body_0=function(){return(Mz=b._emscripten_bind_Anchor_get_m_body_0=b.asm.tx).apply(null,arguments)},Nz=b._emscripten_bind_Anchor_set_m_body_1=function(){return(Nz=
|
||||||
|
b._emscripten_bind_Anchor_set_m_body_1=b.asm.ux).apply(null,arguments)},Oz=b._emscripten_bind_Anchor_get_m_influence_0=function(){return(Oz=b._emscripten_bind_Anchor_get_m_influence_0=b.asm.vx).apply(null,arguments)},Pz=b._emscripten_bind_Anchor_set_m_influence_1=function(){return(Pz=b._emscripten_bind_Anchor_set_m_influence_1=b.asm.wx).apply(null,arguments)},Qz=b._emscripten_bind_Anchor_get_m_c0_0=function(){return(Qz=b._emscripten_bind_Anchor_get_m_c0_0=b.asm.xx).apply(null,arguments)},Rz=b._emscripten_bind_Anchor_set_m_c0_1=
|
||||||
|
function(){return(Rz=b._emscripten_bind_Anchor_set_m_c0_1=b.asm.yx).apply(null,arguments)},Sz=b._emscripten_bind_Anchor_get_m_c1_0=function(){return(Sz=b._emscripten_bind_Anchor_get_m_c1_0=b.asm.zx).apply(null,arguments)},Tz=b._emscripten_bind_Anchor_set_m_c1_1=function(){return(Tz=b._emscripten_bind_Anchor_set_m_c1_1=b.asm.Ax).apply(null,arguments)},Uz=b._emscripten_bind_Anchor_get_m_c2_0=function(){return(Uz=b._emscripten_bind_Anchor_get_m_c2_0=b.asm.Bx).apply(null,arguments)},Vz=b._emscripten_bind_Anchor_set_m_c2_1=
|
||||||
|
function(){return(Vz=b._emscripten_bind_Anchor_set_m_c2_1=b.asm.Cx).apply(null,arguments)},Wz=b._emscripten_bind_Anchor___destroy___0=function(){return(Wz=b._emscripten_bind_Anchor___destroy___0=b.asm.Dx).apply(null,arguments)},Xz=b._emscripten_bind_tAnchorArray_size_0=function(){return(Xz=b._emscripten_bind_tAnchorArray_size_0=b.asm.Ex).apply(null,arguments)},Yz=b._emscripten_bind_tAnchorArray_at_1=function(){return(Yz=b._emscripten_bind_tAnchorArray_at_1=b.asm.Fx).apply(null,arguments)},Zz=b._emscripten_bind_tAnchorArray_clear_0=
|
||||||
|
function(){return(Zz=b._emscripten_bind_tAnchorArray_clear_0=b.asm.Gx).apply(null,arguments)},$z=b._emscripten_bind_tAnchorArray_push_back_1=function(){return($z=b._emscripten_bind_tAnchorArray_push_back_1=b.asm.Hx).apply(null,arguments)},aA=b._emscripten_bind_tAnchorArray_pop_back_0=function(){return(aA=b._emscripten_bind_tAnchorArray_pop_back_0=b.asm.Ix).apply(null,arguments)},bA=b._emscripten_bind_tAnchorArray___destroy___0=function(){return(bA=b._emscripten_bind_tAnchorArray___destroy___0=b.asm.Jx).apply(null,
|
||||||
|
arguments)},cA=b._emscripten_bind_Config_get_kVCF_0=function(){return(cA=b._emscripten_bind_Config_get_kVCF_0=b.asm.Kx).apply(null,arguments)},dA=b._emscripten_bind_Config_set_kVCF_1=function(){return(dA=b._emscripten_bind_Config_set_kVCF_1=b.asm.Lx).apply(null,arguments)},eA=b._emscripten_bind_Config_get_kDP_0=function(){return(eA=b._emscripten_bind_Config_get_kDP_0=b.asm.Mx).apply(null,arguments)},fA=b._emscripten_bind_Config_set_kDP_1=function(){return(fA=b._emscripten_bind_Config_set_kDP_1=b.asm.Nx).apply(null,
|
||||||
|
arguments)},gA=b._emscripten_bind_Config_get_kDG_0=function(){return(gA=b._emscripten_bind_Config_get_kDG_0=b.asm.Ox).apply(null,arguments)},hA=b._emscripten_bind_Config_set_kDG_1=function(){return(hA=b._emscripten_bind_Config_set_kDG_1=b.asm.Px).apply(null,arguments)},iA=b._emscripten_bind_Config_get_kLF_0=function(){return(iA=b._emscripten_bind_Config_get_kLF_0=b.asm.Qx).apply(null,arguments)},jA=b._emscripten_bind_Config_set_kLF_1=function(){return(jA=b._emscripten_bind_Config_set_kLF_1=b.asm.Rx).apply(null,
|
||||||
|
arguments)},kA=b._emscripten_bind_Config_get_kPR_0=function(){return(kA=b._emscripten_bind_Config_get_kPR_0=b.asm.Sx).apply(null,arguments)},lA=b._emscripten_bind_Config_set_kPR_1=function(){return(lA=b._emscripten_bind_Config_set_kPR_1=b.asm.Tx).apply(null,arguments)},mA=b._emscripten_bind_Config_get_kVC_0=function(){return(mA=b._emscripten_bind_Config_get_kVC_0=b.asm.Ux).apply(null,arguments)},nA=b._emscripten_bind_Config_set_kVC_1=function(){return(nA=b._emscripten_bind_Config_set_kVC_1=b.asm.Vx).apply(null,
|
||||||
|
arguments)},oA=b._emscripten_bind_Config_get_kDF_0=function(){return(oA=b._emscripten_bind_Config_get_kDF_0=b.asm.Wx).apply(null,arguments)},pA=b._emscripten_bind_Config_set_kDF_1=function(){return(pA=b._emscripten_bind_Config_set_kDF_1=b.asm.Xx).apply(null,arguments)},qA=b._emscripten_bind_Config_get_kMT_0=function(){return(qA=b._emscripten_bind_Config_get_kMT_0=b.asm.Yx).apply(null,arguments)},rA=b._emscripten_bind_Config_set_kMT_1=function(){return(rA=b._emscripten_bind_Config_set_kMT_1=b.asm.Zx).apply(null,
|
||||||
|
arguments)},sA=b._emscripten_bind_Config_get_kCHR_0=function(){return(sA=b._emscripten_bind_Config_get_kCHR_0=b.asm._x).apply(null,arguments)},tA=b._emscripten_bind_Config_set_kCHR_1=function(){return(tA=b._emscripten_bind_Config_set_kCHR_1=b.asm.$x).apply(null,arguments)},uA=b._emscripten_bind_Config_get_kKHR_0=function(){return(uA=b._emscripten_bind_Config_get_kKHR_0=b.asm.ay).apply(null,arguments)},vA=b._emscripten_bind_Config_set_kKHR_1=function(){return(vA=b._emscripten_bind_Config_set_kKHR_1=
|
||||||
|
b.asm.by).apply(null,arguments)},wA=b._emscripten_bind_Config_get_kSHR_0=function(){return(wA=b._emscripten_bind_Config_get_kSHR_0=b.asm.cy).apply(null,arguments)},xA=b._emscripten_bind_Config_set_kSHR_1=function(){return(xA=b._emscripten_bind_Config_set_kSHR_1=b.asm.dy).apply(null,arguments)},yA=b._emscripten_bind_Config_get_kAHR_0=function(){return(yA=b._emscripten_bind_Config_get_kAHR_0=b.asm.ey).apply(null,arguments)},zA=b._emscripten_bind_Config_set_kAHR_1=function(){return(zA=b._emscripten_bind_Config_set_kAHR_1=
|
||||||
|
b.asm.fy).apply(null,arguments)},AA=b._emscripten_bind_Config_get_kSRHR_CL_0=function(){return(AA=b._emscripten_bind_Config_get_kSRHR_CL_0=b.asm.gy).apply(null,arguments)},BA=b._emscripten_bind_Config_set_kSRHR_CL_1=function(){return(BA=b._emscripten_bind_Config_set_kSRHR_CL_1=b.asm.hy).apply(null,arguments)},CA=b._emscripten_bind_Config_get_kSKHR_CL_0=function(){return(CA=b._emscripten_bind_Config_get_kSKHR_CL_0=b.asm.iy).apply(null,arguments)},DA=b._emscripten_bind_Config_set_kSKHR_CL_1=function(){return(DA=
|
||||||
|
b._emscripten_bind_Config_set_kSKHR_CL_1=b.asm.jy).apply(null,arguments)},EA=b._emscripten_bind_Config_get_kSSHR_CL_0=function(){return(EA=b._emscripten_bind_Config_get_kSSHR_CL_0=b.asm.ky).apply(null,arguments)},FA=b._emscripten_bind_Config_set_kSSHR_CL_1=function(){return(FA=b._emscripten_bind_Config_set_kSSHR_CL_1=b.asm.ly).apply(null,arguments)},GA=b._emscripten_bind_Config_get_kSR_SPLT_CL_0=function(){return(GA=b._emscripten_bind_Config_get_kSR_SPLT_CL_0=b.asm.my).apply(null,arguments)},HA=b._emscripten_bind_Config_set_kSR_SPLT_CL_1=
|
||||||
|
function(){return(HA=b._emscripten_bind_Config_set_kSR_SPLT_CL_1=b.asm.ny).apply(null,arguments)},IA=b._emscripten_bind_Config_get_kSK_SPLT_CL_0=function(){return(IA=b._emscripten_bind_Config_get_kSK_SPLT_CL_0=b.asm.oy).apply(null,arguments)},JA=b._emscripten_bind_Config_set_kSK_SPLT_CL_1=function(){return(JA=b._emscripten_bind_Config_set_kSK_SPLT_CL_1=b.asm.py).apply(null,arguments)},KA=b._emscripten_bind_Config_get_kSS_SPLT_CL_0=function(){return(KA=b._emscripten_bind_Config_get_kSS_SPLT_CL_0=b.asm.qy).apply(null,
|
||||||
|
arguments)},LA=b._emscripten_bind_Config_set_kSS_SPLT_CL_1=function(){return(LA=b._emscripten_bind_Config_set_kSS_SPLT_CL_1=b.asm.ry).apply(null,arguments)},MA=b._emscripten_bind_Config_get_maxvolume_0=function(){return(MA=b._emscripten_bind_Config_get_maxvolume_0=b.asm.sy).apply(null,arguments)},NA=b._emscripten_bind_Config_set_maxvolume_1=function(){return(NA=b._emscripten_bind_Config_set_maxvolume_1=b.asm.ty).apply(null,arguments)},OA=b._emscripten_bind_Config_get_timescale_0=function(){return(OA=
|
||||||
|
b._emscripten_bind_Config_get_timescale_0=b.asm.uy).apply(null,arguments)},PA=b._emscripten_bind_Config_set_timescale_1=function(){return(PA=b._emscripten_bind_Config_set_timescale_1=b.asm.vy).apply(null,arguments)},QA=b._emscripten_bind_Config_get_viterations_0=function(){return(QA=b._emscripten_bind_Config_get_viterations_0=b.asm.wy).apply(null,arguments)},RA=b._emscripten_bind_Config_set_viterations_1=function(){return(RA=b._emscripten_bind_Config_set_viterations_1=b.asm.xy).apply(null,arguments)},
|
||||||
|
SA=b._emscripten_bind_Config_get_piterations_0=function(){return(SA=b._emscripten_bind_Config_get_piterations_0=b.asm.yy).apply(null,arguments)},TA=b._emscripten_bind_Config_set_piterations_1=function(){return(TA=b._emscripten_bind_Config_set_piterations_1=b.asm.zy).apply(null,arguments)},UA=b._emscripten_bind_Config_get_diterations_0=function(){return(UA=b._emscripten_bind_Config_get_diterations_0=b.asm.Ay).apply(null,arguments)},VA=b._emscripten_bind_Config_set_diterations_1=function(){return(VA=
|
||||||
|
b._emscripten_bind_Config_set_diterations_1=b.asm.By).apply(null,arguments)},WA=b._emscripten_bind_Config_get_citerations_0=function(){return(WA=b._emscripten_bind_Config_get_citerations_0=b.asm.Cy).apply(null,arguments)},XA=b._emscripten_bind_Config_set_citerations_1=function(){return(XA=b._emscripten_bind_Config_set_citerations_1=b.asm.Dy).apply(null,arguments)},YA=b._emscripten_bind_Config_get_collisions_0=function(){return(YA=b._emscripten_bind_Config_get_collisions_0=b.asm.Ey).apply(null,arguments)},
|
||||||
|
ZA=b._emscripten_bind_Config_set_collisions_1=function(){return(ZA=b._emscripten_bind_Config_set_collisions_1=b.asm.Fy).apply(null,arguments)},$A=b._emscripten_bind_Config___destroy___0=function(){return($A=b._emscripten_bind_Config___destroy___0=b.asm.Gy).apply(null,arguments)},aB=b._emscripten_bind_btSoftBody_btSoftBody_4=function(){return(aB=b._emscripten_bind_btSoftBody_btSoftBody_4=b.asm.Hy).apply(null,arguments)},bB=b._emscripten_bind_btSoftBody_checkLink_2=function(){return(bB=b._emscripten_bind_btSoftBody_checkLink_2=
|
||||||
|
b.asm.Iy).apply(null,arguments)},cB=b._emscripten_bind_btSoftBody_checkFace_3=function(){return(cB=b._emscripten_bind_btSoftBody_checkFace_3=b.asm.Jy).apply(null,arguments)},dB=b._emscripten_bind_btSoftBody_appendMaterial_0=function(){return(dB=b._emscripten_bind_btSoftBody_appendMaterial_0=b.asm.Ky).apply(null,arguments)},eB=b._emscripten_bind_btSoftBody_appendNode_2=function(){return(eB=b._emscripten_bind_btSoftBody_appendNode_2=b.asm.Ly).apply(null,arguments)},fB=b._emscripten_bind_btSoftBody_appendLink_4=
|
||||||
|
function(){return(fB=b._emscripten_bind_btSoftBody_appendLink_4=b.asm.My).apply(null,arguments)},gB=b._emscripten_bind_btSoftBody_appendFace_4=function(){return(gB=b._emscripten_bind_btSoftBody_appendFace_4=b.asm.Ny).apply(null,arguments)},hB=b._emscripten_bind_btSoftBody_appendTetra_5=function(){return(hB=b._emscripten_bind_btSoftBody_appendTetra_5=b.asm.Oy).apply(null,arguments)},iB=b._emscripten_bind_btSoftBody_appendAnchor_4=function(){return(iB=b._emscripten_bind_btSoftBody_appendAnchor_4=b.asm.Py).apply(null,
|
||||||
|
arguments)},jB=b._emscripten_bind_btSoftBody_addForce_1=function(){return(jB=b._emscripten_bind_btSoftBody_addForce_1=b.asm.Qy).apply(null,arguments)},kB=b._emscripten_bind_btSoftBody_addForce_2=function(){return(kB=b._emscripten_bind_btSoftBody_addForce_2=b.asm.Ry).apply(null,arguments)},lB=b._emscripten_bind_btSoftBody_addAeroForceToNode_2=function(){return(lB=b._emscripten_bind_btSoftBody_addAeroForceToNode_2=b.asm.Sy).apply(null,arguments)},mB=b._emscripten_bind_btSoftBody_getTotalMass_0=function(){return(mB=
|
||||||
|
b._emscripten_bind_btSoftBody_getTotalMass_0=b.asm.Ty).apply(null,arguments)},nB=b._emscripten_bind_btSoftBody_setTotalMass_2=function(){return(nB=b._emscripten_bind_btSoftBody_setTotalMass_2=b.asm.Uy).apply(null,arguments)},oB=b._emscripten_bind_btSoftBody_setMass_2=function(){return(oB=b._emscripten_bind_btSoftBody_setMass_2=b.asm.Vy).apply(null,arguments)},pB=b._emscripten_bind_btSoftBody_transform_1=function(){return(pB=b._emscripten_bind_btSoftBody_transform_1=b.asm.Wy).apply(null,arguments)},
|
||||||
|
qB=b._emscripten_bind_btSoftBody_translate_1=function(){return(qB=b._emscripten_bind_btSoftBody_translate_1=b.asm.Xy).apply(null,arguments)},rB=b._emscripten_bind_btSoftBody_rotate_1=function(){return(rB=b._emscripten_bind_btSoftBody_rotate_1=b.asm.Yy).apply(null,arguments)},sB=b._emscripten_bind_btSoftBody_scale_1=function(){return(sB=b._emscripten_bind_btSoftBody_scale_1=b.asm.Zy).apply(null,arguments)},tB=b._emscripten_bind_btSoftBody_generateClusters_1=function(){return(tB=b._emscripten_bind_btSoftBody_generateClusters_1=
|
||||||
|
b.asm._y).apply(null,arguments)},uB=b._emscripten_bind_btSoftBody_generateClusters_2=function(){return(uB=b._emscripten_bind_btSoftBody_generateClusters_2=b.asm.$y).apply(null,arguments)},vB=b._emscripten_bind_btSoftBody_generateBendingConstraints_2=function(){return(vB=b._emscripten_bind_btSoftBody_generateBendingConstraints_2=b.asm.az).apply(null,arguments)},wB=b._emscripten_bind_btSoftBody_upcast_1=function(){return(wB=b._emscripten_bind_btSoftBody_upcast_1=b.asm.bz).apply(null,arguments)},xB=
|
||||||
|
b._emscripten_bind_btSoftBody_getRestLengthScale_0=function(){return(xB=b._emscripten_bind_btSoftBody_getRestLengthScale_0=b.asm.cz).apply(null,arguments)},yB=b._emscripten_bind_btSoftBody_setRestLengthScale_1=function(){return(yB=b._emscripten_bind_btSoftBody_setRestLengthScale_1=b.asm.dz).apply(null,arguments)},zB=b._emscripten_bind_btSoftBody_setAnisotropicFriction_2=function(){return(zB=b._emscripten_bind_btSoftBody_setAnisotropicFriction_2=b.asm.ez).apply(null,arguments)},AB=b._emscripten_bind_btSoftBody_getCollisionShape_0=
|
||||||
|
function(){return(AB=b._emscripten_bind_btSoftBody_getCollisionShape_0=b.asm.fz).apply(null,arguments)},BB=b._emscripten_bind_btSoftBody_setContactProcessingThreshold_1=function(){return(BB=b._emscripten_bind_btSoftBody_setContactProcessingThreshold_1=b.asm.gz).apply(null,arguments)},CB=b._emscripten_bind_btSoftBody_setActivationState_1=function(){return(CB=b._emscripten_bind_btSoftBody_setActivationState_1=b.asm.hz).apply(null,arguments)},DB=b._emscripten_bind_btSoftBody_forceActivationState_1=function(){return(DB=
|
||||||
|
b._emscripten_bind_btSoftBody_forceActivationState_1=b.asm.iz).apply(null,arguments)},EB=b._emscripten_bind_btSoftBody_activate_0=function(){return(EB=b._emscripten_bind_btSoftBody_activate_0=b.asm.jz).apply(null,arguments)},FB=b._emscripten_bind_btSoftBody_activate_1=function(){return(FB=b._emscripten_bind_btSoftBody_activate_1=b.asm.kz).apply(null,arguments)},GB=b._emscripten_bind_btSoftBody_isActive_0=function(){return(GB=b._emscripten_bind_btSoftBody_isActive_0=b.asm.lz).apply(null,arguments)},
|
||||||
|
HB=b._emscripten_bind_btSoftBody_isKinematicObject_0=function(){return(HB=b._emscripten_bind_btSoftBody_isKinematicObject_0=b.asm.mz).apply(null,arguments)},IB=b._emscripten_bind_btSoftBody_isStaticObject_0=function(){return(IB=b._emscripten_bind_btSoftBody_isStaticObject_0=b.asm.nz).apply(null,arguments)},JB=b._emscripten_bind_btSoftBody_isStaticOrKinematicObject_0=function(){return(JB=b._emscripten_bind_btSoftBody_isStaticOrKinematicObject_0=b.asm.oz).apply(null,arguments)},KB=b._emscripten_bind_btSoftBody_getRestitution_0=
|
||||||
|
function(){return(KB=b._emscripten_bind_btSoftBody_getRestitution_0=b.asm.pz).apply(null,arguments)},LB=b._emscripten_bind_btSoftBody_getFriction_0=function(){return(LB=b._emscripten_bind_btSoftBody_getFriction_0=b.asm.qz).apply(null,arguments)},MB=b._emscripten_bind_btSoftBody_getRollingFriction_0=function(){return(MB=b._emscripten_bind_btSoftBody_getRollingFriction_0=b.asm.rz).apply(null,arguments)},NB=b._emscripten_bind_btSoftBody_setRestitution_1=function(){return(NB=b._emscripten_bind_btSoftBody_setRestitution_1=
|
||||||
|
b.asm.sz).apply(null,arguments)},OB=b._emscripten_bind_btSoftBody_setFriction_1=function(){return(OB=b._emscripten_bind_btSoftBody_setFriction_1=b.asm.tz).apply(null,arguments)},PB=b._emscripten_bind_btSoftBody_setRollingFriction_1=function(){return(PB=b._emscripten_bind_btSoftBody_setRollingFriction_1=b.asm.uz).apply(null,arguments)},QB=b._emscripten_bind_btSoftBody_getWorldTransform_0=function(){return(QB=b._emscripten_bind_btSoftBody_getWorldTransform_0=b.asm.vz).apply(null,arguments)},RB=b._emscripten_bind_btSoftBody_getCollisionFlags_0=
|
||||||
|
function(){return(RB=b._emscripten_bind_btSoftBody_getCollisionFlags_0=b.asm.wz).apply(null,arguments)},SB=b._emscripten_bind_btSoftBody_setCollisionFlags_1=function(){return(SB=b._emscripten_bind_btSoftBody_setCollisionFlags_1=b.asm.xz).apply(null,arguments)},TB=b._emscripten_bind_btSoftBody_setWorldTransform_1=function(){return(TB=b._emscripten_bind_btSoftBody_setWorldTransform_1=b.asm.yz).apply(null,arguments)},UB=b._emscripten_bind_btSoftBody_setCollisionShape_1=function(){return(UB=b._emscripten_bind_btSoftBody_setCollisionShape_1=
|
||||||
|
b.asm.zz).apply(null,arguments)},VB=b._emscripten_bind_btSoftBody_setCcdMotionThreshold_1=function(){return(VB=b._emscripten_bind_btSoftBody_setCcdMotionThreshold_1=b.asm.Az).apply(null,arguments)},WB=b._emscripten_bind_btSoftBody_setCcdSweptSphereRadius_1=function(){return(WB=b._emscripten_bind_btSoftBody_setCcdSweptSphereRadius_1=b.asm.Bz).apply(null,arguments)},XB=b._emscripten_bind_btSoftBody_getUserIndex_0=function(){return(XB=b._emscripten_bind_btSoftBody_getUserIndex_0=b.asm.Cz).apply(null,
|
||||||
|
arguments)},YB=b._emscripten_bind_btSoftBody_setUserIndex_1=function(){return(YB=b._emscripten_bind_btSoftBody_setUserIndex_1=b.asm.Dz).apply(null,arguments)},ZB=b._emscripten_bind_btSoftBody_getUserPointer_0=function(){return(ZB=b._emscripten_bind_btSoftBody_getUserPointer_0=b.asm.Ez).apply(null,arguments)},$B=b._emscripten_bind_btSoftBody_setUserPointer_1=function(){return($B=b._emscripten_bind_btSoftBody_setUserPointer_1=b.asm.Fz).apply(null,arguments)},aC=b._emscripten_bind_btSoftBody_getBroadphaseHandle_0=
|
||||||
|
function(){return(aC=b._emscripten_bind_btSoftBody_getBroadphaseHandle_0=b.asm.Gz).apply(null,arguments)},bC=b._emscripten_bind_btSoftBody_get_m_cfg_0=function(){return(bC=b._emscripten_bind_btSoftBody_get_m_cfg_0=b.asm.Hz).apply(null,arguments)},cC=b._emscripten_bind_btSoftBody_set_m_cfg_1=function(){return(cC=b._emscripten_bind_btSoftBody_set_m_cfg_1=b.asm.Iz).apply(null,arguments)},dC=b._emscripten_bind_btSoftBody_get_m_nodes_0=function(){return(dC=b._emscripten_bind_btSoftBody_get_m_nodes_0=b.asm.Jz).apply(null,
|
||||||
|
arguments)},eC=b._emscripten_bind_btSoftBody_set_m_nodes_1=function(){return(eC=b._emscripten_bind_btSoftBody_set_m_nodes_1=b.asm.Kz).apply(null,arguments)},fC=b._emscripten_bind_btSoftBody_get_m_faces_0=function(){return(fC=b._emscripten_bind_btSoftBody_get_m_faces_0=b.asm.Lz).apply(null,arguments)},gC=b._emscripten_bind_btSoftBody_set_m_faces_1=function(){return(gC=b._emscripten_bind_btSoftBody_set_m_faces_1=b.asm.Mz).apply(null,arguments)},hC=b._emscripten_bind_btSoftBody_get_m_materials_0=function(){return(hC=
|
||||||
|
b._emscripten_bind_btSoftBody_get_m_materials_0=b.asm.Nz).apply(null,arguments)},iC=b._emscripten_bind_btSoftBody_set_m_materials_1=function(){return(iC=b._emscripten_bind_btSoftBody_set_m_materials_1=b.asm.Oz).apply(null,arguments)},jC=b._emscripten_bind_btSoftBody_get_m_anchors_0=function(){return(jC=b._emscripten_bind_btSoftBody_get_m_anchors_0=b.asm.Pz).apply(null,arguments)},kC=b._emscripten_bind_btSoftBody_set_m_anchors_1=function(){return(kC=b._emscripten_bind_btSoftBody_set_m_anchors_1=b.asm.Qz).apply(null,
|
||||||
|
arguments)},lC=b._emscripten_bind_btSoftBody___destroy___0=function(){return(lC=b._emscripten_bind_btSoftBody___destroy___0=b.asm.Rz).apply(null,arguments)},mC=b._emscripten_bind_btSoftBodyRigidBodyCollisionConfiguration_btSoftBodyRigidBodyCollisionConfiguration_0=function(){return(mC=b._emscripten_bind_btSoftBodyRigidBodyCollisionConfiguration_btSoftBodyRigidBodyCollisionConfiguration_0=b.asm.Sz).apply(null,arguments)},nC=b._emscripten_bind_btSoftBodyRigidBodyCollisionConfiguration_btSoftBodyRigidBodyCollisionConfiguration_1=
|
||||||
|
function(){return(nC=b._emscripten_bind_btSoftBodyRigidBodyCollisionConfiguration_btSoftBodyRigidBodyCollisionConfiguration_1=b.asm.Tz).apply(null,arguments)},oC=b._emscripten_bind_btSoftBodyRigidBodyCollisionConfiguration___destroy___0=function(){return(oC=b._emscripten_bind_btSoftBodyRigidBodyCollisionConfiguration___destroy___0=b.asm.Uz).apply(null,arguments)},pC=b._emscripten_bind_btDefaultSoftBodySolver_btDefaultSoftBodySolver_0=function(){return(pC=b._emscripten_bind_btDefaultSoftBodySolver_btDefaultSoftBodySolver_0=
|
||||||
|
b.asm.Vz).apply(null,arguments)},qC=b._emscripten_bind_btDefaultSoftBodySolver___destroy___0=function(){return(qC=b._emscripten_bind_btDefaultSoftBodySolver___destroy___0=b.asm.Wz).apply(null,arguments)},rC=b._emscripten_bind_btSoftBodyArray_size_0=function(){return(rC=b._emscripten_bind_btSoftBodyArray_size_0=b.asm.Xz).apply(null,arguments)},sC=b._emscripten_bind_btSoftBodyArray_at_1=function(){return(sC=b._emscripten_bind_btSoftBodyArray_at_1=b.asm.Yz).apply(null,arguments)},tC=b._emscripten_bind_btSoftBodyArray___destroy___0=
|
||||||
|
function(){return(tC=b._emscripten_bind_btSoftBodyArray___destroy___0=b.asm.Zz).apply(null,arguments)},uC=b._emscripten_bind_btSoftRigidDynamicsWorld_btSoftRigidDynamicsWorld_5=function(){return(uC=b._emscripten_bind_btSoftRigidDynamicsWorld_btSoftRigidDynamicsWorld_5=b.asm._z).apply(null,arguments)},vC=b._emscripten_bind_btSoftRigidDynamicsWorld_addSoftBody_3=function(){return(vC=b._emscripten_bind_btSoftRigidDynamicsWorld_addSoftBody_3=b.asm.$z).apply(null,arguments)},wC=b._emscripten_bind_btSoftRigidDynamicsWorld_removeSoftBody_1=
|
||||||
|
function(){return(wC=b._emscripten_bind_btSoftRigidDynamicsWorld_removeSoftBody_1=b.asm.aA).apply(null,arguments)},xC=b._emscripten_bind_btSoftRigidDynamicsWorld_removeCollisionObject_1=function(){return(xC=b._emscripten_bind_btSoftRigidDynamicsWorld_removeCollisionObject_1=b.asm.bA).apply(null,arguments)},yC=b._emscripten_bind_btSoftRigidDynamicsWorld_getWorldInfo_0=function(){return(yC=b._emscripten_bind_btSoftRigidDynamicsWorld_getWorldInfo_0=b.asm.cA).apply(null,arguments)},zC=b._emscripten_bind_btSoftRigidDynamicsWorld_getSoftBodyArray_0=
|
||||||
|
function(){return(zC=b._emscripten_bind_btSoftRigidDynamicsWorld_getSoftBodyArray_0=b.asm.dA).apply(null,arguments)},AC=b._emscripten_bind_btSoftRigidDynamicsWorld_getDispatcher_0=function(){return(AC=b._emscripten_bind_btSoftRigidDynamicsWorld_getDispatcher_0=b.asm.eA).apply(null,arguments)},BC=b._emscripten_bind_btSoftRigidDynamicsWorld_rayTest_3=function(){return(BC=b._emscripten_bind_btSoftRigidDynamicsWorld_rayTest_3=b.asm.fA).apply(null,arguments)},CC=b._emscripten_bind_btSoftRigidDynamicsWorld_getPairCache_0=
|
||||||
|
function(){return(CC=b._emscripten_bind_btSoftRigidDynamicsWorld_getPairCache_0=b.asm.gA).apply(null,arguments)},DC=b._emscripten_bind_btSoftRigidDynamicsWorld_getDispatchInfo_0=function(){return(DC=b._emscripten_bind_btSoftRigidDynamicsWorld_getDispatchInfo_0=b.asm.hA).apply(null,arguments)},EC=b._emscripten_bind_btSoftRigidDynamicsWorld_addCollisionObject_1=function(){return(EC=b._emscripten_bind_btSoftRigidDynamicsWorld_addCollisionObject_1=b.asm.iA).apply(null,arguments)},FC=b._emscripten_bind_btSoftRigidDynamicsWorld_addCollisionObject_2=
|
||||||
|
function(){return(FC=b._emscripten_bind_btSoftRigidDynamicsWorld_addCollisionObject_2=b.asm.jA).apply(null,arguments)},GC=b._emscripten_bind_btSoftRigidDynamicsWorld_addCollisionObject_3=function(){return(GC=b._emscripten_bind_btSoftRigidDynamicsWorld_addCollisionObject_3=b.asm.kA).apply(null,arguments)},HC=b._emscripten_bind_btSoftRigidDynamicsWorld_getBroadphase_0=function(){return(HC=b._emscripten_bind_btSoftRigidDynamicsWorld_getBroadphase_0=b.asm.lA).apply(null,arguments)},IC=b._emscripten_bind_btSoftRigidDynamicsWorld_convexSweepTest_5=
|
||||||
|
function(){return(IC=b._emscripten_bind_btSoftRigidDynamicsWorld_convexSweepTest_5=b.asm.mA).apply(null,arguments)},JC=b._emscripten_bind_btSoftRigidDynamicsWorld_contactPairTest_3=function(){return(JC=b._emscripten_bind_btSoftRigidDynamicsWorld_contactPairTest_3=b.asm.nA).apply(null,arguments)},KC=b._emscripten_bind_btSoftRigidDynamicsWorld_contactTest_2=function(){return(KC=b._emscripten_bind_btSoftRigidDynamicsWorld_contactTest_2=b.asm.oA).apply(null,arguments)},LC=b._emscripten_bind_btSoftRigidDynamicsWorld_updateSingleAabb_1=
|
||||||
|
function(){return(LC=b._emscripten_bind_btSoftRigidDynamicsWorld_updateSingleAabb_1=b.asm.pA).apply(null,arguments)},MC=b._emscripten_bind_btSoftRigidDynamicsWorld_setDebugDrawer_1=function(){return(MC=b._emscripten_bind_btSoftRigidDynamicsWorld_setDebugDrawer_1=b.asm.qA).apply(null,arguments)},NC=b._emscripten_bind_btSoftRigidDynamicsWorld_getDebugDrawer_0=function(){return(NC=b._emscripten_bind_btSoftRigidDynamicsWorld_getDebugDrawer_0=b.asm.rA).apply(null,arguments)},OC=b._emscripten_bind_btSoftRigidDynamicsWorld_debugDrawWorld_0=
|
||||||
|
function(){return(OC=b._emscripten_bind_btSoftRigidDynamicsWorld_debugDrawWorld_0=b.asm.sA).apply(null,arguments)},PC=b._emscripten_bind_btSoftRigidDynamicsWorld_debugDrawObject_3=function(){return(PC=b._emscripten_bind_btSoftRigidDynamicsWorld_debugDrawObject_3=b.asm.tA).apply(null,arguments)},QC=b._emscripten_bind_btSoftRigidDynamicsWorld_setGravity_1=function(){return(QC=b._emscripten_bind_btSoftRigidDynamicsWorld_setGravity_1=b.asm.uA).apply(null,arguments)},RC=b._emscripten_bind_btSoftRigidDynamicsWorld_getGravity_0=
|
||||||
|
function(){return(RC=b._emscripten_bind_btSoftRigidDynamicsWorld_getGravity_0=b.asm.vA).apply(null,arguments)},SC=b._emscripten_bind_btSoftRigidDynamicsWorld_addRigidBody_1=function(){return(SC=b._emscripten_bind_btSoftRigidDynamicsWorld_addRigidBody_1=b.asm.wA).apply(null,arguments)},TC=b._emscripten_bind_btSoftRigidDynamicsWorld_addRigidBody_3=function(){return(TC=b._emscripten_bind_btSoftRigidDynamicsWorld_addRigidBody_3=b.asm.xA).apply(null,arguments)},UC=b._emscripten_bind_btSoftRigidDynamicsWorld_removeRigidBody_1=
|
||||||
|
function(){return(UC=b._emscripten_bind_btSoftRigidDynamicsWorld_removeRigidBody_1=b.asm.yA).apply(null,arguments)},VC=b._emscripten_bind_btSoftRigidDynamicsWorld_addConstraint_1=function(){return(VC=b._emscripten_bind_btSoftRigidDynamicsWorld_addConstraint_1=b.asm.zA).apply(null,arguments)},WC=b._emscripten_bind_btSoftRigidDynamicsWorld_addConstraint_2=function(){return(WC=b._emscripten_bind_btSoftRigidDynamicsWorld_addConstraint_2=b.asm.AA).apply(null,arguments)},XC=b._emscripten_bind_btSoftRigidDynamicsWorld_removeConstraint_1=
|
||||||
|
function(){return(XC=b._emscripten_bind_btSoftRigidDynamicsWorld_removeConstraint_1=b.asm.BA).apply(null,arguments)},YC=b._emscripten_bind_btSoftRigidDynamicsWorld_stepSimulation_1=function(){return(YC=b._emscripten_bind_btSoftRigidDynamicsWorld_stepSimulation_1=b.asm.CA).apply(null,arguments)},ZC=b._emscripten_bind_btSoftRigidDynamicsWorld_stepSimulation_2=function(){return(ZC=b._emscripten_bind_btSoftRigidDynamicsWorld_stepSimulation_2=b.asm.DA).apply(null,arguments)},$C=b._emscripten_bind_btSoftRigidDynamicsWorld_stepSimulation_3=
|
||||||
|
function(){return($C=b._emscripten_bind_btSoftRigidDynamicsWorld_stepSimulation_3=b.asm.EA).apply(null,arguments)},aD=b._emscripten_bind_btSoftRigidDynamicsWorld_setContactAddedCallback_1=function(){return(aD=b._emscripten_bind_btSoftRigidDynamicsWorld_setContactAddedCallback_1=b.asm.FA).apply(null,arguments)},bD=b._emscripten_bind_btSoftRigidDynamicsWorld_setContactProcessedCallback_1=function(){return(bD=b._emscripten_bind_btSoftRigidDynamicsWorld_setContactProcessedCallback_1=b.asm.GA).apply(null,
|
||||||
|
arguments)},cD=b._emscripten_bind_btSoftRigidDynamicsWorld_setContactDestroyedCallback_1=function(){return(cD=b._emscripten_bind_btSoftRigidDynamicsWorld_setContactDestroyedCallback_1=b.asm.HA).apply(null,arguments)},dD=b._emscripten_bind_btSoftRigidDynamicsWorld_addAction_1=function(){return(dD=b._emscripten_bind_btSoftRigidDynamicsWorld_addAction_1=b.asm.IA).apply(null,arguments)},eD=b._emscripten_bind_btSoftRigidDynamicsWorld_removeAction_1=function(){return(eD=b._emscripten_bind_btSoftRigidDynamicsWorld_removeAction_1=
|
||||||
|
b.asm.JA).apply(null,arguments)},fD=b._emscripten_bind_btSoftRigidDynamicsWorld_getSolverInfo_0=function(){return(fD=b._emscripten_bind_btSoftRigidDynamicsWorld_getSolverInfo_0=b.asm.KA).apply(null,arguments)},gD=b._emscripten_bind_btSoftRigidDynamicsWorld_setInternalTickCallback_1=function(){return(gD=b._emscripten_bind_btSoftRigidDynamicsWorld_setInternalTickCallback_1=b.asm.LA).apply(null,arguments)},hD=b._emscripten_bind_btSoftRigidDynamicsWorld_setInternalTickCallback_2=function(){return(hD=
|
||||||
|
b._emscripten_bind_btSoftRigidDynamicsWorld_setInternalTickCallback_2=b.asm.MA).apply(null,arguments)},iD=b._emscripten_bind_btSoftRigidDynamicsWorld_setInternalTickCallback_3=function(){return(iD=b._emscripten_bind_btSoftRigidDynamicsWorld_setInternalTickCallback_3=b.asm.NA).apply(null,arguments)},jD=b._emscripten_bind_btSoftRigidDynamicsWorld___destroy___0=function(){return(jD=b._emscripten_bind_btSoftRigidDynamicsWorld___destroy___0=b.asm.OA).apply(null,arguments)},kD=b._emscripten_bind_btSoftBodyHelpers_btSoftBodyHelpers_0=
|
||||||
|
function(){return(kD=b._emscripten_bind_btSoftBodyHelpers_btSoftBodyHelpers_0=b.asm.PA).apply(null,arguments)},lD=b._emscripten_bind_btSoftBodyHelpers_CreateRope_5=function(){return(lD=b._emscripten_bind_btSoftBodyHelpers_CreateRope_5=b.asm.QA).apply(null,arguments)},mD=b._emscripten_bind_btSoftBodyHelpers_CreatePatch_9=function(){return(mD=b._emscripten_bind_btSoftBodyHelpers_CreatePatch_9=b.asm.RA).apply(null,arguments)},nD=b._emscripten_bind_btSoftBodyHelpers_CreatePatchUV_10=function(){return(nD=
|
||||||
|
b._emscripten_bind_btSoftBodyHelpers_CreatePatchUV_10=b.asm.SA).apply(null,arguments)},oD=b._emscripten_bind_btSoftBodyHelpers_CreateEllipsoid_4=function(){return(oD=b._emscripten_bind_btSoftBodyHelpers_CreateEllipsoid_4=b.asm.TA).apply(null,arguments)},pD=b._emscripten_bind_btSoftBodyHelpers_CreateFromTriMesh_5=function(){return(pD=b._emscripten_bind_btSoftBodyHelpers_CreateFromTriMesh_5=b.asm.UA).apply(null,arguments)},qD=b._emscripten_bind_btSoftBodyHelpers_CreateFromConvexHull_4=function(){return(qD=
|
||||||
|
b._emscripten_bind_btSoftBodyHelpers_CreateFromConvexHull_4=b.asm.VA).apply(null,arguments)},rD=b._emscripten_bind_btSoftBodyHelpers___destroy___0=function(){return(rD=b._emscripten_bind_btSoftBodyHelpers___destroy___0=b.asm.WA).apply(null,arguments)},sD=b._emscripten_enum_PHY_ScalarType_PHY_FLOAT=function(){return(sD=b._emscripten_enum_PHY_ScalarType_PHY_FLOAT=b.asm.XA).apply(null,arguments)},tD=b._emscripten_enum_PHY_ScalarType_PHY_DOUBLE=function(){return(tD=b._emscripten_enum_PHY_ScalarType_PHY_DOUBLE=
|
||||||
|
b.asm.YA).apply(null,arguments)},uD=b._emscripten_enum_PHY_ScalarType_PHY_INTEGER=function(){return(uD=b._emscripten_enum_PHY_ScalarType_PHY_INTEGER=b.asm.ZA).apply(null,arguments)},vD=b._emscripten_enum_PHY_ScalarType_PHY_SHORT=function(){return(vD=b._emscripten_enum_PHY_ScalarType_PHY_SHORT=b.asm._A).apply(null,arguments)},wD=b._emscripten_enum_PHY_ScalarType_PHY_FIXEDPOINT88=function(){return(wD=b._emscripten_enum_PHY_ScalarType_PHY_FIXEDPOINT88=b.asm.$A).apply(null,arguments)},xD=b._emscripten_enum_PHY_ScalarType_PHY_UCHAR=
|
||||||
|
function(){return(xD=b._emscripten_enum_PHY_ScalarType_PHY_UCHAR=b.asm.aB).apply(null,arguments)},yD=b._emscripten_enum_eGIMPACT_SHAPE_TYPE_CONST_GIMPACT_COMPOUND_SHAPE=function(){return(yD=b._emscripten_enum_eGIMPACT_SHAPE_TYPE_CONST_GIMPACT_COMPOUND_SHAPE=b.asm.bB).apply(null,arguments)},zD=b._emscripten_enum_eGIMPACT_SHAPE_TYPE_CONST_GIMPACT_TRIMESH_SHAPE_PART=function(){return(zD=b._emscripten_enum_eGIMPACT_SHAPE_TYPE_CONST_GIMPACT_TRIMESH_SHAPE_PART=b.asm.cB).apply(null,arguments)},AD=b._emscripten_enum_eGIMPACT_SHAPE_TYPE_CONST_GIMPACT_TRIMESH_SHAPE=
|
||||||
|
function(){return(AD=b._emscripten_enum_eGIMPACT_SHAPE_TYPE_CONST_GIMPACT_TRIMESH_SHAPE=b.asm.dB).apply(null,arguments)},BD=b._emscripten_enum_btConstraintParams_BT_CONSTRAINT_ERP=function(){return(BD=b._emscripten_enum_btConstraintParams_BT_CONSTRAINT_ERP=b.asm.eB).apply(null,arguments)},CD=b._emscripten_enum_btConstraintParams_BT_CONSTRAINT_STOP_ERP=function(){return(CD=b._emscripten_enum_btConstraintParams_BT_CONSTRAINT_STOP_ERP=b.asm.fB).apply(null,arguments)},DD=b._emscripten_enum_btConstraintParams_BT_CONSTRAINT_CFM=
|
||||||
|
function(){return(DD=b._emscripten_enum_btConstraintParams_BT_CONSTRAINT_CFM=b.asm.gB).apply(null,arguments)},ED=b._emscripten_enum_btConstraintParams_BT_CONSTRAINT_STOP_CFM=function(){return(ED=b._emscripten_enum_btConstraintParams_BT_CONSTRAINT_STOP_CFM=b.asm.hB).apply(null,arguments)};b._malloc=function(){return(b._malloc=b.asm.jB).apply(null,arguments)};b.___start_em_js=27240;b.___stop_em_js=27338;b.UTF8ToString=sa;
|
||||||
|
b.addFunction=function(a,c){if(!Ua){Ua=new WeakMap;var d=ya.length;if(Ua)for(var e=0;e<0+d;e++){var g=e;var n=Ta[g];n||(g>=Ta.length&&(Ta.length=g+1),Ta[g]=n=ya.get(g));(g=n)&&Ua.set(g,e)}}if(d=Ua.get(a)||0)return d;if(Va.length)d=Va.pop();else{try{ya.grow(1)}catch(T){if(!(T instanceof RangeError))throw T;throw"Unable to grow wasm table. Set ALLOW_TABLE_GROWTH.";}d=ya.length-1}try{e=d,ya.set(e,a),Ta[e]=ya.get(e)}catch(T){if(!(T instanceof TypeError))throw T;if("function"==typeof WebAssembly.Function){e=
|
||||||
|
WebAssembly.Function;g={i:"i32",j:"i32",f:"f32",d:"f64",p:"i32"};n={parameters:[],results:"v"==c[0]?[]:[g[c[0]]]};for(var z=1;z<c.length;++z)n.parameters.push(g[c[z]]),"j"===c[z]&&n.parameters.push("i32");c=new e(n,a)}else{e=[1];g=c.slice(0,1);c=c.slice(1);n={i:127,p:127,j:126,f:125,d:124};e.push(96);z=c.length;128>z?e.push(z):e.push(z%128|128,z>>7);for(z=0;z<c.length;++z)e.push(n[c[z]]);"v"==g?e.push(0):e.push(1,n[g]);c=[0,97,115,109,1,0,0,0,1];g=e.length;128>g?c.push(g):c.push(g%128|128,g>>7);c.push.apply(c,
|
||||||
|
e);c.push(2,7,1,1,101,1,102,0,0,7,5,1,1,102,0,0);c=new WebAssembly.Module(new Uint8Array(c));c=(new WebAssembly.Instance(c,{e:{f:a}})).exports.f}e=d;ya.set(e,c);Ta[e]=ya.get(e)}Ua.set(a,d);return d};var FD;Ha=function GD(){FD||HD();FD||(Ha=GD)};
|
||||||
|
function HD(){function a(){if(!FD&&(FD=!0,b.calledRun=!0,!qa)){Ca=!0;Qa(Aa);aa(b);if(b.onRuntimeInitialized)b.onRuntimeInitialized();if(b.postRun)for("function"==typeof b.postRun&&(b.postRun=[b.postRun]);b.postRun.length;){var c=b.postRun.shift();Ba.unshift(c)}Qa(Ba)}}if(!(0<Fa)){if(b.preRun)for("function"==typeof b.preRun&&(b.preRun=[b.preRun]);b.preRun.length;)Ea();Qa(za);0<Fa||(b.setStatus?(b.setStatus("Running..."),setTimeout(function(){setTimeout(function(){b.setStatus("")},1);a()},1)):a())}}
|
||||||
|
if(b.preInit)for("function"==typeof b.preInit&&(b.preInit=[b.preInit]);0<b.preInit.length;)b.preInit.pop()();HD();function f(){}f.prototype=Object.create(f.prototype);f.prototype.constructor=f;f.prototype.lB=f;f.mB={};b.WrapperObject=f;function h(a){return(a||f).mB}b.getCache=h;function k(a,c){var d=h(c),e=d[a];if(e)return e;e=Object.create((c||f).prototype);e.kB=a;return d[a]=e}b.wrapPointer=k;b.castObject=function(a,c){return k(a.kB,c)};b.NULL=k(0);
|
||||||
|
b.destroy=function(a){if(!a.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";a.__destroy__();delete h(a.lB)[a.kB]};b.compare=function(a,c){return a.kB===c.kB};b.getPointer=function(a){return a.kB};b.getClass=function(a){return a.lB};var ID=0,JD=0,KD=0,LD=[],MD=0;function ND(){if(MD){for(var a=0;a<LD.length;a++)b._free(LD[a]);LD.length=0;b._free(ID);ID=0;JD+=MD;MD=0}ID||(JD+=128,(ID=b._malloc(JD))||oa());KD=0}
|
||||||
|
function OD(a,c){ID||oa();a=a.length*c.BYTES_PER_ELEMENT;a=a+7&-8;KD+a>=JD?(0<a||oa(),MD+=a,c=b._malloc(a),LD.push(c)):(c=ID+KD,KD+=a);return c}function PD(a,c,d){d>>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var e=0;e<a.length;e++)c[d+e]=a[e]}
|
||||||
|
function QD(a){if("string"===typeof a){for(var c=0,d=0;d<a.length;++d){var e=a.charCodeAt(d);127>=e?c++:2047>=e?c+=2:55296<=e&&57343>=e?(c+=4,++d):c+=3}c=Array(c+1);e=c.length;d=0;if(0<e){e=d+e-1;for(var g=0;g<a.length;++g){var n=a.charCodeAt(g);if(55296<=n&&57343>=n){var z=a.charCodeAt(++g);n=65536+((n&1023)<<10)|z&1023}if(127>=n){if(d>=e)break;c[d++]=n}else{if(2047>=n){if(d+1>=e)break;c[d++]=192|n>>6}else{if(65535>=n){if(d+2>=e)break;c[d++]=224|n>>12}else{if(d+3>=e)break;c[d++]=240|n>>18;c[d++]=
|
||||||
|
128|n>>12&63}c[d++]=128|n>>6&63}c[d++]=128|n&63}}c[d]=0}a=OD(c,ua);PD(c,ua,a);return a}return a}function RD(a){if("object"===typeof a){var c=OD(a,wa);PD(a,wa,c);return c}return a}function l(){throw"cannot construct a btCollisionShape, no constructor in IDL";}l.prototype=Object.create(f.prototype);l.prototype.constructor=l;l.prototype.lB=l;l.mB={};b.btCollisionShape=l;l.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Xa(c,a)};
|
||||||
|
l.prototype.getLocalScaling=function(){return k(Ya(this.kB),m)};l.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Za(d,a,c)};l.prototype.setMargin=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);$a(c,a)};l.prototype.getMargin=function(){return ab(this.kB)};l.prototype.__destroy__=function(){bb(this.kB)};function SD(){throw"cannot construct a btCollisionWorld, no constructor in IDL";}SD.prototype=Object.create(f.prototype);
|
||||||
|
SD.prototype.constructor=SD;SD.prototype.lB=SD;SD.mB={};b.btCollisionWorld=SD;SD.prototype.getDispatcher=function(){return k(cb(this.kB),TD)};SD.prototype.rayTest=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);db(e,a,c,d)};SD.prototype.getPairCache=function(){return k(eb(this.kB),UD)};SD.prototype.getDispatchInfo=function(){return k(fb(this.kB),p)};
|
||||||
|
SD.prototype.addCollisionObject=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);void 0===c?gb(e,a):void 0===d?hb(e,a,c):ib(e,a,c,d)};SD.prototype.removeCollisionObject=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);jb(c,a)};SD.prototype.getBroadphase=function(){return k(kb(this.kB),VD)};
|
||||||
|
SD.prototype.convexSweepTest=function(a,c,d,e,g){var n=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);lb(n,a,c,d,e,g)};SD.prototype.contactPairTest=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);mb(e,a,c,d)};
|
||||||
|
SD.prototype.contactTest=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);nb(d,a,c)};SD.prototype.updateSingleAabb=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ob(c,a)};SD.prototype.setDebugDrawer=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);pb(c,a)};SD.prototype.getDebugDrawer=function(){return k(qb(this.kB),WD)};SD.prototype.debugDrawWorld=function(){rb(this.kB)};
|
||||||
|
SD.prototype.debugDrawObject=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);sb(e,a,c,d)};SD.prototype.__destroy__=function(){tb(this.kB)};function q(){throw"cannot construct a btCollisionObject, no constructor in IDL";}q.prototype=Object.create(f.prototype);q.prototype.constructor=q;q.prototype.lB=q;q.mB={};b.btCollisionObject=q;
|
||||||
|
q.prototype.setAnisotropicFriction=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);ub(d,a,c)};q.prototype.getCollisionShape=function(){return k(vb(this.kB),l)};q.prototype.setContactProcessingThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);wb(c,a)};q.prototype.setActivationState=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);xb(c,a)};
|
||||||
|
q.prototype.forceActivationState=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);yb(c,a)};q.prototype.activate=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);void 0===a?zb(c):Ab(c,a)};q.prototype.isActive=function(){return!!Bb(this.kB)};q.prototype.isKinematicObject=function(){return!!Cb(this.kB)};q.prototype.isStaticObject=function(){return!!Db(this.kB)};q.prototype.isStaticOrKinematicObject=function(){return!!Eb(this.kB)};q.prototype.getRestitution=function(){return Fb(this.kB)};
|
||||||
|
q.prototype.getFriction=function(){return Gb(this.kB)};q.prototype.getRollingFriction=function(){return Hb(this.kB)};q.prototype.setRestitution=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ib(c,a)};q.prototype.setFriction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Jb(c,a)};q.prototype.setRollingFriction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Kb(c,a)};q.prototype.getWorldTransform=function(){return k(Lb(this.kB),r)};q.prototype.getCollisionFlags=function(){return Mb(this.kB)};
|
||||||
|
q.prototype.setCollisionFlags=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Nb(c,a)};q.prototype.setWorldTransform=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ob(c,a)};q.prototype.setCollisionShape=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Pb(c,a)};q.prototype.setCcdMotionThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Qb(c,a)};q.prototype.setCcdSweptSphereRadius=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Rb(c,a)};
|
||||||
|
q.prototype.getUserIndex=function(){return Sb(this.kB)};q.prototype.setUserIndex=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Tb(c,a)};q.prototype.getUserPointer=function(){return k(Ub(this.kB),XD)};q.prototype.setUserPointer=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Vb(c,a)};q.prototype.getBroadphaseHandle=function(){return k(Wb(this.kB),YD)};q.prototype.__destroy__=function(){Xb(this.kB)};
|
||||||
|
function ZD(){throw"cannot construct a btConcaveShape, no constructor in IDL";}ZD.prototype=Object.create(l.prototype);ZD.prototype.constructor=ZD;ZD.prototype.lB=ZD;ZD.mB={};b.btConcaveShape=ZD;ZD.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Yb(c,a)};ZD.prototype.getLocalScaling=function(){return k(Zb(this.kB),m)};ZD.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);$b(d,a,c)};
|
||||||
|
ZD.prototype.__destroy__=function(){ac(this.kB)};function $D(){throw"cannot construct a btCollisionAlgorithm, no constructor in IDL";}$D.prototype=Object.create(f.prototype);$D.prototype.constructor=$D;$D.prototype.lB=$D;$D.mB={};b.btCollisionAlgorithm=$D;$D.prototype.__destroy__=function(){bc(this.kB)};function aE(){throw"cannot construct a btTypedConstraint, no constructor in IDL";}aE.prototype=Object.create(f.prototype);aE.prototype.constructor=aE;aE.prototype.lB=aE;aE.mB={};
|
||||||
|
b.btTypedConstraint=aE;aE.prototype.enableFeedback=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);cc(c,a)};aE.prototype.getBreakingImpulseThreshold=function(){return ec(this.kB)};aE.prototype.setBreakingImpulseThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);fc(c,a)};aE.prototype.getParam=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);return gc(d,a,c)};
|
||||||
|
aE.prototype.setParam=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);hc(e,a,c,d)};aE.prototype.__destroy__=function(){ic(this.kB)};function bE(){throw"cannot construct a btDynamicsWorld, no constructor in IDL";}bE.prototype=Object.create(SD.prototype);bE.prototype.constructor=bE;bE.prototype.lB=bE;bE.mB={};b.btDynamicsWorld=bE;bE.prototype.addAction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);jc(c,a)};
|
||||||
|
bE.prototype.removeAction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);kc(c,a)};bE.prototype.getSolverInfo=function(){return k(lc(this.kB),t)};bE.prototype.setInternalTickCallback=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);void 0===c?mc(e,a):void 0===d?nc(e,a,c):oc(e,a,c,d)};bE.prototype.getDispatcher=function(){return k(pc(this.kB),TD)};
|
||||||
|
bE.prototype.rayTest=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);qc(e,a,c,d)};bE.prototype.getPairCache=function(){return k(rc(this.kB),UD)};bE.prototype.getDispatchInfo=function(){return k(sc(this.kB),p)};
|
||||||
|
bE.prototype.addCollisionObject=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);void 0===c?tc(e,a):void 0===d?uc(e,a,c):vc(e,a,c,d)};bE.prototype.removeCollisionObject=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);wc(c,a)};bE.prototype.getBroadphase=function(){return k(xc(this.kB),VD)};
|
||||||
|
bE.prototype.convexSweepTest=function(a,c,d,e,g){var n=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);yc(n,a,c,d,e,g)};bE.prototype.contactPairTest=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);zc(e,a,c,d)};
|
||||||
|
bE.prototype.contactTest=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Ac(d,a,c)};bE.prototype.updateSingleAabb=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Bc(c,a)};bE.prototype.setDebugDrawer=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Cc(c,a)};bE.prototype.getDebugDrawer=function(){return k(Dc(this.kB),WD)};bE.prototype.debugDrawWorld=function(){Ec(this.kB)};
|
||||||
|
bE.prototype.debugDrawObject=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);Fc(e,a,c,d)};bE.prototype.__destroy__=function(){Gc(this.kB)};function WD(){throw"cannot construct a btIDebugDraw, no constructor in IDL";}WD.prototype=Object.create(f.prototype);WD.prototype.constructor=WD;WD.prototype.lB=WD;WD.mB={};b.btIDebugDraw=WD;
|
||||||
|
WD.prototype.drawLine=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);Hc(e,a,c,d)};WD.prototype.drawContactPoint=function(a,c,d,e,g){var n=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);Ic(n,a,c,d,e,g)};
|
||||||
|
WD.prototype.reportErrorWarning=function(a){var c=this.kB;ND();a=a&&"object"===typeof a?a.kB:QD(a);Jc(c,a)};WD.prototype.draw3dText=function(a,c){var d=this.kB;ND();a&&"object"===typeof a&&(a=a.kB);c=c&&"object"===typeof c?c.kB:QD(c);Kc(d,a,c)};WD.prototype.setDebugMode=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Lc(c,a)};WD.prototype.getDebugMode=function(){return Mc(this.kB)};WD.prototype.__destroy__=function(){Nc(this.kB)};
|
||||||
|
function m(a,c,d){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);this.kB=void 0===a?Oc():void 0===c?_emscripten_bind_btVector3_btVector3_1(a):void 0===d?_emscripten_bind_btVector3_btVector3_2(a,c):Pc(a,c,d);h(m)[this.kB]=this}m.prototype=Object.create(f.prototype);m.prototype.constructor=m;m.prototype.lB=m;m.mB={};b.btVector3=m;m.prototype.length=m.prototype.length=function(){return Qc(this.kB)};m.prototype.x=m.prototype.x=function(){return Rc(this.kB)};
|
||||||
|
m.prototype.y=m.prototype.y=function(){return Sc(this.kB)};m.prototype.z=m.prototype.z=function(){return Tc(this.kB)};m.prototype.setX=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Uc(c,a)};m.prototype.setY=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Vc(c,a)};m.prototype.setZ=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Wc(c,a)};
|
||||||
|
m.prototype.setValue=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);Xc(e,a,c,d)};m.prototype.normalize=m.prototype.normalize=function(){Yc(this.kB)};m.prototype.rotate=m.prototype.rotate=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);return k(Zc(d,a,c),m)};m.prototype.dot=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return $c(c,a)};
|
||||||
|
m.prototype.op_mul=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(ad(c,a),m)};m.prototype.op_add=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(bd(c,a),m)};m.prototype.op_sub=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(cd(c,a),m)};m.prototype.__destroy__=function(){dd(this.kB)};function cE(){throw"cannot construct a btQuadWord, no constructor in IDL";}cE.prototype=Object.create(f.prototype);cE.prototype.constructor=cE;
|
||||||
|
cE.prototype.lB=cE;cE.mB={};b.btQuadWord=cE;cE.prototype.x=cE.prototype.x=function(){return ed(this.kB)};cE.prototype.y=cE.prototype.y=function(){return fd(this.kB)};cE.prototype.z=cE.prototype.z=function(){return gd(this.kB)};cE.prototype.w=cE.prototype.w=function(){return hd(this.kB)};cE.prototype.setX=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);jd(c,a)};cE.prototype.setY=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);kd(c,a)};
|
||||||
|
cE.prototype.setZ=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ld(c,a)};cE.prototype.setW=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);md(c,a)};cE.prototype.__destroy__=function(){nd(this.kB)};function dE(){throw"cannot construct a btMotionState, no constructor in IDL";}dE.prototype=Object.create(f.prototype);dE.prototype.constructor=dE;dE.prototype.lB=dE;dE.mB={};b.btMotionState=dE;
|
||||||
|
dE.prototype.getWorldTransform=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);od(c,a)};dE.prototype.setWorldTransform=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);pd(c,a)};dE.prototype.__destroy__=function(){qd(this.kB)};function u(){throw"cannot construct a RayResultCallback, no constructor in IDL";}u.prototype=Object.create(f.prototype);u.prototype.constructor=u;u.prototype.lB=u;u.mB={};b.RayResultCallback=u;u.prototype.hasHit=function(){return!!rd(this.kB)};
|
||||||
|
u.prototype.get_m_collisionFilterGroup=u.prototype.nB=function(){return sd(this.kB)};u.prototype.set_m_collisionFilterGroup=u.prototype.pB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);td(c,a)};Object.defineProperty(u.prototype,"m_collisionFilterGroup",{get:u.prototype.nB,set:u.prototype.pB});u.prototype.get_m_collisionFilterMask=u.prototype.oB=function(){return ud(this.kB)};
|
||||||
|
u.prototype.set_m_collisionFilterMask=u.prototype.qB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);vd(c,a)};Object.defineProperty(u.prototype,"m_collisionFilterMask",{get:u.prototype.oB,set:u.prototype.qB});u.prototype.get_m_closestHitFraction=u.prototype.rB=function(){return wd(this.kB)};u.prototype.set_m_closestHitFraction=u.prototype.sB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);xd(c,a)};Object.defineProperty(u.prototype,"m_closestHitFraction",{get:u.prototype.rB,set:u.prototype.sB});
|
||||||
|
u.prototype.get_m_collisionObject=u.prototype.vB=function(){return k(yd(this.kB),q)};u.prototype.set_m_collisionObject=u.prototype.CB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);zd(c,a)};Object.defineProperty(u.prototype,"m_collisionObject",{get:u.prototype.vB,set:u.prototype.CB});u.prototype.get_m_flags=u.prototype.tB=function(){return Ad(this.kB)};u.prototype.set_m_flags=u.prototype.uB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Bd(c,a)};
|
||||||
|
Object.defineProperty(u.prototype,"m_flags",{get:u.prototype.tB,set:u.prototype.uB});u.prototype.__destroy__=function(){Cd(this.kB)};function eE(){throw"cannot construct a ContactResultCallback, no constructor in IDL";}eE.prototype=Object.create(f.prototype);eE.prototype.constructor=eE;eE.prototype.lB=eE;eE.mB={};b.ContactResultCallback=eE;
|
||||||
|
eE.prototype.addSingleResult=function(a,c,d,e,g,n,z){var T=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);n&&"object"===typeof n&&(n=n.kB);z&&"object"===typeof z&&(z=z.kB);return Dd(T,a,c,d,e,g,n,z)};eE.prototype.__destroy__=function(){Ed(this.kB)};function v(){throw"cannot construct a ConvexResultCallback, no constructor in IDL";}v.prototype=Object.create(f.prototype);
|
||||||
|
v.prototype.constructor=v;v.prototype.lB=v;v.mB={};b.ConvexResultCallback=v;v.prototype.hasHit=function(){return!!Fd(this.kB)};v.prototype.get_m_collisionFilterGroup=v.prototype.nB=function(){return Gd(this.kB)};v.prototype.set_m_collisionFilterGroup=v.prototype.pB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Hd(c,a)};Object.defineProperty(v.prototype,"m_collisionFilterGroup",{get:v.prototype.nB,set:v.prototype.pB});v.prototype.get_m_collisionFilterMask=v.prototype.oB=function(){return Id(this.kB)};
|
||||||
|
v.prototype.set_m_collisionFilterMask=v.prototype.qB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Jd(c,a)};Object.defineProperty(v.prototype,"m_collisionFilterMask",{get:v.prototype.oB,set:v.prototype.qB});v.prototype.get_m_closestHitFraction=v.prototype.rB=function(){return Kd(this.kB)};v.prototype.set_m_closestHitFraction=v.prototype.sB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ld(c,a)};Object.defineProperty(v.prototype,"m_closestHitFraction",{get:v.prototype.rB,set:v.prototype.sB});
|
||||||
|
v.prototype.__destroy__=function(){Md(this.kB)};function fE(){throw"cannot construct a btConvexShape, no constructor in IDL";}fE.prototype=Object.create(l.prototype);fE.prototype.constructor=fE;fE.prototype.lB=fE;fE.mB={};b.btConvexShape=fE;fE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Nd(c,a)};fE.prototype.getLocalScaling=function(){return k(Od(this.kB),m)};
|
||||||
|
fE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Pd(d,a,c)};fE.prototype.setMargin=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Qd(c,a)};fE.prototype.getMargin=function(){return Rd(this.kB)};fE.prototype.__destroy__=function(){Sd(this.kB)};function gE(a,c){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);this.kB=Td(a,c);h(gE)[this.kB]=this}gE.prototype=Object.create(l.prototype);
|
||||||
|
gE.prototype.constructor=gE;gE.prototype.lB=gE;gE.mB={};b.btCapsuleShape=gE;gE.prototype.setMargin=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ud(c,a)};gE.prototype.getMargin=function(){return Vd(this.kB)};gE.prototype.getUpAxis=function(){return Wd(this.kB)};gE.prototype.getRadius=function(){return Xd(this.kB)};gE.prototype.getHalfHeight=function(){return Yd(this.kB)};gE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Zd(c,a)};
|
||||||
|
gE.prototype.getLocalScaling=function(){return k($d(this.kB),m)};gE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);ae(d,a,c)};gE.prototype.__destroy__=function(){be(this.kB)};function hE(a){a&&"object"===typeof a&&(a=a.kB);this.kB=ce(a);h(hE)[this.kB]=this}hE.prototype=Object.create(l.prototype);hE.prototype.constructor=hE;hE.prototype.lB=hE;hE.mB={};b.btCylinderShape=hE;
|
||||||
|
hE.prototype.setMargin=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);de(c,a)};hE.prototype.getMargin=function(){return ee(this.kB)};hE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);fe(c,a)};hE.prototype.getLocalScaling=function(){return k(ge(this.kB),m)};hE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);he(d,a,c)};hE.prototype.__destroy__=function(){ie(this.kB)};
|
||||||
|
function iE(a,c){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);this.kB=je(a,c);h(iE)[this.kB]=this}iE.prototype=Object.create(l.prototype);iE.prototype.constructor=iE;iE.prototype.lB=iE;iE.mB={};b.btConeShape=iE;iE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ke(c,a)};iE.prototype.getLocalScaling=function(){return k(le(this.kB),m)};
|
||||||
|
iE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);me(d,a,c)};iE.prototype.__destroy__=function(){ne(this.kB)};function jE(){throw"cannot construct a btStridingMeshInterface, no constructor in IDL";}jE.prototype=Object.create(f.prototype);jE.prototype.constructor=jE;jE.prototype.lB=jE;jE.mB={};b.btStridingMeshInterface=jE;jE.prototype.setScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);oe(c,a)};
|
||||||
|
jE.prototype.__destroy__=function(){pe(this.kB)};function kE(){throw"cannot construct a btTriangleMeshShape, no constructor in IDL";}kE.prototype=Object.create(ZD.prototype);kE.prototype.constructor=kE;kE.prototype.lB=kE;kE.mB={};b.btTriangleMeshShape=kE;kE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);qe(c,a)};kE.prototype.getLocalScaling=function(){return k(re(this.kB),m)};
|
||||||
|
kE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);se(d,a,c)};kE.prototype.__destroy__=function(){te(this.kB)};function lE(){throw"cannot construct a btPrimitiveManagerBase, no constructor in IDL";}lE.prototype=Object.create(f.prototype);lE.prototype.constructor=lE;lE.prototype.lB=lE;lE.mB={};b.btPrimitiveManagerBase=lE;lE.prototype.is_trimesh=function(){return!!ue(this.kB)};lE.prototype.get_primitive_count=function(){return ve(this.kB)};
|
||||||
|
lE.prototype.get_primitive_box=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);we(d,a,c)};lE.prototype.get_primitive_triangle=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);xe(d,a,c)};lE.prototype.__destroy__=function(){ye(this.kB)};function w(){throw"cannot construct a btGImpactShapeInterface, no constructor in IDL";}w.prototype=Object.create(ZD.prototype);w.prototype.constructor=w;w.prototype.lB=w;
|
||||||
|
w.mB={};b.btGImpactShapeInterface=w;w.prototype.updateBound=function(){ze(this.kB)};w.prototype.postUpdate=function(){Ae(this.kB)};w.prototype.getShapeType=function(){return Be(this.kB)};w.prototype.getName=function(){return sa(Ce(this.kB))};w.prototype.getGImpactShapeType=function(){return De(this.kB)};w.prototype.getPrimitiveManager=function(){return k(Ee(this.kB),lE)};w.prototype.getNumChildShapes=function(){return Fe(this.kB)};w.prototype.childrenHasTransform=function(){return!!Ge(this.kB)};
|
||||||
|
w.prototype.needsRetrieveTriangles=function(){return!!He(this.kB)};w.prototype.needsRetrieveTetrahedrons=function(){return!!Ie(this.kB)};w.prototype.getBulletTriangle=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Je(d,a,c)};w.prototype.getBulletTetrahedron=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Ke(d,a,c)};
|
||||||
|
w.prototype.getChildShape=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Le(c,a),l)};w.prototype.getChildTransform=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Me(c,a),r)};w.prototype.setChildTransform=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Ne(d,a,c)};w.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Oe(c,a)};
|
||||||
|
w.prototype.getLocalScaling=function(){return k(Pe(this.kB),m)};w.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Qe(d,a,c)};w.prototype.__destroy__=function(){Re(this.kB)};function mE(){throw"cannot construct a btActivatingCollisionAlgorithm, no constructor in IDL";}mE.prototype=Object.create($D.prototype);mE.prototype.constructor=mE;mE.prototype.lB=mE;mE.mB={};b.btActivatingCollisionAlgorithm=mE;
|
||||||
|
mE.prototype.__destroy__=function(){Se(this.kB)};function nE(a){a&&"object"===typeof a&&(a=a.kB);this.kB=void 0===a?Te():Ue(a);h(nE)[this.kB]=this}nE.prototype=Object.create(f.prototype);nE.prototype.constructor=nE;nE.prototype.lB=nE;nE.mB={};b.btDefaultCollisionConfiguration=nE;nE.prototype.__destroy__=function(){Ve(this.kB)};function TD(){throw"cannot construct a btDispatcher, no constructor in IDL";}TD.prototype=Object.create(f.prototype);TD.prototype.constructor=TD;TD.prototype.lB=TD;TD.mB={};
|
||||||
|
b.btDispatcher=TD;TD.prototype.getNumManifolds=function(){return We(this.kB)};TD.prototype.getManifoldByIndexInternal=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Xe(c,a),oE)};TD.prototype.__destroy__=function(){Ye(this.kB)};
|
||||||
|
function pE(a,c,d,e,g){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);this.kB=void 0===e?Ze(a,c,d):void 0===g?_emscripten_bind_btGeneric6DofConstraint_btGeneric6DofConstraint_4(a,c,d,e):$e(a,c,d,e,g);h(pE)[this.kB]=this}pE.prototype=Object.create(aE.prototype);pE.prototype.constructor=pE;pE.prototype.lB=pE;pE.mB={};b.btGeneric6DofConstraint=pE;
|
||||||
|
pE.prototype.setLinearLowerLimit=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);af(c,a)};pE.prototype.setLinearUpperLimit=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);bf(c,a)};pE.prototype.setAngularLowerLimit=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);cf(c,a)};pE.prototype.setAngularUpperLimit=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);df(c,a)};pE.prototype.getFrameOffsetA=function(){return k(ef(this.kB),r)};
|
||||||
|
pE.prototype.enableFeedback=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ff(c,a)};pE.prototype.getBreakingImpulseThreshold=function(){return gf(this.kB)};pE.prototype.setBreakingImpulseThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);hf(c,a)};pE.prototype.getParam=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);return jf(d,a,c)};
|
||||||
|
pE.prototype.setParam=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);kf(e,a,c,d)};pE.prototype.__destroy__=function(){lf(this.kB)};function x(a,c,d,e){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);this.kB=mf(a,c,d,e);h(x)[this.kB]=this}x.prototype=Object.create(bE.prototype);x.prototype.constructor=x;x.prototype.lB=x;x.mB={};
|
||||||
|
b.btDiscreteDynamicsWorld=x;x.prototype.setGravity=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);nf(c,a)};x.prototype.getGravity=function(){return k(of(this.kB),m)};x.prototype.addRigidBody=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);void 0===c?pf(e,a):void 0===d?_emscripten_bind_btDiscreteDynamicsWorld_addRigidBody_2(e,a,c):qf(e,a,c,d)};
|
||||||
|
x.prototype.removeRigidBody=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);rf(c,a)};x.prototype.addConstraint=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);void 0===c?sf(d,a):tf(d,a,c)};x.prototype.removeConstraint=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);uf(c,a)};
|
||||||
|
x.prototype.stepSimulation=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);return void 0===c?vf(e,a):void 0===d?wf(e,a,c):xf(e,a,c,d)};x.prototype.setContactAddedCallback=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);yf(c,a)};x.prototype.setContactProcessedCallback=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);zf(c,a)};
|
||||||
|
x.prototype.setContactDestroyedCallback=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Af(c,a)};x.prototype.getDispatcher=function(){return k(Bf(this.kB),TD)};x.prototype.rayTest=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);Cf(e,a,c,d)};x.prototype.getPairCache=function(){return k(Df(this.kB),UD)};x.prototype.getDispatchInfo=function(){return k(Ef(this.kB),p)};
|
||||||
|
x.prototype.addCollisionObject=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);void 0===c?Ff(e,a):void 0===d?Gf(e,a,c):Hf(e,a,c,d)};x.prototype.removeCollisionObject=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);If(c,a)};x.prototype.getBroadphase=function(){return k(Jf(this.kB),VD)};
|
||||||
|
x.prototype.convexSweepTest=function(a,c,d,e,g){var n=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);Kf(n,a,c,d,e,g)};x.prototype.contactPairTest=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);Lf(e,a,c,d)};
|
||||||
|
x.prototype.contactTest=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Mf(d,a,c)};x.prototype.updateSingleAabb=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Nf(c,a)};x.prototype.setDebugDrawer=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Of(c,a)};x.prototype.getDebugDrawer=function(){return k(Pf(this.kB),WD)};x.prototype.debugDrawWorld=function(){Qf(this.kB)};
|
||||||
|
x.prototype.debugDrawObject=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);Rf(e,a,c,d)};x.prototype.addAction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Sf(c,a)};x.prototype.removeAction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Tf(c,a)};x.prototype.getSolverInfo=function(){return k(Uf(this.kB),t)};
|
||||||
|
x.prototype.setInternalTickCallback=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);void 0===c?Vf(e,a):void 0===d?Wf(e,a,c):Xf(e,a,c,d)};x.prototype.__destroy__=function(){Yf(this.kB)};function qE(){throw"cannot construct a btVehicleRaycaster, no constructor in IDL";}qE.prototype=Object.create(f.prototype);qE.prototype.constructor=qE;qE.prototype.lB=qE;qE.mB={};b.btVehicleRaycaster=qE;
|
||||||
|
qE.prototype.castRay=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);Zf(e,a,c,d)};qE.prototype.__destroy__=function(){$f(this.kB)};function rE(){throw"cannot construct a btActionInterface, no constructor in IDL";}rE.prototype=Object.create(f.prototype);rE.prototype.constructor=rE;rE.prototype.lB=rE;rE.mB={};b.btActionInterface=rE;
|
||||||
|
rE.prototype.updateAction=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);ag(d,a,c)};rE.prototype.__destroy__=function(){bg(this.kB)};function y(){this.kB=cg();h(y)[this.kB]=this}y.prototype=Object.create(q.prototype);y.prototype.constructor=y;y.prototype.lB=y;y.mB={};b.btGhostObject=y;y.prototype.getNumOverlappingObjects=function(){return dg(this.kB)};
|
||||||
|
y.prototype.getOverlappingObject=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(eg(c,a),q)};y.prototype.setAnisotropicFriction=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);fg(d,a,c)};y.prototype.getCollisionShape=function(){return k(gg(this.kB),l)};y.prototype.setContactProcessingThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);hg(c,a)};
|
||||||
|
y.prototype.setActivationState=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ig(c,a)};y.prototype.forceActivationState=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);jg(c,a)};y.prototype.activate=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);void 0===a?kg(c):lg(c,a)};y.prototype.isActive=function(){return!!mg(this.kB)};y.prototype.isKinematicObject=function(){return!!ng(this.kB)};y.prototype.isStaticObject=function(){return!!og(this.kB)};
|
||||||
|
y.prototype.isStaticOrKinematicObject=function(){return!!pg(this.kB)};y.prototype.getRestitution=function(){return qg(this.kB)};y.prototype.getFriction=function(){return rg(this.kB)};y.prototype.getRollingFriction=function(){return sg(this.kB)};y.prototype.setRestitution=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);tg(c,a)};y.prototype.setFriction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ug(c,a)};
|
||||||
|
y.prototype.setRollingFriction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);vg(c,a)};y.prototype.getWorldTransform=function(){return k(wg(this.kB),r)};y.prototype.getCollisionFlags=function(){return xg(this.kB)};y.prototype.setCollisionFlags=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);yg(c,a)};y.prototype.setWorldTransform=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);zg(c,a)};
|
||||||
|
y.prototype.setCollisionShape=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ag(c,a)};y.prototype.setCcdMotionThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Bg(c,a)};y.prototype.setCcdSweptSphereRadius=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Cg(c,a)};y.prototype.getUserIndex=function(){return Dg(this.kB)};y.prototype.setUserIndex=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Eg(c,a)};
|
||||||
|
y.prototype.getUserPointer=function(){return k(Fg(this.kB),XD)};y.prototype.setUserPointer=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Gg(c,a)};y.prototype.getBroadphaseHandle=function(){return k(Hg(this.kB),YD)};y.prototype.__destroy__=function(){Ig(this.kB)};function sE(){throw"cannot construct a btSoftBodySolver, no constructor in IDL";}sE.prototype=Object.create(f.prototype);sE.prototype.constructor=sE;sE.prototype.lB=sE;sE.mB={};b.btSoftBodySolver=sE;sE.prototype.__destroy__=function(){Jg(this.kB)};
|
||||||
|
function XD(){throw"cannot construct a VoidPtr, no constructor in IDL";}XD.prototype=Object.create(f.prototype);XD.prototype.constructor=XD;XD.prototype.lB=XD;XD.mB={};b.VoidPtr=XD;XD.prototype.__destroy__=function(){Kg(this.kB)};function tE(){this.kB=Lg();h(tE)[this.kB]=this}tE.prototype=Object.create(WD.prototype);tE.prototype.constructor=tE;tE.prototype.lB=tE;tE.mB={};b.DebugDrawer=tE;
|
||||||
|
tE.prototype.drawLine=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);Mg(e,a,c,d)};tE.prototype.drawContactPoint=function(a,c,d,e,g){var n=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);Ng(n,a,c,d,e,g)};
|
||||||
|
tE.prototype.reportErrorWarning=function(a){var c=this.kB;ND();a=a&&"object"===typeof a?a.kB:QD(a);Og(c,a)};tE.prototype.draw3dText=function(a,c){var d=this.kB;ND();a&&"object"===typeof a&&(a=a.kB);c=c&&"object"===typeof c?c.kB:QD(c);Pg(d,a,c)};tE.prototype.setDebugMode=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Qg(c,a)};tE.prototype.getDebugMode=function(){return Rg(this.kB)};tE.prototype.__destroy__=function(){Sg(this.kB)};
|
||||||
|
function A(a,c,d,e){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);this.kB=void 0===a?Tg():void 0===c?_emscripten_bind_btVector4_btVector4_1(a):void 0===d?_emscripten_bind_btVector4_btVector4_2(a,c):void 0===e?_emscripten_bind_btVector4_btVector4_3(a,c,d):Ug(a,c,d,e);h(A)[this.kB]=this}A.prototype=Object.create(m.prototype);A.prototype.constructor=A;A.prototype.lB=A;A.mB={};b.btVector4=A;
|
||||||
|
A.prototype.w=A.prototype.w=function(){return Vg(this.kB)};A.prototype.setValue=function(a,c,d,e){var g=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);Wg(g,a,c,d,e)};A.prototype.length=A.prototype.length=function(){return Xg(this.kB)};A.prototype.x=A.prototype.x=function(){return Yg(this.kB)};A.prototype.y=A.prototype.y=function(){return Zg(this.kB)};A.prototype.z=A.prototype.z=function(){return $g(this.kB)};
|
||||||
|
A.prototype.setX=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ah(c,a)};A.prototype.setY=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);bh(c,a)};A.prototype.setZ=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ch(c,a)};A.prototype.normalize=A.prototype.normalize=function(){dh(this.kB)};A.prototype.rotate=A.prototype.rotate=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);return k(eh(d,a,c),m)};
|
||||||
|
A.prototype.dot=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return fh(c,a)};A.prototype.op_mul=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(gh(c,a),m)};A.prototype.op_add=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(hh(c,a),m)};A.prototype.op_sub=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(ih(c,a),m)};A.prototype.__destroy__=function(){jh(this.kB)};
|
||||||
|
function B(a,c,d,e){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);this.kB=kh(a,c,d,e);h(B)[this.kB]=this}B.prototype=Object.create(cE.prototype);B.prototype.constructor=B;B.prototype.lB=B;B.mB={};b.btQuaternion=B;B.prototype.setValue=function(a,c,d,e){var g=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);lh(g,a,c,d,e)};
|
||||||
|
B.prototype.setEulerZYX=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);mh(e,a,c,d)};B.prototype.setRotation=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);nh(d,a,c)};B.prototype.normalize=B.prototype.normalize=function(){oh(this.kB)};B.prototype.length2=function(){return ph(this.kB)};B.prototype.length=B.prototype.length=function(){return qh(this.kB)};
|
||||||
|
B.prototype.dot=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return rh(c,a)};B.prototype.normalized=function(){return k(sh(this.kB),B)};B.prototype.getAxis=function(){return k(th(this.kB),m)};B.prototype.inverse=B.prototype.inverse=function(){return k(uh(this.kB),B)};B.prototype.getAngle=function(){return vh(this.kB)};B.prototype.getAngleShortestPath=function(){return wh(this.kB)};
|
||||||
|
B.prototype.angle=B.prototype.angle=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return xh(c,a)};B.prototype.angleShortestPath=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return yh(c,a)};B.prototype.op_add=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(zh(c,a),B)};B.prototype.op_sub=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Ah(c,a),B)};
|
||||||
|
B.prototype.op_mul=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Bh(c,a),B)};B.prototype.op_mulq=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Ch(c,a),B)};B.prototype.op_div=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Dh(c,a),B)};B.prototype.x=B.prototype.x=function(){return Eh(this.kB)};B.prototype.y=B.prototype.y=function(){return Fh(this.kB)};B.prototype.z=B.prototype.z=function(){return Gh(this.kB)};
|
||||||
|
B.prototype.w=B.prototype.w=function(){return Hh(this.kB)};B.prototype.setX=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ih(c,a)};B.prototype.setY=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Jh(c,a)};B.prototype.setZ=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Kh(c,a)};B.prototype.setW=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Lh(c,a)};B.prototype.__destroy__=function(){Mh(this.kB)};
|
||||||
|
function uE(){throw"cannot construct a btMatrix3x3, no constructor in IDL";}uE.prototype=Object.create(f.prototype);uE.prototype.constructor=uE;uE.prototype.lB=uE;uE.mB={};b.btMatrix3x3=uE;uE.prototype.setEulerZYX=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);Nh(e,a,c,d)};uE.prototype.getRotation=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Oh(c,a)};
|
||||||
|
uE.prototype.getRow=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Ph(c,a),m)};uE.prototype.__destroy__=function(){Qh(this.kB)};function r(a,c){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);this.kB=void 0===a?Rh():void 0===c?_emscripten_bind_btTransform_btTransform_1(a):Sh(a,c);h(r)[this.kB]=this}r.prototype=Object.create(f.prototype);r.prototype.constructor=r;r.prototype.lB=r;r.mB={};b.btTransform=r;r.prototype.setIdentity=function(){Th(this.kB)};
|
||||||
|
r.prototype.setOrigin=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Uh(c,a)};r.prototype.setRotation=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Vh(c,a)};r.prototype.getOrigin=function(){return k(Wh(this.kB),m)};r.prototype.getRotation=function(){return k(Xh(this.kB),B)};r.prototype.getBasis=function(){return k(Yh(this.kB),uE)};r.prototype.setFromOpenGLMatrix=function(a){var c=this.kB;ND();"object"==typeof a&&(a=RD(a));Zh(c,a)};
|
||||||
|
r.prototype.inverse=r.prototype.inverse=function(){return k($h(this.kB),r)};r.prototype.op_mul=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(ai(c,a),r)};r.prototype.__destroy__=function(){bi(this.kB)};function vE(){this.kB=ci();h(vE)[this.kB]=this}vE.prototype=Object.create(dE.prototype);vE.prototype.constructor=vE;vE.prototype.lB=vE;vE.mB={};b.MotionState=vE;vE.prototype.getWorldTransform=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);di(c,a)};
|
||||||
|
vE.prototype.setWorldTransform=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ei(c,a)};vE.prototype.__destroy__=function(){fi(this.kB)};function wE(a,c){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);this.kB=void 0===a?gi():void 0===c?hi(a):ii(a,c);h(wE)[this.kB]=this}wE.prototype=Object.create(dE.prototype);wE.prototype.constructor=wE;wE.prototype.lB=wE;wE.mB={};b.btDefaultMotionState=wE;
|
||||||
|
wE.prototype.getWorldTransform=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ji(c,a)};wE.prototype.setWorldTransform=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ki(c,a)};wE.prototype.get_m_graphicsWorldTrans=wE.prototype.lD=function(){return k(li(this.kB),r)};wE.prototype.set_m_graphicsWorldTrans=wE.prototype.cG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);mi(c,a)};Object.defineProperty(wE.prototype,"m_graphicsWorldTrans",{get:wE.prototype.lD,set:wE.prototype.cG});
|
||||||
|
wE.prototype.__destroy__=function(){ni(this.kB)};function xE(){throw"cannot construct a btCollisionObjectWrapper, no constructor in IDL";}xE.prototype=Object.create(f.prototype);xE.prototype.constructor=xE;xE.prototype.lB=xE;xE.mB={};b.btCollisionObjectWrapper=xE;xE.prototype.getWorldTransform=function(){return k(oi(this.kB),r)};xE.prototype.getCollisionObject=function(){return k(pi(this.kB),q)};xE.prototype.getCollisionShape=function(){return k(qi(this.kB),l)};
|
||||||
|
function C(a,c){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);this.kB=ri(a,c);h(C)[this.kB]=this}C.prototype=Object.create(u.prototype);C.prototype.constructor=C;C.prototype.lB=C;C.mB={};b.ClosestRayResultCallback=C;C.prototype.hasHit=function(){return!!si(this.kB)};C.prototype.get_m_rayFromWorld=C.prototype.NB=function(){return k(ti(this.kB),m)};C.prototype.set_m_rayFromWorld=C.prototype.XB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ui(c,a)};
|
||||||
|
Object.defineProperty(C.prototype,"m_rayFromWorld",{get:C.prototype.NB,set:C.prototype.XB});C.prototype.get_m_rayToWorld=C.prototype.OB=function(){return k(vi(this.kB),m)};C.prototype.set_m_rayToWorld=C.prototype.YB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);wi(c,a)};Object.defineProperty(C.prototype,"m_rayToWorld",{get:C.prototype.OB,set:C.prototype.YB});C.prototype.get_m_hitNormalWorld=C.prototype.xB=function(){return k(xi(this.kB),m)};
|
||||||
|
C.prototype.set_m_hitNormalWorld=C.prototype.EB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);yi(c,a)};Object.defineProperty(C.prototype,"m_hitNormalWorld",{get:C.prototype.xB,set:C.prototype.EB});C.prototype.get_m_hitPointWorld=C.prototype.yB=function(){return k(zi(this.kB),m)};C.prototype.set_m_hitPointWorld=C.prototype.FB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ai(c,a)};Object.defineProperty(C.prototype,"m_hitPointWorld",{get:C.prototype.yB,set:C.prototype.FB});
|
||||||
|
C.prototype.get_m_collisionFilterGroup=C.prototype.nB=function(){return Bi(this.kB)};C.prototype.set_m_collisionFilterGroup=C.prototype.pB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ci(c,a)};Object.defineProperty(C.prototype,"m_collisionFilterGroup",{get:C.prototype.nB,set:C.prototype.pB});C.prototype.get_m_collisionFilterMask=C.prototype.oB=function(){return Di(this.kB)};
|
||||||
|
C.prototype.set_m_collisionFilterMask=C.prototype.qB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ei(c,a)};Object.defineProperty(C.prototype,"m_collisionFilterMask",{get:C.prototype.oB,set:C.prototype.qB});C.prototype.get_m_closestHitFraction=C.prototype.rB=function(){return Fi(this.kB)};C.prototype.set_m_closestHitFraction=C.prototype.sB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Gi(c,a)};Object.defineProperty(C.prototype,"m_closestHitFraction",{get:C.prototype.rB,set:C.prototype.sB});
|
||||||
|
C.prototype.get_m_collisionObject=C.prototype.vB=function(){return k(Hi(this.kB),q)};C.prototype.set_m_collisionObject=C.prototype.CB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ii(c,a)};Object.defineProperty(C.prototype,"m_collisionObject",{get:C.prototype.vB,set:C.prototype.CB});C.prototype.get_m_flags=C.prototype.tB=function(){return Ji(this.kB)};C.prototype.set_m_flags=C.prototype.uB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ki(c,a)};
|
||||||
|
Object.defineProperty(C.prototype,"m_flags",{get:C.prototype.tB,set:C.prototype.uB});C.prototype.__destroy__=function(){Li(this.kB)};function yE(){throw"cannot construct a btConstCollisionObjectArray, no constructor in IDL";}yE.prototype=Object.create(f.prototype);yE.prototype.constructor=yE;yE.prototype.lB=yE;yE.mB={};b.btConstCollisionObjectArray=yE;yE.prototype.size=yE.prototype.size=function(){return Mi(this.kB)};
|
||||||
|
yE.prototype.at=yE.prototype.at=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Ni(c,a),q)};yE.prototype.__destroy__=function(){Oi(this.kB)};function zE(){throw"cannot construct a btScalarArray, no constructor in IDL";}zE.prototype=Object.create(f.prototype);zE.prototype.constructor=zE;zE.prototype.lB=zE;zE.mB={};b.btScalarArray=zE;zE.prototype.size=zE.prototype.size=function(){return Pi(this.kB)};
|
||||||
|
zE.prototype.at=zE.prototype.at=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return Qi(c,a)};zE.prototype.__destroy__=function(){Ri(this.kB)};function D(a,c){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);this.kB=Si(a,c);h(D)[this.kB]=this}D.prototype=Object.create(u.prototype);D.prototype.constructor=D;D.prototype.lB=D;D.mB={};b.AllHitsRayResultCallback=D;D.prototype.hasHit=function(){return!!Ti(this.kB)};
|
||||||
|
D.prototype.get_m_collisionObjects=D.prototype.UC=function(){return k(Ui(this.kB),yE)};D.prototype.set_m_collisionObjects=D.prototype.LF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Vi(c,a)};Object.defineProperty(D.prototype,"m_collisionObjects",{get:D.prototype.UC,set:D.prototype.LF});D.prototype.get_m_rayFromWorld=D.prototype.NB=function(){return k(Wi(this.kB),m)};D.prototype.set_m_rayFromWorld=D.prototype.XB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Xi(c,a)};
|
||||||
|
Object.defineProperty(D.prototype,"m_rayFromWorld",{get:D.prototype.NB,set:D.prototype.XB});D.prototype.get_m_rayToWorld=D.prototype.OB=function(){return k(Yi(this.kB),m)};D.prototype.set_m_rayToWorld=D.prototype.YB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Zi(c,a)};Object.defineProperty(D.prototype,"m_rayToWorld",{get:D.prototype.OB,set:D.prototype.YB});D.prototype.get_m_hitNormalWorld=D.prototype.xB=function(){return k($i(this.kB),AE)};
|
||||||
|
D.prototype.set_m_hitNormalWorld=D.prototype.EB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);aj(c,a)};Object.defineProperty(D.prototype,"m_hitNormalWorld",{get:D.prototype.xB,set:D.prototype.EB});D.prototype.get_m_hitPointWorld=D.prototype.yB=function(){return k(bj(this.kB),AE)};D.prototype.set_m_hitPointWorld=D.prototype.FB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);cj(c,a)};Object.defineProperty(D.prototype,"m_hitPointWorld",{get:D.prototype.yB,set:D.prototype.FB});
|
||||||
|
D.prototype.get_m_hitFractions=D.prototype.qD=function(){return k(dj(this.kB),zE)};D.prototype.set_m_hitFractions=D.prototype.hG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ej(c,a)};Object.defineProperty(D.prototype,"m_hitFractions",{get:D.prototype.qD,set:D.prototype.hG});D.prototype.get_m_collisionFilterGroup=D.prototype.nB=function(){return fj(this.kB)};D.prototype.set_m_collisionFilterGroup=D.prototype.pB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);gj(c,a)};
|
||||||
|
Object.defineProperty(D.prototype,"m_collisionFilterGroup",{get:D.prototype.nB,set:D.prototype.pB});D.prototype.get_m_collisionFilterMask=D.prototype.oB=function(){return hj(this.kB)};D.prototype.set_m_collisionFilterMask=D.prototype.qB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ij(c,a)};Object.defineProperty(D.prototype,"m_collisionFilterMask",{get:D.prototype.oB,set:D.prototype.qB});D.prototype.get_m_closestHitFraction=D.prototype.rB=function(){return jj(this.kB)};
|
||||||
|
D.prototype.set_m_closestHitFraction=D.prototype.sB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);kj(c,a)};Object.defineProperty(D.prototype,"m_closestHitFraction",{get:D.prototype.rB,set:D.prototype.sB});D.prototype.get_m_collisionObject=D.prototype.vB=function(){return k(lj(this.kB),q)};D.prototype.set_m_collisionObject=D.prototype.CB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);mj(c,a)};Object.defineProperty(D.prototype,"m_collisionObject",{get:D.prototype.vB,set:D.prototype.CB});
|
||||||
|
D.prototype.get_m_flags=D.prototype.tB=function(){return nj(this.kB)};D.prototype.set_m_flags=D.prototype.uB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);oj(c,a)};Object.defineProperty(D.prototype,"m_flags",{get:D.prototype.tB,set:D.prototype.uB});D.prototype.__destroy__=function(){pj(this.kB)};function E(){throw"cannot construct a btManifoldPoint, no constructor in IDL";}E.prototype=Object.create(f.prototype);E.prototype.constructor=E;E.prototype.lB=E;E.mB={};b.btManifoldPoint=E;
|
||||||
|
E.prototype.getPositionWorldOnA=function(){return k(qj(this.kB),m)};E.prototype.getPositionWorldOnB=function(){return k(rj(this.kB),m)};E.prototype.getAppliedImpulse=function(){return sj(this.kB)};E.prototype.getDistance=function(){return tj(this.kB)};E.prototype.get_m_localPointA=E.prototype.GD=function(){return k(uj(this.kB),m)};E.prototype.set_m_localPointA=E.prototype.xG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);vj(c,a)};
|
||||||
|
Object.defineProperty(E.prototype,"m_localPointA",{get:E.prototype.GD,set:E.prototype.xG});E.prototype.get_m_localPointB=E.prototype.HD=function(){return k(wj(this.kB),m)};E.prototype.set_m_localPointB=E.prototype.yG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);xj(c,a)};Object.defineProperty(E.prototype,"m_localPointB",{get:E.prototype.HD,set:E.prototype.yG});E.prototype.get_m_positionWorldOnB=E.prototype.YD=function(){return k(yj(this.kB),m)};
|
||||||
|
E.prototype.set_m_positionWorldOnB=E.prototype.PG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);zj(c,a)};Object.defineProperty(E.prototype,"m_positionWorldOnB",{get:E.prototype.YD,set:E.prototype.PG});E.prototype.get_m_positionWorldOnA=E.prototype.XD=function(){return k(Aj(this.kB),m)};E.prototype.set_m_positionWorldOnA=E.prototype.OG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Bj(c,a)};Object.defineProperty(E.prototype,"m_positionWorldOnA",{get:E.prototype.XD,set:E.prototype.OG});
|
||||||
|
E.prototype.get_m_normalWorldOnB=E.prototype.SD=function(){return k(Cj(this.kB),m)};E.prototype.set_m_normalWorldOnB=E.prototype.JG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Dj(c,a)};Object.defineProperty(E.prototype,"m_normalWorldOnB",{get:E.prototype.SD,set:E.prototype.JG});E.prototype.get_m_userPersistentData=E.prototype.zE=function(){return Ej(this.kB)};E.prototype.set_m_userPersistentData=E.prototype.rH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Fj(c,a)};
|
||||||
|
Object.defineProperty(E.prototype,"m_userPersistentData",{get:E.prototype.zE,set:E.prototype.rH});E.prototype.__destroy__=function(){Gj(this.kB)};function BE(){this.kB=Hj();h(BE)[this.kB]=this}BE.prototype=Object.create(eE.prototype);BE.prototype.constructor=BE;BE.prototype.lB=BE;BE.mB={};b.ConcreteContactResultCallback=BE;
|
||||||
|
BE.prototype.addSingleResult=function(a,c,d,e,g,n,z){var T=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);n&&"object"===typeof n&&(n=n.kB);z&&"object"===typeof z&&(z=z.kB);return Ij(T,a,c,d,e,g,n,z)};BE.prototype.__destroy__=function(){Jj(this.kB)};function CE(){throw"cannot construct a LocalShapeInfo, no constructor in IDL";}CE.prototype=Object.create(f.prototype);
|
||||||
|
CE.prototype.constructor=CE;CE.prototype.lB=CE;CE.mB={};b.LocalShapeInfo=CE;CE.prototype.get_m_shapePart=CE.prototype.gE=function(){return Kj(this.kB)};CE.prototype.set_m_shapePart=CE.prototype.ZG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Lj(c,a)};Object.defineProperty(CE.prototype,"m_shapePart",{get:CE.prototype.gE,set:CE.prototype.ZG});CE.prototype.get_m_triangleIndex=CE.prototype.vE=function(){return Mj(this.kB)};
|
||||||
|
CE.prototype.set_m_triangleIndex=CE.prototype.nH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Nj(c,a)};Object.defineProperty(CE.prototype,"m_triangleIndex",{get:CE.prototype.vE,set:CE.prototype.nH});CE.prototype.__destroy__=function(){Oj(this.kB)};function F(a,c,d,e,g){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);this.kB=Pj(a,c,d,e,g);h(F)[this.kB]=this}
|
||||||
|
F.prototype=Object.create(f.prototype);F.prototype.constructor=F;F.prototype.lB=F;F.mB={};b.LocalConvexResult=F;F.prototype.get_m_hitCollisionObject=F.prototype.LB=function(){return k(Qj(this.kB),q)};F.prototype.set_m_hitCollisionObject=F.prototype.VB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Rj(c,a)};Object.defineProperty(F.prototype,"m_hitCollisionObject",{get:F.prototype.LB,set:F.prototype.VB});F.prototype.get_m_localShapeInfo=F.prototype.ID=function(){return k(Sj(this.kB),CE)};
|
||||||
|
F.prototype.set_m_localShapeInfo=F.prototype.zG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Tj(c,a)};Object.defineProperty(F.prototype,"m_localShapeInfo",{get:F.prototype.ID,set:F.prototype.zG});F.prototype.get_m_hitNormalLocal=F.prototype.sD=function(){return k(Uj(this.kB),m)};F.prototype.set_m_hitNormalLocal=F.prototype.jG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Vj(c,a)};Object.defineProperty(F.prototype,"m_hitNormalLocal",{get:F.prototype.sD,set:F.prototype.jG});
|
||||||
|
F.prototype.get_m_hitPointLocal=F.prototype.uD=function(){return k(Wj(this.kB),m)};F.prototype.set_m_hitPointLocal=F.prototype.lG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Xj(c,a)};Object.defineProperty(F.prototype,"m_hitPointLocal",{get:F.prototype.uD,set:F.prototype.lG});F.prototype.get_m_hitFraction=F.prototype.pD=function(){return Yj(this.kB)};F.prototype.set_m_hitFraction=F.prototype.gG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Zj(c,a)};
|
||||||
|
Object.defineProperty(F.prototype,"m_hitFraction",{get:F.prototype.pD,set:F.prototype.gG});F.prototype.__destroy__=function(){ak(this.kB)};function G(a,c){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);this.kB=bk(a,c);h(G)[this.kB]=this}G.prototype=Object.create(v.prototype);G.prototype.constructor=G;G.prototype.lB=G;G.mB={};b.ClosestConvexResultCallback=G;G.prototype.hasHit=function(){return!!ck(this.kB)};
|
||||||
|
G.prototype.get_m_hitCollisionObject=G.prototype.LB=function(){return k(dk(this.kB),q)};G.prototype.set_m_hitCollisionObject=G.prototype.VB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ek(c,a)};Object.defineProperty(G.prototype,"m_hitCollisionObject",{get:G.prototype.LB,set:G.prototype.VB});G.prototype.get_m_convexFromWorld=G.prototype.ZC=function(){return k(fk(this.kB),m)};
|
||||||
|
G.prototype.set_m_convexFromWorld=G.prototype.QF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);gk(c,a)};Object.defineProperty(G.prototype,"m_convexFromWorld",{get:G.prototype.ZC,set:G.prototype.QF});G.prototype.get_m_convexToWorld=G.prototype.$C=function(){return k(hk(this.kB),m)};G.prototype.set_m_convexToWorld=G.prototype.RF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ik(c,a)};Object.defineProperty(G.prototype,"m_convexToWorld",{get:G.prototype.$C,set:G.prototype.RF});
|
||||||
|
G.prototype.get_m_hitNormalWorld=G.prototype.xB=function(){return k(jk(this.kB),m)};G.prototype.set_m_hitNormalWorld=G.prototype.EB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);kk(c,a)};Object.defineProperty(G.prototype,"m_hitNormalWorld",{get:G.prototype.xB,set:G.prototype.EB});G.prototype.get_m_hitPointWorld=G.prototype.yB=function(){return k(lk(this.kB),m)};G.prototype.set_m_hitPointWorld=G.prototype.FB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);mk(c,a)};
|
||||||
|
Object.defineProperty(G.prototype,"m_hitPointWorld",{get:G.prototype.yB,set:G.prototype.FB});G.prototype.get_m_collisionFilterGroup=G.prototype.nB=function(){return nk(this.kB)};G.prototype.set_m_collisionFilterGroup=G.prototype.pB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ok(c,a)};Object.defineProperty(G.prototype,"m_collisionFilterGroup",{get:G.prototype.nB,set:G.prototype.pB});G.prototype.get_m_collisionFilterMask=G.prototype.oB=function(){return pk(this.kB)};
|
||||||
|
G.prototype.set_m_collisionFilterMask=G.prototype.qB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);qk(c,a)};Object.defineProperty(G.prototype,"m_collisionFilterMask",{get:G.prototype.oB,set:G.prototype.qB});G.prototype.get_m_closestHitFraction=G.prototype.rB=function(){return rk(this.kB)};G.prototype.set_m_closestHitFraction=G.prototype.sB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);sk(c,a)};Object.defineProperty(G.prototype,"m_closestHitFraction",{get:G.prototype.rB,set:G.prototype.sB});
|
||||||
|
G.prototype.__destroy__=function(){tk(this.kB)};function DE(a,c){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);this.kB=void 0===c?uk(a):vk(a,c);h(DE)[this.kB]=this}DE.prototype=Object.create(fE.prototype);DE.prototype.constructor=DE;DE.prototype.lB=DE;DE.mB={};b.btConvexTriangleMeshShape=DE;DE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);wk(c,a)};DE.prototype.getLocalScaling=function(){return k(xk(this.kB),m)};
|
||||||
|
DE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);yk(d,a,c)};DE.prototype.setMargin=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);zk(c,a)};DE.prototype.getMargin=function(){return Ak(this.kB)};DE.prototype.__destroy__=function(){Bk(this.kB)};function EE(a){a&&"object"===typeof a&&(a=a.kB);this.kB=Ck(a);h(EE)[this.kB]=this}EE.prototype=Object.create(l.prototype);EE.prototype.constructor=EE;EE.prototype.lB=EE;
|
||||||
|
EE.mB={};b.btBoxShape=EE;EE.prototype.setMargin=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Dk(c,a)};EE.prototype.getMargin=function(){return Ek(this.kB)};EE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Fk(c,a)};EE.prototype.getLocalScaling=function(){return k(Gk(this.kB),m)};EE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Hk(d,a,c)};EE.prototype.__destroy__=function(){Ik(this.kB)};
|
||||||
|
function FE(a,c){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);this.kB=Jk(a,c);h(FE)[this.kB]=this}FE.prototype=Object.create(gE.prototype);FE.prototype.constructor=FE;FE.prototype.lB=FE;FE.mB={};b.btCapsuleShapeX=FE;FE.prototype.setMargin=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Kk(c,a)};FE.prototype.getMargin=function(){return Lk(this.kB)};FE.prototype.getUpAxis=function(){return Mk(this.kB)};FE.prototype.getRadius=function(){return Nk(this.kB)};
|
||||||
|
FE.prototype.getHalfHeight=function(){return Ok(this.kB)};FE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Pk(c,a)};FE.prototype.getLocalScaling=function(){return k(Qk(this.kB),m)};FE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Rk(d,a,c)};FE.prototype.__destroy__=function(){Sk(this.kB)};
|
||||||
|
function GE(a,c){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);this.kB=Tk(a,c);h(GE)[this.kB]=this}GE.prototype=Object.create(gE.prototype);GE.prototype.constructor=GE;GE.prototype.lB=GE;GE.mB={};b.btCapsuleShapeZ=GE;GE.prototype.setMargin=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Uk(c,a)};GE.prototype.getMargin=function(){return Vk(this.kB)};GE.prototype.getUpAxis=function(){return Wk(this.kB)};GE.prototype.getRadius=function(){return Xk(this.kB)};
|
||||||
|
GE.prototype.getHalfHeight=function(){return Yk(this.kB)};GE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Zk(c,a)};GE.prototype.getLocalScaling=function(){return k($k(this.kB),m)};GE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);al(d,a,c)};GE.prototype.__destroy__=function(){bl(this.kB)};function HE(a){a&&"object"===typeof a&&(a=a.kB);this.kB=cl(a);h(HE)[this.kB]=this}
|
||||||
|
HE.prototype=Object.create(hE.prototype);HE.prototype.constructor=HE;HE.prototype.lB=HE;HE.mB={};b.btCylinderShapeX=HE;HE.prototype.setMargin=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);dl(c,a)};HE.prototype.getMargin=function(){return el(this.kB)};HE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);fl(c,a)};HE.prototype.getLocalScaling=function(){return k(gl(this.kB),m)};
|
||||||
|
HE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);hl(d,a,c)};HE.prototype.__destroy__=function(){il(this.kB)};function IE(a){a&&"object"===typeof a&&(a=a.kB);this.kB=jl(a);h(IE)[this.kB]=this}IE.prototype=Object.create(hE.prototype);IE.prototype.constructor=IE;IE.prototype.lB=IE;IE.mB={};b.btCylinderShapeZ=IE;IE.prototype.setMargin=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);kl(c,a)};
|
||||||
|
IE.prototype.getMargin=function(){return ll(this.kB)};IE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ml(c,a)};IE.prototype.getLocalScaling=function(){return k(nl(this.kB),m)};IE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);ol(d,a,c)};IE.prototype.__destroy__=function(){pl(this.kB)};function JE(a){a&&"object"===typeof a&&(a=a.kB);this.kB=ql(a);h(JE)[this.kB]=this}JE.prototype=Object.create(l.prototype);
|
||||||
|
JE.prototype.constructor=JE;JE.prototype.lB=JE;JE.mB={};b.btSphereShape=JE;JE.prototype.setMargin=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);rl(c,a)};JE.prototype.getMargin=function(){return sl(this.kB)};JE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);tl(c,a)};JE.prototype.getLocalScaling=function(){return k(ul(this.kB),m)};
|
||||||
|
JE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);vl(d,a,c)};JE.prototype.__destroy__=function(){wl(this.kB)};function KE(a,c,d){ND();a&&"object"===typeof a&&(a=a.kB);"object"==typeof c&&(c=RD(c));d&&"object"===typeof d&&(d=d.kB);this.kB=xl(a,c,d);h(KE)[this.kB]=this}KE.prototype=Object.create(l.prototype);KE.prototype.constructor=KE;KE.prototype.lB=KE;KE.mB={};b.btMultiSphereShape=KE;
|
||||||
|
KE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);yl(c,a)};KE.prototype.getLocalScaling=function(){return k(zl(this.kB),m)};KE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Al(d,a,c)};KE.prototype.__destroy__=function(){Bl(this.kB)};function LE(a,c){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);this.kB=Cl(a,c);h(LE)[this.kB]=this}LE.prototype=Object.create(iE.prototype);
|
||||||
|
LE.prototype.constructor=LE;LE.prototype.lB=LE;LE.mB={};b.btConeShapeX=LE;LE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Dl(c,a)};LE.prototype.getLocalScaling=function(){return k(El(this.kB),m)};LE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Fl(d,a,c)};LE.prototype.__destroy__=function(){Gl(this.kB)};
|
||||||
|
function ME(a,c){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);this.kB=Hl(a,c);h(ME)[this.kB]=this}ME.prototype=Object.create(iE.prototype);ME.prototype.constructor=ME;ME.prototype.lB=ME;ME.mB={};b.btConeShapeZ=ME;ME.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Il(c,a)};ME.prototype.getLocalScaling=function(){return k(Jl(this.kB),m)};
|
||||||
|
ME.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Kl(d,a,c)};ME.prototype.__destroy__=function(){Ll(this.kB)};function NE(){throw"cannot construct a btIntArray, no constructor in IDL";}NE.prototype=Object.create(f.prototype);NE.prototype.constructor=NE;NE.prototype.lB=NE;NE.mB={};b.btIntArray=NE;NE.prototype.size=NE.prototype.size=function(){return Ml(this.kB)};
|
||||||
|
NE.prototype.at=NE.prototype.at=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return Nl(c,a)};NE.prototype.__destroy__=function(){Ol(this.kB)};function OE(){throw"cannot construct a btFace, no constructor in IDL";}OE.prototype=Object.create(f.prototype);OE.prototype.constructor=OE;OE.prototype.lB=OE;OE.mB={};b.btFace=OE;OE.prototype.get_m_indices=OE.prototype.xD=function(){return k(Pl(this.kB),NE)};
|
||||||
|
OE.prototype.set_m_indices=OE.prototype.oG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ql(c,a)};Object.defineProperty(OE.prototype,"m_indices",{get:OE.prototype.xD,set:OE.prototype.oG});OE.prototype.get_m_plane=OE.prototype.WD=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return Rl(c,a)};OE.prototype.set_m_plane=OE.prototype.NG=function(a,c){var d=this.kB;ND();a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Sl(d,a,c)};
|
||||||
|
Object.defineProperty(OE.prototype,"m_plane",{get:OE.prototype.WD,set:OE.prototype.NG});OE.prototype.__destroy__=function(){Tl(this.kB)};function AE(){throw"cannot construct a btVector3Array, no constructor in IDL";}AE.prototype=Object.create(f.prototype);AE.prototype.constructor=AE;AE.prototype.lB=AE;AE.mB={};b.btVector3Array=AE;AE.prototype.size=AE.prototype.size=function(){return Ul(this.kB)};
|
||||||
|
AE.prototype.at=AE.prototype.at=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Vl(c,a),m)};AE.prototype.__destroy__=function(){Wl(this.kB)};function PE(){throw"cannot construct a btFaceArray, no constructor in IDL";}PE.prototype=Object.create(f.prototype);PE.prototype.constructor=PE;PE.prototype.lB=PE;PE.mB={};b.btFaceArray=PE;PE.prototype.size=PE.prototype.size=function(){return Xl(this.kB)};
|
||||||
|
PE.prototype.at=PE.prototype.at=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Yl(c,a),OE)};PE.prototype.__destroy__=function(){Zl(this.kB)};function QE(){throw"cannot construct a btConvexPolyhedron, no constructor in IDL";}QE.prototype=Object.create(f.prototype);QE.prototype.constructor=QE;QE.prototype.lB=QE;QE.mB={};b.btConvexPolyhedron=QE;QE.prototype.get_m_vertices=QE.prototype.BE=function(){return k($l(this.kB),AE)};
|
||||||
|
QE.prototype.set_m_vertices=QE.prototype.tH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);am(c,a)};Object.defineProperty(QE.prototype,"m_vertices",{get:QE.prototype.BE,set:QE.prototype.tH});QE.prototype.get_m_faces=QE.prototype.KB=function(){return k(bm(this.kB),PE)};QE.prototype.set_m_faces=QE.prototype.UB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);cm(c,a)};Object.defineProperty(QE.prototype,"m_faces",{get:QE.prototype.KB,set:QE.prototype.UB});
|
||||||
|
QE.prototype.__destroy__=function(){dm(this.kB)};function RE(a,c){ND();"object"==typeof a&&(a=RD(a));c&&"object"===typeof c&&(c=c.kB);this.kB=void 0===a?em():void 0===c?fm(a):gm(a,c);h(RE)[this.kB]=this}RE.prototype=Object.create(l.prototype);RE.prototype.constructor=RE;RE.prototype.lB=RE;RE.mB={};b.btConvexHullShape=RE;RE.prototype.addPoint=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);void 0===c?hm(d,a):im(d,a,c)};
|
||||||
|
RE.prototype.setMargin=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);jm(c,a)};RE.prototype.getMargin=function(){return km(this.kB)};RE.prototype.getNumVertices=function(){return lm(this.kB)};RE.prototype.initializePolyhedralFeatures=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return!!mm(c,a)};RE.prototype.recalcLocalAabb=function(){nm(this.kB)};RE.prototype.getConvexPolyhedron=function(){return k(om(this.kB),QE)};
|
||||||
|
RE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);pm(c,a)};RE.prototype.getLocalScaling=function(){return k(qm(this.kB),m)};RE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);rm(d,a,c)};RE.prototype.__destroy__=function(){sm(this.kB)};function SE(a){a&&"object"===typeof a&&(a=a.kB);this.kB=tm(a);h(SE)[this.kB]=this}SE.prototype=Object.create(f.prototype);SE.prototype.constructor=SE;
|
||||||
|
SE.prototype.lB=SE;SE.mB={};b.btShapeHull=SE;SE.prototype.buildHull=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return!!um(c,a)};SE.prototype.numVertices=function(){return wm(this.kB)};SE.prototype.getVertexPointer=function(){return k(xm(this.kB),m)};SE.prototype.__destroy__=function(){ym(this.kB)};function TE(a){a&&"object"===typeof a&&(a=a.kB);this.kB=void 0===a?zm():Am(a);h(TE)[this.kB]=this}TE.prototype=Object.create(l.prototype);TE.prototype.constructor=TE;TE.prototype.lB=TE;
|
||||||
|
TE.mB={};b.btCompoundShape=TE;TE.prototype.addChildShape=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Bm(d,a,c)};TE.prototype.removeChildShape=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Cm(c,a)};TE.prototype.removeChildShapeByIndex=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Dm(c,a)};TE.prototype.getNumChildShapes=function(){return Em(this.kB)};
|
||||||
|
TE.prototype.getChildShape=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Fm(c,a),l)};TE.prototype.updateChildTransform=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);void 0===d?Gm(e,a,c):Hm(e,a,c,d)};TE.prototype.setMargin=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Im(c,a)};TE.prototype.getMargin=function(){return Jm(this.kB)};
|
||||||
|
TE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Km(c,a)};TE.prototype.getLocalScaling=function(){return k(Lm(this.kB),m)};TE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Mm(d,a,c)};TE.prototype.__destroy__=function(){Nm(this.kB)};function UE(){throw"cannot construct a btIndexedMesh, no constructor in IDL";}UE.prototype=Object.create(f.prototype);UE.prototype.constructor=UE;
|
||||||
|
UE.prototype.lB=UE;UE.mB={};b.btIndexedMesh=UE;UE.prototype.get_m_numTriangles=UE.prototype.UD=function(){return Om(this.kB)};UE.prototype.set_m_numTriangles=UE.prototype.LG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Pm(c,a)};Object.defineProperty(UE.prototype,"m_numTriangles",{get:UE.prototype.UD,set:UE.prototype.LG});UE.prototype.__destroy__=function(){Qm(this.kB)};function VE(){throw"cannot construct a btIndexedMeshArray, no constructor in IDL";}VE.prototype=Object.create(f.prototype);
|
||||||
|
VE.prototype.constructor=VE;VE.prototype.lB=VE;VE.mB={};b.btIndexedMeshArray=VE;VE.prototype.size=VE.prototype.size=function(){return Rm(this.kB)};VE.prototype.at=VE.prototype.at=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Sm(c,a),UE)};VE.prototype.__destroy__=function(){Tm(this.kB)};function WE(a,c){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);this.kB=void 0===a?Um():void 0===c?Vm(a):Wm(a,c);h(WE)[this.kB]=this}WE.prototype=Object.create(jE.prototype);
|
||||||
|
WE.prototype.constructor=WE;WE.prototype.lB=WE;WE.mB={};b.btTriangleMesh=WE;WE.prototype.addTriangle=function(a,c,d,e){var g=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);void 0===e?Xm(g,a,c,d):Ym(g,a,c,d,e)};WE.prototype.findOrAddVertex=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);return Zm(d,a,c)};
|
||||||
|
WE.prototype.addIndex=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);$m(c,a)};WE.prototype.getIndexedMeshArray=function(){return k(an(this.kB),VE)};WE.prototype.setScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);bn(c,a)};WE.prototype.__destroy__=function(){cn(this.kB)};function XE(){this.kB=dn();h(XE)[this.kB]=this}XE.prototype=Object.create(ZD.prototype);XE.prototype.constructor=XE;XE.prototype.lB=XE;XE.mB={};b.btEmptyShape=XE;
|
||||||
|
XE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);en(c,a)};XE.prototype.getLocalScaling=function(){return k(fn(this.kB),m)};XE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);gn(d,a,c)};XE.prototype.__destroy__=function(){hn(this.kB)};function YE(a,c){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);this.kB=jn(a,c);h(YE)[this.kB]=this}YE.prototype=Object.create(ZD.prototype);
|
||||||
|
YE.prototype.constructor=YE;YE.prototype.lB=YE;YE.mB={};b.btStaticPlaneShape=YE;YE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);kn(c,a)};YE.prototype.getLocalScaling=function(){return k(ln(this.kB),m)};YE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);mn(d,a,c)};YE.prototype.__destroy__=function(){nn(this.kB)};
|
||||||
|
function ZE(a,c,d){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);this.kB=void 0===d?on(a,c):pn(a,c,d);h(ZE)[this.kB]=this}ZE.prototype=Object.create(kE.prototype);ZE.prototype.constructor=ZE;ZE.prototype.lB=ZE;ZE.mB={};b.btBvhTriangleMeshShape=ZE;ZE.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);qn(c,a)};ZE.prototype.getLocalScaling=function(){return k(rn(this.kB),m)};
|
||||||
|
ZE.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);sn(d,a,c)};ZE.prototype.__destroy__=function(){tn(this.kB)};
|
||||||
|
function $E(a,c,d,e,g,n,z,T,Da){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);n&&"object"===typeof n&&(n=n.kB);z&&"object"===typeof z&&(z=z.kB);T&&"object"===typeof T&&(T=T.kB);Da&&"object"===typeof Da&&(Da=Da.kB);this.kB=un(a,c,d,e,g,n,z,T,Da);h($E)[this.kB]=this}$E.prototype=Object.create(ZD.prototype);$E.prototype.constructor=$E;$E.prototype.lB=$E;$E.mB={};
|
||||||
|
b.btHeightfieldTerrainShape=$E;$E.prototype.setMargin=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);vn(c,a)};$E.prototype.getMargin=function(){return wn(this.kB)};$E.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);xn(c,a)};$E.prototype.getLocalScaling=function(){return k(yn(this.kB),m)};$E.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);zn(d,a,c)};
|
||||||
|
$E.prototype.__destroy__=function(){An(this.kB)};function aF(a,c,d,e){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);this.kB=Bn(a,c,d,e);h(aF)[this.kB]=this}aF.prototype=Object.create(f.prototype);aF.prototype.constructor=aF;aF.prototype.lB=aF;aF.mB={};b.btAABB=aF;aF.prototype.invalidate=function(){Cn(this.kB)};aF.prototype.increment_margin=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Dn(c,a)};
|
||||||
|
aF.prototype.copy_with_margin=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);En(d,a,c)};aF.prototype.__destroy__=function(){Fn(this.kB)};function bF(){this.kB=Gn();h(bF)[this.kB]=this}bF.prototype=Object.create(f.prototype);bF.prototype.constructor=bF;bF.prototype.lB=bF;bF.mB={};b.btPrimitiveTriangle=bF;bF.prototype.__destroy__=function(){Hn(this.kB)};
|
||||||
|
function cF(a,c,d){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);this.kB=In(a,c,d);h(cF)[this.kB]=this}cF.prototype=Object.create(f.prototype);cF.prototype.constructor=cF;cF.prototype.lB=cF;cF.mB={};b.btTriangleShapeEx=cF;cF.prototype.getAabb=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);Jn(e,a,c,d)};
|
||||||
|
cF.prototype.applyTransform=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Kn(c,a)};cF.prototype.buildTriPlane=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ln(c,a)};cF.prototype.__destroy__=function(){Mn(this.kB)};function dF(){this.kB=Nn();h(dF)[this.kB]=this}dF.prototype=Object.create(f.prototype);dF.prototype.constructor=dF;dF.prototype.lB=dF;dF.mB={};b.btTetrahedronShapeEx=dF;
|
||||||
|
dF.prototype.setVertices=function(a,c,d,e){var g=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);On(g,a,c,d,e)};dF.prototype.__destroy__=function(){Pn(this.kB)};function eF(){throw"cannot construct a CompoundPrimitiveManager, no constructor in IDL";}eF.prototype=Object.create(lE.prototype);eF.prototype.constructor=eF;eF.prototype.lB=eF;eF.mB={};b.CompoundPrimitiveManager=eF;
|
||||||
|
eF.prototype.get_primitive_count=function(){return Qn(this.kB)};eF.prototype.get_primitive_box=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Rn(d,a,c)};eF.prototype.get_primitive_triangle=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Sn(d,a,c)};eF.prototype.is_trimesh=function(){return!!Tn(this.kB)};eF.prototype.get_m_compoundShape=eF.prototype.VC=function(){return k(Un(this.kB),H)};
|
||||||
|
eF.prototype.set_m_compoundShape=eF.prototype.MF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Vn(c,a)};Object.defineProperty(eF.prototype,"m_compoundShape",{get:eF.prototype.VC,set:eF.prototype.MF});eF.prototype.__destroy__=function(){Wn(this.kB)};function H(a){a&&"object"===typeof a&&(a=a.kB);this.kB=void 0===a?Xn():Yn(a);h(H)[this.kB]=this}H.prototype=Object.create(w.prototype);H.prototype.constructor=H;H.prototype.lB=H;H.mB={};b.btGImpactCompoundShape=H;
|
||||||
|
H.prototype.childrenHasTransform=function(){return!!Zn(this.kB)};H.prototype.getPrimitiveManager=function(){return k($n(this.kB),lE)};H.prototype.getCompoundPrimitiveManager=function(){return k(ao(this.kB),eF)};H.prototype.getNumChildShapes=function(){return bo(this.kB)};H.prototype.addChildShape=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);co(d,a,c)};
|
||||||
|
H.prototype.getChildShape=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(eo(c,a),l)};H.prototype.getChildAabb=function(a,c,d,e){var g=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);fo(g,a,c,d,e)};H.prototype.getChildTransform=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(go(c,a),r)};
|
||||||
|
H.prototype.setChildTransform=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);ho(d,a,c)};H.prototype.calculateLocalInertia=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);io(d,a,c)};H.prototype.getName=function(){return sa(jo(this.kB))};H.prototype.getGImpactShapeType=function(){return ko(this.kB)};H.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);lo(c,a)};
|
||||||
|
H.prototype.getLocalScaling=function(){return k(mo(this.kB),m)};H.prototype.updateBound=function(){no(this.kB)};H.prototype.postUpdate=function(){oo(this.kB)};H.prototype.getShapeType=function(){return po(this.kB)};H.prototype.needsRetrieveTriangles=function(){return!!qo(this.kB)};H.prototype.needsRetrieveTetrahedrons=function(){return!!ro(this.kB)};H.prototype.getBulletTriangle=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);so(d,a,c)};
|
||||||
|
H.prototype.getBulletTetrahedron=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);to(d,a,c)};H.prototype.__destroy__=function(){uo(this.kB)};function I(a){a&&"object"===typeof a&&(a=a.kB);this.kB=void 0===a?vo():wo(a);h(I)[this.kB]=this}I.prototype=Object.create(lE.prototype);I.prototype.constructor=I;I.prototype.lB=I;I.mB={};b.TrimeshPrimitiveManager=I;I.prototype.lock=I.prototype.lock=function(){xo(this.kB)};I.prototype.unlock=I.prototype.unlock=function(){yo(this.kB)};
|
||||||
|
I.prototype.is_trimesh=function(){return!!zo(this.kB)};I.prototype.get_vertex_count=function(){return Ao(this.kB)};I.prototype.get_indices=function(a,c,d,e){var g=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);Bo(g,a,c,d,e)};I.prototype.get_vertex=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Co(d,a,c)};
|
||||||
|
I.prototype.get_bullet_triangle=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Do(d,a,c)};I.prototype.get_m_margin=I.prototype.LD=function(){return Eo(this.kB)};I.prototype.set_m_margin=I.prototype.CG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Fo(c,a)};Object.defineProperty(I.prototype,"m_margin",{get:I.prototype.LD,set:I.prototype.CG});I.prototype.get_m_meshInterface=I.prototype.OD=function(){return k(Go(this.kB),jE)};
|
||||||
|
I.prototype.set_m_meshInterface=I.prototype.FG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ho(c,a)};Object.defineProperty(I.prototype,"m_meshInterface",{get:I.prototype.OD,set:I.prototype.FG});I.prototype.get_m_part=I.prototype.VD=function(){return Io(this.kB)};I.prototype.set_m_part=I.prototype.MG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Jo(c,a)};Object.defineProperty(I.prototype,"m_part",{get:I.prototype.VD,set:I.prototype.MG});
|
||||||
|
I.prototype.get_m_lock_count=I.prototype.JD=function(){return Ko(this.kB)};I.prototype.set_m_lock_count=I.prototype.AG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Lo(c,a)};Object.defineProperty(I.prototype,"m_lock_count",{get:I.prototype.JD,set:I.prototype.AG});I.prototype.get_numverts=I.prototype.LE=function(){return Mo(this.kB)};I.prototype.set_numverts=I.prototype.DH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);No(c,a)};
|
||||||
|
Object.defineProperty(I.prototype,"numverts",{get:I.prototype.LE,set:I.prototype.DH});I.prototype.get_type=I.prototype.PE=function(){return Oo(this.kB)};I.prototype.set_type=I.prototype.HH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Po(c,a)};Object.defineProperty(I.prototype,"type",{get:I.prototype.PE,set:I.prototype.HH});I.prototype.get_stride=I.prototype.NE=function(){return Qo(this.kB)};
|
||||||
|
I.prototype.set_stride=I.prototype.FH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ro(c,a)};Object.defineProperty(I.prototype,"stride",{get:I.prototype.NE,set:I.prototype.FH});I.prototype.get_indexstride=I.prototype.gC=function(){return So(this.kB)};I.prototype.set_indexstride=I.prototype.YE=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);To(c,a)};Object.defineProperty(I.prototype,"indexstride",{get:I.prototype.gC,set:I.prototype.YE});
|
||||||
|
I.prototype.get_numfaces=I.prototype.KE=function(){return Uo(this.kB)};I.prototype.set_numfaces=I.prototype.CH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Vo(c,a)};Object.defineProperty(I.prototype,"numfaces",{get:I.prototype.KE,set:I.prototype.CH});I.prototype.get_indicestype=I.prototype.hC=function(){return Wo(this.kB)};I.prototype.set_indicestype=I.prototype.ZE=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Xo(c,a)};
|
||||||
|
Object.defineProperty(I.prototype,"indicestype",{get:I.prototype.hC,set:I.prototype.ZE});I.prototype.__destroy__=function(){Yo(this.kB)};function fF(a,c){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);this.kB=Zo(a,c);h(fF)[this.kB]=this}fF.prototype=Object.create(w.prototype);fF.prototype.constructor=fF;fF.prototype.lB=fF;fF.mB={};b.btGImpactMeshShapePart=fF;fF.prototype.getTrimeshPrimitiveManager=function(){return k($o(this.kB),I)};fF.prototype.getVertexCount=function(){return ap(this.kB)};
|
||||||
|
fF.prototype.getVertex=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);bp(d,a,c)};fF.prototype.getPart=function(){return cp(this.kB)};fF.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);dp(c,a)};fF.prototype.getLocalScaling=function(){return k(ep(this.kB),m)};fF.prototype.updateBound=function(){fp(this.kB)};fF.prototype.postUpdate=function(){gp(this.kB)};fF.prototype.getShapeType=function(){return hp(this.kB)};
|
||||||
|
fF.prototype.needsRetrieveTriangles=function(){return!!ip(this.kB)};fF.prototype.needsRetrieveTetrahedrons=function(){return!!jp(this.kB)};fF.prototype.getBulletTriangle=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);kp(d,a,c)};fF.prototype.getBulletTetrahedron=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);lp(d,a,c)};fF.prototype.__destroy__=function(){mp(this.kB)};
|
||||||
|
function gF(a){a&&"object"===typeof a&&(a=a.kB);this.kB=np(a);h(gF)[this.kB]=this}gF.prototype=Object.create(w.prototype);gF.prototype.constructor=gF;gF.prototype.lB=gF;gF.mB={};b.btGImpactMeshShape=gF;gF.prototype.getMeshInterface=function(){return k(op(this.kB),jE)};gF.prototype.getMeshPartCount=function(){return pp(this.kB)};gF.prototype.getMeshPart=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(qp(c,a),fF)};gF.prototype.calculateSerializeBufferSize=function(){return rp(this.kB)};
|
||||||
|
gF.prototype.setLocalScaling=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);sp(c,a)};gF.prototype.getLocalScaling=function(){return k(tp(this.kB),m)};gF.prototype.updateBound=function(){up(this.kB)};gF.prototype.postUpdate=function(){vp(this.kB)};gF.prototype.getShapeType=function(){return wp(this.kB)};gF.prototype.needsRetrieveTriangles=function(){return!!xp(this.kB)};gF.prototype.needsRetrieveTetrahedrons=function(){return!!yp(this.kB)};
|
||||||
|
gF.prototype.getBulletTriangle=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);zp(d,a,c)};gF.prototype.getBulletTetrahedron=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Ap(d,a,c)};gF.prototype.__destroy__=function(){Bp(this.kB)};
|
||||||
|
function hF(a,c){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);this.kB=void 0===a?Cp():void 0===c?_emscripten_bind_btCollisionAlgorithmConstructionInfo_btCollisionAlgorithmConstructionInfo_1(a):Dp(a,c);h(hF)[this.kB]=this}hF.prototype=Object.create(f.prototype);hF.prototype.constructor=hF;hF.prototype.lB=hF;hF.mB={};b.btCollisionAlgorithmConstructionInfo=hF;hF.prototype.get_m_dispatcher1=hF.prototype.eD=function(){return k(Ep(this.kB),TD)};
|
||||||
|
hF.prototype.set_m_dispatcher1=hF.prototype.WF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Fp(c,a)};Object.defineProperty(hF.prototype,"m_dispatcher1",{get:hF.prototype.eD,set:hF.prototype.WF});hF.prototype.get_m_manifold=hF.prototype.KD=function(){return k(Gp(this.kB),oE)};hF.prototype.set_m_manifold=hF.prototype.BG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Hp(c,a)};Object.defineProperty(hF.prototype,"m_manifold",{get:hF.prototype.KD,set:hF.prototype.BG});
|
||||||
|
hF.prototype.__destroy__=function(){Ip(this.kB)};function iF(a,c,d){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);this.kB=Jp(a,c,d);h(iF)[this.kB]=this}iF.prototype=Object.create(mE.prototype);iF.prototype.constructor=iF;iF.prototype.lB=iF;iF.mB={};b.btGImpactCollisionAlgorithm=iF;iF.prototype.registerAlgorithm=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Kp(c,a)};iF.prototype.__destroy__=function(){Lp(this.kB)};
|
||||||
|
function jF(){this.kB=Mp();h(jF)[this.kB]=this}jF.prototype=Object.create(f.prototype);jF.prototype.constructor=jF;jF.prototype.lB=jF;jF.mB={};b.btDefaultCollisionConstructionInfo=jF;jF.prototype.__destroy__=function(){Np(this.kB)};function oE(){this.kB=Op();h(oE)[this.kB]=this}oE.prototype=Object.create(f.prototype);oE.prototype.constructor=oE;oE.prototype.lB=oE;oE.mB={};b.btPersistentManifold=oE;oE.prototype.getBody0=function(){return k(Pp(this.kB),q)};
|
||||||
|
oE.prototype.getBody1=function(){return k(Qp(this.kB),q)};oE.prototype.getNumContacts=function(){return Rp(this.kB)};oE.prototype.getContactPoint=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Sp(c,a),E)};oE.prototype.__destroy__=function(){Tp(this.kB)};function kF(a){a&&"object"===typeof a&&(a=a.kB);this.kB=Up(a);h(kF)[this.kB]=this}kF.prototype=Object.create(TD.prototype);kF.prototype.constructor=kF;kF.prototype.lB=kF;kF.mB={};b.btCollisionDispatcher=kF;
|
||||||
|
kF.prototype.getNumManifolds=function(){return Vp(this.kB)};kF.prototype.getManifoldByIndexInternal=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Wp(c,a),oE)};kF.prototype.__destroy__=function(){Xp(this.kB)};function lF(){throw"cannot construct a btOverlappingPairCallback, no constructor in IDL";}lF.prototype=Object.create(f.prototype);lF.prototype.constructor=lF;lF.prototype.lB=lF;lF.mB={};b.btOverlappingPairCallback=lF;lF.prototype.__destroy__=function(){Yp(this.kB)};
|
||||||
|
function UD(){throw"cannot construct a btOverlappingPairCache, no constructor in IDL";}UD.prototype=Object.create(f.prototype);UD.prototype.constructor=UD;UD.prototype.lB=UD;UD.mB={};b.btOverlappingPairCache=UD;UD.prototype.setInternalGhostPairCallback=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Zp(c,a)};UD.prototype.getNumOverlappingPairs=function(){return $p(this.kB)};UD.prototype.__destroy__=function(){aq(this.kB)};
|
||||||
|
function mF(a,c,d,e,g){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);this.kB=void 0===d?bq(a,c):void 0===e?cq(a,c,d):void 0===g?dq(a,c,d,e):eq(a,c,d,e,g);h(mF)[this.kB]=this}mF.prototype=Object.create(f.prototype);mF.prototype.constructor=mF;mF.prototype.lB=mF;mF.mB={};b.btAxisSweep3=mF;mF.prototype.__destroy__=function(){fq(this.kB)};
|
||||||
|
function VD(){throw"cannot construct a btBroadphaseInterface, no constructor in IDL";}VD.prototype=Object.create(f.prototype);VD.prototype.constructor=VD;VD.prototype.lB=VD;VD.mB={};b.btBroadphaseInterface=VD;VD.prototype.getOverlappingPairCache=function(){return k(gq(this.kB),UD)};VD.prototype.__destroy__=function(){hq(this.kB)};function nF(){throw"cannot construct a btCollisionConfiguration, no constructor in IDL";}nF.prototype=Object.create(f.prototype);nF.prototype.constructor=nF;
|
||||||
|
nF.prototype.lB=nF;nF.mB={};b.btCollisionConfiguration=nF;nF.prototype.__destroy__=function(){iq(this.kB)};function oF(){this.kB=jq();h(oF)[this.kB]=this}oF.prototype=Object.create(f.prototype);oF.prototype.constructor=oF;oF.prototype.lB=oF;oF.mB={};b.btDbvtBroadphase=oF;oF.prototype.__destroy__=function(){kq(this.kB)};function YD(){throw"cannot construct a btBroadphaseProxy, no constructor in IDL";}YD.prototype=Object.create(f.prototype);YD.prototype.constructor=YD;YD.prototype.lB=YD;YD.mB={};
|
||||||
|
b.btBroadphaseProxy=YD;YD.prototype.get_m_collisionFilterGroup=YD.prototype.nB=function(){return lq(this.kB)};YD.prototype.set_m_collisionFilterGroup=YD.prototype.pB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);mq(c,a)};Object.defineProperty(YD.prototype,"m_collisionFilterGroup",{get:YD.prototype.nB,set:YD.prototype.pB});YD.prototype.get_m_collisionFilterMask=YD.prototype.oB=function(){return nq(this.kB)};
|
||||||
|
YD.prototype.set_m_collisionFilterMask=YD.prototype.qB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);oq(c,a)};Object.defineProperty(YD.prototype,"m_collisionFilterMask",{get:YD.prototype.oB,set:YD.prototype.qB});YD.prototype.__destroy__=function(){pq(this.kB)};function J(a,c,d,e){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);this.kB=void 0===e?qq(a,c,d):rq(a,c,d,e);h(J)[this.kB]=this}J.prototype=Object.create(f.prototype);
|
||||||
|
J.prototype.constructor=J;J.prototype.lB=J;J.mB={};b.btRigidBodyConstructionInfo=J;J.prototype.get_m_linearDamping=J.prototype.DD=function(){return sq(this.kB)};J.prototype.set_m_linearDamping=J.prototype.uG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);tq(c,a)};Object.defineProperty(J.prototype,"m_linearDamping",{get:J.prototype.DD,set:J.prototype.uG});J.prototype.get_m_angularDamping=J.prototype.HC=function(){return uq(this.kB)};
|
||||||
|
J.prototype.set_m_angularDamping=J.prototype.yF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);vq(c,a)};Object.defineProperty(J.prototype,"m_angularDamping",{get:J.prototype.HC,set:J.prototype.yF});J.prototype.get_m_friction=J.prototype.kD=function(){return wq(this.kB)};J.prototype.set_m_friction=J.prototype.bG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);xq(c,a)};Object.defineProperty(J.prototype,"m_friction",{get:J.prototype.kD,set:J.prototype.bG});
|
||||||
|
J.prototype.get_m_rollingFriction=J.prototype.dE=function(){return yq(this.kB)};J.prototype.set_m_rollingFriction=J.prototype.WG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);zq(c,a)};Object.defineProperty(J.prototype,"m_rollingFriction",{get:J.prototype.dE,set:J.prototype.WG});J.prototype.get_m_restitution=J.prototype.bE=function(){return Aq(this.kB)};J.prototype.set_m_restitution=J.prototype.UG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Bq(c,a)};
|
||||||
|
Object.defineProperty(J.prototype,"m_restitution",{get:J.prototype.bE,set:J.prototype.UG});J.prototype.get_m_linearSleepingThreshold=J.prototype.ED=function(){return Cq(this.kB)};J.prototype.set_m_linearSleepingThreshold=J.prototype.vG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Dq(c,a)};Object.defineProperty(J.prototype,"m_linearSleepingThreshold",{get:J.prototype.ED,set:J.prototype.vG});J.prototype.get_m_angularSleepingThreshold=J.prototype.IC=function(){return Eq(this.kB)};
|
||||||
|
J.prototype.set_m_angularSleepingThreshold=J.prototype.zF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Fq(c,a)};Object.defineProperty(J.prototype,"m_angularSleepingThreshold",{get:J.prototype.IC,set:J.prototype.zF});J.prototype.get_m_additionalDamping=J.prototype.CC=function(){return!!Gq(this.kB)};J.prototype.set_m_additionalDamping=J.prototype.tF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Hq(c,a)};
|
||||||
|
Object.defineProperty(J.prototype,"m_additionalDamping",{get:J.prototype.CC,set:J.prototype.tF});J.prototype.get_m_additionalDampingFactor=J.prototype.DC=function(){return Iq(this.kB)};J.prototype.set_m_additionalDampingFactor=J.prototype.uF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Jq(c,a)};Object.defineProperty(J.prototype,"m_additionalDampingFactor",{get:J.prototype.DC,set:J.prototype.uF});J.prototype.get_m_additionalLinearDampingThresholdSqr=J.prototype.EC=function(){return Kq(this.kB)};
|
||||||
|
J.prototype.set_m_additionalLinearDampingThresholdSqr=J.prototype.vF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Lq(c,a)};Object.defineProperty(J.prototype,"m_additionalLinearDampingThresholdSqr",{get:J.prototype.EC,set:J.prototype.vF});J.prototype.get_m_additionalAngularDampingThresholdSqr=J.prototype.BC=function(){return Mq(this.kB)};J.prototype.set_m_additionalAngularDampingThresholdSqr=J.prototype.sF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Nq(c,a)};
|
||||||
|
Object.defineProperty(J.prototype,"m_additionalAngularDampingThresholdSqr",{get:J.prototype.BC,set:J.prototype.sF});J.prototype.get_m_additionalAngularDampingFactor=J.prototype.AC=function(){return Oq(this.kB)};J.prototype.set_m_additionalAngularDampingFactor=J.prototype.rF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Pq(c,a)};Object.defineProperty(J.prototype,"m_additionalAngularDampingFactor",{get:J.prototype.AC,set:J.prototype.rF});J.prototype.__destroy__=function(){Qq(this.kB)};
|
||||||
|
function K(a){a&&"object"===typeof a&&(a=a.kB);this.kB=Rq(a);h(K)[this.kB]=this}K.prototype=Object.create(q.prototype);K.prototype.constructor=K;K.prototype.lB=K;K.mB={};b.btRigidBody=K;K.prototype.getCenterOfMassTransform=function(){return k(Sq(this.kB),r)};K.prototype.setCenterOfMassTransform=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Tq(c,a)};K.prototype.setSleepingThresholds=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Uq(d,a,c)};
|
||||||
|
K.prototype.getLinearDamping=function(){return Vq(this.kB)};K.prototype.getAngularDamping=function(){return Wq(this.kB)};K.prototype.setDamping=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Xq(d,a,c)};K.prototype.setMassProps=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Yq(d,a,c)};K.prototype.getLinearFactor=function(){return k(Zq(this.kB),m)};
|
||||||
|
K.prototype.setLinearFactor=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);$q(c,a)};K.prototype.applyTorque=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ar(c,a)};K.prototype.applyLocalTorque=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);br(c,a)};K.prototype.applyForce=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);cr(d,a,c)};
|
||||||
|
K.prototype.applyCentralForce=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);dr(c,a)};K.prototype.applyCentralLocalForce=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);er(c,a)};K.prototype.applyTorqueImpulse=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);fr(c,a)};K.prototype.applyImpulse=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);gr(d,a,c)};
|
||||||
|
K.prototype.applyCentralImpulse=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);hr(c,a)};K.prototype.updateInertiaTensor=function(){ir(this.kB)};K.prototype.getLinearVelocity=function(){return k(jr(this.kB),m)};K.prototype.getAngularVelocity=function(){return k(kr(this.kB),m)};K.prototype.setLinearVelocity=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);lr(c,a)};K.prototype.setAngularVelocity=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);mr(c,a)};
|
||||||
|
K.prototype.getMotionState=function(){return k(nr(this.kB),dE)};K.prototype.setMotionState=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);or(c,a)};K.prototype.getAngularFactor=function(){return k(pr(this.kB),m)};K.prototype.setAngularFactor=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);qr(c,a)};K.prototype.upcast=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(rr(c,a),K)};
|
||||||
|
K.prototype.getAabb=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);sr(d,a,c)};K.prototype.applyGravity=function(){tr(this.kB)};K.prototype.getGravity=function(){return k(ur(this.kB),m)};K.prototype.setGravity=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);vr(c,a)};K.prototype.getBroadphaseProxy=function(){return k(wr(this.kB),YD)};K.prototype.clearForces=function(){xr(this.kB)};
|
||||||
|
K.prototype.setFlags=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);yr(c,a)};K.prototype.getFlags=function(){return zr(this.kB)};K.prototype.setAnisotropicFriction=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Ar(d,a,c)};K.prototype.getCollisionShape=function(){return k(Br(this.kB),l)};K.prototype.setContactProcessingThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Cr(c,a)};
|
||||||
|
K.prototype.setActivationState=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Dr(c,a)};K.prototype.forceActivationState=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Er(c,a)};K.prototype.activate=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);void 0===a?Fr(c):Gr(c,a)};K.prototype.isActive=function(){return!!Hr(this.kB)};K.prototype.isKinematicObject=function(){return!!Ir(this.kB)};K.prototype.isStaticObject=function(){return!!Jr(this.kB)};
|
||||||
|
K.prototype.isStaticOrKinematicObject=function(){return!!Kr(this.kB)};K.prototype.getRestitution=function(){return Lr(this.kB)};K.prototype.getFriction=function(){return Mr(this.kB)};K.prototype.getRollingFriction=function(){return Nr(this.kB)};K.prototype.setRestitution=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Or(c,a)};K.prototype.setFriction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Pr(c,a)};
|
||||||
|
K.prototype.setRollingFriction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Qr(c,a)};K.prototype.getWorldTransform=function(){return k(Rr(this.kB),r)};K.prototype.getCollisionFlags=function(){return Sr(this.kB)};K.prototype.setCollisionFlags=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Tr(c,a)};K.prototype.setWorldTransform=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ur(c,a)};
|
||||||
|
K.prototype.setCollisionShape=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Vr(c,a)};K.prototype.setCcdMotionThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Wr(c,a)};K.prototype.setCcdSweptSphereRadius=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Xr(c,a)};K.prototype.getUserIndex=function(){return Yr(this.kB)};K.prototype.setUserIndex=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Zr(c,a)};
|
||||||
|
K.prototype.getUserPointer=function(){return k($r(this.kB),XD)};K.prototype.setUserPointer=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);as(c,a)};K.prototype.getBroadphaseHandle=function(){return k(bs(this.kB),YD)};K.prototype.__destroy__=function(){cs(this.kB)};function L(){this.kB=ds();h(L)[this.kB]=this}L.prototype=Object.create(f.prototype);L.prototype.constructor=L;L.prototype.lB=L;L.mB={};b.btConstraintSetting=L;L.prototype.get_m_tau=L.prototype.sE=function(){return es(this.kB)};
|
||||||
|
L.prototype.set_m_tau=L.prototype.kH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);gs(c,a)};Object.defineProperty(L.prototype,"m_tau",{get:L.prototype.sE,set:L.prototype.kH});L.prototype.get_m_damping=L.prototype.aD=function(){return hs(this.kB)};L.prototype.set_m_damping=L.prototype.SF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);is(c,a)};Object.defineProperty(L.prototype,"m_damping",{get:L.prototype.aD,set:L.prototype.SF});
|
||||||
|
L.prototype.get_m_impulseClamp=L.prototype.wD=function(){return js(this.kB)};L.prototype.set_m_impulseClamp=L.prototype.nG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ks(c,a)};Object.defineProperty(L.prototype,"m_impulseClamp",{get:L.prototype.wD,set:L.prototype.nG});L.prototype.__destroy__=function(){ls(this.kB)};
|
||||||
|
function pF(a,c,d,e){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);this.kB=void 0===d?ms(a,c):void 0===e?_emscripten_bind_btPoint2PointConstraint_btPoint2PointConstraint_3(a,c,d):ns(a,c,d,e);h(pF)[this.kB]=this}pF.prototype=Object.create(aE.prototype);pF.prototype.constructor=pF;pF.prototype.lB=pF;pF.mB={};b.btPoint2PointConstraint=pF;
|
||||||
|
pF.prototype.setPivotA=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ps(c,a)};pF.prototype.setPivotB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);qs(c,a)};pF.prototype.getPivotInA=function(){return k(rs(this.kB),m)};pF.prototype.getPivotInB=function(){return k(ss(this.kB),m)};pF.prototype.enableFeedback=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ts(c,a)};pF.prototype.getBreakingImpulseThreshold=function(){return us(this.kB)};
|
||||||
|
pF.prototype.setBreakingImpulseThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);vs(c,a)};pF.prototype.getParam=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);return xs(d,a,c)};pF.prototype.setParam=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);ys(e,a,c,d)};pF.prototype.get_m_setting=pF.prototype.fE=function(){return k(zs(this.kB),L)};
|
||||||
|
pF.prototype.set_m_setting=pF.prototype.YG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);As(c,a)};Object.defineProperty(pF.prototype,"m_setting",{get:pF.prototype.fE,set:pF.prototype.YG});pF.prototype.__destroy__=function(){Bs(this.kB)};
|
||||||
|
function qF(a,c,d,e,g){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);this.kB=void 0===e?Cs(a,c,d):void 0===g?_emscripten_bind_btGeneric6DofSpringConstraint_btGeneric6DofSpringConstraint_4(a,c,d,e):Ds(a,c,d,e,g);h(qF)[this.kB]=this}qF.prototype=Object.create(pE.prototype);qF.prototype.constructor=qF;qF.prototype.lB=qF;qF.mB={};b.btGeneric6DofSpringConstraint=qF;
|
||||||
|
qF.prototype.enableSpring=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Es(d,a,c)};qF.prototype.setStiffness=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Fs(d,a,c)};qF.prototype.setDamping=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Gs(d,a,c)};
|
||||||
|
qF.prototype.setEquilibriumPoint=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);void 0===a?Hs(d):void 0===c?Is(d,a):Js(d,a,c)};qF.prototype.setLinearLowerLimit=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ks(c,a)};qF.prototype.setLinearUpperLimit=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ls(c,a)};qF.prototype.setAngularLowerLimit=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ms(c,a)};
|
||||||
|
qF.prototype.setAngularUpperLimit=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ns(c,a)};qF.prototype.getFrameOffsetA=function(){return k(Os(this.kB),r)};qF.prototype.enableFeedback=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ps(c,a)};qF.prototype.getBreakingImpulseThreshold=function(){return Qs(this.kB)};qF.prototype.setBreakingImpulseThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Rs(c,a)};
|
||||||
|
qF.prototype.getParam=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);return Ss(d,a,c)};qF.prototype.setParam=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);Ts(e,a,c,d)};qF.prototype.__destroy__=function(){Us(this.kB)};function rF(){this.kB=Vs();h(rF)[this.kB]=this}rF.prototype=Object.create(f.prototype);rF.prototype.constructor=rF;rF.prototype.lB=rF;rF.mB={};
|
||||||
|
b.btSequentialImpulseConstraintSolver=rF;rF.prototype.__destroy__=function(){Ws(this.kB)};function sF(a,c,d,e){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);this.kB=void 0===d?Xs(a,c):void 0===e?_emscripten_bind_btConeTwistConstraint_btConeTwistConstraint_3(a,c,d):Ys(a,c,d,e);h(sF)[this.kB]=this}sF.prototype=Object.create(aE.prototype);sF.prototype.constructor=sF;sF.prototype.lB=sF;sF.mB={};
|
||||||
|
b.btConeTwistConstraint=sF;sF.prototype.setLimit=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Zs(d,a,c)};sF.prototype.setAngularOnly=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);$s(c,a)};sF.prototype.setDamping=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);at(c,a)};sF.prototype.enableMotor=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);bt(c,a)};
|
||||||
|
sF.prototype.setMaxMotorImpulse=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ct(c,a)};sF.prototype.setMaxMotorImpulseNormalized=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);dt(c,a)};sF.prototype.setMotorTarget=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);et(c,a)};sF.prototype.setMotorTargetInConstraintSpace=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ft(c,a)};
|
||||||
|
sF.prototype.enableFeedback=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);gt(c,a)};sF.prototype.getBreakingImpulseThreshold=function(){return ht(this.kB)};sF.prototype.setBreakingImpulseThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);it(c,a)};sF.prototype.getParam=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);return jt(d,a,c)};
|
||||||
|
sF.prototype.setParam=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);kt(e,a,c,d)};sF.prototype.__destroy__=function(){lt(this.kB)};
|
||||||
|
function tF(a,c,d,e,g,n,z){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);n&&"object"===typeof n&&(n=n.kB);z&&"object"===typeof z&&(z=z.kB);this.kB=void 0===d?mt(a,c):void 0===e?nt(a,c,d):void 0===g?ot(a,c,d,e):void 0===n?pt(a,c,d,e,g):void 0===z?qt(a,c,d,e,g,n):rt(a,c,d,e,g,n,z);h(tF)[this.kB]=this}tF.prototype=Object.create(aE.prototype);tF.prototype.constructor=tF;
|
||||||
|
tF.prototype.lB=tF;tF.mB={};b.btHingeConstraint=tF;tF.prototype.getHingeAngle=function(){return st(this.kB)};tF.prototype.setLimit=function(a,c,d,e,g){var n=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);void 0===g?tt(n,a,c,d,e):ut(n,a,c,d,e,g)};
|
||||||
|
tF.prototype.enableAngularMotor=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);vt(e,a,c,d)};tF.prototype.setAngularOnly=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);wt(c,a)};tF.prototype.enableMotor=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);xt(c,a)};tF.prototype.setMaxMotorImpulse=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);yt(c,a)};
|
||||||
|
tF.prototype.setMotorTarget=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);zt(d,a,c)};tF.prototype.enableFeedback=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);At(c,a)};tF.prototype.getBreakingImpulseThreshold=function(){return Bt(this.kB)};tF.prototype.setBreakingImpulseThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ct(c,a)};
|
||||||
|
tF.prototype.getParam=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);return Dt(d,a,c)};tF.prototype.setParam=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);Et(e,a,c,d)};tF.prototype.__destroy__=function(){Ft(this.kB)};
|
||||||
|
function uF(a,c,d,e,g){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);this.kB=void 0===e?Gt(a,c,d):void 0===g?_emscripten_bind_btSliderConstraint_btSliderConstraint_4(a,c,d,e):Ht(a,c,d,e,g);h(uF)[this.kB]=this}uF.prototype=Object.create(aE.prototype);uF.prototype.constructor=uF;uF.prototype.lB=uF;uF.mB={};b.btSliderConstraint=uF;uF.prototype.getLinearPos=function(){return It(this.kB)};
|
||||||
|
uF.prototype.getAngularPos=function(){return Jt(this.kB)};uF.prototype.setLowerLinLimit=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Kt(c,a)};uF.prototype.setUpperLinLimit=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Lt(c,a)};uF.prototype.setLowerAngLimit=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Mt(c,a)};uF.prototype.setUpperAngLimit=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Nt(c,a)};
|
||||||
|
uF.prototype.setPoweredLinMotor=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ot(c,a)};uF.prototype.setMaxLinMotorForce=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Pt(c,a)};uF.prototype.setTargetLinMotorVelocity=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Qt(c,a)};uF.prototype.enableFeedback=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Rt(c,a)};uF.prototype.getBreakingImpulseThreshold=function(){return St(this.kB)};
|
||||||
|
uF.prototype.setBreakingImpulseThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Tt(c,a)};uF.prototype.getParam=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);return Ut(d,a,c)};uF.prototype.setParam=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);Vt(e,a,c,d)};uF.prototype.__destroy__=function(){Wt(this.kB)};
|
||||||
|
function vF(a,c,d,e){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);this.kB=Xt(a,c,d,e);h(vF)[this.kB]=this}vF.prototype=Object.create(aE.prototype);vF.prototype.constructor=vF;vF.prototype.lB=vF;vF.mB={};b.btFixedConstraint=vF;vF.prototype.enableFeedback=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Yt(c,a)};vF.prototype.getBreakingImpulseThreshold=function(){return Zt(this.kB)};
|
||||||
|
vF.prototype.setBreakingImpulseThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);$t(c,a)};vF.prototype.getParam=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);return au(d,a,c)};vF.prototype.setParam=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);bu(e,a,c,d)};vF.prototype.__destroy__=function(){cu(this.kB)};
|
||||||
|
function wF(){throw"cannot construct a btConstraintSolver, no constructor in IDL";}wF.prototype=Object.create(f.prototype);wF.prototype.constructor=wF;wF.prototype.lB=wF;wF.mB={};b.btConstraintSolver=wF;wF.prototype.__destroy__=function(){du(this.kB)};function p(){throw"cannot construct a btDispatcherInfo, no constructor in IDL";}p.prototype=Object.create(f.prototype);p.prototype.constructor=p;p.prototype.lB=p;p.mB={};b.btDispatcherInfo=p;p.prototype.get_m_timeStep=p.prototype.uE=function(){return eu(this.kB)};
|
||||||
|
p.prototype.set_m_timeStep=p.prototype.mH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);fu(c,a)};Object.defineProperty(p.prototype,"m_timeStep",{get:p.prototype.uE,set:p.prototype.mH});p.prototype.get_m_stepCount=p.prototype.lE=function(){return gu(this.kB)};p.prototype.set_m_stepCount=p.prototype.dH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);hu(c,a)};Object.defineProperty(p.prototype,"m_stepCount",{get:p.prototype.lE,set:p.prototype.dH});
|
||||||
|
p.prototype.get_m_dispatchFunc=p.prototype.cD=function(){return iu(this.kB)};p.prototype.set_m_dispatchFunc=p.prototype.UF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ju(c,a)};Object.defineProperty(p.prototype,"m_dispatchFunc",{get:p.prototype.cD,set:p.prototype.UF});p.prototype.get_m_timeOfImpact=p.prototype.tE=function(){return ku(this.kB)};p.prototype.set_m_timeOfImpact=p.prototype.lH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);lu(c,a)};
|
||||||
|
Object.defineProperty(p.prototype,"m_timeOfImpact",{get:p.prototype.tE,set:p.prototype.lH});p.prototype.get_m_useContinuous=p.prototype.wE=function(){return!!mu(this.kB)};p.prototype.set_m_useContinuous=p.prototype.oH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);nu(c,a)};Object.defineProperty(p.prototype,"m_useContinuous",{get:p.prototype.wE,set:p.prototype.oH});p.prototype.get_m_enableSatConvex=p.prototype.hD=function(){return!!ou(this.kB)};
|
||||||
|
p.prototype.set_m_enableSatConvex=p.prototype.ZF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);pu(c,a)};Object.defineProperty(p.prototype,"m_enableSatConvex",{get:p.prototype.hD,set:p.prototype.ZF});p.prototype.get_m_enableSPU=p.prototype.gD=function(){return!!qu(this.kB)};p.prototype.set_m_enableSPU=p.prototype.YF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ru(c,a)};Object.defineProperty(p.prototype,"m_enableSPU",{get:p.prototype.gD,set:p.prototype.YF});
|
||||||
|
p.prototype.get_m_useEpa=p.prototype.yE=function(){return!!su(this.kB)};p.prototype.set_m_useEpa=p.prototype.qH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);tu(c,a)};Object.defineProperty(p.prototype,"m_useEpa",{get:p.prototype.yE,set:p.prototype.qH});p.prototype.get_m_allowedCcdPenetration=p.prototype.FC=function(){return uu(this.kB)};p.prototype.set_m_allowedCcdPenetration=p.prototype.wF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);vu(c,a)};
|
||||||
|
Object.defineProperty(p.prototype,"m_allowedCcdPenetration",{get:p.prototype.FC,set:p.prototype.wF});p.prototype.get_m_useConvexConservativeDistanceUtil=p.prototype.xE=function(){return!!wu(this.kB)};p.prototype.set_m_useConvexConservativeDistanceUtil=p.prototype.pH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);xu(c,a)};Object.defineProperty(p.prototype,"m_useConvexConservativeDistanceUtil",{get:p.prototype.xE,set:p.prototype.pH});
|
||||||
|
p.prototype.get_m_convexConservativeDistanceThreshold=p.prototype.YC=function(){return yu(this.kB)};p.prototype.set_m_convexConservativeDistanceThreshold=p.prototype.PF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);zu(c,a)};Object.defineProperty(p.prototype,"m_convexConservativeDistanceThreshold",{get:p.prototype.YC,set:p.prototype.PF});p.prototype.__destroy__=function(){Au(this.kB)};function t(){throw"cannot construct a btContactSolverInfo, no constructor in IDL";}t.prototype=Object.create(f.prototype);
|
||||||
|
t.prototype.constructor=t;t.prototype.lB=t;t.mB={};b.btContactSolverInfo=t;t.prototype.get_m_splitImpulse=t.prototype.iE=function(){return!!Bu(this.kB)};t.prototype.set_m_splitImpulse=t.prototype.aH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Cu(c,a)};Object.defineProperty(t.prototype,"m_splitImpulse",{get:t.prototype.iE,set:t.prototype.aH});t.prototype.get_m_splitImpulsePenetrationThreshold=t.prototype.jE=function(){return Du(this.kB)};
|
||||||
|
t.prototype.set_m_splitImpulsePenetrationThreshold=t.prototype.bH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Eu(c,a)};Object.defineProperty(t.prototype,"m_splitImpulsePenetrationThreshold",{get:t.prototype.jE,set:t.prototype.bH});t.prototype.get_m_numIterations=t.prototype.TD=function(){return Fu(this.kB)};t.prototype.set_m_numIterations=t.prototype.KG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Gu(c,a)};
|
||||||
|
Object.defineProperty(t.prototype,"m_numIterations",{get:t.prototype.TD,set:t.prototype.KG});t.prototype.__destroy__=function(){Hu(this.kB)};function M(){this.kB=Iu();h(M)[this.kB]=this}M.prototype=Object.create(f.prototype);M.prototype.constructor=M;M.prototype.lB=M;M.mB={};b.btVehicleTuning=M;M.prototype.get_m_suspensionStiffness=M.prototype.BB=function(){return Ju(this.kB)};M.prototype.set_m_suspensionStiffness=M.prototype.IB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ku(c,a)};
|
||||||
|
Object.defineProperty(M.prototype,"m_suspensionStiffness",{get:M.prototype.BB,set:M.prototype.IB});M.prototype.get_m_suspensionCompression=M.prototype.mE=function(){return Lu(this.kB)};M.prototype.set_m_suspensionCompression=M.prototype.eH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Mu(c,a)};Object.defineProperty(M.prototype,"m_suspensionCompression",{get:M.prototype.mE,set:M.prototype.eH});M.prototype.get_m_suspensionDamping=M.prototype.nE=function(){return Nu(this.kB)};
|
||||||
|
M.prototype.set_m_suspensionDamping=M.prototype.fH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ou(c,a)};Object.defineProperty(M.prototype,"m_suspensionDamping",{get:M.prototype.nE,set:M.prototype.fH});M.prototype.get_m_maxSuspensionTravelCm=M.prototype.AB=function(){return Pu(this.kB)};M.prototype.set_m_maxSuspensionTravelCm=M.prototype.HB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Qu(c,a)};
|
||||||
|
Object.defineProperty(M.prototype,"m_maxSuspensionTravelCm",{get:M.prototype.AB,set:M.prototype.HB});M.prototype.get_m_frictionSlip=M.prototype.wB=function(){return Ru(this.kB)};M.prototype.set_m_frictionSlip=M.prototype.DB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Su(c,a)};Object.defineProperty(M.prototype,"m_frictionSlip",{get:M.prototype.wB,set:M.prototype.DB});M.prototype.get_m_maxSuspensionForce=M.prototype.zB=function(){return Tu(this.kB)};
|
||||||
|
M.prototype.set_m_maxSuspensionForce=M.prototype.GB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Uu(c,a)};Object.defineProperty(M.prototype,"m_maxSuspensionForce",{get:M.prototype.zB,set:M.prototype.GB});function xF(){throw"cannot construct a btVehicleRaycasterResult, no constructor in IDL";}xF.prototype=Object.create(f.prototype);xF.prototype.constructor=xF;xF.prototype.lB=xF;xF.mB={};b.btVehicleRaycasterResult=xF;
|
||||||
|
xF.prototype.get_m_hitPointInWorld=xF.prototype.tD=function(){return k(Vu(this.kB),m)};xF.prototype.set_m_hitPointInWorld=xF.prototype.kG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Wu(c,a)};Object.defineProperty(xF.prototype,"m_hitPointInWorld",{get:xF.prototype.tD,set:xF.prototype.kG});xF.prototype.get_m_hitNormalInWorld=xF.prototype.rD=function(){return k(Xu(this.kB),m)};
|
||||||
|
xF.prototype.set_m_hitNormalInWorld=xF.prototype.iG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Yu(c,a)};Object.defineProperty(xF.prototype,"m_hitNormalInWorld",{get:xF.prototype.rD,set:xF.prototype.iG});xF.prototype.get_m_distFraction=xF.prototype.fD=function(){return Zu(this.kB)};xF.prototype.set_m_distFraction=xF.prototype.XF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);$u(c,a)};Object.defineProperty(xF.prototype,"m_distFraction",{get:xF.prototype.fD,set:xF.prototype.XF});
|
||||||
|
xF.prototype.__destroy__=function(){av(this.kB)};function yF(a){a&&"object"===typeof a&&(a=a.kB);this.kB=bv(a);h(yF)[this.kB]=this}yF.prototype=Object.create(qE.prototype);yF.prototype.constructor=yF;yF.prototype.lB=yF;yF.mB={};b.btDefaultVehicleRaycaster=yF;yF.prototype.castRay=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);cv(e,a,c,d)};yF.prototype.__destroy__=function(){dv(this.kB)};
|
||||||
|
function N(){throw"cannot construct a RaycastInfo, no constructor in IDL";}N.prototype=Object.create(f.prototype);N.prototype.constructor=N;N.prototype.lB=N;N.mB={};b.RaycastInfo=N;N.prototype.get_m_contactNormalWS=N.prototype.WC=function(){return k(ev(this.kB),m)};N.prototype.set_m_contactNormalWS=N.prototype.NF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);fv(c,a)};Object.defineProperty(N.prototype,"m_contactNormalWS",{get:N.prototype.WC,set:N.prototype.NF});
|
||||||
|
N.prototype.get_m_contactPointWS=N.prototype.XC=function(){return k(gv(this.kB),m)};N.prototype.set_m_contactPointWS=N.prototype.OF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);hv(c,a)};Object.defineProperty(N.prototype,"m_contactPointWS",{get:N.prototype.XC,set:N.prototype.OF});N.prototype.get_m_suspensionLength=N.prototype.oE=function(){return iv(this.kB)};N.prototype.set_m_suspensionLength=N.prototype.gH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);jv(c,a)};
|
||||||
|
Object.defineProperty(N.prototype,"m_suspensionLength",{get:N.prototype.oE,set:N.prototype.gH});N.prototype.get_m_hardPointWS=N.prototype.oD=function(){return k(kv(this.kB),m)};N.prototype.set_m_hardPointWS=N.prototype.fG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);lv(c,a)};Object.defineProperty(N.prototype,"m_hardPointWS",{get:N.prototype.oD,set:N.prototype.fG});N.prototype.get_m_wheelDirectionWS=N.prototype.DE=function(){return k(mv(this.kB),m)};
|
||||||
|
N.prototype.set_m_wheelDirectionWS=N.prototype.vH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);nv(c,a)};Object.defineProperty(N.prototype,"m_wheelDirectionWS",{get:N.prototype.DE,set:N.prototype.vH});N.prototype.get_m_wheelAxleWS=N.prototype.CE=function(){return k(ov(this.kB),m)};N.prototype.set_m_wheelAxleWS=N.prototype.uH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);pv(c,a)};Object.defineProperty(N.prototype,"m_wheelAxleWS",{get:N.prototype.CE,set:N.prototype.uH});
|
||||||
|
N.prototype.get_m_isInContact=N.prototype.zD=function(){return!!qv(this.kB)};N.prototype.set_m_isInContact=N.prototype.qG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);rv(c,a)};Object.defineProperty(N.prototype,"m_isInContact",{get:N.prototype.zD,set:N.prototype.qG});N.prototype.get_m_groundObject=N.prototype.nD=function(){return sv(this.kB)};N.prototype.set_m_groundObject=N.prototype.eG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);tv(c,a)};
|
||||||
|
Object.defineProperty(N.prototype,"m_groundObject",{get:N.prototype.nD,set:N.prototype.eG});N.prototype.__destroy__=function(){uv(this.kB)};function O(){throw"cannot construct a btWheelInfoConstructionInfo, no constructor in IDL";}O.prototype=Object.create(f.prototype);O.prototype.constructor=O;O.prototype.lB=O;O.mB={};b.btWheelInfoConstructionInfo=O;O.prototype.get_m_chassisConnectionCS=O.prototype.RC=function(){return k(vv(this.kB),m)};
|
||||||
|
O.prototype.set_m_chassisConnectionCS=O.prototype.IF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);wv(c,a)};Object.defineProperty(O.prototype,"m_chassisConnectionCS",{get:O.prototype.RC,set:O.prototype.IF});O.prototype.get_m_wheelDirectionCS=O.prototype.QB=function(){return k(xv(this.kB),m)};O.prototype.set_m_wheelDirectionCS=O.prototype.$B=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);yv(c,a)};Object.defineProperty(O.prototype,"m_wheelDirectionCS",{get:O.prototype.QB,set:O.prototype.$B});
|
||||||
|
O.prototype.get_m_wheelAxleCS=O.prototype.PB=function(){return k(zv(this.kB),m)};O.prototype.set_m_wheelAxleCS=O.prototype.ZB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Av(c,a)};Object.defineProperty(O.prototype,"m_wheelAxleCS",{get:O.prototype.PB,set:O.prototype.ZB});O.prototype.get_m_suspensionRestLength=O.prototype.qE=function(){return Bv(this.kB)};O.prototype.set_m_suspensionRestLength=O.prototype.iH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Cv(c,a)};
|
||||||
|
Object.defineProperty(O.prototype,"m_suspensionRestLength",{get:O.prototype.qE,set:O.prototype.iH});O.prototype.get_m_maxSuspensionTravelCm=O.prototype.AB=function(){return Dv(this.kB)};O.prototype.set_m_maxSuspensionTravelCm=O.prototype.HB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ev(c,a)};Object.defineProperty(O.prototype,"m_maxSuspensionTravelCm",{get:O.prototype.AB,set:O.prototype.HB});O.prototype.get_m_wheelRadius=O.prototype.EE=function(){return Fv(this.kB)};
|
||||||
|
O.prototype.set_m_wheelRadius=O.prototype.wH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Gv(c,a)};Object.defineProperty(O.prototype,"m_wheelRadius",{get:O.prototype.EE,set:O.prototype.wH});O.prototype.get_m_suspensionStiffness=O.prototype.BB=function(){return Hv(this.kB)};O.prototype.set_m_suspensionStiffness=O.prototype.IB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Iv(c,a)};Object.defineProperty(O.prototype,"m_suspensionStiffness",{get:O.prototype.BB,set:O.prototype.IB});
|
||||||
|
O.prototype.get_m_wheelsDampingCompression=O.prototype.RB=function(){return Jv(this.kB)};O.prototype.set_m_wheelsDampingCompression=O.prototype.aC=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Kv(c,a)};Object.defineProperty(O.prototype,"m_wheelsDampingCompression",{get:O.prototype.RB,set:O.prototype.aC});O.prototype.get_m_wheelsDampingRelaxation=O.prototype.SB=function(){return Lv(this.kB)};
|
||||||
|
O.prototype.set_m_wheelsDampingRelaxation=O.prototype.bC=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Mv(c,a)};Object.defineProperty(O.prototype,"m_wheelsDampingRelaxation",{get:O.prototype.SB,set:O.prototype.bC});O.prototype.get_m_frictionSlip=O.prototype.wB=function(){return Nv(this.kB)};O.prototype.set_m_frictionSlip=O.prototype.DB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ov(c,a)};Object.defineProperty(O.prototype,"m_frictionSlip",{get:O.prototype.wB,set:O.prototype.DB});
|
||||||
|
O.prototype.get_m_maxSuspensionForce=O.prototype.zB=function(){return Pv(this.kB)};O.prototype.set_m_maxSuspensionForce=O.prototype.GB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Qv(c,a)};Object.defineProperty(O.prototype,"m_maxSuspensionForce",{get:O.prototype.zB,set:O.prototype.GB});O.prototype.get_m_bIsFrontWheel=O.prototype.JB=function(){return!!Rv(this.kB)};O.prototype.set_m_bIsFrontWheel=O.prototype.TB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Sv(c,a)};
|
||||||
|
Object.defineProperty(O.prototype,"m_bIsFrontWheel",{get:O.prototype.JB,set:O.prototype.TB});O.prototype.__destroy__=function(){Tv(this.kB)};function P(a){a&&"object"===typeof a&&(a=a.kB);this.kB=Uv(a);h(P)[this.kB]=this}P.prototype=Object.create(f.prototype);P.prototype.constructor=P;P.prototype.lB=P;P.mB={};b.btWheelInfo=P;P.prototype.getSuspensionRestLength=function(){return Vv(this.kB)};
|
||||||
|
P.prototype.updateWheel=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Wv(d,a,c)};P.prototype.get_m_suspensionStiffness=P.prototype.BB=function(){return Xv(this.kB)};P.prototype.set_m_suspensionStiffness=P.prototype.IB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Yv(c,a)};Object.defineProperty(P.prototype,"m_suspensionStiffness",{get:P.prototype.BB,set:P.prototype.IB});P.prototype.get_m_frictionSlip=P.prototype.wB=function(){return Zv(this.kB)};
|
||||||
|
P.prototype.set_m_frictionSlip=P.prototype.DB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);$v(c,a)};Object.defineProperty(P.prototype,"m_frictionSlip",{get:P.prototype.wB,set:P.prototype.DB});P.prototype.get_m_engineForce=P.prototype.iD=function(){return aw(this.kB)};P.prototype.set_m_engineForce=P.prototype.$F=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);bw(c,a)};Object.defineProperty(P.prototype,"m_engineForce",{get:P.prototype.iD,set:P.prototype.$F});
|
||||||
|
P.prototype.get_m_rollInfluence=P.prototype.cE=function(){return cw(this.kB)};P.prototype.set_m_rollInfluence=P.prototype.VG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);dw(c,a)};Object.defineProperty(P.prototype,"m_rollInfluence",{get:P.prototype.cE,set:P.prototype.VG});P.prototype.get_m_suspensionRestLength1=P.prototype.rE=function(){return ew(this.kB)};P.prototype.set_m_suspensionRestLength1=P.prototype.jH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);fw(c,a)};
|
||||||
|
Object.defineProperty(P.prototype,"m_suspensionRestLength1",{get:P.prototype.rE,set:P.prototype.jH});P.prototype.get_m_wheelsRadius=P.prototype.FE=function(){return gw(this.kB)};P.prototype.set_m_wheelsRadius=P.prototype.xH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);hw(c,a)};Object.defineProperty(P.prototype,"m_wheelsRadius",{get:P.prototype.FE,set:P.prototype.xH});P.prototype.get_m_wheelsDampingCompression=P.prototype.RB=function(){return iw(this.kB)};
|
||||||
|
P.prototype.set_m_wheelsDampingCompression=P.prototype.aC=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);jw(c,a)};Object.defineProperty(P.prototype,"m_wheelsDampingCompression",{get:P.prototype.RB,set:P.prototype.aC});P.prototype.get_m_wheelsDampingRelaxation=P.prototype.SB=function(){return kw(this.kB)};P.prototype.set_m_wheelsDampingRelaxation=P.prototype.bC=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);lw(c,a)};
|
||||||
|
Object.defineProperty(P.prototype,"m_wheelsDampingRelaxation",{get:P.prototype.SB,set:P.prototype.bC});P.prototype.get_m_steering=P.prototype.kE=function(){return mw(this.kB)};P.prototype.set_m_steering=P.prototype.cH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);nw(c,a)};Object.defineProperty(P.prototype,"m_steering",{get:P.prototype.kE,set:P.prototype.cH});P.prototype.get_m_maxSuspensionForce=P.prototype.zB=function(){return ow(this.kB)};
|
||||||
|
P.prototype.set_m_maxSuspensionForce=P.prototype.GB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);pw(c,a)};Object.defineProperty(P.prototype,"m_maxSuspensionForce",{get:P.prototype.zB,set:P.prototype.GB});P.prototype.get_m_maxSuspensionTravelCm=P.prototype.AB=function(){return qw(this.kB)};P.prototype.set_m_maxSuspensionTravelCm=P.prototype.HB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);rw(c,a)};
|
||||||
|
Object.defineProperty(P.prototype,"m_maxSuspensionTravelCm",{get:P.prototype.AB,set:P.prototype.HB});P.prototype.get_m_wheelsSuspensionForce=P.prototype.GE=function(){return sw(this.kB)};P.prototype.set_m_wheelsSuspensionForce=P.prototype.yH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);tw(c,a)};Object.defineProperty(P.prototype,"m_wheelsSuspensionForce",{get:P.prototype.GE,set:P.prototype.yH});P.prototype.get_m_bIsFrontWheel=P.prototype.JB=function(){return!!uw(this.kB)};
|
||||||
|
P.prototype.set_m_bIsFrontWheel=P.prototype.TB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);vw(c,a)};Object.defineProperty(P.prototype,"m_bIsFrontWheel",{get:P.prototype.JB,set:P.prototype.TB});P.prototype.get_m_raycastInfo=P.prototype.aE=function(){return k(ww(this.kB),N)};P.prototype.set_m_raycastInfo=P.prototype.TG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);xw(c,a)};Object.defineProperty(P.prototype,"m_raycastInfo",{get:P.prototype.aE,set:P.prototype.TG});
|
||||||
|
P.prototype.get_m_chassisConnectionPointCS=P.prototype.SC=function(){return k(yw(this.kB),m)};P.prototype.set_m_chassisConnectionPointCS=P.prototype.JF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);zw(c,a)};Object.defineProperty(P.prototype,"m_chassisConnectionPointCS",{get:P.prototype.SC,set:P.prototype.JF});P.prototype.get_m_worldTransform=P.prototype.HE=function(){return k(Aw(this.kB),r)};
|
||||||
|
P.prototype.set_m_worldTransform=P.prototype.zH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Bw(c,a)};Object.defineProperty(P.prototype,"m_worldTransform",{get:P.prototype.HE,set:P.prototype.zH});P.prototype.get_m_wheelDirectionCS=P.prototype.QB=function(){return k(Cw(this.kB),m)};P.prototype.set_m_wheelDirectionCS=P.prototype.$B=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Dw(c,a)};Object.defineProperty(P.prototype,"m_wheelDirectionCS",{get:P.prototype.QB,set:P.prototype.$B});
|
||||||
|
P.prototype.get_m_wheelAxleCS=P.prototype.PB=function(){return k(Ew(this.kB),m)};P.prototype.set_m_wheelAxleCS=P.prototype.ZB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Fw(c,a)};Object.defineProperty(P.prototype,"m_wheelAxleCS",{get:P.prototype.PB,set:P.prototype.ZB});P.prototype.get_m_rotation=P.prototype.eE=function(){return Gw(this.kB)};P.prototype.set_m_rotation=P.prototype.XG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Hw(c,a)};
|
||||||
|
Object.defineProperty(P.prototype,"m_rotation",{get:P.prototype.eE,set:P.prototype.XG});P.prototype.get_m_deltaRotation=P.prototype.bD=function(){return Iw(this.kB)};P.prototype.set_m_deltaRotation=P.prototype.TF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Jw(c,a)};Object.defineProperty(P.prototype,"m_deltaRotation",{get:P.prototype.bD,set:P.prototype.TF});P.prototype.get_m_brake=P.prototype.LC=function(){return Kw(this.kB)};
|
||||||
|
P.prototype.set_m_brake=P.prototype.CF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Lw(c,a)};Object.defineProperty(P.prototype,"m_brake",{get:P.prototype.LC,set:P.prototype.CF});P.prototype.get_m_clippedInvContactDotSuspension=P.prototype.TC=function(){return Mw(this.kB)};P.prototype.set_m_clippedInvContactDotSuspension=P.prototype.KF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Nw(c,a)};
|
||||||
|
Object.defineProperty(P.prototype,"m_clippedInvContactDotSuspension",{get:P.prototype.TC,set:P.prototype.KF});P.prototype.get_m_suspensionRelativeVelocity=P.prototype.pE=function(){return Ow(this.kB)};P.prototype.set_m_suspensionRelativeVelocity=P.prototype.hH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Pw(c,a)};Object.defineProperty(P.prototype,"m_suspensionRelativeVelocity",{get:P.prototype.pE,set:P.prototype.hH});P.prototype.get_m_skidInfo=P.prototype.hE=function(){return Qw(this.kB)};
|
||||||
|
P.prototype.set_m_skidInfo=P.prototype.$G=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Rw(c,a)};Object.defineProperty(P.prototype,"m_skidInfo",{get:P.prototype.hE,set:P.prototype.$G});P.prototype.__destroy__=function(){Sw(this.kB)};function zF(a,c,d,e){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);this.kB=void 0===e?Tw(a,c,d):Uw(a,c,d,e);h(zF)[this.kB]=this}zF.prototype=Object.create(rE.prototype);
|
||||||
|
zF.prototype.constructor=zF;zF.prototype.lB=zF;zF.mB={};b.btKinematicCharacterController=zF;zF.prototype.setUpAxis=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Vw(c,a)};zF.prototype.setWalkDirection=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ww(c,a)};zF.prototype.setVelocityForTimeInterval=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Xw(d,a,c)};
|
||||||
|
zF.prototype.warp=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Yw(c,a)};zF.prototype.preStep=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Zw(c,a)};zF.prototype.playerStep=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);$w(d,a,c)};zF.prototype.setFallSpeed=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ax(c,a)};zF.prototype.setJumpSpeed=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);bx(c,a)};
|
||||||
|
zF.prototype.setMaxJumpHeight=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);cx(c,a)};zF.prototype.canJump=function(){return!!dx(this.kB)};zF.prototype.jump=function(){ex(this.kB)};zF.prototype.setGravity=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);fx(c,a)};zF.prototype.getGravity=function(){return gx(this.kB)};zF.prototype.setMaxSlope=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);hx(c,a)};zF.prototype.getMaxSlope=function(){return ix(this.kB)};
|
||||||
|
zF.prototype.getGhostObject=function(){return k(jx(this.kB),Q)};zF.prototype.setUseGhostSweepTest=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);kx(c,a)};zF.prototype.onGround=function(){return!!lx(this.kB)};zF.prototype.setUpInterpolate=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);mx(c,a)};zF.prototype.updateAction=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);nx(d,a,c)};zF.prototype.__destroy__=function(){ox(this.kB)};
|
||||||
|
function R(a,c,d){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);this.kB=px(a,c,d);h(R)[this.kB]=this}R.prototype=Object.create(rE.prototype);R.prototype.constructor=R;R.prototype.lB=R;R.mB={};b.btRaycastVehicle=R;R.prototype.applyEngineForce=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);qx(d,a,c)};
|
||||||
|
R.prototype.setSteeringValue=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);rx(d,a,c)};R.prototype.getWheelTransformWS=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(sx(c,a),r)};R.prototype.updateWheelTransform=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);tx(d,a,c)};
|
||||||
|
R.prototype.addWheel=function(a,c,d,e,g,n,z){var T=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);n&&"object"===typeof n&&(n=n.kB);z&&"object"===typeof z&&(z=z.kB);return k(ux(T,a,c,d,e,g,n,z),P)};R.prototype.getNumWheels=function(){return vx(this.kB)};R.prototype.getRigidBody=function(){return k(wx(this.kB),K)};
|
||||||
|
R.prototype.getWheelInfo=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(xx(c,a),P)};R.prototype.setBrake=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);yx(d,a,c)};R.prototype.setCoordinateSystem=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);zx(e,a,c,d)};R.prototype.getCurrentSpeedKmHour=function(){return Ax(this.kB)};
|
||||||
|
R.prototype.getChassisWorldTransform=function(){return k(Bx(this.kB),r)};R.prototype.rayCast=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return Cx(c,a)};R.prototype.updateVehicle=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Dx(c,a)};R.prototype.resetSuspension=function(){Ex(this.kB)};R.prototype.getSteeringValue=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return Fx(c,a)};
|
||||||
|
R.prototype.updateWheelTransformsWS=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);void 0===c?Gx(d,a):Hx(d,a,c)};R.prototype.setPitchControl=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ix(c,a)};R.prototype.updateSuspension=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Jx(c,a)};R.prototype.updateFriction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Kx(c,a)};R.prototype.getRightAxis=function(){return Lx(this.kB)};
|
||||||
|
R.prototype.getUpAxis=function(){return Mx(this.kB)};R.prototype.getForwardAxis=function(){return Nx(this.kB)};R.prototype.getForwardVector=function(){return k(Ox(this.kB),m)};R.prototype.getUserConstraintType=function(){return Px(this.kB)};R.prototype.setUserConstraintType=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Qx(c,a)};R.prototype.setUserConstraintId=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Rx(c,a)};R.prototype.getUserConstraintId=function(){return Sx(this.kB)};
|
||||||
|
R.prototype.updateAction=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Tx(d,a,c)};R.prototype.__destroy__=function(){Ux(this.kB)};function Q(){this.kB=Vx();h(Q)[this.kB]=this}Q.prototype=Object.create(y.prototype);Q.prototype.constructor=Q;Q.prototype.lB=Q;Q.mB={};b.btPairCachingGhostObject=Q;Q.prototype.setAnisotropicFriction=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Wx(d,a,c)};
|
||||||
|
Q.prototype.getCollisionShape=function(){return k(Xx(this.kB),l)};Q.prototype.setContactProcessingThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Yx(c,a)};Q.prototype.setActivationState=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Zx(c,a)};Q.prototype.forceActivationState=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);$x(c,a)};Q.prototype.activate=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);void 0===a?ay(c):by(c,a)};
|
||||||
|
Q.prototype.isActive=function(){return!!cy(this.kB)};Q.prototype.isKinematicObject=function(){return!!dy(this.kB)};Q.prototype.isStaticObject=function(){return!!ey(this.kB)};Q.prototype.isStaticOrKinematicObject=function(){return!!fy(this.kB)};Q.prototype.getRestitution=function(){return gy(this.kB)};Q.prototype.getFriction=function(){return hy(this.kB)};Q.prototype.getRollingFriction=function(){return iy(this.kB)};
|
||||||
|
Q.prototype.setRestitution=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);jy(c,a)};Q.prototype.setFriction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ky(c,a)};Q.prototype.setRollingFriction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ly(c,a)};Q.prototype.getWorldTransform=function(){return k(my(this.kB),r)};Q.prototype.getCollisionFlags=function(){return ny(this.kB)};
|
||||||
|
Q.prototype.setCollisionFlags=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);oy(c,a)};Q.prototype.setWorldTransform=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);py(c,a)};Q.prototype.setCollisionShape=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);qy(c,a)};Q.prototype.setCcdMotionThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ry(c,a)};Q.prototype.setCcdSweptSphereRadius=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);sy(c,a)};
|
||||||
|
Q.prototype.getUserIndex=function(){return ty(this.kB)};Q.prototype.setUserIndex=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);uy(c,a)};Q.prototype.getUserPointer=function(){return k(vy(this.kB),XD)};Q.prototype.setUserPointer=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);wy(c,a)};Q.prototype.getBroadphaseHandle=function(){return k(xy(this.kB),YD)};Q.prototype.getNumOverlappingObjects=function(){return yy(this.kB)};
|
||||||
|
Q.prototype.getOverlappingObject=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(zy(c,a),q)};Q.prototype.__destroy__=function(){Ay(this.kB)};function AF(){this.kB=By();h(AF)[this.kB]=this}AF.prototype=Object.create(f.prototype);AF.prototype.constructor=AF;AF.prototype.lB=AF;AF.mB={};b.btGhostPairCallback=AF;AF.prototype.__destroy__=function(){Cy(this.kB)};function S(){this.kB=Dy();h(S)[this.kB]=this}S.prototype=Object.create(f.prototype);S.prototype.constructor=S;
|
||||||
|
S.prototype.lB=S;S.mB={};b.btSoftBodyWorldInfo=S;S.prototype.get_air_density=S.prototype.cC=function(){return Ey(this.kB)};S.prototype.set_air_density=S.prototype.UE=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Fy(c,a)};Object.defineProperty(S.prototype,"air_density",{get:S.prototype.cC,set:S.prototype.UE});S.prototype.get_water_density=S.prototype.RE=function(){return Gy(this.kB)};
|
||||||
|
S.prototype.set_water_density=S.prototype.JH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Hy(c,a)};Object.defineProperty(S.prototype,"water_density",{get:S.prototype.RE,set:S.prototype.JH});S.prototype.get_water_offset=S.prototype.TE=function(){return Iy(this.kB)};S.prototype.set_water_offset=S.prototype.LH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Jy(c,a)};Object.defineProperty(S.prototype,"water_offset",{get:S.prototype.TE,set:S.prototype.LH});
|
||||||
|
S.prototype.get_m_maxDisplacement=S.prototype.ND=function(){return Ky(this.kB)};S.prototype.set_m_maxDisplacement=S.prototype.EG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ly(c,a)};Object.defineProperty(S.prototype,"m_maxDisplacement",{get:S.prototype.ND,set:S.prototype.EG});S.prototype.get_water_normal=S.prototype.SE=function(){return k(My(this.kB),m)};S.prototype.set_water_normal=S.prototype.KH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ny(c,a)};
|
||||||
|
Object.defineProperty(S.prototype,"water_normal",{get:S.prototype.SE,set:S.prototype.KH});S.prototype.get_m_broadphase=S.prototype.MC=function(){return k(Oy(this.kB),VD)};S.prototype.set_m_broadphase=S.prototype.DF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Py(c,a)};Object.defineProperty(S.prototype,"m_broadphase",{get:S.prototype.MC,set:S.prototype.DF});S.prototype.get_m_dispatcher=S.prototype.dD=function(){return k(Qy(this.kB),TD)};
|
||||||
|
S.prototype.set_m_dispatcher=S.prototype.VF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ry(c,a)};Object.defineProperty(S.prototype,"m_dispatcher",{get:S.prototype.dD,set:S.prototype.VF});S.prototype.get_m_gravity=S.prototype.mD=function(){return k(Sy(this.kB),m)};S.prototype.set_m_gravity=S.prototype.dG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Ty(c,a)};Object.defineProperty(S.prototype,"m_gravity",{get:S.prototype.mD,set:S.prototype.dG});S.prototype.__destroy__=function(){Uy(this.kB)};
|
||||||
|
function U(){throw"cannot construct a Face, no constructor in IDL";}U.prototype=Object.create(f.prototype);U.prototype.constructor=U;U.prototype.lB=U;U.mB={};b.Face=U;U.prototype.get_m_n=U.prototype.MB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Vy(c,a),Node)};U.prototype.set_m_n=U.prototype.WB=function(a,c){var d=this.kB;ND();a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);Wy(d,a,c)};Object.defineProperty(U.prototype,"m_n",{get:U.prototype.MB,set:U.prototype.WB});
|
||||||
|
U.prototype.get_m_normal=U.prototype.RD=function(){return k(Xy(this.kB),m)};U.prototype.set_m_normal=U.prototype.IG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Yy(c,a)};Object.defineProperty(U.prototype,"m_normal",{get:U.prototype.RD,set:U.prototype.IG});U.prototype.get_m_ra=U.prototype.$D=function(){return Zy(this.kB)};U.prototype.set_m_ra=U.prototype.SG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);$y(c,a)};
|
||||||
|
Object.defineProperty(U.prototype,"m_ra",{get:U.prototype.$D,set:U.prototype.SG});U.prototype.__destroy__=function(){az(this.kB)};function BF(){throw"cannot construct a tFaceArray, no constructor in IDL";}BF.prototype=Object.create(f.prototype);BF.prototype.constructor=BF;BF.prototype.lB=BF;BF.mB={};b.tFaceArray=BF;BF.prototype.size=BF.prototype.size=function(){return bz(this.kB)};BF.prototype.at=BF.prototype.at=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(cz(c,a),U)};
|
||||||
|
BF.prototype.__destroy__=function(){dz(this.kB)};function Node(){throw"cannot construct a Node, no constructor in IDL";}Node.prototype=Object.create(f.prototype);Node.prototype.constructor=Node;Node.prototype.lB=Node;Node.mB={};b.Node=Node;Node.prototype.get_m_x=Node.prototype.IE=function(){return k(ez(this.kB),m)};Node.prototype.set_m_x=Node.prototype.AH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);fz(c,a)};Object.defineProperty(Node.prototype,"m_x",{get:Node.prototype.IE,set:Node.prototype.AH});
|
||||||
|
Node.prototype.get_m_q=Node.prototype.ZD=function(){return k(gz(this.kB),m)};Node.prototype.set_m_q=Node.prototype.QG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);hz(c,a)};Object.defineProperty(Node.prototype,"m_q",{get:Node.prototype.ZD,set:Node.prototype.QG});Node.prototype.get_m_v=Node.prototype.AE=function(){return k(iz(this.kB),m)};Node.prototype.set_m_v=Node.prototype.sH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);jz(c,a)};
|
||||||
|
Object.defineProperty(Node.prototype,"m_v",{get:Node.prototype.AE,set:Node.prototype.sH});Node.prototype.get_m_f=Node.prototype.jD=function(){return k(kz(this.kB),m)};Node.prototype.set_m_f=Node.prototype.aG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);lz(c,a)};Object.defineProperty(Node.prototype,"m_f",{get:Node.prototype.jD,set:Node.prototype.aG});Node.prototype.get_m_n=Node.prototype.MB=function(){return k(mz(this.kB),m)};
|
||||||
|
Node.prototype.set_m_n=Node.prototype.WB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);nz(c,a)};Object.defineProperty(Node.prototype,"m_n",{get:Node.prototype.MB,set:Node.prototype.WB});Node.prototype.get_m_im=Node.prototype.vD=function(){return oz(this.kB)};Node.prototype.set_m_im=Node.prototype.mG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);pz(c,a)};Object.defineProperty(Node.prototype,"m_im",{get:Node.prototype.vD,set:Node.prototype.mG});
|
||||||
|
Node.prototype.get_m_area=Node.prototype.JC=function(){return qz(this.kB)};Node.prototype.set_m_area=Node.prototype.AF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);rz(c,a)};Object.defineProperty(Node.prototype,"m_area",{get:Node.prototype.JC,set:Node.prototype.AF});Node.prototype.__destroy__=function(){sz(this.kB)};function CF(){throw"cannot construct a tNodeArray, no constructor in IDL";}CF.prototype=Object.create(f.prototype);CF.prototype.constructor=CF;CF.prototype.lB=CF;CF.mB={};
|
||||||
|
b.tNodeArray=CF;CF.prototype.size=CF.prototype.size=function(){return tz(this.kB)};CF.prototype.at=CF.prototype.at=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(uz(c,a),Node)};CF.prototype.__destroy__=function(){vz(this.kB)};function V(){throw"cannot construct a Material, no constructor in IDL";}V.prototype=Object.create(f.prototype);V.prototype.constructor=V;V.prototype.lB=V;V.mB={};b.Material=V;V.prototype.get_m_kLST=V.prototype.BD=function(){return wz(this.kB)};
|
||||||
|
V.prototype.set_m_kLST=V.prototype.sG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);xz(c,a)};Object.defineProperty(V.prototype,"m_kLST",{get:V.prototype.BD,set:V.prototype.sG});V.prototype.get_m_kAST=V.prototype.AD=function(){return yz(this.kB)};V.prototype.set_m_kAST=V.prototype.rG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);zz(c,a)};Object.defineProperty(V.prototype,"m_kAST",{get:V.prototype.AD,set:V.prototype.rG});V.prototype.get_m_kVST=V.prototype.CD=function(){return Az(this.kB)};
|
||||||
|
V.prototype.set_m_kVST=V.prototype.tG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Bz(c,a)};Object.defineProperty(V.prototype,"m_kVST",{get:V.prototype.CD,set:V.prototype.tG});V.prototype.get_m_flags=V.prototype.tB=function(){return Cz(this.kB)};V.prototype.set_m_flags=V.prototype.uB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Dz(c,a)};Object.defineProperty(V.prototype,"m_flags",{get:V.prototype.tB,set:V.prototype.uB});V.prototype.__destroy__=function(){Ez(this.kB)};
|
||||||
|
function DF(){throw"cannot construct a tMaterialArray, no constructor in IDL";}DF.prototype=Object.create(f.prototype);DF.prototype.constructor=DF;DF.prototype.lB=DF;DF.mB={};b.tMaterialArray=DF;DF.prototype.size=DF.prototype.size=function(){return Fz(this.kB)};DF.prototype.at=DF.prototype.at=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Gz(c,a),V)};DF.prototype.__destroy__=function(){Hz(this.kB)};function W(){throw"cannot construct a Anchor, no constructor in IDL";}
|
||||||
|
W.prototype=Object.create(f.prototype);W.prototype.constructor=W;W.prototype.lB=W;W.mB={};b.Anchor=W;W.prototype.get_m_node=W.prototype.PD=function(){return k(Iz(this.kB),Node)};W.prototype.set_m_node=W.prototype.GG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Jz(c,a)};Object.defineProperty(W.prototype,"m_node",{get:W.prototype.PD,set:W.prototype.GG});W.prototype.get_m_local=W.prototype.FD=function(){return k(Kz(this.kB),m)};
|
||||||
|
W.prototype.set_m_local=W.prototype.wG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Lz(c,a)};Object.defineProperty(W.prototype,"m_local",{get:W.prototype.FD,set:W.prototype.wG});W.prototype.get_m_body=W.prototype.KC=function(){return k(Mz(this.kB),K)};W.prototype.set_m_body=W.prototype.BF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Nz(c,a)};Object.defineProperty(W.prototype,"m_body",{get:W.prototype.KC,set:W.prototype.BF});W.prototype.get_m_influence=W.prototype.yD=function(){return Oz(this.kB)};
|
||||||
|
W.prototype.set_m_influence=W.prototype.pG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Pz(c,a)};Object.defineProperty(W.prototype,"m_influence",{get:W.prototype.yD,set:W.prototype.pG});W.prototype.get_m_c0=W.prototype.NC=function(){return k(Qz(this.kB),uE)};W.prototype.set_m_c0=W.prototype.EF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Rz(c,a)};Object.defineProperty(W.prototype,"m_c0",{get:W.prototype.NC,set:W.prototype.EF});
|
||||||
|
W.prototype.get_m_c1=W.prototype.OC=function(){return k(Sz(this.kB),m)};W.prototype.set_m_c1=W.prototype.FF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Tz(c,a)};Object.defineProperty(W.prototype,"m_c1",{get:W.prototype.OC,set:W.prototype.FF});W.prototype.get_m_c2=W.prototype.PC=function(){return Uz(this.kB)};W.prototype.set_m_c2=W.prototype.GF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);Vz(c,a)};Object.defineProperty(W.prototype,"m_c2",{get:W.prototype.PC,set:W.prototype.GF});
|
||||||
|
W.prototype.__destroy__=function(){Wz(this.kB)};function EF(){throw"cannot construct a tAnchorArray, no constructor in IDL";}EF.prototype=Object.create(f.prototype);EF.prototype.constructor=EF;EF.prototype.lB=EF;EF.mB={};b.tAnchorArray=EF;EF.prototype.size=EF.prototype.size=function(){return Xz(this.kB)};EF.prototype.at=EF.prototype.at=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(Yz(c,a),W)};EF.prototype.clear=EF.prototype.clear=function(){Zz(this.kB)};
|
||||||
|
EF.prototype.push_back=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);$z(c,a)};EF.prototype.pop_back=function(){aA(this.kB)};EF.prototype.__destroy__=function(){bA(this.kB)};function X(){throw"cannot construct a Config, no constructor in IDL";}X.prototype=Object.create(f.prototype);X.prototype.constructor=X;X.prototype.lB=X;X.mB={};b.Config=X;X.prototype.get_kVCF=X.prototype.zC=function(){return cA(this.kB)};
|
||||||
|
X.prototype.set_kVCF=X.prototype.qF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);dA(c,a)};Object.defineProperty(X.prototype,"kVCF",{get:X.prototype.zC,set:X.prototype.qF});X.prototype.get_kDP=X.prototype.mC=function(){return eA(this.kB)};X.prototype.set_kDP=X.prototype.dF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);fA(c,a)};Object.defineProperty(X.prototype,"kDP",{get:X.prototype.mC,set:X.prototype.dF});X.prototype.get_kDG=X.prototype.lC=function(){return gA(this.kB)};
|
||||||
|
X.prototype.set_kDG=X.prototype.cF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);hA(c,a)};Object.defineProperty(X.prototype,"kDG",{get:X.prototype.lC,set:X.prototype.cF});X.prototype.get_kLF=X.prototype.oC=function(){return iA(this.kB)};X.prototype.set_kLF=X.prototype.fF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);jA(c,a)};Object.defineProperty(X.prototype,"kLF",{get:X.prototype.oC,set:X.prototype.fF});X.prototype.get_kPR=X.prototype.qC=function(){return kA(this.kB)};
|
||||||
|
X.prototype.set_kPR=X.prototype.hF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);lA(c,a)};Object.defineProperty(X.prototype,"kPR",{get:X.prototype.qC,set:X.prototype.hF});X.prototype.get_kVC=X.prototype.yC=function(){return mA(this.kB)};X.prototype.set_kVC=X.prototype.pF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);nA(c,a)};Object.defineProperty(X.prototype,"kVC",{get:X.prototype.yC,set:X.prototype.pF});X.prototype.get_kDF=X.prototype.kC=function(){return oA(this.kB)};
|
||||||
|
X.prototype.set_kDF=X.prototype.bF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);pA(c,a)};Object.defineProperty(X.prototype,"kDF",{get:X.prototype.kC,set:X.prototype.bF});X.prototype.get_kMT=X.prototype.pC=function(){return qA(this.kB)};X.prototype.set_kMT=X.prototype.gF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);rA(c,a)};Object.defineProperty(X.prototype,"kMT",{get:X.prototype.pC,set:X.prototype.gF});X.prototype.get_kCHR=X.prototype.jC=function(){return sA(this.kB)};
|
||||||
|
X.prototype.set_kCHR=X.prototype.aF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);tA(c,a)};Object.defineProperty(X.prototype,"kCHR",{get:X.prototype.jC,set:X.prototype.aF});X.prototype.get_kKHR=X.prototype.nC=function(){return uA(this.kB)};X.prototype.set_kKHR=X.prototype.eF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);vA(c,a)};Object.defineProperty(X.prototype,"kKHR",{get:X.prototype.nC,set:X.prototype.eF});X.prototype.get_kSHR=X.prototype.rC=function(){return wA(this.kB)};
|
||||||
|
X.prototype.set_kSHR=X.prototype.iF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);xA(c,a)};Object.defineProperty(X.prototype,"kSHR",{get:X.prototype.rC,set:X.prototype.iF});X.prototype.get_kAHR=X.prototype.iC=function(){return yA(this.kB)};X.prototype.set_kAHR=X.prototype.$E=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);zA(c,a)};Object.defineProperty(X.prototype,"kAHR",{get:X.prototype.iC,set:X.prototype.$E});X.prototype.get_kSRHR_CL=X.prototype.uC=function(){return AA(this.kB)};
|
||||||
|
X.prototype.set_kSRHR_CL=X.prototype.lF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);BA(c,a)};Object.defineProperty(X.prototype,"kSRHR_CL",{get:X.prototype.uC,set:X.prototype.lF});X.prototype.get_kSKHR_CL=X.prototype.sC=function(){return CA(this.kB)};X.prototype.set_kSKHR_CL=X.prototype.jF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);DA(c,a)};Object.defineProperty(X.prototype,"kSKHR_CL",{get:X.prototype.sC,set:X.prototype.jF});X.prototype.get_kSSHR_CL=X.prototype.wC=function(){return EA(this.kB)};
|
||||||
|
X.prototype.set_kSSHR_CL=X.prototype.nF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);FA(c,a)};Object.defineProperty(X.prototype,"kSSHR_CL",{get:X.prototype.wC,set:X.prototype.nF});X.prototype.get_kSR_SPLT_CL=X.prototype.vC=function(){return GA(this.kB)};X.prototype.set_kSR_SPLT_CL=X.prototype.mF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);HA(c,a)};Object.defineProperty(X.prototype,"kSR_SPLT_CL",{get:X.prototype.vC,set:X.prototype.mF});
|
||||||
|
X.prototype.get_kSK_SPLT_CL=X.prototype.tC=function(){return IA(this.kB)};X.prototype.set_kSK_SPLT_CL=X.prototype.kF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);JA(c,a)};Object.defineProperty(X.prototype,"kSK_SPLT_CL",{get:X.prototype.tC,set:X.prototype.kF});X.prototype.get_kSS_SPLT_CL=X.prototype.xC=function(){return KA(this.kB)};X.prototype.set_kSS_SPLT_CL=X.prototype.oF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);LA(c,a)};
|
||||||
|
Object.defineProperty(X.prototype,"kSS_SPLT_CL",{get:X.prototype.xC,set:X.prototype.oF});X.prototype.get_maxvolume=X.prototype.JE=function(){return MA(this.kB)};X.prototype.set_maxvolume=X.prototype.BH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);NA(c,a)};Object.defineProperty(X.prototype,"maxvolume",{get:X.prototype.JE,set:X.prototype.BH});X.prototype.get_timescale=X.prototype.OE=function(){return OA(this.kB)};
|
||||||
|
X.prototype.set_timescale=X.prototype.GH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);PA(c,a)};Object.defineProperty(X.prototype,"timescale",{get:X.prototype.OE,set:X.prototype.GH});X.prototype.get_viterations=X.prototype.QE=function(){return QA(this.kB)};X.prototype.set_viterations=X.prototype.IH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);RA(c,a)};Object.defineProperty(X.prototype,"viterations",{get:X.prototype.QE,set:X.prototype.IH});
|
||||||
|
X.prototype.get_piterations=X.prototype.ME=function(){return SA(this.kB)};X.prototype.set_piterations=X.prototype.EH=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);TA(c,a)};Object.defineProperty(X.prototype,"piterations",{get:X.prototype.ME,set:X.prototype.EH});X.prototype.get_diterations=X.prototype.fC=function(){return UA(this.kB)};X.prototype.set_diterations=X.prototype.XE=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);VA(c,a)};
|
||||||
|
Object.defineProperty(X.prototype,"diterations",{get:X.prototype.fC,set:X.prototype.XE});X.prototype.get_citerations=X.prototype.dC=function(){return WA(this.kB)};X.prototype.set_citerations=X.prototype.VE=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);XA(c,a)};Object.defineProperty(X.prototype,"citerations",{get:X.prototype.dC,set:X.prototype.VE});X.prototype.get_collisions=X.prototype.eC=function(){return YA(this.kB)};
|
||||||
|
X.prototype.set_collisions=X.prototype.WE=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);ZA(c,a)};Object.defineProperty(X.prototype,"collisions",{get:X.prototype.eC,set:X.prototype.WE});X.prototype.__destroy__=function(){$A(this.kB)};function Y(a,c,d,e){ND();a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);"object"==typeof e&&(e=RD(e));this.kB=aB(a,c,d,e);h(Y)[this.kB]=this}Y.prototype=Object.create(q.prototype);
|
||||||
|
Y.prototype.constructor=Y;Y.prototype.lB=Y;Y.mB={};b.btSoftBody=Y;Y.prototype.checkLink=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);return!!bB(d,a,c)};Y.prototype.checkFace=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);return!!cB(e,a,c,d)};Y.prototype.appendMaterial=function(){return k(dB(this.kB),V)};
|
||||||
|
Y.prototype.appendNode=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);eB(d,a,c)};Y.prototype.appendLink=function(a,c,d,e){var g=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);fB(g,a,c,d,e)};
|
||||||
|
Y.prototype.appendFace=function(a,c,d,e){var g=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);gB(g,a,c,d,e)};Y.prototype.appendTetra=function(a,c,d,e,g){var n=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);hB(n,a,c,d,e,g)};
|
||||||
|
Y.prototype.appendAnchor=function(a,c,d,e){var g=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);iB(g,a,c,d,e)};Y.prototype.addForce=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);void 0===c?jB(d,a):kB(d,a,c)};Y.prototype.addAeroForceToNode=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);lB(d,a,c)};
|
||||||
|
Y.prototype.getTotalMass=function(){return mB(this.kB)};Y.prototype.setTotalMass=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);nB(d,a,c)};Y.prototype.setMass=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);oB(d,a,c)};Y.prototype.transform=Y.prototype.transform=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);pB(c,a)};
|
||||||
|
Y.prototype.translate=Y.prototype.translate=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);qB(c,a)};Y.prototype.rotate=Y.prototype.rotate=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);rB(c,a)};Y.prototype.scale=Y.prototype.scale=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);sB(c,a)};Y.prototype.generateClusters=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);return void 0===c?tB(d,a):uB(d,a,c)};
|
||||||
|
Y.prototype.generateBendingConstraints=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);return vB(d,a,c)};Y.prototype.upcast=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(wB(c,a),Y)};Y.prototype.getRestLengthScale=function(){return xB(this.kB)};Y.prototype.setRestLengthScale=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);yB(c,a)};
|
||||||
|
Y.prototype.setAnisotropicFriction=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);zB(d,a,c)};Y.prototype.getCollisionShape=function(){return k(AB(this.kB),l)};Y.prototype.setContactProcessingThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);BB(c,a)};Y.prototype.setActivationState=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);CB(c,a)};
|
||||||
|
Y.prototype.forceActivationState=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);DB(c,a)};Y.prototype.activate=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);void 0===a?EB(c):FB(c,a)};Y.prototype.isActive=function(){return!!GB(this.kB)};Y.prototype.isKinematicObject=function(){return!!HB(this.kB)};Y.prototype.isStaticObject=function(){return!!IB(this.kB)};Y.prototype.isStaticOrKinematicObject=function(){return!!JB(this.kB)};Y.prototype.getRestitution=function(){return KB(this.kB)};
|
||||||
|
Y.prototype.getFriction=function(){return LB(this.kB)};Y.prototype.getRollingFriction=function(){return MB(this.kB)};Y.prototype.setRestitution=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);NB(c,a)};Y.prototype.setFriction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);OB(c,a)};Y.prototype.setRollingFriction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);PB(c,a)};Y.prototype.getWorldTransform=function(){return k(QB(this.kB),r)};Y.prototype.getCollisionFlags=function(){return RB(this.kB)};
|
||||||
|
Y.prototype.setCollisionFlags=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);SB(c,a)};Y.prototype.setWorldTransform=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);TB(c,a)};Y.prototype.setCollisionShape=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);UB(c,a)};Y.prototype.setCcdMotionThreshold=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);VB(c,a)};Y.prototype.setCcdSweptSphereRadius=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);WB(c,a)};
|
||||||
|
Y.prototype.getUserIndex=function(){return XB(this.kB)};Y.prototype.setUserIndex=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);YB(c,a)};Y.prototype.getUserPointer=function(){return k(ZB(this.kB),XD)};Y.prototype.setUserPointer=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);$B(c,a)};Y.prototype.getBroadphaseHandle=function(){return k(aC(this.kB),YD)};Y.prototype.get_m_cfg=Y.prototype.QC=function(){return k(bC(this.kB),X)};
|
||||||
|
Y.prototype.set_m_cfg=Y.prototype.HF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);cC(c,a)};Object.defineProperty(Y.prototype,"m_cfg",{get:Y.prototype.QC,set:Y.prototype.HF});Y.prototype.get_m_nodes=Y.prototype.QD=function(){return k(dC(this.kB),CF)};Y.prototype.set_m_nodes=Y.prototype.HG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);eC(c,a)};Object.defineProperty(Y.prototype,"m_nodes",{get:Y.prototype.QD,set:Y.prototype.HG});
|
||||||
|
Y.prototype.get_m_faces=Y.prototype.KB=function(){return k(fC(this.kB),BF)};Y.prototype.set_m_faces=Y.prototype.UB=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);gC(c,a)};Object.defineProperty(Y.prototype,"m_faces",{get:Y.prototype.KB,set:Y.prototype.UB});Y.prototype.get_m_materials=Y.prototype.MD=function(){return k(hC(this.kB),DF)};Y.prototype.set_m_materials=Y.prototype.DG=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);iC(c,a)};
|
||||||
|
Object.defineProperty(Y.prototype,"m_materials",{get:Y.prototype.MD,set:Y.prototype.DG});Y.prototype.get_m_anchors=Y.prototype.GC=function(){return k(jC(this.kB),EF)};Y.prototype.set_m_anchors=Y.prototype.xF=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);kC(c,a)};Object.defineProperty(Y.prototype,"m_anchors",{get:Y.prototype.GC,set:Y.prototype.xF});Y.prototype.__destroy__=function(){lC(this.kB)};
|
||||||
|
function FF(a){a&&"object"===typeof a&&(a=a.kB);this.kB=void 0===a?mC():nC(a);h(FF)[this.kB]=this}FF.prototype=Object.create(nE.prototype);FF.prototype.constructor=FF;FF.prototype.lB=FF;FF.mB={};b.btSoftBodyRigidBodyCollisionConfiguration=FF;FF.prototype.__destroy__=function(){oC(this.kB)};function GF(){this.kB=pC();h(GF)[this.kB]=this}GF.prototype=Object.create(sE.prototype);GF.prototype.constructor=GF;GF.prototype.lB=GF;GF.mB={};b.btDefaultSoftBodySolver=GF;GF.prototype.__destroy__=function(){qC(this.kB)};
|
||||||
|
function HF(){throw"cannot construct a btSoftBodyArray, no constructor in IDL";}HF.prototype=Object.create(f.prototype);HF.prototype.constructor=HF;HF.prototype.lB=HF;HF.mB={};b.btSoftBodyArray=HF;HF.prototype.size=HF.prototype.size=function(){return rC(this.kB)};HF.prototype.at=HF.prototype.at=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);return k(sC(c,a),Y)};HF.prototype.__destroy__=function(){tC(this.kB)};
|
||||||
|
function Z(a,c,d,e,g){a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);this.kB=uC(a,c,d,e,g);h(Z)[this.kB]=this}Z.prototype=Object.create(x.prototype);Z.prototype.constructor=Z;Z.prototype.lB=Z;Z.mB={};b.btSoftRigidDynamicsWorld=Z;
|
||||||
|
Z.prototype.addSoftBody=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);vC(e,a,c,d)};Z.prototype.removeSoftBody=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);wC(c,a)};Z.prototype.removeCollisionObject=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);xC(c,a)};Z.prototype.getWorldInfo=function(){return k(yC(this.kB),S)};Z.prototype.getSoftBodyArray=function(){return k(zC(this.kB),HF)};
|
||||||
|
Z.prototype.getDispatcher=function(){return k(AC(this.kB),TD)};Z.prototype.rayTest=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);BC(e,a,c,d)};Z.prototype.getPairCache=function(){return k(CC(this.kB),UD)};Z.prototype.getDispatchInfo=function(){return k(DC(this.kB),p)};
|
||||||
|
Z.prototype.addCollisionObject=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);void 0===c?EC(e,a):void 0===d?FC(e,a,c):GC(e,a,c,d)};Z.prototype.getBroadphase=function(){return k(HC(this.kB),VD)};
|
||||||
|
Z.prototype.convexSweepTest=function(a,c,d,e,g){var n=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);IC(n,a,c,d,e,g)};Z.prototype.contactPairTest=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);JC(e,a,c,d)};
|
||||||
|
Z.prototype.contactTest=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);KC(d,a,c)};Z.prototype.updateSingleAabb=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);LC(c,a)};Z.prototype.setDebugDrawer=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);MC(c,a)};Z.prototype.getDebugDrawer=function(){return k(NC(this.kB),WD)};Z.prototype.debugDrawWorld=function(){OC(this.kB)};
|
||||||
|
Z.prototype.debugDrawObject=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);PC(e,a,c,d)};Z.prototype.setGravity=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);QC(c,a)};Z.prototype.getGravity=function(){return k(RC(this.kB),m)};
|
||||||
|
Z.prototype.addRigidBody=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);void 0===c?SC(e,a):void 0===d?_emscripten_bind_btSoftRigidDynamicsWorld_addRigidBody_2(e,a,c):TC(e,a,c,d)};Z.prototype.removeRigidBody=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);UC(c,a)};
|
||||||
|
Z.prototype.addConstraint=function(a,c){var d=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);void 0===c?VC(d,a):WC(d,a,c)};Z.prototype.removeConstraint=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);XC(c,a)};Z.prototype.stepSimulation=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);return void 0===c?YC(e,a):void 0===d?ZC(e,a,c):$C(e,a,c,d)};
|
||||||
|
Z.prototype.setContactAddedCallback=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);aD(c,a)};Z.prototype.setContactProcessedCallback=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);bD(c,a)};Z.prototype.setContactDestroyedCallback=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);cD(c,a)};Z.prototype.addAction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);dD(c,a)};Z.prototype.removeAction=function(a){var c=this.kB;a&&"object"===typeof a&&(a=a.kB);eD(c,a)};
|
||||||
|
Z.prototype.getSolverInfo=function(){return k(fD(this.kB),t)};Z.prototype.setInternalTickCallback=function(a,c,d){var e=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);void 0===c?gD(e,a):void 0===d?hD(e,a,c):iD(e,a,c,d)};Z.prototype.__destroy__=function(){jD(this.kB)};function IF(){this.kB=kD();h(IF)[this.kB]=this}IF.prototype=Object.create(f.prototype);IF.prototype.constructor=IF;IF.prototype.lB=IF;IF.mB={};b.btSoftBodyHelpers=IF;
|
||||||
|
IF.prototype.CreateRope=function(a,c,d,e,g){var n=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);return k(lD(n,a,c,d,e,g),Y)};
|
||||||
|
IF.prototype.CreatePatch=function(a,c,d,e,g,n,z,T,Da){var dc=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);n&&"object"===typeof n&&(n=n.kB);z&&"object"===typeof z&&(z=z.kB);T&&"object"===typeof T&&(T=T.kB);Da&&"object"===typeof Da&&(Da=Da.kB);return k(mD(dc,a,c,d,e,g,n,z,T,Da),Y)};
|
||||||
|
IF.prototype.CreatePatchUV=function(a,c,d,e,g,n,z,T,Da,dc){var JF=this.kB;ND();a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);n&&"object"===typeof n&&(n=n.kB);z&&"object"===typeof z&&(z=z.kB);T&&"object"===typeof T&&(T=T.kB);Da&&"object"===typeof Da&&(Da=Da.kB);"object"==typeof dc&&(dc=RD(dc));return k(nD(JF,a,c,d,e,g,n,z,T,Da,dc),Y)};
|
||||||
|
IF.prototype.CreateEllipsoid=function(a,c,d,e){var g=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);return k(oD(g,a,c,d,e),Y)};
|
||||||
|
IF.prototype.CreateFromTriMesh=function(a,c,d,e,g){var n=this.kB;ND();a&&"object"===typeof a&&(a=a.kB);"object"==typeof c&&(c=RD(c));if("object"==typeof d&&"object"===typeof d){var z=OD(d,va);PD(d,va,z);d=z}e&&"object"===typeof e&&(e=e.kB);g&&"object"===typeof g&&(g=g.kB);return k(pD(n,a,c,d,e,g),Y)};
|
||||||
|
IF.prototype.CreateFromConvexHull=function(a,c,d,e){var g=this.kB;a&&"object"===typeof a&&(a=a.kB);c&&"object"===typeof c&&(c=c.kB);d&&"object"===typeof d&&(d=d.kB);e&&"object"===typeof e&&(e=e.kB);return k(qD(g,a,c,d,e),Y)};IF.prototype.__destroy__=function(){rD(this.kB)};
|
||||||
|
(function(){function a(){b.PHY_FLOAT=sD();b.PHY_DOUBLE=tD();b.PHY_INTEGER=uD();b.PHY_SHORT=vD();b.PHY_FIXEDPOINT88=wD();b.PHY_UCHAR=xD();b.CONST_GIMPACT_COMPOUND_SHAPE=yD();b.CONST_GIMPACT_TRIMESH_SHAPE_PART=zD();b.CONST_GIMPACT_TRIMESH_SHAPE=AD();b.BT_CONSTRAINT_ERP=BD();b.BT_CONSTRAINT_STOP_ERP=CD();b.BT_CONSTRAINT_CFM=DD();b.BT_CONSTRAINT_STOP_CFM=ED()}Ca?a():Aa.unshift(a)})();b.CONTACT_ADDED_CALLBACK_SIGNATURE="iiiiiiii";b.CONTACT_DESTROYED_CALLBACK_SIGNATURE="ii";
|
||||||
|
b.CONTACT_PROCESSED_CALLBACK_SIGNATURE="iiii";b.INTERNAL_TICK_CALLBACK_SIGNATURE="vif";this.Ammo=b;
|
||||||
|
|
||||||
|
|
||||||
|
return Ammo.ready
|
||||||
|
}
|
||||||
|
|
||||||
|
);
|
||||||
|
})();
|
||||||
|
if (typeof exports === 'object' && typeof module === 'object')
|
||||||
|
module.exports = Ammo;
|
||||||
|
else if (typeof define === 'function' && define['amd'])
|
||||||
|
define([], function() { return Ammo; });
|
||||||
|
else if (typeof exports === 'object')
|
||||||
|
exports["Ammo"] = Ammo;
|
||||||
Binary file not shown.
@@ -0,0 +1,16 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Синхронизированная запись</title>
|
||||||
|
<style>
|
||||||
|
html, body, #viewer { width: 100%; height: 100%; margin: 0; overflow: hidden; }
|
||||||
|
canvas { display: block; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="viewer"></div>
|
||||||
|
<script type="module" src="/src/components/rerun/isolatedRerunEntry.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import {SceneDisplayControls} from "../../../packages/spatial-ui/src/SceneDisplayControls";
|
||||||
|
import {SceneLayerControls} from "../../../packages/spatial-ui/src/SceneLayerControls";
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
AdminNavigationPanel,
|
AdminNavigationPanel,
|
||||||
@@ -5,16 +7,11 @@ import {
|
|||||||
ApplicationPanel,
|
ApplicationPanel,
|
||||||
ApplicationShell,
|
ApplicationShell,
|
||||||
Button,
|
Button,
|
||||||
Checker,
|
|
||||||
ColorField,
|
|
||||||
ControlRow,
|
ControlRow,
|
||||||
HeaderNavigation,
|
HeaderNavigation,
|
||||||
HeaderProfile,
|
HeaderProfile,
|
||||||
HeaderWorkspace,
|
|
||||||
Icon,
|
Icon,
|
||||||
Inspector,
|
Inspector,
|
||||||
RangeControl,
|
|
||||||
Select,
|
|
||||||
StatusBadge,
|
StatusBadge,
|
||||||
TextField,
|
TextField,
|
||||||
UserProfileMenu,
|
UserProfileMenu,
|
||||||
@@ -67,8 +64,6 @@ import {
|
|||||||
import { backendLabel, localConnectionPhaseLabel, phaseTone } from "./presentation";
|
import { backendLabel, localConnectionPhaseLabel, phaseTone } from "./presentation";
|
||||||
import {
|
import {
|
||||||
defaultSceneSettings,
|
defaultSceneSettings,
|
||||||
type PointColorMode,
|
|
||||||
type PointPalette,
|
|
||||||
type SceneSettings,
|
type SceneSettings,
|
||||||
} from "./sceneSettings";
|
} from "./sceneSettings";
|
||||||
import { DeviceWorkspace } from "./workspaces/DeviceWorkspace";
|
import { DeviceWorkspace } from "./workspaces/DeviceWorkspace";
|
||||||
@@ -78,22 +73,6 @@ import "./styles/scene-windows.css";
|
|||||||
type SceneToolWindowId = "sources" | "display" | "layers";
|
type SceneToolWindowId = "sources" | "display" | "layers";
|
||||||
const viewerSettingsQuietPeriodMs = 750;
|
const viewerSettingsQuietPeriodMs = 750;
|
||||||
|
|
||||||
const colorModeOptions: Array<{ value: PointColorMode; label: string; description: string }> = [
|
|
||||||
{ value: "intensity", label: "Интенсивность", description: "Значение отражённого сигнала" },
|
|
||||||
{ value: "height", label: "Высота Z", description: "Градиент по вертикальной координате" },
|
|
||||||
{ value: "distance", label: "Расстояние", description: "Дальность точки от сенсора" },
|
|
||||||
{ value: "rgb", label: "RGB", description: "Цвет, если он присутствует в данных" },
|
|
||||||
{ value: "class", label: "Класс", description: "Категория внешнего модуля восприятия" },
|
|
||||||
];
|
|
||||||
|
|
||||||
const paletteOptions: Array<{ value: PointPalette; label: string; description: string }> = [
|
|
||||||
{ value: "turbo", label: "Turbo", description: "Контрастный спектральный градиент" },
|
|
||||||
{ value: "viridis", label: "Viridis", description: "Равномерный перцептивный градиент" },
|
|
||||||
{ value: "plasma", label: "Plasma", description: "Тёплый контрастный градиент" },
|
|
||||||
{ value: "grayscale", label: "Серый", description: "Монохромное отображение" },
|
|
||||||
{ value: "custom", label: "Свой цвет", description: "Один назначенный цвет" },
|
|
||||||
];
|
|
||||||
|
|
||||||
interface LivePerceptionLayers {
|
interface LivePerceptionLayers {
|
||||||
detections2d: boolean;
|
detections2d: boolean;
|
||||||
segmentation: boolean;
|
segmentation: boolean;
|
||||||
@@ -629,7 +608,10 @@ export default function App() {
|
|||||||
return () => window.clearTimeout(timer);
|
return () => window.clearTimeout(timer);
|
||||||
}, [layoutSaveNotice]);
|
}, [layoutSaveNotice]);
|
||||||
|
|
||||||
|
const [fleetCreateRequest, setFleetCreateRequest] = useState(0);
|
||||||
|
const onAddVehicle = useCallback(() => setFleetCreateRequest(value => value + 1), []);
|
||||||
const contentActions = useApplicationPanelActions({
|
const contentActions = useApplicationPanelActions({
|
||||||
|
onAddVehicle,
|
||||||
definition: activeDefinition,
|
definition: activeDefinition,
|
||||||
refreshRuntime: runtime.refresh,
|
refreshRuntime: runtime.refresh,
|
||||||
resetConnectionScenario: runtime.resetConnectionScenario,
|
resetConnectionScenario: runtime.resetConnectionScenario,
|
||||||
@@ -645,8 +627,6 @@ export default function App() {
|
|||||||
brandHref="/"
|
brandHref="/"
|
||||||
brandLabel="NODEDC MISSION CORE"
|
brandLabel="NODEDC MISSION CORE"
|
||||||
center={
|
center={
|
||||||
<>
|
|
||||||
<HeaderWorkspace kind="mark" label="Mission Core" imageUrl="/nodedc-mark.svg" />
|
|
||||||
<HeaderNavigation
|
<HeaderNavigation
|
||||||
label="Архитектурные блоки пункта управления"
|
label="Архитектурные блоки пункта управления"
|
||||||
value={activeRoot ?? undefined}
|
value={activeRoot ?? undefined}
|
||||||
@@ -656,7 +636,6 @@ export default function App() {
|
|||||||
}))}
|
}))}
|
||||||
onChange={selectRoot}
|
onChange={selectRoot}
|
||||||
/>
|
/>
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
right={
|
right={
|
||||||
<HeaderProfile>
|
<HeaderProfile>
|
||||||
@@ -802,10 +781,12 @@ export default function App() {
|
|||||||
settleRecordedReplaySwitch(outcome)}
|
settleRecordedReplaySwitch(outcome)}
|
||||||
onDeleteBegin={releaseRecordedReplayForDelete}
|
onDeleteBegin={releaseRecordedReplayForDelete}
|
||||||
/>
|
/>
|
||||||
) : activeDefinition.kind === "datasets" ? (
|
) : activeDefinition.kind === "vehicles" ? null : activeDefinition.kind === "datasets" ? (
|
||||||
<StatusBadge tone="neutral">Offline evaluation</StatusBadge>
|
<StatusBadge tone="neutral">Offline evaluation</StatusBadge>
|
||||||
) : activeDefinition.kind === "lab-archive" ? (
|
) : activeDefinition.kind === "lab-archive" ? (
|
||||||
laboratoryAnnotation.control
|
laboratoryAnnotation.control
|
||||||
|
) : activeDefinition.kind === "observatory" ? (
|
||||||
|
<StatusBadge tone="neutral">Только наблюдение</StatusBadge>
|
||||||
) : activeDefinition.root === "system" ? (
|
) : activeDefinition.root === "system" ? (
|
||||||
<SystemWorkspaceSelector
|
<SystemWorkspaceSelector
|
||||||
value={activeDefinition.id}
|
value={activeDefinition.id}
|
||||||
@@ -826,6 +807,7 @@ export default function App() {
|
|||||||
) : (
|
) : (
|
||||||
<WorkspaceRenderer
|
<WorkspaceRenderer
|
||||||
definition={activeDefinition}
|
definition={activeDefinition}
|
||||||
|
fleetCreateRequest={fleetCreateRequest}
|
||||||
state={activeRuntimeState}
|
state={activeRuntimeState}
|
||||||
backendStatus={runtime.backendStatus}
|
backendStatus={runtime.backendStatus}
|
||||||
sourceUrl={effectiveSourceUrl}
|
sourceUrl={effectiveSourceUrl}
|
||||||
@@ -1027,132 +1009,8 @@ export default function App() {
|
|||||||
onPointerDown={() => activateSceneWindow("display")}
|
onPointerDown={() => activateSceneWindow("display")}
|
||||||
onClose={() => closeSceneWindow("display")}
|
onClose={() => closeSceneWindow("display")}
|
||||||
>
|
>
|
||||||
<Inspector
|
<SceneDisplayControls displayDraft={displayDraft} stageDisplayPatch={stageDisplayPatch}
|
||||||
defaultOpen={["points"]}
|
commitDisplayPatch={commitDisplayPatch} flushDisplaySettings={flushDisplaySettings} replayPresented={replayPresented}/>
|
||||||
singleOpen
|
|
||||||
sections={[
|
|
||||||
{
|
|
||||||
id: "points",
|
|
||||||
label: "Облако точек",
|
|
||||||
description: "Размер и способ окрашивания",
|
|
||||||
content: (
|
|
||||||
<div className="inspector-control-stack">
|
|
||||||
<div
|
|
||||||
className="scene-settings-commit-field"
|
|
||||||
onPointerUp={flushDisplaySettings}
|
|
||||||
onKeyUp={flushDisplaySettings}
|
|
||||||
onBlur={flushDisplaySettings}
|
|
||||||
>
|
|
||||||
<RangeControl
|
|
||||||
label="Размер точки"
|
|
||||||
value={displayDraft.pointSize}
|
|
||||||
min={0.5}
|
|
||||||
max={12}
|
|
||||||
step={0.5}
|
|
||||||
formatValue={(value) => `${value.toFixed(1)} пкс`}
|
|
||||||
onChange={(pointSize) => stageDisplayPatch({ pointSize })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="scene-settings-commit-field" onBlur={flushDisplaySettings}>
|
|
||||||
<ControlRow label="Атрибут цвета">
|
|
||||||
<Select
|
|
||||||
variant="split"
|
|
||||||
label="Атрибут цвета"
|
|
||||||
value={displayDraft.colorMode}
|
|
||||||
options={colorModeOptions}
|
|
||||||
onChange={(colorMode) => stageDisplayPatch({ colorMode })}
|
|
||||||
/>
|
|
||||||
</ControlRow>
|
|
||||||
</div>
|
|
||||||
<div className="scene-settings-commit-field" onBlur={flushDisplaySettings}>
|
|
||||||
<ControlRow label="Палитра">
|
|
||||||
<Select
|
|
||||||
variant="split"
|
|
||||||
label="Палитра"
|
|
||||||
value={displayDraft.palette}
|
|
||||||
options={paletteOptions}
|
|
||||||
onChange={(palette) => stageDisplayPatch({ palette })}
|
|
||||||
/>
|
|
||||||
</ControlRow>
|
|
||||||
</div>
|
|
||||||
{displayDraft.palette === "custom" || displayDraft.colorMode === "class" ? (
|
|
||||||
<div
|
|
||||||
className="scene-settings-commit-field"
|
|
||||||
onPointerUp={flushDisplaySettings}
|
|
||||||
onKeyUp={flushDisplaySettings}
|
|
||||||
onBlur={flushDisplaySettings}
|
|
||||||
>
|
|
||||||
<ControlRow label="Цвет точек">
|
|
||||||
<ColorField
|
|
||||||
label="Цвет точек"
|
|
||||||
value={displayDraft.customColor}
|
|
||||||
onChange={(customColor) => stageDisplayPatch({ customColor })}
|
|
||||||
/>
|
|
||||||
</ControlRow>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{replayPresented ? (
|
|
||||||
<p className="scene-window-note">
|
|
||||||
Первый новый режим читает индекс архивных точек. Следующие палитры
|
|
||||||
переключаются из подготовленного цветового кэша без переэкспорта геометрии.
|
|
||||||
</p>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "history",
|
|
||||||
label: "Накопление и время",
|
|
||||||
description: "История облака и траектория",
|
|
||||||
content: (
|
|
||||||
<div className="inspector-control-stack">
|
|
||||||
<div
|
|
||||||
className="scene-settings-commit-field"
|
|
||||||
onPointerUp={flushDisplaySettings}
|
|
||||||
onKeyUp={flushDisplaySettings}
|
|
||||||
onBlur={flushDisplaySettings}
|
|
||||||
>
|
|
||||||
<RangeControl
|
|
||||||
label="Окно накопления"
|
|
||||||
value={displayDraft.accumulationSeconds}
|
|
||||||
min={0}
|
|
||||||
max={120}
|
|
||||||
step={1}
|
|
||||||
formatValue={(value) => (value === 0 ? "Только кадр" : `${value} с`)}
|
|
||||||
onChange={(accumulationSeconds) => stageDisplayPatch({ accumulationSeconds })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="nodedc-field">
|
|
||||||
<span className="nodedc-field__description">Линия пути устройства в координатах сцены</span>
|
|
||||||
<Checker
|
|
||||||
checked={displayDraft.showTrajectory}
|
|
||||||
label="Показывать траекторию"
|
|
||||||
onChange={(showTrajectory) => commitDisplayPatch({ showTrajectory })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "scene",
|
|
||||||
label: "Окружение сцены",
|
|
||||||
description: "Сетка, подписи и камеры",
|
|
||||||
content: (
|
|
||||||
<div className="inspector-control-stack">
|
|
||||||
<Checker checked={displayDraft.showGrid} label="Сетка и оси" onChange={(showGrid) => commitDisplayPatch({ showGrid })} />
|
|
||||||
<div className="nodedc-field">
|
|
||||||
<span className="nodedc-field__description">Появятся после подключения семантических сущностей</span>
|
|
||||||
<Checker checked={false} disabled label="Подписи сущностей" onChange={() => undefined} />
|
|
||||||
</div>
|
|
||||||
<div className="nodedc-field">
|
|
||||||
<span className="nodedc-field__description">Камерный канал пока не подключён</span>
|
|
||||||
<Checker checked={false} disabled label="Области обзора камер" onChange={() => undefined} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Window>
|
</Window>
|
||||||
|
|
||||||
<Window
|
<Window
|
||||||
@@ -1178,64 +1036,7 @@ export default function App() {
|
|||||||
</span>
|
</span>
|
||||||
<p>Структура повторяет продуктовые сущности, а не внутренние панели визуального движка.</p>
|
<p>Структура повторяет продуктовые сущности, а не внутренние панели визуального движка.</p>
|
||||||
</div>
|
</div>
|
||||||
<Inspector
|
<SceneLayerControls sceneSettings={sceneSettings} pending={runtime.pendingAction === "viewer"} applyScenePatch={applyScenePatch}/>
|
||||||
defaultOpen={["geometry"]}
|
|
||||||
singleOpen
|
|
||||||
sections={[
|
|
||||||
{
|
|
||||||
id: "geometry",
|
|
||||||
label: "Геометрия",
|
|
||||||
description: "Облако точек и путь",
|
|
||||||
content: (
|
|
||||||
<div className="inspector-control-stack">
|
|
||||||
<div className="nodedc-field">
|
|
||||||
<span className="nodedc-field__description">Основной поток геометрии лидара</span>
|
|
||||||
<Checker disabled={runtime.pendingAction === "viewer"} checked={sceneSettings.showPoints} label="Облако точек" onChange={(showPoints) => void applyScenePatch({ showPoints })} />
|
|
||||||
</div>
|
|
||||||
<div className="nodedc-field">
|
|
||||||
<span className="nodedc-field__description">Положение и ориентация устройства во времени</span>
|
|
||||||
<Checker disabled={runtime.pendingAction === "viewer"} checked={sceneSettings.showTrajectory} label="Траектория" onChange={(showTrajectory) => void applyScenePatch({ showTrajectory })} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "frames",
|
|
||||||
label: "Координаты и камеры",
|
|
||||||
description: "Преобразования и области обзора",
|
|
||||||
content: (
|
|
||||||
<div className="inspector-control-stack">
|
|
||||||
<Checker disabled={runtime.pendingAction === "viewer"} checked={sceneSettings.showGrid} label="Сетка и оси" onChange={(showGrid) => void applyScenePatch({ showGrid })} />
|
|
||||||
<div className="nodedc-field">
|
|
||||||
<span className="nodedc-field__description">Камерный канал пока не подключён</span>
|
|
||||||
<Checker checked={false} disabled label="Области обзора камер" onChange={() => undefined} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "perception",
|
|
||||||
label: "Объекты и маски",
|
|
||||||
description: "Ожидают внешние обработчики",
|
|
||||||
content: (
|
|
||||||
<div className="inspector-control-stack">
|
|
||||||
<div className="nodedc-field">
|
|
||||||
<span className="nodedc-field__description">Внешний обработчик не подключён</span>
|
|
||||||
<Checker checked={false} disabled label="Рамки 2D и 3D" onChange={() => undefined} />
|
|
||||||
</div>
|
|
||||||
<div className="nodedc-field">
|
|
||||||
<span className="nodedc-field__description">Внешний обработчик не подключён</span>
|
|
||||||
<Checker checked={false} disabled label="Маски сегментации" onChange={() => undefined} />
|
|
||||||
</div>
|
|
||||||
<div className="nodedc-field">
|
|
||||||
<span className="nodedc-field__description">Внешний обработчик не подключён</span>
|
|
||||||
<Checker checked={false} disabled label="Ключевые точки и треки" onChange={() => undefined} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Window>
|
</Window>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,92 +1 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
export { EnvironmentBackgroundMedia } from "@nodedc/ui-react";
|
||||||
|
|
||||||
import type {
|
|
||||||
EnvironmentBackground,
|
|
||||||
EnvironmentMediaItem,
|
|
||||||
} from "../core/environment/environmentSettings";
|
|
||||||
|
|
||||||
type ReadyEnvironmentMediaItem = EnvironmentMediaItem & {
|
|
||||||
url: string;
|
|
||||||
mediaKind: "image" | "video";
|
|
||||||
};
|
|
||||||
|
|
||||||
function isReadyMediaItem(
|
|
||||||
item: EnvironmentMediaItem,
|
|
||||||
): item is ReadyEnvironmentMediaItem {
|
|
||||||
return Boolean(item.url && item.mediaKind);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function EnvironmentBackgroundMedia({
|
|
||||||
background,
|
|
||||||
}: {
|
|
||||||
background: EnvironmentBackground;
|
|
||||||
}) {
|
|
||||||
const items = useMemo(
|
|
||||||
() => background.items.filter(isReadyMediaItem),
|
|
||||||
[background.items],
|
|
||||||
);
|
|
||||||
const playlistIdentity = items.map((item) => `${item.id}:${item.url}`).join("|");
|
|
||||||
const [activeIndex, setActiveIndex] = useState(0);
|
|
||||||
const [failedIds, setFailedIds] = useState<Set<string>>(new Set());
|
|
||||||
const playableItems = items.filter((item) => !failedIds.has(item.id));
|
|
||||||
const activeItem = playableItems[activeIndex] ?? playableItems[0] ?? null;
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setActiveIndex(0);
|
|
||||||
setFailedIds(new Set());
|
|
||||||
}, [playlistIdentity]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
!background.enabled
|
|
||||||
|| !activeItem
|
|
||||||
|| activeItem.mediaKind !== "image"
|
|
||||||
|| playableItems.length < 2
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const timer = window.setTimeout(() => {
|
|
||||||
setActiveIndex((current) => (current + 1) % playableItems.length);
|
|
||||||
}, background.imageDurationSeconds * 1_000);
|
|
||||||
return () => window.clearTimeout(timer);
|
|
||||||
}, [
|
|
||||||
activeItem,
|
|
||||||
background.enabled,
|
|
||||||
background.imageDurationSeconds,
|
|
||||||
playableItems.length,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!background.enabled || !activeItem) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="landing-stage__media" aria-hidden="true">
|
|
||||||
{activeItem.mediaKind === "video" ? (
|
|
||||||
<video
|
|
||||||
key={activeItem.url}
|
|
||||||
src={activeItem.url}
|
|
||||||
autoPlay
|
|
||||||
muted
|
|
||||||
loop={playableItems.length === 1}
|
|
||||||
playsInline
|
|
||||||
onEnded={() => setActiveIndex((current) => (
|
|
||||||
(current + 1) % playableItems.length
|
|
||||||
))}
|
|
||||||
onError={() => {
|
|
||||||
setFailedIds((current) => new Set(current).add(activeItem.id));
|
|
||||||
setActiveIndex(0);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<img
|
|
||||||
key={activeItem.url}
|
|
||||||
src={activeItem.url}
|
|
||||||
alt=""
|
|
||||||
onError={() => {
|
|
||||||
setFailedIds((current) => new Set(current).add(activeItem.id));
|
|
||||||
setActiveIndex(0);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,248 +1 @@
|
|||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
export { EnvironmentMediaPlaylistEditor } from "@nodedc/ui-react";
|
||||||
import {
|
|
||||||
Icon,
|
|
||||||
IconButton,
|
|
||||||
MediaSourceField,
|
|
||||||
RangeControl,
|
|
||||||
SortableList,
|
|
||||||
} from "@nodedc/ui-react";
|
|
||||||
|
|
||||||
import {
|
|
||||||
appendEnvironmentMediaItem,
|
|
||||||
inferEnvironmentMediaKind,
|
|
||||||
maxEnvironmentMediaItems,
|
|
||||||
removeEnvironmentMediaItem,
|
|
||||||
type EnvironmentBackground,
|
|
||||||
type EnvironmentMediaItem,
|
|
||||||
type EnvironmentSurfaceId,
|
|
||||||
type UploadedEnvironmentMedia,
|
|
||||||
} from "../core/environment/environmentSettings";
|
|
||||||
|
|
||||||
interface EnvironmentMediaPlaylistEditorProps {
|
|
||||||
surfaceId: EnvironmentSurfaceId;
|
|
||||||
background: EnvironmentBackground;
|
|
||||||
disabled: boolean;
|
|
||||||
error: string | null;
|
|
||||||
onChange: (background: EnvironmentBackground) => void;
|
|
||||||
onBusyChange: (busy: boolean) => void;
|
|
||||||
onUpload: (
|
|
||||||
surfaceId: EnvironmentSurfaceId,
|
|
||||||
itemId: string,
|
|
||||||
file: File,
|
|
||||||
) => Promise<UploadedEnvironmentMedia>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const acceptedEnvironmentMedia = [
|
|
||||||
"image/png",
|
|
||||||
"image/jpeg",
|
|
||||||
"image/gif",
|
|
||||||
"image/webp",
|
|
||||||
"image/avif",
|
|
||||||
"video/mp4",
|
|
||||||
"video/webm",
|
|
||||||
"video/quicktime",
|
|
||||||
".png",
|
|
||||||
".jpg",
|
|
||||||
".jpeg",
|
|
||||||
".gif",
|
|
||||||
".webp",
|
|
||||||
".avif",
|
|
||||||
".mp4",
|
|
||||||
".webm",
|
|
||||||
".mov",
|
|
||||||
].join(",");
|
|
||||||
|
|
||||||
function patchItem(
|
|
||||||
background: EnvironmentBackground,
|
|
||||||
itemId: string,
|
|
||||||
patch: Partial<EnvironmentMediaItem>,
|
|
||||||
): EnvironmentBackground {
|
|
||||||
return {
|
|
||||||
...background,
|
|
||||||
items: background.items.map((item) => (
|
|
||||||
item.id === itemId ? { ...item, ...patch } : item
|
|
||||||
)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function EnvironmentMediaPlaylistEditor({
|
|
||||||
surfaceId,
|
|
||||||
background,
|
|
||||||
disabled,
|
|
||||||
error,
|
|
||||||
onChange,
|
|
||||||
onBusyChange,
|
|
||||||
onUpload,
|
|
||||||
}: EnvironmentMediaPlaylistEditorProps) {
|
|
||||||
const [uploadingIds, setUploadingIds] = useState<Set<string>>(new Set());
|
|
||||||
const [itemErrors, setItemErrors] = useState<Record<string, string>>({});
|
|
||||||
const backgroundRef = useRef(background);
|
|
||||||
backgroundRef.current = background;
|
|
||||||
const displayedItems = useMemo(
|
|
||||||
() => [...background.items].reverse(),
|
|
||||||
[background.items],
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
onBusyChange(uploadingIds.size > 0);
|
|
||||||
}, [onBusyChange, uploadingIds.size]);
|
|
||||||
|
|
||||||
useEffect(() => () => onBusyChange(false), [onBusyChange]);
|
|
||||||
|
|
||||||
const setItemError = (itemId: string, message?: string) => {
|
|
||||||
setItemErrors((current) => {
|
|
||||||
const next = { ...current };
|
|
||||||
if (message) next[itemId] = message;
|
|
||||||
else delete next[itemId];
|
|
||||||
return next;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const uploadFile = async (itemId: string, file?: File) => {
|
|
||||||
if (!file) return;
|
|
||||||
setUploadingIds((current) => new Set(current).add(itemId));
|
|
||||||
setItemError(itemId);
|
|
||||||
try {
|
|
||||||
const uploaded = await onUpload(surfaceId, itemId, file);
|
|
||||||
onChange(patchItem(backgroundRef.current, itemId, {
|
|
||||||
source: "file",
|
|
||||||
url: uploaded.url,
|
|
||||||
mediaKind: uploaded.mediaKind,
|
|
||||||
fileName: uploaded.fileName,
|
|
||||||
}));
|
|
||||||
} catch (reason) {
|
|
||||||
setItemError(
|
|
||||||
itemId,
|
|
||||||
reason instanceof Error
|
|
||||||
? reason.message
|
|
||||||
: "Не удалось загрузить медиаконтент.",
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
setUploadingIds((current) => {
|
|
||||||
const next = new Set(current);
|
|
||||||
next.delete(itemId);
|
|
||||||
return next;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="environment-media-playlist">
|
|
||||||
<div className="environment-media-playlist__head">
|
|
||||||
<div>
|
|
||||||
<span>Видео / картинка</span>
|
|
||||||
<p>MP4, WebM, MOV, PNG, JPEG, GIF, WebP или AVIF · до 256 МБ.</p>
|
|
||||||
</div>
|
|
||||||
<IconButton
|
|
||||||
label="Добавить медиаконтент"
|
|
||||||
disabled={disabled || background.items.length >= maxEnvironmentMediaItems}
|
|
||||||
onClick={() => onChange(appendEnvironmentMediaItem(background))}
|
|
||||||
>
|
|
||||||
<Icon name="plus" />
|
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{displayedItems.length ? (
|
|
||||||
<SortableList
|
|
||||||
items={displayedItems}
|
|
||||||
getId={(item) => item.id}
|
|
||||||
className="environment-media-playlist__items"
|
|
||||||
onReorder={(items) => onChange({
|
|
||||||
...background,
|
|
||||||
items: [...items].reverse(),
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{(item, { handle }) => {
|
|
||||||
const playbackIndex = background.items.findIndex(
|
|
||||||
(candidate) => candidate.id === item.id,
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<div className="environment-media-playlist__item">
|
|
||||||
<MediaSourceField
|
|
||||||
label={`Медиаконтент ${String(playbackIndex + 1).padStart(2, "0")}`}
|
|
||||||
kindLabel={item.mediaKind ?? "media"}
|
|
||||||
source={item.source}
|
|
||||||
url={item.url ?? ""}
|
|
||||||
fileName={item.fileName}
|
|
||||||
uploading={uploadingIds.has(item.id)}
|
|
||||||
previewSrc={item.url}
|
|
||||||
previewKind={item.mediaKind}
|
|
||||||
accept={acceptedEnvironmentMedia}
|
|
||||||
path={`${surfaceId}.background.items[${playbackIndex}] → server environment media`}
|
|
||||||
hint="Файл сохраняется в Mission Core data root. URL должен вести прямо на media по HTTP(S)."
|
|
||||||
error={itemErrors[item.id] ?? (
|
|
||||||
playbackIndex === background.items.length - 1 ? error : null
|
|
||||||
)}
|
|
||||||
onSourceChange={(source) => {
|
|
||||||
if (source === item.source) return;
|
|
||||||
setItemError(item.id);
|
|
||||||
onChange(patchItem(background, item.id, {
|
|
||||||
source,
|
|
||||||
url: null,
|
|
||||||
mediaKind: null,
|
|
||||||
fileName: null,
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
onUrlChange={(url) => {
|
|
||||||
setItemError(item.id);
|
|
||||||
onChange(patchItem(background, item.id, {
|
|
||||||
source: "url",
|
|
||||||
url: url || null,
|
|
||||||
mediaKind: url ? inferEnvironmentMediaKind(url) : null,
|
|
||||||
fileName: null,
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
onFileChange={(file) => void uploadFile(item.id, file)}
|
|
||||||
/>
|
|
||||||
<div className="environment-media-playlist__item-actions">
|
|
||||||
<IconButton
|
|
||||||
label={`Удалить медиаконтент ${playbackIndex + 1}`}
|
|
||||||
disabled={disabled || uploadingIds.has(item.id)}
|
|
||||||
onClick={() => {
|
|
||||||
setItemError(item.id);
|
|
||||||
onChange(removeEnvironmentMediaItem(background, item.id));
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon name="trash" />
|
|
||||||
</IconButton>
|
|
||||||
{handle}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</SortableList>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<p className="environment-media-playlist__empty">
|
|
||||||
Добавьте первый файл или прямую ссылку на медиаконтент.
|
|
||||||
</p>
|
|
||||||
{error ? (
|
|
||||||
<p className="environment-media-playlist__error" role="alert">
|
|
||||||
{error}
|
|
||||||
</p>
|
|
||||||
) : null}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="environment-media-playlist__timing">
|
|
||||||
<RangeControl
|
|
||||||
label="Показывать изображение"
|
|
||||||
value={background.imageDurationSeconds}
|
|
||||||
min={1}
|
|
||||||
max={60}
|
|
||||||
step={1}
|
|
||||||
disabled={disabled}
|
|
||||||
formatValue={(value) => `${value} с`}
|
|
||||||
onChange={(imageDurationSeconds) => onChange({
|
|
||||||
...background,
|
|
||||||
imageDurationSeconds,
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
Новые элементы появляются сверху. Воспроизведение начинается снизу;
|
|
||||||
перетаскивание меняет порядок.
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,358 +1,25 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { EnvironmentSettingsWindow as SharedEnvironmentSettingsWindow } from "@nodedc/ui-react";
|
||||||
import {
|
import { environmentSurfaceIds, type EnvironmentSettings, type EnvironmentSurfaceId, type UploadedEnvironmentMedia } from "../core/environment/environmentSettings";
|
||||||
Button,
|
import { roots, workspaces, workspacesForRoot } from "../productModel";
|
||||||
FeatureSettingsWindow,
|
|
||||||
Select,
|
|
||||||
SettingsCard,
|
|
||||||
Switch,
|
|
||||||
TextAreaField,
|
|
||||||
TextField,
|
|
||||||
WindowFooterActions,
|
|
||||||
} from "@nodedc/ui-react";
|
|
||||||
|
|
||||||
import {
|
interface Props {
|
||||||
cloneEnvironmentSettings,
|
|
||||||
type EnvironmentBackground,
|
|
||||||
type EnvironmentPage,
|
|
||||||
type EnvironmentSettings,
|
|
||||||
type EnvironmentSurfaceId,
|
|
||||||
type UploadedEnvironmentMedia,
|
|
||||||
} from "../core/environment/environmentSettings";
|
|
||||||
import { EnvironmentMediaPlaylistEditor } from "./EnvironmentMediaPlaylistEditor";
|
|
||||||
import {
|
|
||||||
roots,
|
|
||||||
workspaces,
|
|
||||||
workspacesForRoot,
|
|
||||||
} from "../productModel";
|
|
||||||
|
|
||||||
interface EnvironmentSettingsWindowProps {
|
|
||||||
open: boolean;
|
open: boolean;
|
||||||
settings: EnvironmentSettings;
|
settings: EnvironmentSettings;
|
||||||
state: "loading" | "ready" | "saving" | "error";
|
state: "loading" | "ready" | "saving" | "error";
|
||||||
error: string | null;
|
error: string | null;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onSave: (settings: EnvironmentSettings) => Promise<EnvironmentSettings>;
|
onSave: (settings: EnvironmentSettings) => Promise<EnvironmentSettings>;
|
||||||
onUpload: (
|
onUpload: (surfaceId: EnvironmentSurfaceId, itemId: string, file: File) => Promise<UploadedEnvironmentMedia>;
|
||||||
surfaceId: EnvironmentSurfaceId,
|
|
||||||
itemId: string,
|
|
||||||
file: File,
|
|
||||||
) => Promise<UploadedEnvironmentMedia>;
|
|
||||||
}
|
}
|
||||||
|
const surfaces = environmentSurfaceIds.map(id => ({
|
||||||
function patchPage(
|
id, home: id === "home", description: id === "home" ? "Главная страница продукта" : `Стартовая страница раздела «${roots.find(root => root.id === id)?.label}»`,
|
||||||
draft: EnvironmentSettings,
|
actions: id === "home" ? workspaces.filter(item => !item.internalOnly) : workspacesForRoot(id),
|
||||||
surfaceId: EnvironmentSurfaceId,
|
}));
|
||||||
patch: Partial<EnvironmentPage>,
|
export function EnvironmentSettingsWindow(props: Props) {
|
||||||
): EnvironmentSettings {
|
return <SharedEnvironmentSettingsWindow {...props} productName="Mission Core" surfaces={surfaces} initialSurfaceId="fleet"
|
||||||
return {
|
onSave={draft => props.onSave({ ...draft, pages: Object.fromEntries(environmentSurfaceIds.map(id => [id, draft.pages[id]])) as EnvironmentSettings["pages"] })}
|
||||||
...draft,
|
onUpload={(id, itemId, file) => {
|
||||||
pages: {
|
if (!environmentSurfaceIds.includes(id as EnvironmentSurfaceId)) throw new Error("Страница окружения недоступна.");
|
||||||
...draft.pages,
|
return props.onUpload(id as EnvironmentSurfaceId, itemId, file);
|
||||||
[surfaceId]: {
|
}} />;
|
||||||
...draft.pages[surfaceId],
|
|
||||||
...patch,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function patchBackground(
|
|
||||||
draft: EnvironmentSettings,
|
|
||||||
surfaceId: EnvironmentSurfaceId,
|
|
||||||
patch: Partial<EnvironmentBackground>,
|
|
||||||
): EnvironmentSettings {
|
|
||||||
const page = draft.pages[surfaceId];
|
|
||||||
return patchPage(draft, surfaceId, {
|
|
||||||
background: {
|
|
||||||
...page.background,
|
|
||||||
...patch,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function EnvironmentSettingsWindow({
|
|
||||||
open,
|
|
||||||
settings,
|
|
||||||
state,
|
|
||||||
error,
|
|
||||||
onClose,
|
|
||||||
onSave,
|
|
||||||
onUpload,
|
|
||||||
}: EnvironmentSettingsWindowProps) {
|
|
||||||
const [draft, setDraft] = useState(() => cloneEnvironmentSettings(settings));
|
|
||||||
const [surfaceId, setSurfaceId] = useState<EnvironmentSurfaceId>("fleet");
|
|
||||||
const [uploading, setUploading] = useState(false);
|
|
||||||
const [localError, setLocalError] = useState<string | null>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!open) return;
|
|
||||||
setDraft(cloneEnvironmentSettings(settings));
|
|
||||||
setLocalError(null);
|
|
||||||
}, [open, settings]);
|
|
||||||
|
|
||||||
const selectedPage = draft.pages[surfaceId];
|
|
||||||
const selectedBackground = selectedPage.background;
|
|
||||||
const pageOptions = useMemo(() => [
|
|
||||||
{
|
|
||||||
value: "home" as const,
|
|
||||||
label: draft.pages.home.headerLabel,
|
|
||||||
description: "Главная страница продукта",
|
|
||||||
},
|
|
||||||
...roots.map((root) => ({
|
|
||||||
value: root.id,
|
|
||||||
label: draft.pages[root.id].headerLabel,
|
|
||||||
description: `Стартовая страница раздела «${draft.pages[root.id].title}»`,
|
|
||||||
})),
|
|
||||||
], [draft.pages]);
|
|
||||||
const quickActionWorkspaces = useMemo(
|
|
||||||
() => surfaceId === "home"
|
|
||||||
? workspaces.filter((workspace) => !workspace.internalOnly)
|
|
||||||
: workspacesForRoot(surfaceId),
|
|
||||||
[surfaceId],
|
|
||||||
);
|
|
||||||
const quickActionOptions = useMemo(() => [
|
|
||||||
{
|
|
||||||
value: "none",
|
|
||||||
label: "Не показывать",
|
|
||||||
description: "Кнопка скрыта на стартовом экране",
|
|
||||||
},
|
|
||||||
...quickActionWorkspaces.map((workspace) => ({
|
|
||||||
value: workspace.id,
|
|
||||||
label: workspace.label,
|
|
||||||
description: workspace.description,
|
|
||||||
})),
|
|
||||||
], [quickActionWorkspaces]);
|
|
||||||
const dirty = useMemo(
|
|
||||||
() => JSON.stringify(draft) !== JSON.stringify(settings),
|
|
||||||
[draft, settings],
|
|
||||||
);
|
|
||||||
const busy = state === "saving" || uploading;
|
|
||||||
|
|
||||||
const updatePage = (patch: Partial<EnvironmentPage>) => {
|
|
||||||
setDraft((current) => patchPage(current, surfaceId, patch));
|
|
||||||
};
|
|
||||||
|
|
||||||
const save = async () => {
|
|
||||||
const invalid = Object.entries(draft.pages).find(([, page]) => (
|
|
||||||
!page.headerLabel.trim()
|
|
||||||
|| !page.eyebrow.trim()
|
|
||||||
|| !page.title.trim()
|
|
||||||
|| !page.description.trim()
|
|
||||||
));
|
|
||||||
if (invalid) {
|
|
||||||
setLocalError("Название, надзаголовок, заголовок и описание не могут быть пустыми.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
selectedPage.primaryWorkspaceId
|
|
||||||
&& selectedPage.primaryWorkspaceId === selectedPage.secondaryWorkspaceId
|
|
||||||
) {
|
|
||||||
setLocalError("Быстрые кнопки должны вести на разные рабочие поверхности.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const invalidMedia = Object.entries(draft.pages).find(([, page]) => (
|
|
||||||
(page.background.enabled && !page.background.items.length)
|
|
||||||
|| page.background.items.some((item) => {
|
|
||||||
if (!item.url || !item.mediaKind) return true;
|
|
||||||
if (item.source !== "url") return false;
|
|
||||||
try {
|
|
||||||
const parsed = new URL(item.url);
|
|
||||||
return !["http:", "https:"].includes(parsed.protocol);
|
|
||||||
} catch {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
));
|
|
||||||
if (invalidMedia) {
|
|
||||||
setSurfaceId(invalidMedia[0] as EnvironmentSurfaceId);
|
|
||||||
setLocalError(
|
|
||||||
"Каждый элемент фона должен содержать загруженный файл или прямой HTTP(S) URL.",
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setLocalError(null);
|
|
||||||
try {
|
|
||||||
await onSave({
|
|
||||||
...draft,
|
|
||||||
pages: Object.fromEntries(
|
|
||||||
Object.entries(draft.pages).map(([id, page]) => [id, {
|
|
||||||
...page,
|
|
||||||
headerLabel: page.headerLabel.trim(),
|
|
||||||
eyebrow: page.eyebrow.trim(),
|
|
||||||
title: page.title.trim(),
|
|
||||||
description: page.description.trim(),
|
|
||||||
}]),
|
|
||||||
) as EnvironmentSettings["pages"],
|
|
||||||
});
|
|
||||||
onClose();
|
|
||||||
} catch (reason) {
|
|
||||||
setLocalError(reason instanceof Error
|
|
||||||
? reason.message
|
|
||||||
: "Не удалось сохранить настройки окружения.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<FeatureSettingsWindow
|
|
||||||
open={open}
|
|
||||||
title="Настройки Mission Core"
|
|
||||||
subtitle="Локальное операторское окружение"
|
|
||||||
identity={{
|
|
||||||
title: "DC",
|
|
||||||
subtitle: "Mission Core",
|
|
||||||
avatarLabel: "DC",
|
|
||||||
}}
|
|
||||||
sections={[
|
|
||||||
{
|
|
||||||
id: "environment",
|
|
||||||
label: "Окружение",
|
|
||||||
group: "MISSION CORE",
|
|
||||||
icon: "settings",
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
activeSection="environment"
|
|
||||||
onSectionChange={() => undefined}
|
|
||||||
onClose={onClose}
|
|
||||||
footer={(
|
|
||||||
<WindowFooterActions>
|
|
||||||
<Button
|
|
||||||
variant="secondary"
|
|
||||||
disabled={!dirty || busy}
|
|
||||||
onClick={() => {
|
|
||||||
setDraft(cloneEnvironmentSettings(settings));
|
|
||||||
setLocalError(null);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Сбросить изменения
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
variant="primary"
|
|
||||||
disabled={!dirty || busy}
|
|
||||||
onClick={() => void save()}
|
|
||||||
>
|
|
||||||
{state === "saving" ? "Сохраняем…" : "Сохранить"}
|
|
||||||
</Button>
|
|
||||||
</WindowFooterActions>
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="environment-settings">
|
|
||||||
<SettingsCard
|
|
||||||
eyebrow="ОКРУЖЕНИЕ"
|
|
||||||
title="Основные элементы управления"
|
|
||||||
description="Выберите страницу и настройте её название в шапке, содержание стартового экрана, подложку и быстрые переходы."
|
|
||||||
actions={(
|
|
||||||
<Switch
|
|
||||||
checked={selectedBackground.enabled}
|
|
||||||
label="Показывать фон"
|
|
||||||
onChange={(enabled) => {
|
|
||||||
if (enabled && !selectedBackground.items.length) {
|
|
||||||
setLocalError("Сначала добавьте медиаконтент.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setLocalError(null);
|
|
||||||
setDraft((current) =>
|
|
||||||
patchBackground(current, surfaceId, { enabled }));
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div className="environment-settings__editor">
|
|
||||||
<div className="environment-settings__surface">
|
|
||||||
<span>Страница</span>
|
|
||||||
<Select
|
|
||||||
label="Выбрать страницу окружения"
|
|
||||||
value={surfaceId}
|
|
||||||
options={pageOptions}
|
|
||||||
variant="split"
|
|
||||||
menuWidth="anchor"
|
|
||||||
onChange={(value) => {
|
|
||||||
setSurfaceId(value);
|
|
||||||
setLocalError(null);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="environment-settings__copy">
|
|
||||||
<TextField
|
|
||||||
label={surfaceId === "home" ? "Название продукта" : "Название в шапке"}
|
|
||||||
value={selectedPage.headerLabel}
|
|
||||||
maxLength={40}
|
|
||||||
onChange={(event) => updatePage({
|
|
||||||
headerLabel: event.currentTarget.value,
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
label="Надзаголовок"
|
|
||||||
value={selectedPage.eyebrow}
|
|
||||||
maxLength={80}
|
|
||||||
onChange={(event) => updatePage({
|
|
||||||
eyebrow: event.currentTarget.value,
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
label="Основной заголовок"
|
|
||||||
value={selectedPage.title}
|
|
||||||
maxLength={120}
|
|
||||||
onChange={(event) => updatePage({
|
|
||||||
title: event.currentTarget.value,
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
<TextAreaField
|
|
||||||
label="Описание"
|
|
||||||
value={selectedPage.description}
|
|
||||||
maxLength={500}
|
|
||||||
rows={3}
|
|
||||||
onChange={(event) => updatePage({
|
|
||||||
description: event.currentTarget.value,
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="environment-settings__quick-actions">
|
|
||||||
<div>
|
|
||||||
<span>Кнопка 1</span>
|
|
||||||
<Select
|
|
||||||
label="Выбрать первую быструю кнопку"
|
|
||||||
value={selectedPage.primaryWorkspaceId ?? "none"}
|
|
||||||
options={quickActionOptions}
|
|
||||||
variant="split"
|
|
||||||
menuWidth="anchor"
|
|
||||||
onChange={(value) => updatePage({
|
|
||||||
primaryWorkspaceId: value === "none" ? null : value,
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span>Кнопка 2</span>
|
|
||||||
<Select
|
|
||||||
label="Выбрать вторую быструю кнопку"
|
|
||||||
value={selectedPage.secondaryWorkspaceId ?? "none"}
|
|
||||||
options={quickActionOptions}
|
|
||||||
variant="split"
|
|
||||||
menuWidth="anchor"
|
|
||||||
onChange={(value) => updatePage({
|
|
||||||
secondaryWorkspaceId: value === "none" ? null : value,
|
|
||||||
})}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<EnvironmentMediaPlaylistEditor
|
|
||||||
surfaceId={surfaceId}
|
|
||||||
background={selectedBackground}
|
|
||||||
disabled={busy}
|
|
||||||
error={localError ?? error}
|
|
||||||
onBusyChange={setUploading}
|
|
||||||
onChange={(background) => {
|
|
||||||
setLocalError(null);
|
|
||||||
setDraft((current) =>
|
|
||||||
patchBackground(current, surfaceId, background));
|
|
||||||
}}
|
|
||||||
onUpload={onUpload}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</SettingsCard>
|
|
||||||
</div>
|
|
||||||
</FeatureSettingsWindow>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useLayoutEffect, useState, type RefObject } from "react";
|
import type {RefObject} from "react";
|
||||||
import { WorkspaceWindow } from "@nodedc/ui-react";
|
import {FloatingMediaWindow} from "../../../../packages/spatial-ui/src/FloatingMediaWindow";
|
||||||
|
export {initialObservationWindowRect,shouldCaptureWorkspacePointer} from "../../../../packages/spatial-ui/src/FloatingMediaWindow";
|
||||||
|
|
||||||
import type { ObservationWindowRect } from "../core/observation/useObservationLayout";
|
import type { ObservationWindowRect } from "../core/observation/useObservationLayout";
|
||||||
import type { ObservationSourceDescriptor } from "../core/runtime/contracts";
|
import type { ObservationSourceDescriptor } from "../core/runtime/contracts";
|
||||||
@@ -10,60 +11,6 @@ import type {
|
|||||||
RecordedCameraAdmissionState,
|
RecordedCameraAdmissionState,
|
||||||
} from "../core/observation/recordedSessionAdmission";
|
} from "../core/observation/recordedSessionAdmission";
|
||||||
|
|
||||||
const WINDOW_WIDTH = 336;
|
|
||||||
const WINDOW_HEIGHT = 210;
|
|
||||||
const WINDOW_GAP = 16;
|
|
||||||
const WINDOW_INSET = 18;
|
|
||||||
const TIMELINE_CLEARANCE = 64;
|
|
||||||
|
|
||||||
type ClosestTarget = { closest: (selectors: string) => unknown };
|
|
||||||
|
|
||||||
export function shouldCaptureWorkspacePointer(
|
|
||||||
button: number,
|
|
||||||
target: ClosestTarget | null,
|
|
||||||
): boolean {
|
|
||||||
if (button !== 0 || !target) return false;
|
|
||||||
if (target.closest(".nodedc-workspace-window__resize")) return true;
|
|
||||||
if (!target.closest(".nodedc-workspace-window__head")) return false;
|
|
||||||
return !target.closest("button, input, select, textarea, a");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function initialObservationWindowRect(
|
|
||||||
index: number,
|
|
||||||
count = 1,
|
|
||||||
bounds: { width: number; height: number } = { width: 1280, height: 720 },
|
|
||||||
): ObservationWindowRect {
|
|
||||||
const availableWidth = Math.max(0, bounds.width - WINDOW_INSET * 2);
|
|
||||||
const width = Math.max(1, Math.min(WINDOW_WIDTH, availableWidth || WINDOW_WIDTH));
|
|
||||||
const availableHeight = Math.max(0, bounds.height - WINDOW_INSET - TIMELINE_CLEARANCE);
|
|
||||||
const maxColumns = Math.max(
|
|
||||||
1,
|
|
||||||
Math.floor((availableWidth + WINDOW_GAP) / (width + WINDOW_GAP)),
|
|
||||||
);
|
|
||||||
const columns = Math.max(1, Math.min(Math.max(1, count), maxColumns));
|
|
||||||
const rows = Math.max(1, Math.ceil(Math.max(1, count) / columns));
|
|
||||||
const rowHeight = Math.max(
|
|
||||||
1,
|
|
||||||
(availableHeight - Math.max(0, rows - 1) * WINDOW_GAP) / rows,
|
|
||||||
);
|
|
||||||
const height = Math.max(1, Math.min(WINDOW_HEIGHT, rowHeight));
|
|
||||||
const row = Math.floor(index / columns);
|
|
||||||
const column = index % columns;
|
|
||||||
const rowItemCount = Math.min(columns, Math.max(1, count - row * columns));
|
|
||||||
const rowWidth = rowItemCount * width + Math.max(0, rowItemCount - 1) * WINDOW_GAP;
|
|
||||||
const rowStart = Math.max(0, bounds.width - WINDOW_INSET - rowWidth);
|
|
||||||
|
|
||||||
return {
|
|
||||||
x: rowStart + Math.min(column, rowItemCount - 1) * (width + WINDOW_GAP),
|
|
||||||
y: Math.max(
|
|
||||||
0,
|
|
||||||
bounds.height - TIMELINE_CLEARANCE - height - row * (height + WINDOW_GAP),
|
|
||||||
),
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function FloatingObservationWindow({
|
export function FloatingObservationWindow({
|
||||||
source,
|
source,
|
||||||
index,
|
index,
|
||||||
@@ -104,76 +51,12 @@ export function FloatingObservationWindow({
|
|||||||
state: RecordedCameraAdmissionState,
|
state: RecordedCameraAdmissionState,
|
||||||
) => void;
|
) => void;
|
||||||
}) {
|
}) {
|
||||||
const [bounds, setBounds] = useState<{ width: number; height: number } | null>(null);
|
return <FloatingMediaWindow title={source.label} subtitle={source.description}
|
||||||
|
index={index} count={count} boundsRef={boundsRef} rect={rect} maximized={maximized}
|
||||||
useLayoutEffect(() => {
|
active={active} hidden={hidden} onRectChange={onRectChange} onMaximizedChange={onMaximizedChange}
|
||||||
const element = boundsRef.current;
|
onActivate={onActivate} onClose={onClose} resizable={source.capabilities.resizable}
|
||||||
if (!element) return;
|
status={<span className="floating-observation-window__status"><i data-availability={source.availability} aria-hidden="true"/>{observationSourceStatusLabel(source)}</span>}
|
||||||
|
footer={<span className="floating-observation-window__footer"><span>{source.endpointLabel || source.transport}</span><span>{source.capabilities.timelineMode === "live-only" ? "Эфир без буфера" : "Временная шкала"}</span></span>}>
|
||||||
const measure = () => {
|
|
||||||
const next = { width: element.clientWidth, height: element.clientHeight };
|
|
||||||
setBounds((current) => (
|
|
||||||
current?.width === next.width && current.height === next.height ? current : next
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
measure();
|
|
||||||
const observer = new ResizeObserver(measure);
|
|
||||||
observer.observe(element);
|
|
||||||
return () => observer.disconnect();
|
|
||||||
}, [boundsRef]);
|
|
||||||
|
|
||||||
// Camera media admission must not wait for the viewport observer. A source
|
|
||||||
// can arrive in the same commit as a workspace transition; mounting it with
|
|
||||||
// the canonical fallback geometry keeps the browser transport alive until
|
|
||||||
// the real bounds are measured immediately afterward.
|
|
||||||
const initialRect = initialObservationWindowRect(index, count, bounds ?? undefined);
|
|
||||||
const windowRect = rect ?? initialRect;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<WorkspaceWindow
|
|
||||||
boundsRef={boundsRef}
|
|
||||||
rect={windowRect}
|
|
||||||
onRectChange={onRectChange}
|
|
||||||
maximized={maximized}
|
|
||||||
onMaximizedChange={onMaximizedChange}
|
|
||||||
onActivate={onActivate}
|
|
||||||
onClose={onClose}
|
|
||||||
title={source.label}
|
|
||||||
subtitle={source.description}
|
|
||||||
status={(
|
|
||||||
<span className="floating-observation-window__status">
|
|
||||||
<i data-availability={source.availability} aria-hidden="true" />
|
|
||||||
{observationSourceStatusLabel(source)}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
footer={(
|
|
||||||
<span className="floating-observation-window__footer">
|
|
||||||
<span>{source.endpointLabel || source.transport}</span>
|
|
||||||
<span>{source.capabilities.timelineMode === "live-only" ? "Эфир без буфера" : "Временная шкала"}</span>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
minWidth={Math.min(280, windowRect.width)}
|
|
||||||
minHeight={Math.min(190, windowRect.height)}
|
|
||||||
resizable={source.capabilities.resizable}
|
|
||||||
active={active}
|
|
||||||
zIndex={maximized ? 15 : active ? 9 : 7}
|
|
||||||
className={`floating-observation-window${hidden ? " floating-observation-window--hidden" : ""}`}
|
|
||||||
onPointerDownCapture={(event) => {
|
|
||||||
if (!shouldCaptureWorkspacePointer(event.button, event.target as HTMLElement)) return;
|
|
||||||
try {
|
|
||||||
event.currentTarget.setPointerCapture(event.pointerId);
|
|
||||||
} catch {
|
|
||||||
// Pointer capture can fail if the browser ended the pointer between
|
|
||||||
// dispatch and capture. The donor's window listeners remain fallback.
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
closeLabel={`Закрыть ${source.label}`}
|
|
||||||
maximizeLabel={`Развернуть ${source.label}`}
|
|
||||||
restoreLabel={`Восстановить ${source.label}`}
|
|
||||||
moveLabel={`Переместить ${source.label}`}
|
|
||||||
resizeLabel={`Изменить размер ${source.label}`}
|
|
||||||
>
|
|
||||||
<ObservationMedia
|
<ObservationMedia
|
||||||
source={source}
|
source={source}
|
||||||
playback={playback}
|
playback={playback}
|
||||||
@@ -182,6 +65,5 @@ export function FloatingObservationWindow({
|
|||||||
recordedAdmissionKey={recordedAdmissionKey}
|
recordedAdmissionKey={recordedAdmissionKey}
|
||||||
onRecordedAdmissionChange={onRecordedAdmissionChange}
|
onRecordedAdmissionChange={onRecordedAdmissionChange}
|
||||||
/>
|
/>
|
||||||
</WorkspaceWindow>
|
</FloatingMediaWindow>;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import { Button, Icon, StatusBadge } from "@nodedc/ui-react";
|
import { LandingStage as SharedLandingStage, StatusBadge } from "@nodedc/ui-react";
|
||||||
|
|
||||||
import type { BackendStatus, RuntimePhase } from "../core/runtime/contracts";
|
import type { BackendStatus, RuntimePhase } from "../core/runtime/contracts";
|
||||||
import type { EnvironmentPage } from "../core/environment/environmentSettings";
|
import type { EnvironmentPage } from "../core/environment/environmentSettings";
|
||||||
import type { RootDefinition, WorkspaceDefinition } from "../productModel";
|
import type { RootDefinition, WorkspaceDefinition } from "../productModel";
|
||||||
import { backendLabel, backendTone, phaseLabel, phaseTone } from "../presentation";
|
import { backendLabel, backendTone, phaseLabel, phaseTone } from "../presentation";
|
||||||
import { EnvironmentBackgroundMedia } from "./EnvironmentBackgroundMedia";
|
|
||||||
|
|
||||||
export interface LandingStageProps {
|
export interface LandingStageProps {
|
||||||
root: RootDefinition | null;
|
root: RootDefinition | null;
|
||||||
@@ -25,40 +24,10 @@ export function LandingStage({
|
|||||||
quickActions,
|
quickActions,
|
||||||
onOpenWorkspace,
|
onOpenWorkspace,
|
||||||
}: LandingStageProps) {
|
}: LandingStageProps) {
|
||||||
const { background } = page;
|
return <SharedLandingStage page={page} pageId={root?.id ?? "home"}
|
||||||
const hasMedia = background.enabled && background.items.some(
|
actions={quickActions.map(workspace => ({ id: workspace.id, label: workspace.label, icon: workspace.icon, onSelect: () => onOpenWorkspace(workspace.id) }))}
|
||||||
(item) => item.url && item.mediaKind,
|
status={<>
|
||||||
);
|
<div>
|
||||||
return (
|
|
||||||
<section
|
|
||||||
className="landing-stage"
|
|
||||||
data-root={root?.id ?? "home"}
|
|
||||||
data-has-media={hasMedia ? "true" : undefined}
|
|
||||||
>
|
|
||||||
<EnvironmentBackgroundMedia background={background} />
|
|
||||||
<div className="landing-stage__shade" aria-hidden="true" />
|
|
||||||
<div className="landing-stage__copy">
|
|
||||||
<span className="section-eyebrow">{page.eyebrow}</span>
|
|
||||||
<h1>{page.title}</h1>
|
|
||||||
<p>{page.description}</p>
|
|
||||||
{quickActions.length ? (
|
|
||||||
<div className="landing-stage__actions">
|
|
||||||
{quickActions.map((workspace, index) => (
|
|
||||||
<Button
|
|
||||||
key={workspace.id}
|
|
||||||
variant={index === 0 ? "primary" : "secondary"}
|
|
||||||
icon={<Icon name={workspace.icon} />}
|
|
||||||
onClick={() => onOpenWorkspace(workspace.id)}
|
|
||||||
>
|
|
||||||
{workspace.label}
|
|
||||||
</Button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="landing-stage__status">
|
|
||||||
<div>
|
|
||||||
<span className="section-eyebrow">ЛОКАЛЬНЫЙ КОНТУР</span>
|
<span className="section-eyebrow">ЛОКАЛЬНЫЙ КОНТУР</span>
|
||||||
<StatusBadge tone={backendTone(backendStatus)}>{backendLabel(backendStatus)}</StatusBadge>
|
<StatusBadge tone={backendTone(backendStatus)}>{backendLabel(backendStatus)}</StatusBadge>
|
||||||
</div>
|
</div>
|
||||||
@@ -67,12 +36,9 @@ export function LandingStage({
|
|||||||
<StatusBadge tone={phaseTone(phase)}>{phaseLabel(phase)}</StatusBadge>
|
<StatusBadge tone={phaseTone(phase)}>{phaseLabel(phase)}</StatusBadge>
|
||||||
</div>
|
</div>
|
||||||
<p>{message || "Выберите архитектурный блок в верхней навигации."}</p>
|
<p>{message || "Выберите архитектурный блок в верхней навигации."}</p>
|
||||||
</div>
|
</>}
|
||||||
|
footer={<>
|
||||||
<footer className="landing-stage__footer">
|
<span>{root?.accent ?? "НАБЛЮДЕНИЕ · МИССИИ · ДАННЫЕ"}</span>
|
||||||
<span>{root?.accent ?? "НАБЛЮДЕНИЕ · МИССИИ · ДАННЫЕ"}</span>
|
|
||||||
<span>01 / ЛОКАЛЬНЫЙ СТЕНД</span>
|
<span>01 / ЛОКАЛЬНЫЙ СТЕНД</span>
|
||||||
</footer>
|
</>} />;
|
||||||
</section>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Dropdown, Icon, type IconName } from "@nodedc/ui-react";
|
import {sourceIcon,observationSourceStatusLabel} from "../../../../packages/spatial-ui/src/ObservationSourcePicker";
|
||||||
|
export {ObservationSourcePicker,observationSourceStatusLabel} from "../../../../packages/spatial-ui/src/ObservationSourcePicker";
|
||||||
|
import { Icon } from "@nodedc/ui-react";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ObservationSourceAvailability,
|
|
||||||
ObservationSourceDescriptor,
|
ObservationSourceDescriptor,
|
||||||
ObservationSourceModality,
|
|
||||||
} from "../core/runtime/contracts";
|
} from "../core/runtime/contracts";
|
||||||
import { MseFmp4WebSocketPlayer } from "./MseFmp4WebSocketPlayer";
|
import { MseFmp4WebSocketPlayer } from "./MseFmp4WebSocketPlayer";
|
||||||
import {
|
import {
|
||||||
@@ -16,28 +16,6 @@ import type {
|
|||||||
} from "../core/observation/recordedSessionAdmission";
|
} from "../core/observation/recordedSessionAdmission";
|
||||||
import { liveCameraPlaybackAuthorityIdentity } from "../core/observation/liveCameraRecovery";
|
import { liveCameraPlaybackAuthorityIdentity } from "../core/observation/liveCameraRecovery";
|
||||||
|
|
||||||
const sourceIcon: Record<ObservationSourceModality, IconName> = {
|
|
||||||
"point-cloud": "globe",
|
|
||||||
video: "video",
|
|
||||||
image: "image",
|
|
||||||
depth: "image",
|
|
||||||
};
|
|
||||||
|
|
||||||
const availabilityCopy: Record<ObservationSourceAvailability, string> = {
|
|
||||||
unverified: "Не подтверждён",
|
|
||||||
declared: "Канал объявлен",
|
|
||||||
available: "Доступен",
|
|
||||||
connecting: "Подключение",
|
|
||||||
streaming: "Эфир",
|
|
||||||
degraded: "Нестабильно",
|
|
||||||
unavailable: "Недоступен",
|
|
||||||
error: "Ошибка",
|
|
||||||
};
|
|
||||||
|
|
||||||
export function observationSourceStatusLabel(source: ObservationSourceDescriptor): string {
|
|
||||||
return availabilityCopy[source.availability];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ObservationMedia({
|
export function ObservationMedia({
|
||||||
source,
|
source,
|
||||||
playback,
|
playback,
|
||||||
@@ -159,105 +137,3 @@ export function ObservationMedia({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ObservationSourcePicker({
|
|
||||||
sources,
|
|
||||||
visibleSourceIds,
|
|
||||||
pendingSourceIds,
|
|
||||||
onToggle,
|
|
||||||
}: {
|
|
||||||
sources: readonly ObservationSourceDescriptor[];
|
|
||||||
visibleSourceIds: ReadonlySet<string>;
|
|
||||||
pendingSourceIds?: ReadonlySet<string>;
|
|
||||||
onToggle: (sourceId: string) => void | Promise<boolean>;
|
|
||||||
}) {
|
|
||||||
const visibleCount = sources.filter((source) => visibleSourceIds.has(source.id)).length;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dropdown
|
|
||||||
className="scene-source-picker"
|
|
||||||
placement="bottom-start"
|
|
||||||
width={340}
|
|
||||||
offset={10}
|
|
||||||
surfaceRole="dialog"
|
|
||||||
surfaceClassName="observation-source-menu"
|
|
||||||
trigger={({ open, toggle, setAnchorRef, setTriggerRef, surfaceId }) => (
|
|
||||||
<button
|
|
||||||
ref={(node) => {
|
|
||||||
setAnchorRef(node);
|
|
||||||
setTriggerRef(node);
|
|
||||||
}}
|
|
||||||
type="button"
|
|
||||||
className="scene-source-picker__trigger"
|
|
||||||
data-active={open || visibleCount > 0 ? "true" : undefined}
|
|
||||||
aria-label="Источники данных сцены"
|
|
||||||
aria-haspopup="dialog"
|
|
||||||
aria-expanded={open}
|
|
||||||
aria-controls={surfaceId}
|
|
||||||
onClick={toggle}
|
|
||||||
>
|
|
||||||
<Icon name="database" size={17} />
|
|
||||||
{sources.length > 0 ? <span>{visibleCount}</span> : null}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<header className="observation-source-menu__head">
|
|
||||||
<div>
|
|
||||||
<span className="section-eyebrow">НАБЛЮДЕНИЕ</span>
|
|
||||||
<strong>Источники данных</strong>
|
|
||||||
</div>
|
|
||||||
<small>{sources.length ? `${visibleCount} из ${sources.length}` : "нет источников"}</small>
|
|
||||||
</header>
|
|
||||||
{sources.length ? (
|
|
||||||
<div className="observation-source-menu__list">
|
|
||||||
{sources.map((source) => {
|
|
||||||
const selected = visibleSourceIds.has(source.id);
|
|
||||||
const pending = pendingSourceIds?.has(source.id) ?? false;
|
|
||||||
const canOpen = Boolean(
|
|
||||||
selected ||
|
|
||||||
source.modality === "point-cloud" ||
|
|
||||||
source.previewUrl ||
|
|
||||||
source.delivery ||
|
|
||||||
source.activation?.controllable,
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
key={source.id}
|
|
||||||
type="button"
|
|
||||||
className="nodedc-dropdown-option observation-source-option"
|
|
||||||
data-selected={selected ? "true" : undefined}
|
|
||||||
aria-pressed={selected}
|
|
||||||
disabled={pending || !canOpen}
|
|
||||||
onClick={() => void onToggle(source.id)}
|
|
||||||
>
|
|
||||||
<span className="nodedc-dropdown-option__icon">
|
|
||||||
<Icon name={sourceIcon[source.modality]} size={16} />
|
|
||||||
</span>
|
|
||||||
<span className="nodedc-dropdown-option__body">
|
|
||||||
<span className="nodedc-dropdown-option__label">{source.label}</span>
|
|
||||||
<span className="nodedc-dropdown-option__description">
|
|
||||||
<i data-availability={source.availability} aria-hidden="true" />
|
|
||||||
{pending ? "Переключение" : observationSourceStatusLabel(source)} · {source.endpointLabel || source.transport}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<span className="nodedc-dropdown-option__check">
|
|
||||||
{selected ? <Icon name="check" size={15} /> : null}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="observation-source-menu__empty">
|
|
||||||
<Icon name="database" size={18} />
|
|
||||||
<strong>Каталог пока пуст</strong>
|
|
||||||
<span>Выберите профиль устройства — его плагин опубликует доступные каналы.</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<footer className="observation-source-menu__foot">
|
|
||||||
Каналы приходят из активного контура или выбранной сохранённой сессии; сцена не знает
|
|
||||||
модель оборудования.
|
|
||||||
</footer>
|
|
||||||
</Dropdown>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,197 +1 @@
|
|||||||
import { Button, Icon, Select } from "@nodedc/ui-react";
|
export * from "../../../../packages/spatial-ui/src/ObservationTimeline";
|
||||||
|
|
||||||
import type { ObservationTimelineMode } from "../core/runtime/contracts";
|
|
||||||
|
|
||||||
export function ObservationTimeline({
|
|
||||||
active,
|
|
||||||
sourceCount,
|
|
||||||
mode = "live-only",
|
|
||||||
seekable = false,
|
|
||||||
synchronization = "host-arrival-best-effort",
|
|
||||||
rangeNs = null,
|
|
||||||
currentNs = null,
|
|
||||||
playing = false,
|
|
||||||
onSeek,
|
|
||||||
onPlayingChange,
|
|
||||||
onJumpToEnd,
|
|
||||||
showJumpToEnd = true,
|
|
||||||
playbackRate,
|
|
||||||
onPlaybackRateChange,
|
|
||||||
accumulationSeconds,
|
|
||||||
onAccumulationChange,
|
|
||||||
onAccumulationCommit,
|
|
||||||
className = "",
|
|
||||||
}: {
|
|
||||||
active: boolean;
|
|
||||||
sourceCount: number;
|
|
||||||
mode?: ObservationTimelineMode;
|
|
||||||
seekable?: boolean;
|
|
||||||
synchronization?: "host-arrival-best-effort" | "shared-clock" | "frame-accurate";
|
|
||||||
rangeNs?: { min: number; max: number } | null;
|
|
||||||
currentNs?: number | null;
|
|
||||||
playing?: boolean;
|
|
||||||
onSeek?: (timeNs: number) => void;
|
|
||||||
onPlayingChange?: (playing: boolean) => void;
|
|
||||||
onJumpToEnd?: () => void;
|
|
||||||
showJumpToEnd?: boolean;
|
|
||||||
playbackRate?: number;
|
|
||||||
onPlaybackRateChange?: (rate: number) => void;
|
|
||||||
accumulationSeconds?: number;
|
|
||||||
onAccumulationChange?: (value: number) => void;
|
|
||||||
onAccumulationCommit?: () => void;
|
|
||||||
className?: string;
|
|
||||||
}) {
|
|
||||||
const playbackRange = normalizeTimelineRange(rangeNs);
|
|
||||||
const buffered = Boolean(
|
|
||||||
seekable &&
|
|
||||||
(mode === "buffered" || mode === "recorded") &&
|
|
||||||
playbackRange,
|
|
||||||
);
|
|
||||||
const elapsedSeconds = playbackRange
|
|
||||||
? timelineOffsetSeconds(playbackRange, currentNs ?? playbackRange.min)
|
|
||||||
: 0;
|
|
||||||
const durationSeconds = playbackRange
|
|
||||||
? Math.max(0, (playbackRange.max - playbackRange.min) / 1_000_000_000)
|
|
||||||
: 0;
|
|
||||||
const synchronizationLabel = {
|
|
||||||
"host-arrival-best-effort": "Синхронизация по приходу",
|
|
||||||
"shared-clock": "Общие часы",
|
|
||||||
"frame-accurate": "Покадровая синхронизация",
|
|
||||||
}[synchronization];
|
|
||||||
const accumulationValue = accumulationSeconds === undefined
|
|
||||||
? null
|
|
||||||
: normalizeAccumulationSeconds(accumulationSeconds);
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`observation-timeline ${className}`.trim()}
|
|
||||||
data-active={active ? "true" : undefined}
|
|
||||||
data-mode={mode}
|
|
||||||
data-accumulation={accumulationValue !== null ? "true" : undefined}
|
|
||||||
>
|
|
||||||
{accumulationValue !== null ? (
|
|
||||||
<div className="observation-timeline__accumulation">
|
|
||||||
<span>Накопление</span>
|
|
||||||
<input
|
|
||||||
className="observation-timeline__track"
|
|
||||||
type="range"
|
|
||||||
min={0}
|
|
||||||
max={120}
|
|
||||||
step={1}
|
|
||||||
value={accumulationValue}
|
|
||||||
aria-label="Окно накопления облака точек"
|
|
||||||
aria-valuetext={formatAccumulationDuration(accumulationValue)}
|
|
||||||
onChange={(event) =>
|
|
||||||
onAccumulationChange?.(normalizeAccumulationSeconds(Number(event.target.value)))}
|
|
||||||
onPointerUp={onAccumulationCommit}
|
|
||||||
onTouchEnd={onAccumulationCommit}
|
|
||||||
onKeyUp={onAccumulationCommit}
|
|
||||||
onBlur={onAccumulationCommit}
|
|
||||||
/>
|
|
||||||
<code>{formatAccumulationDuration(accumulationValue)}</code>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<div className="observation-timeline__playback">
|
|
||||||
<Button
|
|
||||||
size="compact"
|
|
||||||
variant="ghost"
|
|
||||||
disabled={!buffered || !onSeek}
|
|
||||||
aria-label="Перейти к началу"
|
|
||||||
onClick={() => playbackRange && onSeek?.(playbackRange.min)}
|
|
||||||
>
|
|
||||||
<Icon name="chevron-left" />
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
className="observation-timeline__transport"
|
|
||||||
size="compact"
|
|
||||||
variant="ghost"
|
|
||||||
disabled={!buffered || !onPlayingChange}
|
|
||||||
aria-label={buffered ? (playing ? "Пауза" : "Воспроизвести") : "Только эфир"}
|
|
||||||
icon={<Icon name={playing ? "stop" : "play"} />}
|
|
||||||
onClick={() => onPlayingChange?.(!playing)}
|
|
||||||
/>
|
|
||||||
{buffered && playbackRate !== undefined && onPlaybackRateChange ? (
|
|
||||||
<Select
|
|
||||||
label="Скорость воспроизведения"
|
|
||||||
value={String(playbackRate)}
|
|
||||||
options={[
|
|
||||||
{ value: "0.5", label: "0,5×" },
|
|
||||||
{ value: "1", label: "1×" },
|
|
||||||
{ value: "2", label: "2×" },
|
|
||||||
]}
|
|
||||||
variant="inline"
|
|
||||||
menuWidth="anchor"
|
|
||||||
onChange={(value) => onPlaybackRateChange(Number(value))}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
<input
|
|
||||||
className="observation-timeline__track"
|
|
||||||
type="range"
|
|
||||||
min={0}
|
|
||||||
max={Math.max(durationSeconds, 0.001)}
|
|
||||||
step={0.01}
|
|
||||||
value={Math.min(elapsedSeconds, durationSeconds)}
|
|
||||||
disabled={!buffered || !onSeek}
|
|
||||||
aria-label="Позиция воспроизведения"
|
|
||||||
onChange={(event) => {
|
|
||||||
if (!playbackRange) return;
|
|
||||||
onSeek?.(playbackRange.min + Number(event.target.value) * 1_000_000_000);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div className="observation-timeline__meta">
|
|
||||||
<code>
|
|
||||||
{buffered
|
|
||||||
? `${formatTimelineDuration(elapsedSeconds)} / ${formatTimelineDuration(durationSeconds)}`
|
|
||||||
: active ? "LIVE" : "—:—:—.———"}
|
|
||||||
</code>
|
|
||||||
<small>
|
|
||||||
{buffered ? `${sourceCount} каналов` : `${synchronizationLabel} · буфер не включён`}
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
{showJumpToEnd ? (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="observation-timeline__follow"
|
|
||||||
data-active={!buffered && active ? "true" : undefined}
|
|
||||||
disabled={buffered ? !onJumpToEnd : true}
|
|
||||||
onClick={onJumpToEnd}
|
|
||||||
>
|
|
||||||
{buffered ? "К КОНЦУ" : "ЭФИР"}
|
|
||||||
</button>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeAccumulationSeconds(value: number): number {
|
|
||||||
if (!Number.isFinite(value)) return 0;
|
|
||||||
return Math.min(120, Math.max(0, Math.round(value)));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatAccumulationDuration(value: number): string {
|
|
||||||
const seconds = normalizeAccumulationSeconds(value);
|
|
||||||
return seconds === 0 ? "Кадр" : `${seconds} с`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeTimelineRange(
|
|
||||||
range: { min: number; max: number } | null | undefined,
|
|
||||||
): { min: number; max: number } | null {
|
|
||||||
if (!range || !Number.isFinite(range.min) || !Number.isFinite(range.max)) return null;
|
|
||||||
if (range.max <= range.min) return null;
|
|
||||||
return range;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function timelineOffsetSeconds(
|
|
||||||
range: { min: number; max: number },
|
|
||||||
currentNs: number,
|
|
||||||
): number {
|
|
||||||
const clamped = Math.min(Math.max(currentNs, range.min), range.max);
|
|
||||||
return Math.max(0, (clamped - range.min) / 1_000_000_000);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatTimelineDuration(seconds: number): string {
|
|
||||||
if (!Number.isFinite(seconds) || seconds < 0) return "00:00.000";
|
|
||||||
const wholeMinutes = Math.floor(seconds / 60);
|
|
||||||
const remaining = seconds - wholeMinutes * 60;
|
|
||||||
return `${String(wholeMinutes).padStart(2, "0")}:${remaining.toFixed(3).padStart(6, "0")}`;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ export type RecordedMediaPresentationState = "loading" | "ready" | "waiting" | "
|
|||||||
|
|
||||||
export const RECORDED_MEDIA_DURATION_TOLERANCE_SECONDS = 1;
|
export const RECORDED_MEDIA_DURATION_TOLERANCE_SECONDS = 1;
|
||||||
const RECORDED_MEDIA_SOURCE_OPEN_TIMEOUT_MS = 10_000;
|
const RECORDED_MEDIA_SOURCE_OPEN_TIMEOUT_MS = 10_000;
|
||||||
const RECORDED_MEDIA_TARGET_TIMEOUT_MS = 10_000;
|
// A valid local fragment becomes decoder-ready well below one second. Keeping
|
||||||
|
// a damaged GOP on screen for ten seconds only delays the keyframe recovery.
|
||||||
|
const RECORDED_MEDIA_TARGET_TIMEOUT_MS = 2_500;
|
||||||
const RECORDED_MEDIA_FRAGMENT_TIMEOUT_MS = 15_000;
|
const RECORDED_MEDIA_FRAGMENT_TIMEOUT_MS = 15_000;
|
||||||
const RECORDED_MEDIA_REQUIRED_AHEAD_SEGMENTS = 12;
|
const RECORDED_MEDIA_REQUIRED_AHEAD_SEGMENTS = 12;
|
||||||
const RECORDED_MEDIA_SEGMENTS_AHEAD = 36;
|
const RECORDED_MEDIA_SEGMENTS_AHEAD = 36;
|
||||||
@@ -72,6 +74,39 @@ export function recordedMediaDecodeStartSequence(
|
|||||||
return selected;
|
return selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function nextRecordedMediaRandomAccessSequence(
|
||||||
|
randomAccessSequences: readonly number[],
|
||||||
|
failedSequence: number,
|
||||||
|
): number | null {
|
||||||
|
if (!Number.isInteger(failedSequence) || failedSequence < 1) return null;
|
||||||
|
for (const sequence of randomAccessSequences) {
|
||||||
|
if (!Number.isInteger(sequence) || sequence < 1) return null;
|
||||||
|
if (sequence > failedSequence) return sequence;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function recordedMediaRecoveryTargetSequence(
|
||||||
|
requestedSequence: number | null,
|
||||||
|
failedSequence: number | null,
|
||||||
|
recoverySequence: number | null,
|
||||||
|
): number | null {
|
||||||
|
if (requestedSequence === null) return null;
|
||||||
|
if (
|
||||||
|
!Number.isInteger(requestedSequence)
|
||||||
|
|| requestedSequence < 1
|
||||||
|
|| failedSequence === null
|
||||||
|
|| recoverySequence === null
|
||||||
|
|| !Number.isInteger(failedSequence)
|
||||||
|
|| !Number.isInteger(recoverySequence)
|
||||||
|
|| failedSequence < 1
|
||||||
|
|| recoverySequence <= failedSequence
|
||||||
|
) return requestedSequence;
|
||||||
|
return requestedSequence >= failedSequence && requestedSequence < recoverySequence
|
||||||
|
? recoverySequence
|
||||||
|
: requestedSequence;
|
||||||
|
}
|
||||||
|
|
||||||
export function recordedMediaSegmentAppendOrder(
|
export function recordedMediaSegmentAppendOrder(
|
||||||
appended: ReadonlySet<number>,
|
appended: ReadonlySet<number>,
|
||||||
decodeStartSequence: number,
|
decodeStartSequence: number,
|
||||||
@@ -90,6 +125,32 @@ export function recordedMediaSegmentAppendOrder(
|
|||||||
return missing;
|
return missing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Resolve a source-clock timestamp to the first fMP4 fragment covering it. */
|
||||||
|
export function recordedMediaSegmentSequenceAtTime(
|
||||||
|
segmentEndTimesSeconds: readonly number[],
|
||||||
|
epochStartSeconds: number,
|
||||||
|
currentSeconds: number,
|
||||||
|
): number | null {
|
||||||
|
if (
|
||||||
|
!segmentEndTimesSeconds.length ||
|
||||||
|
!Number.isFinite(epochStartSeconds) ||
|
||||||
|
!Number.isFinite(currentSeconds)
|
||||||
|
) return null;
|
||||||
|
const localSeconds = Math.max(0, currentSeconds - epochStartSeconds);
|
||||||
|
let left = 0;
|
||||||
|
let right = segmentEndTimesSeconds.length - 1;
|
||||||
|
while (left < right) {
|
||||||
|
const middle = Math.floor((left + right) / 2);
|
||||||
|
const endSeconds = segmentEndTimesSeconds[middle];
|
||||||
|
if (!Number.isFinite(endSeconds) || endSeconds <= 0) return null;
|
||||||
|
if (endSeconds + 0.001 >= localSeconds) right = middle;
|
||||||
|
else left = middle + 1;
|
||||||
|
}
|
||||||
|
const finalEndSeconds = segmentEndTimesSeconds[left];
|
||||||
|
if (!Number.isFinite(finalEndSeconds) || finalEndSeconds + 0.001 < localSeconds) return null;
|
||||||
|
return left + 1;
|
||||||
|
}
|
||||||
|
|
||||||
export function recordedMediaCanRollTarget(
|
export function recordedMediaCanRollTarget(
|
||||||
previousSequence: number,
|
previousSequence: number,
|
||||||
nextSequence: number,
|
nextSequence: number,
|
||||||
@@ -121,6 +182,27 @@ function recordedMediaTimeRangesContain(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function recordedMediaTimestampStallRecoveryTarget(
|
||||||
|
currentSeconds: number,
|
||||||
|
bufferedRanges: readonly (readonly [number, number])[],
|
||||||
|
skipSeconds = 0.18,
|
||||||
|
): number | null {
|
||||||
|
if (!Number.isFinite(currentSeconds) || !Number.isFinite(skipSeconds) || skipSeconds <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (const [startSeconds, endSeconds] of bufferedRanges) {
|
||||||
|
if (
|
||||||
|
!Number.isFinite(startSeconds)
|
||||||
|
|| !Number.isFinite(endSeconds)
|
||||||
|
|| currentSeconds < startSeconds - 0.05
|
||||||
|
|| currentSeconds > endSeconds
|
||||||
|
) continue;
|
||||||
|
const targetSeconds = Math.min(currentSeconds + skipSeconds, endSeconds - 0.05);
|
||||||
|
return targetSeconds >= currentSeconds + 0.04 ? targetSeconds : null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
export function recordedMediaPresentationState(
|
export function recordedMediaPresentationState(
|
||||||
state: "loading" | "ready" | "error",
|
state: "loading" | "ready" | "error",
|
||||||
readyGeneration: string | null,
|
readyGeneration: string | null,
|
||||||
@@ -151,6 +233,24 @@ export function selectRecordedMediaEpoch(
|
|||||||
return selected && currentSeconds <= selected.timelineEndSeconds ? selected : null;
|
return selected && currentSeconds <= selected.timelineEndSeconds ? selected : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep admission bounded even when the shared Rerun clock is currently before,
|
||||||
|
* between, or after camera epochs. Presentation still reports `waiting`; this
|
||||||
|
* selector only chooses the nearest epoch whose first/last fragment can prove
|
||||||
|
* that the camera transport is usable without downloading the whole MP4.
|
||||||
|
*/
|
||||||
|
export function selectRecordedMediaPreparationEpoch(
|
||||||
|
epochs: readonly ObservationRecordedMediaEpoch[],
|
||||||
|
currentSeconds: number,
|
||||||
|
): ObservationRecordedMediaEpoch | null {
|
||||||
|
if (!epochs.length || !Number.isFinite(currentSeconds)) return null;
|
||||||
|
const active = selectRecordedMediaEpoch(epochs, currentSeconds);
|
||||||
|
if (active) return active;
|
||||||
|
return epochs.find((epoch) => epoch.timelineStartSeconds > currentSeconds)
|
||||||
|
?? epochs.at(-1)
|
||||||
|
?? null;
|
||||||
|
}
|
||||||
|
|
||||||
export function recordedMediaSeekableCoverage(
|
export function recordedMediaSeekableCoverage(
|
||||||
durationSeconds: number,
|
durationSeconds: number,
|
||||||
seekableEndSeconds: number,
|
seekableEndSeconds: number,
|
||||||
@@ -231,29 +331,14 @@ export async function fetchRecordedMediaArchive(
|
|||||||
return { manifest, byteLength: totalBytes };
|
return { manifest, byteLength: totalBytes };
|
||||||
}
|
}
|
||||||
|
|
||||||
function videoHasSeekableArchive(
|
function waitForRecordedVideoInitialFrame(
|
||||||
video: HTMLVideoElement,
|
video: HTMLVideoElement,
|
||||||
declaredDurationSeconds: number,
|
|
||||||
): boolean {
|
|
||||||
if (video.readyState < 1 || video.seekable.length < 1) return false;
|
|
||||||
return recordedMediaSeekableCoverage(
|
|
||||||
video.duration,
|
|
||||||
video.seekable.end(video.seekable.length - 1),
|
|
||||||
declaredDurationSeconds,
|
|
||||||
RECORDED_MEDIA_DURATION_TOLERANCE_SECONDS,
|
|
||||||
video.seekable.start(0),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function waitForSeekableArchive(
|
|
||||||
video: HTMLVideoElement,
|
|
||||||
declaredDurationSeconds: number,
|
|
||||||
signal: AbortSignal,
|
signal: AbortSignal,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (signal.aborted) return Promise.reject(new DOMException("Aborted", "AbortError"));
|
if (signal.aborted) return Promise.reject(new DOMException("Aborted", "AbortError"));
|
||||||
if (videoHasSeekableArchive(video, declaredDurationSeconds)) return Promise.resolve();
|
if (video.readyState >= HTMLMediaElement.HAVE_CURRENT_DATA) return Promise.resolve();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const events = ["loadedmetadata", "durationchange", "progress", "canplay"] as const;
|
const events = ["loadeddata", "canplay", "progress"] as const;
|
||||||
let stallTimer: ReturnType<typeof globalThis.setTimeout> | undefined;
|
let stallTimer: ReturnType<typeof globalThis.setTimeout> | undefined;
|
||||||
const armStallTimer = () => {
|
const armStallTimer = () => {
|
||||||
if (stallTimer !== undefined) globalThis.clearTimeout(stallTimer);
|
if (stallTimer !== undefined) globalThis.clearTimeout(stallTimer);
|
||||||
@@ -270,7 +355,7 @@ function waitForSeekableArchive(
|
|||||||
};
|
};
|
||||||
const onProgress = () => {
|
const onProgress = () => {
|
||||||
armStallTimer();
|
armStallTimer();
|
||||||
if (!videoHasSeekableArchive(video, declaredDurationSeconds)) return;
|
if (video.readyState < HTMLMediaElement.HAVE_CURRENT_DATA) return;
|
||||||
cleanup();
|
cleanup();
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
@@ -313,11 +398,7 @@ async function mountRecordedEpochStream(
|
|||||||
};
|
};
|
||||||
video.load();
|
video.load();
|
||||||
try {
|
try {
|
||||||
await waitForSeekableArchive(
|
await waitForRecordedVideoInitialFrame(video, signal);
|
||||||
video,
|
|
||||||
descriptor.timelineEndSeconds - descriptor.timelineStartSeconds,
|
|
||||||
signal,
|
|
||||||
);
|
|
||||||
return cleanup;
|
return cleanup;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
cleanup();
|
cleanup();
|
||||||
@@ -336,6 +417,11 @@ interface RecordedSegmentTarget {
|
|||||||
resetAttempts: number;
|
resetAttempts: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RecordedSegmentRecovery {
|
||||||
|
readonly failedSequence: number;
|
||||||
|
readonly recoverySequence: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface RecordedSegmentStreamRuntime {
|
interface RecordedSegmentStreamRuntime {
|
||||||
readonly generation: string;
|
readonly generation: string;
|
||||||
readonly mediaSource: MediaSource;
|
readonly mediaSource: MediaSource;
|
||||||
@@ -716,6 +802,9 @@ export function RecordedFmp4Player({
|
|||||||
onAdmissionChange,
|
onAdmissionChange,
|
||||||
onPlaybackChange,
|
onPlaybackChange,
|
||||||
onPlayingRejected,
|
onPlayingRejected,
|
||||||
|
playbackAuthority = "media",
|
||||||
|
playbackTransport = "segmented",
|
||||||
|
recoverTimestampStalls = false,
|
||||||
}: {
|
}: {
|
||||||
source: ObservationSourceDescriptor;
|
source: ObservationSourceDescriptor;
|
||||||
playback?: RecordedObservationPlayback | null;
|
playback?: RecordedObservationPlayback | null;
|
||||||
@@ -728,6 +817,9 @@ export function RecordedFmp4Player({
|
|||||||
onAdmissionChange?: (state: RecordedCameraAdmissionState) => void;
|
onAdmissionChange?: (state: RecordedCameraAdmissionState) => void;
|
||||||
onPlaybackChange?: (playback: RecordedObservationPlayback) => void;
|
onPlaybackChange?: (playback: RecordedObservationPlayback) => void;
|
||||||
onPlayingRejected?: () => void;
|
onPlayingRejected?: () => void;
|
||||||
|
playbackAuthority?: "media" | "host";
|
||||||
|
playbackTransport?: "segmented" | "epoch-stream";
|
||||||
|
recoverTimestampStalls?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const videoRef = useRef<HTMLVideoElement>(null);
|
const videoRef = useRef<HTMLVideoElement>(null);
|
||||||
const onAdmissionChangeRef = useRef(onAdmissionChange);
|
const onAdmissionChangeRef = useRef(onAdmissionChange);
|
||||||
@@ -768,12 +860,16 @@ export function RecordedFmp4Player({
|
|||||||
);
|
);
|
||||||
const [archive, setArchive] = useState<RecordedMediaArchive | null>(null);
|
const [archive, setArchive] = useState<RecordedMediaArchive | null>(null);
|
||||||
const [state, setState] = useState<"loading" | "ready" | "error">("loading");
|
const [state, setState] = useState<"loading" | "ready" | "error">("loading");
|
||||||
|
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||||
const [readyGeneration, setReadyGeneration] = useState<string | null>(null);
|
const [readyGeneration, setReadyGeneration] = useState<string | null>(null);
|
||||||
const [bufferRevision, setBufferRevision] = useState(0);
|
const [bufferRevision, setBufferRevision] = useState(0);
|
||||||
|
const [segmentRecoveryGeneration, setSegmentRecoveryGeneration] = useState(0);
|
||||||
|
const [segmentRecovery, setSegmentRecovery] = useState<RecordedSegmentRecovery | null>(null);
|
||||||
const segmentedRuntimeRef = useRef<RecordedSegmentStreamRuntime | null>(null);
|
const segmentedRuntimeRef = useRef<RecordedSegmentStreamRuntime | null>(null);
|
||||||
const [segmentedRuntimeGeneration, setSegmentedRuntimeGeneration] = useState<string | null>(null);
|
const [segmentedRuntimeGeneration, setSegmentedRuntimeGeneration] = useState<string | null>(null);
|
||||||
const targetRevisionRef = useRef(0);
|
const targetRevisionRef = useRef(0);
|
||||||
const targetReadyAbortRef = useRef<AbortController | null>(null);
|
const targetReadyAbortRef = useRef<AbortController | null>(null);
|
||||||
|
const lastSegmentRecoveryRef = useRef<string | null>(null);
|
||||||
const playAttemptRevisionRef = useRef(0);
|
const playAttemptRevisionRef = useRef(0);
|
||||||
const currentSeconds = playback?.currentSeconds ?? contract?.timelineStartSeconds ?? 0;
|
const currentSeconds = playback?.currentSeconds ?? contract?.timelineStartSeconds ?? 0;
|
||||||
const playbackPlayingRef = useRef(Boolean(playback?.playing));
|
const playbackPlayingRef = useRef(Boolean(playback?.playing));
|
||||||
@@ -781,25 +877,63 @@ export function RecordedFmp4Player({
|
|||||||
const playbackRate = playback?.rate && Number.isFinite(playback.rate)
|
const playbackRate = playback?.rate && Number.isFinite(playback.rate)
|
||||||
? Math.min(4, Math.max(0.25, playback.rate))
|
? Math.min(4, Math.max(0.25, playback.rate))
|
||||||
: 1;
|
: 1;
|
||||||
const epoch = useMemo(
|
const playbackRateRef = useRef(playbackRate);
|
||||||
|
playbackRateRef.current = playbackRate;
|
||||||
|
const presentationEpoch = useMemo(
|
||||||
() => selectRecordedMediaEpoch(archive?.manifest.epochs ?? [], currentSeconds),
|
() => selectRecordedMediaEpoch(archive?.manifest.epochs ?? [], currentSeconds),
|
||||||
[archive?.manifest.epochs, currentSeconds],
|
[archive?.manifest.epochs, currentSeconds],
|
||||||
);
|
);
|
||||||
|
const epoch = useMemo(
|
||||||
|
() => selectRecordedMediaPreparationEpoch(
|
||||||
|
archive?.manifest.epochs ?? [],
|
||||||
|
currentSeconds,
|
||||||
|
),
|
||||||
|
[archive?.manifest.epochs, currentSeconds],
|
||||||
|
);
|
||||||
|
const segmentClockSeconds = epoch
|
||||||
|
? Math.min(
|
||||||
|
Math.max(currentSeconds, epoch.timelineStartSeconds),
|
||||||
|
epoch.timelineEndSeconds,
|
||||||
|
)
|
||||||
|
: currentSeconds;
|
||||||
|
const effectiveSegmentCount = segmentCount ?? epoch?.segmentCount ?? null;
|
||||||
|
const requestedSegmentSequence = segmentSequence ?? (epoch
|
||||||
|
? recordedMediaSegmentSequenceAtTime(
|
||||||
|
epoch.segmentEndTimesSeconds,
|
||||||
|
epoch.timelineStartSeconds,
|
||||||
|
segmentClockSeconds,
|
||||||
|
)
|
||||||
|
: null);
|
||||||
|
const effectiveSegmentSequence = recordedMediaRecoveryTargetSequence(
|
||||||
|
requestedSegmentSequence,
|
||||||
|
segmentRecovery?.failedSequence ?? null,
|
||||||
|
segmentRecovery?.recoverySequence ?? null,
|
||||||
|
);
|
||||||
|
const holdingForSegmentRecovery = Boolean(
|
||||||
|
segmentRecovery
|
||||||
|
&& requestedSegmentSequence !== null
|
||||||
|
&& effectiveSegmentSequence !== requestedSegmentSequence,
|
||||||
|
);
|
||||||
const segmented = Boolean(
|
const segmented = Boolean(
|
||||||
segmentCount !== null
|
playbackTransport === "segmented"
|
||||||
&& Number.isInteger(segmentCount)
|
&& requestedSegmentSequence !== null
|
||||||
&& segmentCount >= 1
|
&& Number.isInteger(requestedSegmentSequence)
|
||||||
|
&& requestedSegmentSequence >= 1
|
||||||
|
&&
|
||||||
|
effectiveSegmentCount !== null
|
||||||
|
&& Number.isInteger(effectiveSegmentCount)
|
||||||
|
&& effectiveSegmentCount >= 1
|
||||||
&& typeof MediaSource !== "undefined"
|
&& typeof MediaSource !== "undefined"
|
||||||
&& epoch
|
&& epoch
|
||||||
&& epoch.segmentCount === segmentCount
|
&& epoch.segmentCount === effectiveSegmentCount
|
||||||
&& epoch.randomAccessSequences.length > 0
|
&& epoch.randomAccessSequences.length > 0
|
||||||
&& epoch.segmentEndTimesSeconds.length === segmentCount
|
&& epoch.segmentEndTimesSeconds.length === effectiveSegmentCount
|
||||||
&& MediaSource.isTypeSupported(epoch.mediaType),
|
&& MediaSource.isTypeSupported(epoch.mediaType),
|
||||||
);
|
);
|
||||||
const directPlaybackSeconds = segmented ? null : currentSeconds;
|
const directPlaybackSeconds = segmented ? null : currentSeconds;
|
||||||
const waitingForEpoch = Boolean(archive && !epoch);
|
const waitingForEpoch = Boolean(archive && !presentationEpoch);
|
||||||
const selectedGeneration = contract && epoch
|
const selectedGeneration = contract && presentationEpoch
|
||||||
? `${contract.manifestGenerationSha256}:${epoch.ordinal}:${epoch.timelineStartSeconds}:${epoch.timelineEndSeconds}`
|
? `${contract.manifestGenerationSha256}:${presentationEpoch.ordinal}:${presentationEpoch.timelineStartSeconds}:${presentationEpoch.timelineEndSeconds}`
|
||||||
: null;
|
: null;
|
||||||
const visualState = recordedMediaPresentationState(
|
const visualState = recordedMediaPresentationState(
|
||||||
state,
|
state,
|
||||||
@@ -813,6 +947,7 @@ export function RecordedFmp4Player({
|
|||||||
if (!contract) {
|
if (!contract) {
|
||||||
setArchive(null);
|
setArchive(null);
|
||||||
setReadyGeneration(null);
|
setReadyGeneration(null);
|
||||||
|
setErrorMessage("Некорректный descriptor записанной камеры.");
|
||||||
setState("error");
|
setState("error");
|
||||||
reportAdmission({
|
reportAdmission({
|
||||||
phase: "error",
|
phase: "error",
|
||||||
@@ -825,6 +960,9 @@ export function RecordedFmp4Player({
|
|||||||
const abort = new AbortController();
|
const abort = new AbortController();
|
||||||
setArchive(null);
|
setArchive(null);
|
||||||
setReadyGeneration(null);
|
setReadyGeneration(null);
|
||||||
|
setSegmentRecovery(null);
|
||||||
|
lastSegmentRecoveryRef.current = null;
|
||||||
|
setErrorMessage(null);
|
||||||
setState("loading");
|
setState("loading");
|
||||||
reportAdmission({
|
reportAdmission({
|
||||||
phase: "loading",
|
phase: "loading",
|
||||||
@@ -842,6 +980,7 @@ export function RecordedFmp4Player({
|
|||||||
}
|
}
|
||||||
setArchive(null);
|
setArchive(null);
|
||||||
setReadyGeneration(null);
|
setReadyGeneration(null);
|
||||||
|
setErrorMessage("Архив записанной камеры не прошёл проверку.");
|
||||||
setState("error");
|
setState("error");
|
||||||
reportAdmission({
|
reportAdmission({
|
||||||
phase: "error",
|
phase: "error",
|
||||||
@@ -853,43 +992,14 @@ export function RecordedFmp4Player({
|
|||||||
}, [admissionKey, contract, prepare]);
|
}, [admissionKey, contract, prepare]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!archive || !contract || !prepare || segmented) return;
|
if (!segmentRecovery || requestedSegmentSequence === null) return;
|
||||||
const abort = new AbortController();
|
if (
|
||||||
let disposed = false;
|
requestedSegmentSequence >= segmentRecovery.failedSequence
|
||||||
void (async () => {
|
&& requestedSegmentSequence < segmentRecovery.recoverySequence
|
||||||
for (const candidate of archive.manifest.epochs) {
|
) return;
|
||||||
const probe = document.createElement("video");
|
lastSegmentRecoveryRef.current = null;
|
||||||
probe.muted = true;
|
setSegmentRecovery(null);
|
||||||
probe.playsInline = true;
|
}, [requestedSegmentSequence, segmentRecovery]);
|
||||||
const cleanup = await mountRecordedEpochStream(probe, candidate, abort.signal);
|
|
||||||
cleanup();
|
|
||||||
if (disposed || abort.signal.aborted) return;
|
|
||||||
}
|
|
||||||
if (disposed || abort.signal.aborted) return;
|
|
||||||
reportAdmission({
|
|
||||||
phase: "ready",
|
|
||||||
byteLength: archive.byteLength,
|
|
||||||
message: null,
|
|
||||||
});
|
|
||||||
})().catch((error: unknown) => {
|
|
||||||
if (
|
|
||||||
disposed ||
|
|
||||||
abort.signal.aborted ||
|
|
||||||
(error instanceof DOMException && error.name === "AbortError")
|
|
||||||
) return;
|
|
||||||
setReadyGeneration(null);
|
|
||||||
setState("error");
|
|
||||||
reportAdmission({
|
|
||||||
phase: "error",
|
|
||||||
byteLength: archive.byteLength,
|
|
||||||
message: "Не все codec epoch записанной камеры декодируются и доступны для seek.",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return () => {
|
|
||||||
disposed = true;
|
|
||||||
abort.abort();
|
|
||||||
};
|
|
||||||
}, [admissionKey, archive, contract, prepare, segmented]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const video = videoRef.current;
|
const video = videoRef.current;
|
||||||
@@ -910,6 +1020,7 @@ export function RecordedFmp4Player({
|
|||||||
|
|
||||||
setReadyGeneration(null);
|
setReadyGeneration(null);
|
||||||
setSegmentedRuntimeGeneration(null);
|
setSegmentedRuntimeGeneration(null);
|
||||||
|
setErrorMessage(null);
|
||||||
setState("loading");
|
setState("loading");
|
||||||
video.pause();
|
video.pause();
|
||||||
const sourceOpened = waitForMediaSourceOpen(mediaSource, abort.signal);
|
const sourceOpened = waitForMediaSourceOpen(mediaSource, abort.signal);
|
||||||
@@ -957,6 +1068,7 @@ export function RecordedFmp4Player({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setReadyGeneration(null);
|
setReadyGeneration(null);
|
||||||
|
setErrorMessage("Покадровый буфер записанной камеры не открылся.");
|
||||||
setState("error");
|
setState("error");
|
||||||
reportAdmission({
|
reportAdmission({
|
||||||
phase: "error",
|
phase: "error",
|
||||||
@@ -982,7 +1094,14 @@ export function RecordedFmp4Player({
|
|||||||
}
|
}
|
||||||
URL.revokeObjectURL(objectUrl);
|
URL.revokeObjectURL(objectUrl);
|
||||||
};
|
};
|
||||||
}, [archive, contract, epoch, segmentCount, segmented]);
|
}, [
|
||||||
|
archive,
|
||||||
|
contract,
|
||||||
|
effectiveSegmentCount,
|
||||||
|
epoch,
|
||||||
|
segmented,
|
||||||
|
segmentRecoveryGeneration,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const runtime = segmentedRuntimeRef.current;
|
const runtime = segmentedRuntimeRef.current;
|
||||||
@@ -993,18 +1112,19 @@ export function RecordedFmp4Player({
|
|||||||
|| !archive
|
|| !archive
|
||||||
|| !segmented
|
|| !segmented
|
||||||
|| segmentedRuntimeGeneration !== runtime.generation
|
|| segmentedRuntimeGeneration !== runtime.generation
|
||||||
|| segmentSequence === null
|
|| effectiveSegmentSequence === null
|
||||||
|| !Number.isInteger(segmentSequence)
|
|| !Number.isInteger(effectiveSegmentSequence)
|
||||||
|| segmentSequence < 1
|
|| effectiveSegmentSequence < 1
|
||||||
|| segmentSequence > runtime.segmentCount
|
|| effectiveSegmentSequence > runtime.segmentCount
|
||||||
) return;
|
) return;
|
||||||
const archiveByteLength = archive.byteLength;
|
const archiveByteLength = archive.byteLength;
|
||||||
const decodeStart = recordedMediaDecodeStartSequence(
|
const decodeStart = recordedMediaDecodeStartSequence(
|
||||||
runtime.randomAccessSequences,
|
runtime.randomAccessSequences,
|
||||||
segmentSequence,
|
effectiveSegmentSequence,
|
||||||
);
|
);
|
||||||
if (decodeStart === null) {
|
if (decodeStart === null) {
|
||||||
setReadyGeneration(null);
|
setReadyGeneration(null);
|
||||||
|
setErrorMessage("Для кадра записанной камеры нет random-access фрагмента.");
|
||||||
setState("error");
|
setState("error");
|
||||||
reportAdmission({
|
reportAdmission({
|
||||||
phase: "error",
|
phase: "error",
|
||||||
@@ -1015,10 +1135,11 @@ export function RecordedFmp4Player({
|
|||||||
}
|
}
|
||||||
const targetSeconds = recordedSegmentStartSeconds(
|
const targetSeconds = recordedSegmentStartSeconds(
|
||||||
runtime.segmentEndTimesSeconds,
|
runtime.segmentEndTimesSeconds,
|
||||||
segmentSequence,
|
effectiveSegmentSequence,
|
||||||
);
|
);
|
||||||
if (targetSeconds === null) {
|
if (targetSeconds === null) {
|
||||||
setReadyGeneration(null);
|
setReadyGeneration(null);
|
||||||
|
setErrorMessage("Для кадра записанной камеры нет точной media timestamp.");
|
||||||
setState("error");
|
setState("error");
|
||||||
reportAdmission({
|
reportAdmission({
|
||||||
phase: "error",
|
phase: "error",
|
||||||
@@ -1030,15 +1151,15 @@ export function RecordedFmp4Player({
|
|||||||
const previousTarget = runtime.target;
|
const previousTarget = runtime.target;
|
||||||
const readyEnd = Math.min(
|
const readyEnd = Math.min(
|
||||||
runtime.segmentCount,
|
runtime.segmentCount,
|
||||||
segmentSequence + RECORDED_MEDIA_REQUIRED_AHEAD_SEGMENTS,
|
effectiveSegmentSequence + RECORDED_MEDIA_REQUIRED_AHEAD_SEGMENTS,
|
||||||
);
|
);
|
||||||
const desiredEnd = Math.min(
|
const desiredEnd = Math.min(
|
||||||
runtime.segmentCount,
|
runtime.segmentCount,
|
||||||
segmentSequence + RECORDED_MEDIA_SEGMENTS_AHEAD,
|
effectiveSegmentSequence + RECORDED_MEDIA_SEGMENTS_AHEAD,
|
||||||
);
|
);
|
||||||
const candidateTarget: RecordedSegmentTarget = {
|
const candidateTarget: RecordedSegmentTarget = {
|
||||||
revision: previousTarget?.revision ?? 0,
|
revision: previousTarget?.revision ?? 0,
|
||||||
sequence: segmentSequence,
|
sequence: effectiveSegmentSequence,
|
||||||
decodeStart,
|
decodeStart,
|
||||||
readyEnd,
|
readyEnd,
|
||||||
desiredEnd,
|
desiredEnd,
|
||||||
@@ -1046,6 +1167,24 @@ export function RecordedFmp4Player({
|
|||||||
forceReset: false,
|
forceReset: false,
|
||||||
resetAttempts: 0,
|
resetAttempts: 0,
|
||||||
};
|
};
|
||||||
|
const recoverFromSegmentFailure = (failedSequence: number): boolean => {
|
||||||
|
const recoverySequence = nextRecordedMediaRandomAccessSequence(
|
||||||
|
runtime.randomAccessSequences,
|
||||||
|
failedSequence,
|
||||||
|
);
|
||||||
|
if (recoverySequence === null) return false;
|
||||||
|
const recoveryToken = `${runtime.generation}:${failedSequence}:${recoverySequence}`;
|
||||||
|
if (lastSegmentRecoveryRef.current === recoveryToken) return true;
|
||||||
|
lastSegmentRecoveryRef.current = recoveryToken;
|
||||||
|
setReadyGeneration(null);
|
||||||
|
setSegmentRecovery({ failedSequence, recoverySequence });
|
||||||
|
setErrorMessage(
|
||||||
|
`Восстанавливаем камеру с ключевого кадра ${recoverySequence}.`,
|
||||||
|
);
|
||||||
|
setState("loading");
|
||||||
|
setSegmentRecoveryGeneration((generation) => generation + 1);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
const rollingTarget = Boolean(previousTarget && recordedMediaCanRollTarget(
|
const rollingTarget = Boolean(previousTarget && recordedMediaCanRollTarget(
|
||||||
previousTarget.sequence,
|
previousTarget.sequence,
|
||||||
candidateTarget.sequence,
|
candidateTarget.sequence,
|
||||||
@@ -1059,21 +1198,62 @@ export function RecordedFmp4Player({
|
|||||||
|| runtime.abort.signal.aborted
|
|| runtime.abort.signal.aborted
|
||||||
|| (error instanceof DOMException && error.name === "AbortError")
|
|| (error instanceof DOMException && error.name === "AbortError")
|
||||||
) return;
|
) return;
|
||||||
|
const failedSequence = runtime.target?.sequence ?? effectiveSegmentSequence;
|
||||||
|
if (failedSequence !== null && recoverFromSegmentFailure(failedSequence)) return;
|
||||||
|
const detail = error instanceof Error && error.message
|
||||||
|
? `: ${error.message}`
|
||||||
|
: ".";
|
||||||
|
const message = `Покадровый фрагмент записанной камеры недоступен${detail}`;
|
||||||
setReadyGeneration(null);
|
setReadyGeneration(null);
|
||||||
|
setErrorMessage(message);
|
||||||
setState("error");
|
setState("error");
|
||||||
reportAdmission({
|
reportAdmission({
|
||||||
phase: "error",
|
phase: "error",
|
||||||
byteLength: archiveByteLength,
|
byteLength: archiveByteLength,
|
||||||
message: "Покадровый фрагмент записанной камеры недоступен.",
|
message,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const resumePlaybackIfRequested = async (revision: number) => {
|
||||||
|
if (
|
||||||
|
runtime.disposed
|
||||||
|
|| segmentedRuntimeRef.current !== runtime
|
||||||
|
|| runtime.target?.revision !== revision
|
||||||
|
|| !playbackPlayingRef.current
|
||||||
|
) return;
|
||||||
|
video.playbackRate = playbackRateRef.current;
|
||||||
|
try {
|
||||||
|
await video.play();
|
||||||
|
} catch {
|
||||||
|
if (
|
||||||
|
runtime.disposed
|
||||||
|
|| segmentedRuntimeRef.current !== runtime
|
||||||
|
|| runtime.target?.revision !== revision
|
||||||
|
) return;
|
||||||
|
// Canonical recorded LABs run one host-owned clock for camera and
|
||||||
|
// spatial evidence. A transient MSE play() rejection (commonly a
|
||||||
|
// pause/reset race while the next fragment is admitted) must stay a
|
||||||
|
// decoder concern: the rolling target will retry and catch up.
|
||||||
|
if (playbackAuthority === "host") return;
|
||||||
|
onPlayingRejectedRef.current?.();
|
||||||
|
setReadyGeneration(null);
|
||||||
|
setErrorMessage("Запуск записанной камеры отклонён браузером.");
|
||||||
|
setState("error");
|
||||||
|
reportAdmission({
|
||||||
|
phase: "error",
|
||||||
|
byteLength: archiveByteLength,
|
||||||
|
message: "Запуск записанной камеры отклонён браузером.",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
if (rollingTarget && previousTarget) {
|
if (rollingTarget && previousTarget) {
|
||||||
runtime.target = {
|
runtime.target = {
|
||||||
...candidateTarget,
|
...candidateTarget,
|
||||||
revision: previousTarget.revision,
|
revision: previousTarget.revision,
|
||||||
};
|
};
|
||||||
runtime.onTargetBuffered = null;
|
runtime.onTargetBuffered = null;
|
||||||
void pumpRecordedSegmentWindow(runtime).catch(reportPumpError);
|
void pumpRecordedSegmentWindow(runtime)
|
||||||
|
.then(() => resumePlaybackIfRequested(previousTarget.revision))
|
||||||
|
.catch(reportPumpError);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1128,18 +1308,22 @@ export function RecordedFmp4Player({
|
|||||||
setBufferRevision((revision) => revision + 1);
|
setBufferRevision((revision) => revision + 1);
|
||||||
runtime.hasPresentedFrame = true;
|
runtime.hasPresentedFrame = true;
|
||||||
setReadyGeneration(runtime.generation);
|
setReadyGeneration(runtime.generation);
|
||||||
|
setErrorMessage(null);
|
||||||
setState("ready");
|
setState("ready");
|
||||||
reportAdmission({
|
reportAdmission({
|
||||||
phase: "ready",
|
phase: "ready",
|
||||||
byteLength: archiveByteLength,
|
byteLength: archiveByteLength,
|
||||||
message: null,
|
message: null,
|
||||||
});
|
});
|
||||||
|
await resumePlaybackIfRequested(bufferedTarget.revision);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (
|
if (
|
||||||
targetReadyAbort.signal.aborted
|
targetReadyAbort.signal.aborted
|
||||||
|| (error instanceof DOMException && error.name === "AbortError")
|
|| (error instanceof DOMException && error.name === "AbortError")
|
||||||
) return;
|
) return;
|
||||||
|
if (recoverFromSegmentFailure(bufferedTarget.sequence)) return;
|
||||||
setReadyGeneration(null);
|
setReadyGeneration(null);
|
||||||
|
setErrorMessage("Кадр записанной камеры не стал decoder-ready.");
|
||||||
setState("error");
|
setState("error");
|
||||||
reportAdmission({
|
reportAdmission({
|
||||||
phase: "error",
|
phase: "error",
|
||||||
@@ -1160,7 +1344,13 @@ export function RecordedFmp4Player({
|
|||||||
if (targetReadyAbortRef.current === targetReadyAbort) targetReadyAbortRef.current = null;
|
if (targetReadyAbortRef.current === targetReadyAbort) targetReadyAbortRef.current = null;
|
||||||
if (runtime.onTargetBuffered === markBuffered) runtime.onTargetBuffered = null;
|
if (runtime.onTargetBuffered === markBuffered) runtime.onTargetBuffered = null;
|
||||||
};
|
};
|
||||||
}, [archive?.byteLength, segmentSequence, segmented, segmentedRuntimeGeneration]);
|
}, [
|
||||||
|
archive?.byteLength,
|
||||||
|
effectiveSegmentSequence,
|
||||||
|
playbackAuthority,
|
||||||
|
segmented,
|
||||||
|
segmentedRuntimeGeneration,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const video = videoRef.current;
|
const video = videoRef.current;
|
||||||
@@ -1170,6 +1360,7 @@ export function RecordedFmp4Player({
|
|||||||
? `${contract.manifestGenerationSha256}:${epochDescriptor.ordinal}:${epochDescriptor.timelineStartSeconds}:${epochDescriptor.timelineEndSeconds}`
|
? `${contract.manifestGenerationSha256}:${epochDescriptor.ordinal}:${epochDescriptor.timelineStartSeconds}:${epochDescriptor.timelineEndSeconds}`
|
||||||
: null;
|
: null;
|
||||||
setReadyGeneration(null);
|
setReadyGeneration(null);
|
||||||
|
setErrorMessage(null);
|
||||||
setState("loading");
|
setState("loading");
|
||||||
const abort = new AbortController();
|
const abort = new AbortController();
|
||||||
let disposed = false;
|
let disposed = false;
|
||||||
@@ -1185,7 +1376,13 @@ export function RecordedFmp4Player({
|
|||||||
}
|
}
|
||||||
setBufferRevision((revision) => revision + 1);
|
setBufferRevision((revision) => revision + 1);
|
||||||
setReadyGeneration(generation);
|
setReadyGeneration(generation);
|
||||||
|
setErrorMessage(null);
|
||||||
setState("ready");
|
setState("ready");
|
||||||
|
reportAdmission({
|
||||||
|
phase: "ready",
|
||||||
|
byteLength: archive?.byteLength ?? null,
|
||||||
|
message: null,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (
|
if (
|
||||||
disposed ||
|
disposed ||
|
||||||
@@ -1195,11 +1392,12 @@ export function RecordedFmp4Player({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setReadyGeneration(null);
|
setReadyGeneration(null);
|
||||||
|
setErrorMessage("Записанная камера не открыла первый декодируемый кадр.");
|
||||||
setState("error");
|
setState("error");
|
||||||
reportAdmission({
|
reportAdmission({
|
||||||
phase: "error",
|
phase: "error",
|
||||||
byteLength: archive?.byteLength ?? null,
|
byteLength: archive?.byteLength ?? null,
|
||||||
message: "Записанная камера не стала seekable.",
|
message: "Записанная камера не открыла первый декодируемый кадр.",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1234,6 +1432,7 @@ export function RecordedFmp4Player({
|
|||||||
video.currentTime = target;
|
video.currentTime = target;
|
||||||
} catch {
|
} catch {
|
||||||
setReadyGeneration(null);
|
setReadyGeneration(null);
|
||||||
|
setErrorMessage("Seek записанной камеры завершился ошибкой.");
|
||||||
setState("error");
|
setState("error");
|
||||||
reportAdmission({
|
reportAdmission({
|
||||||
phase: "error",
|
phase: "error",
|
||||||
@@ -1244,11 +1443,13 @@ export function RecordedFmp4Player({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
video.playbackRate = playbackRate;
|
video.playbackRate = playbackRate;
|
||||||
if (playback?.playing) {
|
if (playback?.playing && !holdingForSegmentRecovery) {
|
||||||
void video.play().catch(() => {
|
void video.play().catch(() => {
|
||||||
if (playAttemptRevisionRef.current !== playAttemptRevision) return;
|
if (playAttemptRevisionRef.current !== playAttemptRevision) return;
|
||||||
|
if (playbackAuthority === "host") return;
|
||||||
onPlayingRejectedRef.current?.();
|
onPlayingRejectedRef.current?.();
|
||||||
setReadyGeneration(null);
|
setReadyGeneration(null);
|
||||||
|
setErrorMessage("Запуск записанной камеры отклонён браузером.");
|
||||||
setState("error");
|
setState("error");
|
||||||
reportAdmission({
|
reportAdmission({
|
||||||
phase: "error",
|
phase: "error",
|
||||||
@@ -1264,12 +1465,95 @@ export function RecordedFmp4Player({
|
|||||||
bufferRevision,
|
bufferRevision,
|
||||||
directPlaybackSeconds,
|
directPlaybackSeconds,
|
||||||
epoch,
|
epoch,
|
||||||
|
holdingForSegmentRecovery,
|
||||||
playback?.playing,
|
playback?.playing,
|
||||||
|
playbackAuthority,
|
||||||
playbackRate,
|
playbackRate,
|
||||||
segmented,
|
segmented,
|
||||||
visualState,
|
visualState,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const video = videoRef.current;
|
||||||
|
if (!video || !recoverTimestampStalls || !playback?.playing || visualState !== "ready") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let lastSeconds = video.currentTime;
|
||||||
|
let lastProgressAtMs = performance.now();
|
||||||
|
const interval = window.setInterval(() => {
|
||||||
|
if (
|
||||||
|
!playbackPlayingRef.current
|
||||||
|
|| video.paused
|
||||||
|
|| video.ended
|
||||||
|
|| video.seeking
|
||||||
|
|| video.readyState < HTMLMediaElement.HAVE_CURRENT_DATA
|
||||||
|
) {
|
||||||
|
lastSeconds = video.currentTime;
|
||||||
|
lastProgressAtMs = performance.now();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const nowMs = performance.now();
|
||||||
|
if (video.currentTime >= lastSeconds + 0.02) {
|
||||||
|
lastSeconds = video.currentTime;
|
||||||
|
lastProgressAtMs = nowMs;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (nowMs - lastProgressAtMs < 1_250) return;
|
||||||
|
const bufferedRanges = Array.from(
|
||||||
|
{ length: video.buffered.length },
|
||||||
|
(_, index) => [video.buffered.start(index), video.buffered.end(index)] as const,
|
||||||
|
);
|
||||||
|
const targetSeconds = recordedMediaTimestampStallRecoveryTarget(
|
||||||
|
video.currentTime,
|
||||||
|
bufferedRanges,
|
||||||
|
);
|
||||||
|
lastProgressAtMs = nowMs;
|
||||||
|
if (targetSeconds === null) return;
|
||||||
|
// Field recordings can contain non-monotonic or corrupt H.264 timestamps.
|
||||||
|
// If decoded time is frozen despite proven buffered media ahead, skip only
|
||||||
|
// the broken timestamp interval and return authority to the media clock.
|
||||||
|
video.currentTime = targetSeconds;
|
||||||
|
lastSeconds = targetSeconds;
|
||||||
|
}, 250);
|
||||||
|
return () => window.clearInterval(interval);
|
||||||
|
}, [playback?.playing, recoverTimestampStalls, visualState]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const video = videoRef.current;
|
||||||
|
if (!video || !segmented || !playback?.playing || visualState !== "ready") return;
|
||||||
|
let cancelled = false;
|
||||||
|
const resumeIfDecoderReady = () => {
|
||||||
|
if (cancelled || !playbackPlayingRef.current || !video.paused) return;
|
||||||
|
const runtime = segmentedRuntimeRef.current;
|
||||||
|
const target = runtime?.target;
|
||||||
|
if (
|
||||||
|
!runtime
|
||||||
|
|| !target
|
||||||
|
|| runtime.disposed
|
||||||
|
|| video.seeking
|
||||||
|
|| video.readyState < HTMLMediaElement.HAVE_CURRENT_DATA
|
||||||
|
|| Math.abs(video.currentTime - target.targetSeconds) > 0.25
|
||||||
|
|| !recordedMediaTimeRangesContain(video.buffered, target.targetSeconds)
|
||||||
|
) return;
|
||||||
|
playAttemptRevisionRef.current += 1;
|
||||||
|
const playAttemptRevision = playAttemptRevisionRef.current;
|
||||||
|
video.playbackRate = playbackRateRef.current;
|
||||||
|
void video.play().catch(() => {
|
||||||
|
if (cancelled || playAttemptRevisionRef.current !== playAttemptRevision) return;
|
||||||
|
if (playbackAuthority === "host") return;
|
||||||
|
onPlayingRejectedRef.current?.();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const queueResume = () => window.queueMicrotask(resumeIfDecoderReady);
|
||||||
|
const events = ["pause", "canplay", "seeked"] as const;
|
||||||
|
for (const event of events) video.addEventListener(event, queueResume);
|
||||||
|
resumeIfDecoderReady();
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
for (const event of events) video.removeEventListener(event, queueResume);
|
||||||
|
};
|
||||||
|
}, [bufferRevision, playback?.playing, playbackAuthority, segmented, visualState]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const video = videoRef.current;
|
const video = videoRef.current;
|
||||||
if ((!interactive && onPlaybackChange === undefined) || !video || !epoch || visualState !== "ready") return;
|
if ((!interactive && onPlaybackChange === undefined) || !video || !epoch || visualState !== "ready") return;
|
||||||
@@ -1327,7 +1611,9 @@ export function RecordedFmp4Player({
|
|||||||
{visualState === "waiting"
|
{visualState === "waiting"
|
||||||
? "Камера на этой позиции ещё не записывалась"
|
? "Камера на этой позиции ещё не записывалась"
|
||||||
: visualState === "error"
|
: visualState === "error"
|
||||||
? "Записанное видео недоступно"
|
? errorMessage ?? "Записанное видео недоступно"
|
||||||
|
: errorMessage
|
||||||
|
? errorMessage
|
||||||
: archive
|
: archive
|
||||||
? "Проверяем seek и codec записанного видео…"
|
? "Проверяем seek и codec записанного видео…"
|
||||||
: "Читаем manifest записанного видео…"}
|
: "Читаем manifest записанного видео…"}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,33 @@
|
|||||||
|
import { CanonicalResultRerunReplay } from "./CanonicalResultRerunReplay";
|
||||||
|
import { resolveAICompositionReplay } from "../../core/laboratory/canonicalLabReplay";
|
||||||
|
import type { ObservationSessionReplayLaunch } from "../../core/observation/sessionArchive";
|
||||||
|
import type { AICompositionRun } from "../../core/observatory/aiComposition";
|
||||||
|
|
||||||
|
function resolveComposition(
|
||||||
|
runId: string,
|
||||||
|
launch: ObservationSessionReplayLaunch,
|
||||||
|
options: { signal: AbortSignal },
|
||||||
|
) {
|
||||||
|
return resolveAICompositionReplay(runId, launch, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AICompositionReplay({ run }: { run: AICompositionRun }) {
|
||||||
|
const semantics = run.moduleIds.includes("ddrnet") || run.moduleIds.includes("eomt");
|
||||||
|
const detections = run.moduleIds.includes("rf-detr")
|
||||||
|
|| run.moduleIds.includes("object-distance");
|
||||||
|
const costmap = run.moduleIds.includes("tgs");
|
||||||
|
return (
|
||||||
|
<CanonicalResultRerunReplay
|
||||||
|
key={run.runId}
|
||||||
|
resultId={run.runId}
|
||||||
|
sessionId={run.sourceSessionId}
|
||||||
|
resolveReplay={resolveComposition}
|
||||||
|
semantics={semantics}
|
||||||
|
detections={detections}
|
||||||
|
costmap={costmap}
|
||||||
|
moduleIds={run.moduleIds}
|
||||||
|
viewerLayers={run.presentation.viewerLayers}
|
||||||
|
label={run.configurationLabel}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
import { useCallback, useEffect, useState, type ReactNode } from "react";
|
||||||
|
import { SegmentedControl, SplitPane, type SplitPaneOrientation } from "@nodedc/ui-react";
|
||||||
|
|
||||||
|
import { LaboratoryEvidenceViewer } from "./LaboratoryEvidenceViewer";
|
||||||
|
|
||||||
|
export const CANONICAL_RECORDED_LAB_REPLAY_CONTRACT =
|
||||||
|
"missioncore.canonical-recorded-lab-replay/v1";
|
||||||
|
|
||||||
|
export interface CanonicalRecordedLabMode<T extends string> {
|
||||||
|
value: T;
|
||||||
|
label: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interaction contract from the accepted recorded-LAB instrument.
|
||||||
|
*
|
||||||
|
* Keeping pane selection, collapse semantics, responsive split orientation,
|
||||||
|
* expansion and splitter state here prevents individual experiments from
|
||||||
|
* quietly growing their own replay behaviour. Experiments provide evidence
|
||||||
|
* layers; they do not reimplement the laboratory shell.
|
||||||
|
*/
|
||||||
|
export function useCanonicalRecordedLabReplayState<
|
||||||
|
TMedia extends string,
|
||||||
|
TSpatial extends string,
|
||||||
|
>({
|
||||||
|
initialMediaMode,
|
||||||
|
initialSpatialMode,
|
||||||
|
}: {
|
||||||
|
initialMediaMode: TMedia;
|
||||||
|
initialSpatialMode: TSpatial | null;
|
||||||
|
}) {
|
||||||
|
const [mediaMode, setMediaMode] = useState<TMedia | null>(initialMediaMode);
|
||||||
|
const [spatialMode, setSpatialMode] = useState<TSpatial | null>(initialSpatialMode);
|
||||||
|
const [splitPrimarySize, setSplitPrimarySize] = useState(50);
|
||||||
|
const [splitOrientation, setSplitOrientation] = useState<SplitPaneOrientation>(() => (
|
||||||
|
typeof window !== "undefined" && window.matchMedia("(max-width: 900px)").matches
|
||||||
|
? "horizontal"
|
||||||
|
: "vertical"
|
||||||
|
));
|
||||||
|
const [expanded, setExpanded] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const query = window.matchMedia("(max-width: 900px)");
|
||||||
|
const update = () => setSplitOrientation(query.matches ? "horizontal" : "vertical");
|
||||||
|
update();
|
||||||
|
query.addEventListener("change", update);
|
||||||
|
return () => query.removeEventListener("change", update);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onMediaModeChange = useCallback((next: TMedia | "none") => {
|
||||||
|
if (next === "none") return;
|
||||||
|
setMediaMode((current) => current === next ? null : next);
|
||||||
|
}, []);
|
||||||
|
const onSpatialModeChange = useCallback((next: TSpatial | "none") => {
|
||||||
|
if (next === "none") return;
|
||||||
|
setSpatialMode((current) => current === next ? null : next);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
mediaMode,
|
||||||
|
spatialMode,
|
||||||
|
splitView: mediaMode !== null && spatialMode !== null,
|
||||||
|
splitPrimarySize,
|
||||||
|
splitOrientation,
|
||||||
|
expanded,
|
||||||
|
onMediaModeChange,
|
||||||
|
onSpatialModeChange,
|
||||||
|
onSplitPrimarySizeChange: setSplitPrimarySize,
|
||||||
|
onExpandedChange: setExpanded,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CanonicalRecordedLabReplay<
|
||||||
|
TMedia extends string,
|
||||||
|
TSpatial extends string,
|
||||||
|
>({
|
||||||
|
label,
|
||||||
|
mediaMode,
|
||||||
|
mediaModes,
|
||||||
|
spatialMode,
|
||||||
|
spatialModes,
|
||||||
|
expanded,
|
||||||
|
splitPrimarySize,
|
||||||
|
splitOrientation,
|
||||||
|
mediaAriaLabel,
|
||||||
|
spatialAriaLabel,
|
||||||
|
mediaLayerControls,
|
||||||
|
spatialLayerControls,
|
||||||
|
spatialLeadingControl,
|
||||||
|
spatialTrailingControl,
|
||||||
|
mediaModeControlsVisible = true,
|
||||||
|
paneToolbarsAlwaysVisible = false,
|
||||||
|
mediaMultiLayer = false,
|
||||||
|
mediaContent,
|
||||||
|
spatialContent,
|
||||||
|
unifiedContent,
|
||||||
|
emptyMessage,
|
||||||
|
deckOverlays,
|
||||||
|
actions,
|
||||||
|
overlay,
|
||||||
|
transport,
|
||||||
|
trailingActions,
|
||||||
|
onMediaModeChange,
|
||||||
|
onSpatialModeChange,
|
||||||
|
onExpandedChange,
|
||||||
|
onSplitPrimarySizeChange,
|
||||||
|
}: {
|
||||||
|
label: string;
|
||||||
|
mediaMode: TMedia;
|
||||||
|
mediaModes: readonly CanonicalRecordedLabMode<TMedia>[];
|
||||||
|
spatialMode: TSpatial;
|
||||||
|
spatialModes: readonly CanonicalRecordedLabMode<TSpatial>[];
|
||||||
|
expanded: boolean;
|
||||||
|
splitPrimarySize: number;
|
||||||
|
splitOrientation: SplitPaneOrientation;
|
||||||
|
mediaAriaLabel: string;
|
||||||
|
spatialAriaLabel: string;
|
||||||
|
mediaLayerControls?: ReactNode;
|
||||||
|
spatialLayerControls?: ReactNode;
|
||||||
|
spatialLeadingControl?: ReactNode;
|
||||||
|
spatialTrailingControl?: ReactNode;
|
||||||
|
mediaModeControlsVisible?: boolean;
|
||||||
|
/** Keeps renderer-local controls inside their viewport when only one pane is present. */
|
||||||
|
paneToolbarsAlwaysVisible?: boolean;
|
||||||
|
mediaMultiLayer?: boolean;
|
||||||
|
mediaContent?: ReactNode;
|
||||||
|
spatialContent?: ReactNode;
|
||||||
|
/** One upstream Rerun viewer owns both panes; this controlled split owns their shared geometry. */
|
||||||
|
unifiedContent?: ReactNode;
|
||||||
|
emptyMessage: string;
|
||||||
|
deckOverlays?: ReactNode;
|
||||||
|
actions?: ReactNode;
|
||||||
|
overlay?: ReactNode;
|
||||||
|
transport?: ReactNode;
|
||||||
|
trailingActions?: ReactNode;
|
||||||
|
onMediaModeChange: (mode: TMedia) => void;
|
||||||
|
onSpatialModeChange: (mode: TSpatial) => void;
|
||||||
|
onExpandedChange: (expanded: boolean) => void;
|
||||||
|
onSplitPrimarySizeChange: (size: number) => void;
|
||||||
|
}) {
|
||||||
|
const splitView = mediaMode !== "none" && spatialMode !== "none";
|
||||||
|
const mediaModeControls = (
|
||||||
|
<div className="m4-replay-threat-visual__pane-mode-controls" data-pane-mode="media">
|
||||||
|
<SegmentedControl
|
||||||
|
value={mediaMode}
|
||||||
|
items={[...mediaModes]}
|
||||||
|
label="Видео и камера"
|
||||||
|
size="dense"
|
||||||
|
onChange={onMediaModeChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
const spatialModeControls = (
|
||||||
|
<div className="m4-replay-threat-visual__pane-mode-controls" data-pane-mode="spatial">
|
||||||
|
<SegmentedControl
|
||||||
|
value={spatialMode}
|
||||||
|
items={[...spatialModes]}
|
||||||
|
label="3D и план"
|
||||||
|
size="dense"
|
||||||
|
onChange={onSpatialModeChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
const mediaPane = (
|
||||||
|
<section
|
||||||
|
className="m4-replay-threat-visual__pane"
|
||||||
|
data-pane="media"
|
||||||
|
aria-label={mediaAriaLabel}
|
||||||
|
hidden={mediaMode === "none"}
|
||||||
|
>
|
||||||
|
{splitView || paneToolbarsAlwaysVisible ? (
|
||||||
|
<div
|
||||||
|
className="m4-replay-threat-visual__pane-toolbar"
|
||||||
|
data-pane-toolbar="media"
|
||||||
|
data-multi-semantic={mediaMultiLayer ? "true" : undefined}
|
||||||
|
>
|
||||||
|
{mediaLayerControls}
|
||||||
|
{mediaModeControlsVisible ? mediaModeControls : null}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{mediaContent}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
const spatialPane = spatialMode !== "none" ? (
|
||||||
|
<section
|
||||||
|
className="m4-replay-threat-visual__pane"
|
||||||
|
data-pane="spatial"
|
||||||
|
aria-label={spatialAriaLabel}
|
||||||
|
>
|
||||||
|
{splitView || paneToolbarsAlwaysVisible ? (
|
||||||
|
<div
|
||||||
|
className="m4-replay-threat-visual__pane-toolbar"
|
||||||
|
data-pane-toolbar="spatial"
|
||||||
|
>
|
||||||
|
{spatialLeadingControl}
|
||||||
|
<div className="m4-replay-threat-visual__spatial-toolbar-end">
|
||||||
|
{spatialLayerControls}
|
||||||
|
{spatialModeControls}
|
||||||
|
{spatialTrailingControl}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{spatialContent}
|
||||||
|
</section>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="l3-visual-audit m4-replay-threat-visual"
|
||||||
|
data-contract={CANONICAL_RECORDED_LAB_REPLAY_CONTRACT}
|
||||||
|
>
|
||||||
|
<LaboratoryEvidenceViewer
|
||||||
|
label={label}
|
||||||
|
className="m4-replay-threat-evidence-viewer"
|
||||||
|
mode={mediaMode}
|
||||||
|
modes={[...mediaModes]}
|
||||||
|
secondaryMode={{
|
||||||
|
value: spatialMode,
|
||||||
|
modes: [...spatialModes],
|
||||||
|
label: "3D и план",
|
||||||
|
onChange: onSpatialModeChange,
|
||||||
|
}}
|
||||||
|
expanded={expanded}
|
||||||
|
onModeChange={onMediaModeChange}
|
||||||
|
onExpandedChange={onExpandedChange}
|
||||||
|
modeControlsVisible={!splitView && !paneToolbarsAlwaysVisible}
|
||||||
|
actions={actions}
|
||||||
|
overlay={overlay}
|
||||||
|
transport={transport}
|
||||||
|
trailingActions={trailingActions}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="m4-replay-threat-visual__deck"
|
||||||
|
data-split={splitView ? "true" : undefined}
|
||||||
|
data-empty={mediaMode === "none" && spatialMode === "none" ? "true" : undefined}
|
||||||
|
data-native-rerun={unifiedContent ? "true" : undefined}
|
||||||
|
>
|
||||||
|
{unifiedContent ? (
|
||||||
|
<div className="m4-replay-threat-visual__unified-content">
|
||||||
|
{unifiedContent}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<SplitPane
|
||||||
|
primary={mediaPane}
|
||||||
|
secondary={spatialPane ?? <div />}
|
||||||
|
primarySize={splitView ? splitPrimarySize : mediaMode !== "none" ? 100 : 0}
|
||||||
|
onPrimarySizeChange={onSplitPrimarySizeChange}
|
||||||
|
orientation={splitOrientation}
|
||||||
|
minPrimarySize={splitView ? 24 : 0}
|
||||||
|
minSecondarySize={splitView ? 24 : 0}
|
||||||
|
resizable={splitView}
|
||||||
|
separatorLabel="Изменить размер видео/камеры и 3D/плана"
|
||||||
|
/>
|
||||||
|
{mediaMode === "none" && spatialMode === "none" ? (
|
||||||
|
<div className="l3-visual-audit__state" role="status">
|
||||||
|
{emptyMessage}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{deckOverlays}
|
||||||
|
</div>
|
||||||
|
</LaboratoryEvidenceViewer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,525 @@
|
|||||||
|
import {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
Button,
|
||||||
|
Checker,
|
||||||
|
ControlRow,
|
||||||
|
Icon,
|
||||||
|
IconButton,
|
||||||
|
Inspector,
|
||||||
|
InspectorSelectField,
|
||||||
|
RangeControl,
|
||||||
|
ToastStack,
|
||||||
|
Window,
|
||||||
|
type ToastItem,
|
||||||
|
} from "@nodedc/ui-react";
|
||||||
|
|
||||||
|
import { ObservationTimeline } from "../ObservationTimeline";
|
||||||
|
import {
|
||||||
|
RerunViewport,
|
||||||
|
isRecordedPlaybackPresentationReady,
|
||||||
|
type RerunPlaybackController,
|
||||||
|
type RerunPlaybackState,
|
||||||
|
type RerunViewportStatus,
|
||||||
|
} from "../RerunViewport";
|
||||||
|
import {
|
||||||
|
CanonicalRecordedLabReplay,
|
||||||
|
useCanonicalRecordedLabReplayState,
|
||||||
|
} from "../laboratory/CanonicalRecordedLabReplay";
|
||||||
|
import type { CanonicalLabReplayDescriptor } from "../../core/laboratory/canonicalLabReplay";
|
||||||
|
import type { ObservationSessionReplayLaunch } from "../../core/observation/sessionArchive";
|
||||||
|
import { laboratoryResultRerunProfile } from "../../core/observation/viewerProfile";
|
||||||
|
import { resolveObservationSessionReplay } from "../../core/observation/useObservationSessions";
|
||||||
|
import type { AIViewerLayer } from "../../core/observatory/aiComposition";
|
||||||
|
import {
|
||||||
|
fetchLabViewProfile,
|
||||||
|
saveLabViewProfile,
|
||||||
|
} from "../../core/observatory/labViewProfile";
|
||||||
|
import {
|
||||||
|
defaultSceneSettings,
|
||||||
|
type PointColorMode,
|
||||||
|
type PointPalette,
|
||||||
|
type SceneSettings,
|
||||||
|
} from "../../sceneSettings";
|
||||||
|
|
||||||
|
type MediaMode = "camera";
|
||||||
|
type SpatialMode = "3d" | "plan";
|
||||||
|
|
||||||
|
interface CanonicalReplayLaunch {
|
||||||
|
base: ObservationSessionReplayLaunch;
|
||||||
|
replay: CanonicalLabReplayDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RERUN_UNIFIED_CAMERA_SHARE_PERCENT = 46;
|
||||||
|
const LAB_SCENE_SETTINGS_STORAGE_PREFIX = "missioncore.observatory.lab-scene-settings.v1:";
|
||||||
|
const LAB_VIEW_PROFILE_ERROR_TOAST = "observatory-lab-view-profile-error";
|
||||||
|
|
||||||
|
const COLOR_OPTIONS: readonly { value: PointColorMode; label: string }[] = [
|
||||||
|
{ value: "intensity", label: "Интенсивность" },
|
||||||
|
{ value: "height", label: "Высота Z" },
|
||||||
|
{ value: "distance", label: "Дистанция" },
|
||||||
|
{ value: "rgb", label: "RGB" },
|
||||||
|
{ value: "class", label: "Класс" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const PALETTE_OPTIONS: readonly { value: PointPalette; label: string }[] = [
|
||||||
|
{ value: "turbo", label: "Turbo" },
|
||||||
|
{ value: "viridis", label: "Viridis" },
|
||||||
|
{ value: "plasma", label: "Plasma" },
|
||||||
|
{ value: "grayscale", label: "Серый" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const LAB_SCENE_DEFAULTS: SceneSettings = {
|
||||||
|
...defaultSceneSettings,
|
||||||
|
pointSize: 3.8,
|
||||||
|
accumulationSeconds: 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function normalizeLabSceneSettings(settings: SceneSettings): SceneSettings {
|
||||||
|
const pointSize = Number.isFinite(settings.pointSize) ? settings.pointSize : LAB_SCENE_DEFAULTS.pointSize;
|
||||||
|
const accumulationSeconds = Number.isFinite(settings.accumulationSeconds)
|
||||||
|
? settings.accumulationSeconds : LAB_SCENE_DEFAULTS.accumulationSeconds;
|
||||||
|
return {
|
||||||
|
...settings,
|
||||||
|
pointSize: Math.round(Math.max(0.1, pointSize) * 10) / 10,
|
||||||
|
accumulationSeconds: Math.round(Math.max(0, accumulationSeconds)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadLabSceneSettings(resultId: string): SceneSettings {
|
||||||
|
if (typeof window === "undefined") return LAB_SCENE_DEFAULTS;
|
||||||
|
try {
|
||||||
|
const raw = window.localStorage.getItem(`${LAB_SCENE_SETTINGS_STORAGE_PREFIX}${resultId}`);
|
||||||
|
if (raw === null) return LAB_SCENE_DEFAULTS;
|
||||||
|
const value: unknown = JSON.parse(raw);
|
||||||
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return LAB_SCENE_DEFAULTS;
|
||||||
|
const row = value as Record<string, unknown>;
|
||||||
|
const pointSize = row.pointSize;
|
||||||
|
const accumulationSeconds = row.accumulationSeconds;
|
||||||
|
const colorMode = row.colorMode;
|
||||||
|
const palette = row.palette;
|
||||||
|
const showGrid = row.showGrid;
|
||||||
|
const showLabels = row.showLabels;
|
||||||
|
const showCameraFrustums = row.showCameraFrustums;
|
||||||
|
if (
|
||||||
|
typeof pointSize !== "number" || !Number.isFinite(pointSize) || pointSize < 0.1
|
||||||
|
|| typeof accumulationSeconds !== "number" || !Number.isFinite(accumulationSeconds)
|
||||||
|
|| accumulationSeconds < 0
|
||||||
|
|| !COLOR_OPTIONS.some((option) => option.value === colorMode)
|
||||||
|
|| !PALETTE_OPTIONS.some((option) => option.value === palette)
|
||||||
|
|| typeof showGrid !== "boolean"
|
||||||
|
|| typeof showLabels !== "boolean"
|
||||||
|
|| typeof showCameraFrustums !== "boolean"
|
||||||
|
) return LAB_SCENE_DEFAULTS;
|
||||||
|
return {
|
||||||
|
...LAB_SCENE_DEFAULTS,
|
||||||
|
pointSize,
|
||||||
|
accumulationSeconds,
|
||||||
|
colorMode: colorMode as PointColorMode,
|
||||||
|
palette: palette as PointPalette,
|
||||||
|
showGrid,
|
||||||
|
showLabels,
|
||||||
|
showCameraFrustums,
|
||||||
|
};
|
||||||
|
} catch {
|
||||||
|
return LAB_SCENE_DEFAULTS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CanonicalResultRerunReplay({
|
||||||
|
resultId,
|
||||||
|
sessionId,
|
||||||
|
initialPlaybackStartSeconds,
|
||||||
|
resolveReplay,
|
||||||
|
semantics = false,
|
||||||
|
detections = false,
|
||||||
|
costmap = false,
|
||||||
|
moduleIds = [],
|
||||||
|
viewerLayers = [],
|
||||||
|
label = "Сохранённый результат",
|
||||||
|
}: {
|
||||||
|
resultId: string;
|
||||||
|
sessionId: string;
|
||||||
|
initialPlaybackStartSeconds?: number;
|
||||||
|
resolveReplay: (resultId: string, launch: ObservationSessionReplayLaunch, options: { signal: AbortSignal }) => Promise<CanonicalLabReplayDescriptor>;
|
||||||
|
semantics?: boolean;
|
||||||
|
detections?: boolean;
|
||||||
|
costmap?: boolean;
|
||||||
|
portable?: boolean;
|
||||||
|
moduleIds?: readonly string[];
|
||||||
|
viewerLayers?: readonly AIViewerLayer[];
|
||||||
|
label?: string;
|
||||||
|
}) {
|
||||||
|
const {
|
||||||
|
mediaMode,
|
||||||
|
spatialMode,
|
||||||
|
splitPrimarySize,
|
||||||
|
splitOrientation,
|
||||||
|
expanded,
|
||||||
|
onMediaModeChange,
|
||||||
|
onSpatialModeChange,
|
||||||
|
onSplitPrimarySizeChange,
|
||||||
|
onExpandedChange,
|
||||||
|
} = useCanonicalRecordedLabReplayState<MediaMode, SpatialMode>({
|
||||||
|
initialMediaMode: "camera",
|
||||||
|
initialSpatialMode: "3d",
|
||||||
|
});
|
||||||
|
const layerIds = useMemo(
|
||||||
|
() => new Set(viewerLayers.map((layer) => layer.layerId)),
|
||||||
|
[viewerLayers],
|
||||||
|
);
|
||||||
|
const hasProjectedLayers = viewerLayers.length > 0;
|
||||||
|
const hasDDRNet = layerIds.has("camera.ddrnet")
|
||||||
|
|| (!hasProjectedLayers && semantics && moduleIds.includes("ddrnet"));
|
||||||
|
const hasEoMT = layerIds.has("camera.eomt")
|
||||||
|
|| (!hasProjectedLayers && semantics && moduleIds.includes("eomt"));
|
||||||
|
const hasFallbackSemantics = semantics && !hasDDRNet && !hasEoMT;
|
||||||
|
const hasDetections = layerIds.has("camera.detections") || (!hasProjectedLayers && detections);
|
||||||
|
const hasTgs = layerIds.has("spatial.tgs") || (!hasProjectedLayers && costmap);
|
||||||
|
const legacyUnclassified = !hasProjectedLayers
|
||||||
|
&& moduleIds.length === 0
|
||||||
|
&& !semantics
|
||||||
|
&& !detections
|
||||||
|
&& !costmap;
|
||||||
|
const hasCameraPane = layerIds.has("camera.source")
|
||||||
|
|| (!hasProjectedLayers && (legacyUnclassified || semantics || detections));
|
||||||
|
const hasSourcePoints = layerIds.has("spatial.source-points")
|
||||||
|
|| (!hasProjectedLayers && (legacyUnclassified || costmap || moduleIds.includes("object-distance")));
|
||||||
|
const hasLocalSlam = layerIds.has("spatial.local-slam")
|
||||||
|
|| (!hasProjectedLayers && (legacyUnclassified || costmap || moduleIds.includes("object-distance")));
|
||||||
|
const hasSpatialPane = hasSourcePoints || hasLocalSlam || hasTgs;
|
||||||
|
const splitView = hasCameraPane && hasSpatialPane;
|
||||||
|
|
||||||
|
const [showCamera, setShowCamera] = useState(true);
|
||||||
|
const [showDDRNet, setShowDDRNet] = useState(hasDDRNet || hasFallbackSemantics);
|
||||||
|
const [showEoMT, setShowEoMT] = useState(hasEoMT);
|
||||||
|
const [showDetections, setShowDetections] = useState(hasDetections);
|
||||||
|
const [showSourcePoints, setShowSourcePoints] = useState(hasSourcePoints);
|
||||||
|
const [showLocalSlam, setShowLocalSlam] = useState(hasLocalSlam);
|
||||||
|
const [showTgs, setShowTgs] = useState(hasTgs);
|
||||||
|
const [followTarget, setFollowTarget] = useState(false);
|
||||||
|
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||||
|
const [sceneDraft, setSceneDraft] = useState<SceneSettings>(() => loadLabSceneSettings(resultId));
|
||||||
|
const [blueprintSplitPrimarySize, setBlueprintSplitPrimarySize] = useState(
|
||||||
|
RERUN_UNIFIED_CAMERA_SHARE_PERCENT,
|
||||||
|
);
|
||||||
|
const [toasts, setToasts] = useState<ToastItem[]>([]);
|
||||||
|
const [viewResetGeneration, setViewResetGeneration] = useState<0 | 1>(hasTgs ? 1 : 0);
|
||||||
|
const [playback, setPlayback] = useState<RerunPlaybackState | null>(null);
|
||||||
|
const [playbackController, setPlaybackController] = useState<RerunPlaybackController | null>(null);
|
||||||
|
const [viewerStatus, setViewerStatus] = useState<RerunViewportStatus>("idle");
|
||||||
|
const [launch, setLaunch] = useState<CanonicalReplayLaunch | null>(null);
|
||||||
|
const [launchError, setLaunchError] = useState<string | null>(null);
|
||||||
|
const sceneDraftRef = useRef(sceneDraft);
|
||||||
|
const previousSplitViewRef = useRef(splitView);
|
||||||
|
const trackedLaunchSha256Ref = useRef<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const launchSha256 = launch?.replay.sha256 ?? null;
|
||||||
|
if (trackedLaunchSha256Ref.current !== launchSha256 || (splitView && !previousSplitViewRef.current)) {
|
||||||
|
onSplitPrimarySizeChange(RERUN_UNIFIED_CAMERA_SHARE_PERCENT);
|
||||||
|
setBlueprintSplitPrimarySize(RERUN_UNIFIED_CAMERA_SHARE_PERCENT);
|
||||||
|
}
|
||||||
|
trackedLaunchSha256Ref.current = launchSha256;
|
||||||
|
previousSplitViewRef.current = splitView;
|
||||||
|
}, [launch?.replay.sha256, onSplitPrimarySizeChange, splitView]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!splitView) return;
|
||||||
|
const timer = window.setTimeout(() => setBlueprintSplitPrimarySize(splitPrimarySize), 75);
|
||||||
|
return () => window.clearTimeout(timer);
|
||||||
|
}, [splitPrimarySize, splitView]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
try {
|
||||||
|
window.localStorage.setItem(
|
||||||
|
`${LAB_SCENE_SETTINGS_STORAGE_PREFIX}${resultId}`,
|
||||||
|
JSON.stringify({
|
||||||
|
pointSize: sceneDraft.pointSize,
|
||||||
|
accumulationSeconds: sceneDraft.accumulationSeconds,
|
||||||
|
colorMode: sceneDraft.colorMode,
|
||||||
|
palette: sceneDraft.palette,
|
||||||
|
showGrid: sceneDraft.showGrid,
|
||||||
|
showLabels: sceneDraft.showLabels,
|
||||||
|
showCameraFrustums: sceneDraft.showCameraFrustums,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
// The visual remains usable when the browser refuses local persistence.
|
||||||
|
}
|
||||||
|
}, [resultId, sceneDraft]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
void fetchLabViewProfile(resultId, controller.signal).then((settings) => {
|
||||||
|
if (!controller.signal.aborted && settings) {
|
||||||
|
sceneDraftRef.current = settings;
|
||||||
|
setSceneDraft(settings);
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
// The per-browser copy remains a usable fallback while the local service recovers.
|
||||||
|
});
|
||||||
|
return () => controller.abort();
|
||||||
|
}, [resultId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const controller = new AbortController();
|
||||||
|
setLaunch(null);
|
||||||
|
setLaunchError(null);
|
||||||
|
setPlayback(null);
|
||||||
|
setPlaybackController(null);
|
||||||
|
setViewerStatus("idle");
|
||||||
|
void resolveObservationSessionReplay(sessionId, {
|
||||||
|
signal: controller.signal,
|
||||||
|
maximumWaitMs: 30 * 60 * 1000,
|
||||||
|
onUpdate: () => undefined,
|
||||||
|
}).then(async (value) => ({
|
||||||
|
base: value,
|
||||||
|
replay: await resolveReplay(resultId, value, { signal: controller.signal }),
|
||||||
|
})).then((value) => {
|
||||||
|
if (!controller.signal.aborted) setLaunch(value);
|
||||||
|
}).catch((caught: unknown) => {
|
||||||
|
if (!controller.signal.aborted) {
|
||||||
|
setLaunchError(caught instanceof Error ? caught.message : "Сохранённая запись недоступна.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () => controller.abort();
|
||||||
|
}, [resultId, sessionId, resolveReplay]);
|
||||||
|
|
||||||
|
const presentationReady = playbackController !== null
|
||||||
|
&& isRecordedPlaybackPresentationReady(viewerStatus, playback);
|
||||||
|
const presentationState = launchError || viewerStatus === "error"
|
||||||
|
? "error"
|
||||||
|
: presentationReady ? "ready" : "loading";
|
||||||
|
const sceneSettings = useMemo<SceneSettings>(() => ({
|
||||||
|
...sceneDraft,
|
||||||
|
projection: spatialMode === "plan" ? "2d" : "3d",
|
||||||
|
showPoints: spatialMode !== null && (showSourcePoints || showLocalSlam),
|
||||||
|
showTrajectory: spatialMode !== null && showLocalSlam,
|
||||||
|
accumulationSeconds: showLocalSlam ? sceneDraft.accumulationSeconds : 0,
|
||||||
|
}), [sceneDraft, showLocalSlam, showSourcePoints, spatialMode]);
|
||||||
|
const semanticVisible = showDDRNet || showEoMT;
|
||||||
|
const profile = launch ? laboratoryResultRerunProfile({
|
||||||
|
resultId,
|
||||||
|
sourceUrl: launch.replay.sourceUrl,
|
||||||
|
artifact: {
|
||||||
|
sourceUrl: launch.replay.sourceUrl,
|
||||||
|
viewerSourceUrl: launch.replay.viewerSourceUrl,
|
||||||
|
byteLength: launch.replay.byteLength,
|
||||||
|
sha256: launch.replay.sha256,
|
||||||
|
},
|
||||||
|
blueprintSourceUrl: launch.replay.blueprintSourceUrl,
|
||||||
|
autoplayWhenReady: false,
|
||||||
|
presentationGate: "ready",
|
||||||
|
expectedTimelineStartSeconds: launch.base.timelineStartSeconds,
|
||||||
|
expectedTimelineEndSeconds: launch.base.timelineEndSeconds,
|
||||||
|
initialPlaybackStartSeconds: initialPlaybackStartSeconds
|
||||||
|
?? ((launch.base.mediaSources[0]?.timelineStartSeconds ?? launch.base.timelineStartSeconds)
|
||||||
|
+ (hasTgs ? 0.000001 : 0)),
|
||||||
|
view: hasCameraPane && !hasSpatialPane ? "perception" : "spatial",
|
||||||
|
viewResetGeneration,
|
||||||
|
followTrajectory: followTarget,
|
||||||
|
semanticLayer: showEoMT ? "city" : "vegetation",
|
||||||
|
unifiedPerception: splitView,
|
||||||
|
unifiedCameraShare: blueprintSplitPrimarySize / 100,
|
||||||
|
planView: spatialMode === "plan",
|
||||||
|
perceptionLayers: {
|
||||||
|
enabled: true,
|
||||||
|
cameraImage: showCamera,
|
||||||
|
detections2d: hasDetections && showDetections,
|
||||||
|
segmentation: semantics && semanticVisible,
|
||||||
|
cuboids3d: false,
|
||||||
|
costmap: hasTgs && showTgs,
|
||||||
|
},
|
||||||
|
perceptionRetryGeneration: 0,
|
||||||
|
lockPerceptionCameraInteraction: false,
|
||||||
|
}) : null;
|
||||||
|
|
||||||
|
const toggle = (caption: string, active: boolean, onClick: () => void) => (
|
||||||
|
<Button key={caption} size="dense" shape="pill" variant={active ? "primary" : "secondary"}
|
||||||
|
aria-pressed={active} onClick={onClick}>
|
||||||
|
{caption}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
const openSlamSettings = useCallback(() => {
|
||||||
|
if (expanded) onExpandedChange(false);
|
||||||
|
setSettingsOpen(true);
|
||||||
|
}, [expanded, onExpandedChange]);
|
||||||
|
const patchSceneDraft = useCallback((patch: Partial<SceneSettings>) => {
|
||||||
|
const next = { ...sceneDraftRef.current, ...patch };
|
||||||
|
sceneDraftRef.current = next;
|
||||||
|
setSceneDraft(next);
|
||||||
|
}, []);
|
||||||
|
const closeSlamSettings = useCallback(() => {
|
||||||
|
const next = normalizeLabSceneSettings(sceneDraftRef.current);
|
||||||
|
sceneDraftRef.current = next;
|
||||||
|
setSceneDraft(next);
|
||||||
|
setSettingsOpen(false);
|
||||||
|
void saveLabViewProfile(resultId, next).then((settings) => {
|
||||||
|
sceneDraftRef.current = settings;
|
||||||
|
setSceneDraft(settings);
|
||||||
|
setToasts((current) => current.filter(({ id }) => id !== LAB_VIEW_PROFILE_ERROR_TOAST));
|
||||||
|
}).catch(() => {
|
||||||
|
setToasts((current) => [
|
||||||
|
...current.filter(({ id }) => id !== LAB_VIEW_PROFILE_ERROR_TOAST),
|
||||||
|
{
|
||||||
|
id: LAB_VIEW_PROFILE_ERROR_TOAST,
|
||||||
|
tone: "error",
|
||||||
|
title: "Настройки LAB не сохранились",
|
||||||
|
description: "Изменения остались в текущем просмотре. Откройте настройки и повторите.",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}, [resultId]);
|
||||||
|
const mediaLayerControls = (
|
||||||
|
<div className="m4-replay-threat-visual__pane-layer-controls" role="group" aria-label="Слои камеры">
|
||||||
|
{toggle("КАМЕРА", showCamera, () => setShowCamera((value) => !value))}
|
||||||
|
{hasEoMT ? toggle("EOMT", showEoMT, () => setShowEoMT((value) => !value)) : null}
|
||||||
|
{hasDDRNet || hasFallbackSemantics
|
||||||
|
? toggle("DDRNET", showDDRNet, () => setShowDDRNet((value) => !value)) : null}
|
||||||
|
{hasDetections
|
||||||
|
? toggle("РАМКИ", showDetections, () => setShowDetections((value) => !value)) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
const spatialLayerControls = (
|
||||||
|
<div className="m4-replay-threat-visual__pane-layer-controls" role="group" aria-label="Слои облака точек">
|
||||||
|
{hasSourcePoints
|
||||||
|
? toggle("ИСХ. ТОЧКИ", showSourcePoints, () => setShowSourcePoints((value) => !value)) : null}
|
||||||
|
{hasLocalSlam ? (
|
||||||
|
<div className="m4-replay-threat-visual__layer-with-settings">
|
||||||
|
{toggle("ЛОК. SLAM", showLocalSlam, () => setShowLocalSlam((value) => !value))}
|
||||||
|
<IconButton label="Настройки облака SLAM" onClick={openSlamSettings}>
|
||||||
|
<Icon name="settings" size={14} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{hasTgs ? toggle("TGS", showTgs, () => setShowTgs((value) => !value)) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
const followTargetControl = toggle(
|
||||||
|
"СЛЕДОВАТЬ",
|
||||||
|
followTarget,
|
||||||
|
() => setFollowTarget((value) => !value),
|
||||||
|
);
|
||||||
|
const resetSpatialView = (
|
||||||
|
<IconButton label="Сбросить положение видов"
|
||||||
|
onClick={() => setViewResetGeneration((value) => value === 0 ? 1 : 0)}>
|
||||||
|
<Icon name="refresh" size={16} />
|
||||||
|
</IconButton>
|
||||||
|
);
|
||||||
|
|
||||||
|
const transport = presentationReady && playback && playbackController ? (
|
||||||
|
<ObservationTimeline
|
||||||
|
className="m4-replay-threat-visual__timeline canonical-vegetation-rerun-replay__timeline"
|
||||||
|
active
|
||||||
|
sourceCount={3}
|
||||||
|
mode="recorded"
|
||||||
|
seekable
|
||||||
|
synchronization="shared-clock"
|
||||||
|
rangeNs={playback.rangeNs}
|
||||||
|
currentNs={playback.currentNs}
|
||||||
|
playing={playback.playing}
|
||||||
|
onSeek={playbackController.seek}
|
||||||
|
onPlayingChange={playbackController.setPlaying}
|
||||||
|
showJumpToEnd={false}
|
||||||
|
/>
|
||||||
|
) : undefined;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="canonical-vegetation-rerun-replay" data-presentation-state={presentationState}
|
||||||
|
aria-busy={presentationState === "loading"}>
|
||||||
|
<CanonicalRecordedLabReplay
|
||||||
|
label={label}
|
||||||
|
mediaMode={hasCameraPane ? mediaMode ?? "camera" : "none"}
|
||||||
|
mediaModes={[{ value: "camera", label: "КАМЕРА" }]}
|
||||||
|
spatialMode={hasSpatialPane ? spatialMode ?? "3d" : "none"}
|
||||||
|
spatialModes={[{ value: "3d", label: "3D" }, { value: "plan", label: "ПЛАН" }]}
|
||||||
|
expanded={expanded}
|
||||||
|
splitPrimarySize={splitPrimarySize}
|
||||||
|
splitOrientation={splitView ? "vertical" : splitOrientation}
|
||||||
|
mediaAriaLabel="Камера и рассчитанные слои"
|
||||||
|
spatialAriaLabel={spatialMode === "plan" ? "Вид сверху" : "Трёхмерная сцена"}
|
||||||
|
mediaLayerControls={mediaLayerControls}
|
||||||
|
spatialLayerControls={spatialLayerControls}
|
||||||
|
spatialTrailingControl={followTargetControl}
|
||||||
|
mediaModeControlsVisible={false}
|
||||||
|
paneToolbarsAlwaysVisible
|
||||||
|
mediaMultiLayer
|
||||||
|
actions={resetSpatialView}
|
||||||
|
unifiedContent={profile ? (
|
||||||
|
<div className="canonical-vegetation-rerun-replay__viewport-lock"
|
||||||
|
data-split-view={splitView ? "true" : undefined}>
|
||||||
|
<RerunViewport profile={profile} sceneSettings={sceneSettings}
|
||||||
|
onStatusChange={setViewerStatus} onPlaybackChange={setPlayback}
|
||||||
|
onPlaybackControllerChange={setPlaybackController} />
|
||||||
|
</div>
|
||||||
|
) : launchError ? (
|
||||||
|
<div className="l3-visual-audit__state" role="alert">{launchError}</div>
|
||||||
|
) : <div aria-hidden="true" />}
|
||||||
|
emptyMessage="Слои результата недоступны."
|
||||||
|
deckOverlays={presentationState === "loading" ? (
|
||||||
|
<div className="canonical-vegetation-rerun-replay__loading">
|
||||||
|
<ActivityIndicator label="Загружаем синхронизированную запись" />
|
||||||
|
</div>
|
||||||
|
) : undefined}
|
||||||
|
transport={transport}
|
||||||
|
onMediaModeChange={onMediaModeChange}
|
||||||
|
onSpatialModeChange={onSpatialModeChange}
|
||||||
|
onExpandedChange={onExpandedChange}
|
||||||
|
onSplitPrimarySizeChange={onSplitPrimarySizeChange}
|
||||||
|
/>
|
||||||
|
<Window open={settingsOpen} title="Отображение" subtitle="Параметры облака SLAM"
|
||||||
|
placement="end" size="sm" onClose={closeSlamSettings}>
|
||||||
|
<Inspector defaultOpen={["points"]} sections={[
|
||||||
|
{
|
||||||
|
id: "points",
|
||||||
|
label: "Облако точек",
|
||||||
|
description: "Размер и способ окрашивания",
|
||||||
|
content: <>
|
||||||
|
<RangeControl label="Размер точки" min={0.1} max={10} step={0.1}
|
||||||
|
exactValueBounds={{ min: 0.1 }}
|
||||||
|
value={sceneDraft.pointSize} formatValue={(value) => `${value.toFixed(1)} пкс`}
|
||||||
|
onChange={(pointSize) => patchSceneDraft({ pointSize })} />
|
||||||
|
<InspectorSelectField label="Атрибут цвета" value={sceneDraft.colorMode}
|
||||||
|
options={[...COLOR_OPTIONS]}
|
||||||
|
onChange={(colorMode) => patchSceneDraft({ colorMode })} />
|
||||||
|
<InspectorSelectField label="Палитра" value={sceneDraft.palette}
|
||||||
|
options={[...PALETTE_OPTIONS]}
|
||||||
|
onChange={(palette) => patchSceneDraft({ palette })} />
|
||||||
|
</>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "history",
|
||||||
|
label: "Накопление и время",
|
||||||
|
description: "История облака и траектория",
|
||||||
|
content: <RangeControl label="Накопление" min={0} max={500} step={1}
|
||||||
|
exactValueBounds={{ min: 0 }}
|
||||||
|
value={sceneDraft.accumulationSeconds} formatValue={(value) => `${value} с`}
|
||||||
|
onChange={(accumulationSeconds) => patchSceneDraft({ accumulationSeconds })} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "environment",
|
||||||
|
label: "Окружение сцены",
|
||||||
|
description: "Сетка, подписи и камеры",
|
||||||
|
content: <>
|
||||||
|
<ControlRow label="Сетка"><Checker checked={sceneDraft.showGrid} label="Показывать"
|
||||||
|
onChange={(showGrid) => patchSceneDraft({ showGrid })} /></ControlRow>
|
||||||
|
<ControlRow label="Подписи"><Checker checked={sceneDraft.showLabels} label="Показывать"
|
||||||
|
onChange={(showLabels) => patchSceneDraft({ showLabels })} /></ControlRow>
|
||||||
|
<ControlRow label="Камеры"><Checker checked={sceneDraft.showCameraFrustums} label="Показывать"
|
||||||
|
onChange={(showCameraFrustums) => patchSceneDraft({ showCameraFrustums })} /></ControlRow>
|
||||||
|
</>,
|
||||||
|
},
|
||||||
|
]} />
|
||||||
|
</Window>
|
||||||
|
<ToastStack items={toasts}
|
||||||
|
onDismiss={(id) => setToasts((current) => current.filter((item) => item.id !== id))} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { CanonicalResultRerunReplay } from "./CanonicalResultRerunReplay";
|
||||||
|
import { resolveCanonicalLabReplay } from "../../core/laboratory/canonicalLabReplay";
|
||||||
|
import type { VegetationFullRouteReview } from "../../core/laboratory/vegetationShadow";
|
||||||
|
|
||||||
|
export function CanonicalVegetationRerunReplay({ resultId, review }: {
|
||||||
|
resultId: string;
|
||||||
|
review: VegetationFullRouteReview;
|
||||||
|
}) {
|
||||||
|
return <CanonicalResultRerunReplay resultId={resultId} sessionId={review.sessionId}
|
||||||
|
initialPlaybackStartSeconds={review.timelineStartSeconds} semantics
|
||||||
|
resolveReplay={resolveCanonicalLabReplay} label="RAVNOVES004TREE · канонический повтор Rerun" />;
|
||||||
|
}
|
||||||
@@ -53,7 +53,7 @@ export const LABORATORY_REPORT_TEMPLATE_VERSION =
|
|||||||
|
|
||||||
const EXECUTION_LABELS: Record<LaboratoryExecutionClass, string> = {
|
const EXECUTION_LABELS: Record<LaboratoryExecutionClass, string> = {
|
||||||
deterministic: "Детерминированный",
|
deterministic: "Детерминированный",
|
||||||
"ai-inference": "AI inference",
|
"ai-inference": "AI Inference",
|
||||||
hybrid: "Гибридный",
|
hybrid: "Гибридный",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,16 @@ export function laboratoryRecordedClipEndExclusiveNs(
|
|||||||
return last.sourceTimeNs + typicalDelta;
|
return last.sourceTimeNs + typicalDelta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function laboratoryRecordedClipClockGate(
|
||||||
|
pendingSequence: number | null,
|
||||||
|
observedSequence: number,
|
||||||
|
): { accept: boolean; pendingSequence: number | null } {
|
||||||
|
if (pendingSequence !== null && pendingSequence !== observedSequence) {
|
||||||
|
return { accept: false, pendingSequence };
|
||||||
|
}
|
||||||
|
return { accept: true, pendingSequence: null };
|
||||||
|
}
|
||||||
|
|
||||||
export function LaboratoryRecordedClipPlayer({
|
export function LaboratoryRecordedClipPlayer({
|
||||||
source,
|
source,
|
||||||
segmentCount,
|
segmentCount,
|
||||||
@@ -94,7 +104,8 @@ export function LaboratoryRecordedClipPlayer({
|
|||||||
}) {
|
}) {
|
||||||
const [companionSpatialSize, setCompanionSpatialSize] = useState(69);
|
const [companionSpatialSize, setCompanionSpatialSize] = useState(69);
|
||||||
const lastEmittedSequenceRef = useRef(sequence);
|
const lastEmittedSequenceRef = useRef(sequence);
|
||||||
lastEmittedSequenceRef.current = sequence;
|
const lastObservedSequenceRef = useRef<number | null>(sequence);
|
||||||
|
const pendingSequenceRef = useRef<number | null>(null);
|
||||||
const frame = useMemo(
|
const frame = useMemo(
|
||||||
() => frames.find((candidate) => candidate.sequence === sequence) ?? frames[0] ?? null,
|
() => frames.find((candidate) => candidate.sequence === sequence) ?? frames[0] ?? null,
|
||||||
[frames, sequence],
|
[frames, sequence],
|
||||||
@@ -113,23 +124,42 @@ export function LaboratoryRecordedClipPlayer({
|
|||||||
if (!continuousPlayback && playing) onPlayingChange(false);
|
if (!continuousPlayback && playing) onPlayingChange(false);
|
||||||
}, [continuousPlayback, onPlayingChange, playing]);
|
}, [continuousPlayback, onPlayingChange, playing]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (lastObservedSequenceRef.current !== sequence) {
|
||||||
|
pendingSequenceRef.current = sequence;
|
||||||
|
}
|
||||||
|
lastEmittedSequenceRef.current = sequence;
|
||||||
|
}, [sequence]);
|
||||||
|
|
||||||
const emitSequence = useCallback((nextSequence: number) => {
|
const emitSequence = useCallback((nextSequence: number) => {
|
||||||
if (lastEmittedSequenceRef.current === nextSequence) return;
|
if (lastEmittedSequenceRef.current === nextSequence) return;
|
||||||
lastEmittedSequenceRef.current = nextSequence;
|
lastEmittedSequenceRef.current = nextSequence;
|
||||||
onSequenceChange(nextSequence);
|
onSequenceChange(nextSequence);
|
||||||
}, [onSequenceChange]);
|
}, [onSequenceChange]);
|
||||||
|
|
||||||
|
const requestSequence = useCallback((nextSequence: number) => {
|
||||||
|
pendingSequenceRef.current = nextSequence;
|
||||||
|
emitSequence(nextSequence);
|
||||||
|
}, [emitSequence]);
|
||||||
|
|
||||||
const handlePlaybackChange = useCallback((next: RecordedObservationPlayback) => {
|
const handlePlaybackChange = useCallback((next: RecordedObservationPlayback) => {
|
||||||
const sourceTimeNs = Math.round(next.currentSeconds * 1_000_000_000);
|
const sourceTimeNs = Math.round(next.currentSeconds * 1_000_000_000);
|
||||||
const first = frames[0];
|
const first = frames[0];
|
||||||
if (!first || endExclusiveNs === null) return;
|
if (!first || endExclusiveNs === null) return;
|
||||||
if (sourceTimeNs >= endExclusiveNs) {
|
if (sourceTimeNs >= endExclusiveNs) {
|
||||||
emitSequence(first.sequence);
|
requestSequence(first.sequence);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const nearest = nearestLaboratoryRecordedClipFrame(frames, sourceTimeNs);
|
const nearest = nearestLaboratoryRecordedClipFrame(frames, sourceTimeNs);
|
||||||
if (nearest) emitSequence(nearest.sequence);
|
if (!nearest) return;
|
||||||
}, [emitSequence, endExclusiveNs, frames]);
|
lastObservedSequenceRef.current = nearest.sequence;
|
||||||
|
const gate = laboratoryRecordedClipClockGate(
|
||||||
|
pendingSequenceRef.current,
|
||||||
|
nearest.sequence,
|
||||||
|
);
|
||||||
|
pendingSequenceRef.current = gate.pendingSequence;
|
||||||
|
if (gate.accept) emitSequence(nearest.sequence);
|
||||||
|
}, [emitSequence, endExclusiveNs, frames, requestSequence]);
|
||||||
|
|
||||||
const timelineStart = frames[0]?.sourceTimeNs ?? 0;
|
const timelineStart = frames[0]?.sourceTimeNs ?? 0;
|
||||||
const timelineEnd = frames.at(-1)?.sourceTimeNs ?? timelineStart + 1;
|
const timelineEnd = frames.at(-1)?.sourceTimeNs ?? timelineStart + 1;
|
||||||
@@ -142,7 +172,7 @@ export function LaboratoryRecordedClipPlayer({
|
|||||||
className="laboratory-recorded-clip-player__spatial"
|
className="laboratory-recorded-clip-player__spatial"
|
||||||
aria-hidden={cameraPresentation === "primary"}
|
aria-hidden={cameraPresentation === "primary"}
|
||||||
>
|
>
|
||||||
{cameraPresentation !== "primary" ? alternativeScene : null}
|
{alternativeScene}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
const cameraPane = (
|
const cameraPane = (
|
||||||
@@ -202,7 +232,7 @@ export function LaboratoryRecordedClipPlayer({
|
|||||||
onPlayingChange={continuousPlayback ? onPlayingChange : undefined}
|
onPlayingChange={continuousPlayback ? onPlayingChange : undefined}
|
||||||
onSeek={(timeNs) => {
|
onSeek={(timeNs) => {
|
||||||
const nearest = nearestLaboratoryRecordedClipFrame(frames, timeNs);
|
const nearest = nearestLaboratoryRecordedClipFrame(frames, timeNs);
|
||||||
if (nearest) emitSequence(nearest.sequence);
|
if (nearest) requestSequence(nearest.sequence);
|
||||||
}}
|
}}
|
||||||
showJumpToEnd={false}
|
showJumpToEnd={false}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { GlassSurface } from "@nodedc/ui-react";
|
||||||
|
import { CanonicalResultRerunReplay } from "./CanonicalResultRerunReplay";
|
||||||
|
import { resolveCanonicalLabReplay } from "../../core/laboratory/canonicalLabReplay";
|
||||||
|
import type { ObservatoryPortableResultReview } from "../../core/observatory/recordedRun";
|
||||||
|
import type { ObservationSessionReplayLaunch } from "../../core/observation/sessionArchive";
|
||||||
|
|
||||||
|
function resolvePortableTgsReplay(resultId: string, launch: ObservationSessionReplayLaunch,
|
||||||
|
options: { signal: AbortSignal }) {
|
||||||
|
return resolveCanonicalLabReplay(resultId, launch, { ...options, sourceKind: "portable-tgs" });
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolvePortableSemanticReplay(resultId: string, launch: ObservationSessionReplayLaunch,
|
||||||
|
options: { signal: AbortSignal }) {
|
||||||
|
return resolveCanonicalLabReplay(resultId, launch, { ...options, sourceKind: "portable-semantic" });
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolvePortableObjectReplay(resultId: string, launch: ObservationSessionReplayLaunch,
|
||||||
|
options: { signal: AbortSignal }) {
|
||||||
|
return resolveCanonicalLabReplay(resultId, launch, { ...options, sourceKind: "portable-objects" });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PortableResultReplay({
|
||||||
|
review,
|
||||||
|
label,
|
||||||
|
}: {
|
||||||
|
review: ObservatoryPortableResultReview;
|
||||||
|
label?: string;
|
||||||
|
}) {
|
||||||
|
const schema = review.resultDocument.schema_version;
|
||||||
|
const tgs = schema === "missioncore.recorded-tgs-costmap-review/v2";
|
||||||
|
const module = review.resultDocument.module;
|
||||||
|
const moduleId = module && typeof module === "object" && "module_id" in module
|
||||||
|
? module.module_id : undefined;
|
||||||
|
const semantics = schema === "missioncore.recorded-eomt-ddrnet-review/v2"
|
||||||
|
|| (schema === "missioncore.recorded-ai-layer-review/v1"
|
||||||
|
&& (moduleId === "ddrnet" || moduleId === "eomt"));
|
||||||
|
const objects = schema === "missioncore.recorded-ai-layer-review/v1"
|
||||||
|
&& (moduleId === "rf-detr" || moduleId === "object-distance");
|
||||||
|
if (!tgs && !semantics && !objects) return (
|
||||||
|
<GlassSurface className="observatory-replay-state" padding="lg">
|
||||||
|
<p role="alert">Сохранённый результат этого профиля пока не поддерживает визуальный разбор.</p>
|
||||||
|
</GlassSurface>
|
||||||
|
);
|
||||||
|
return <CanonicalResultRerunReplay key={review.resultId} resultId={review.resultId}
|
||||||
|
sessionId={review.sourceSessionId} portable costmap={tgs} semantics={semantics}
|
||||||
|
detections={objects}
|
||||||
|
moduleIds={typeof moduleId === "string" ? [moduleId] : []}
|
||||||
|
label={label}
|
||||||
|
resolveReplay={tgs ? resolvePortableTgsReplay
|
||||||
|
: objects ? resolvePortableObjectReplay : resolvePortableSemanticReplay} />;
|
||||||
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
RecordedFmp4Player,
|
RecordedFmp4Player,
|
||||||
type RecordedObservationPlayback,
|
type RecordedObservationPlayback,
|
||||||
} from "../RecordedFmp4Player";
|
} from "../RecordedFmp4Player";
|
||||||
import type { ObservationSourceDescriptor } from "../../core/runtime/contracts";
|
import type { ObservationSourceDescriptor } from "../../core/runtime/contracts";
|
||||||
|
import type { RecordedCameraAdmissionState } from "../../core/observation/recordedSessionAdmission";
|
||||||
import {
|
import {
|
||||||
RecordedEvidenceBoxOverlay,
|
RecordedEvidenceBoxOverlay,
|
||||||
type RecordedEvidenceBox,
|
type RecordedEvidenceBox,
|
||||||
@@ -25,6 +28,7 @@ export function RecordedEvidenceVideoScene({
|
|||||||
imageWidth,
|
imageWidth,
|
||||||
imageHeight,
|
imageHeight,
|
||||||
boxes,
|
boxes,
|
||||||
|
overlaySeconds,
|
||||||
semanticOverlay,
|
semanticOverlay,
|
||||||
pointCloudOverlay,
|
pointCloudOverlay,
|
||||||
ariaLabel,
|
ariaLabel,
|
||||||
@@ -33,12 +37,17 @@ export function RecordedEvidenceVideoScene({
|
|||||||
segmentCount,
|
segmentCount,
|
||||||
onPlaybackChange,
|
onPlaybackChange,
|
||||||
onPlayingRejected,
|
onPlayingRejected,
|
||||||
|
onAdmissionChange,
|
||||||
|
playbackAuthority = "media",
|
||||||
|
playbackTransport = "segmented",
|
||||||
|
recoverTimestampStalls = false,
|
||||||
}: {
|
}: {
|
||||||
source: ObservationSourceDescriptor;
|
source: ObservationSourceDescriptor;
|
||||||
playback: RecordedObservationPlayback;
|
playback: RecordedObservationPlayback;
|
||||||
imageWidth: number;
|
imageWidth: number;
|
||||||
imageHeight: number;
|
imageHeight: number;
|
||||||
boxes: readonly RecordedEvidenceBox[];
|
boxes: readonly RecordedEvidenceBox[];
|
||||||
|
overlaySeconds?: number;
|
||||||
semanticOverlay?: RecordedEvidenceSemanticOverlay;
|
semanticOverlay?: RecordedEvidenceSemanticOverlay;
|
||||||
pointCloudOverlay?: RecordedEvidencePointCloudOverlayData;
|
pointCloudOverlay?: RecordedEvidencePointCloudOverlayData;
|
||||||
ariaLabel: string;
|
ariaLabel: string;
|
||||||
@@ -47,7 +56,41 @@ export function RecordedEvidenceVideoScene({
|
|||||||
segmentCount?: number;
|
segmentCount?: number;
|
||||||
onPlaybackChange?: (playback: RecordedObservationPlayback) => void;
|
onPlaybackChange?: (playback: RecordedObservationPlayback) => void;
|
||||||
onPlayingRejected?: () => void;
|
onPlayingRejected?: () => void;
|
||||||
|
onAdmissionChange?: (state: RecordedCameraAdmissionState) => void;
|
||||||
|
playbackAuthority?: "media" | "host";
|
||||||
|
playbackTransport?: "segmented" | "epoch-stream";
|
||||||
|
recoverTimestampStalls?: boolean;
|
||||||
}) {
|
}) {
|
||||||
|
const generation = source.delivery?.kind === "recorded-fmp4-manifest"
|
||||||
|
? source.delivery.manifestGenerationSha256
|
||||||
|
: "invalid";
|
||||||
|
const [admissionPhase, setAdmissionPhase] = useState<RecordedCameraAdmissionState["phase"]>(
|
||||||
|
"loading",
|
||||||
|
);
|
||||||
|
const [presentedSeconds, setPresentedSeconds] = useState<number | null>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
setAdmissionPhase("loading");
|
||||||
|
setPresentedSeconds(null);
|
||||||
|
}, [generation, source.id]);
|
||||||
|
const handleAdmissionChange = (next: RecordedCameraAdmissionState) => {
|
||||||
|
setAdmissionPhase(next.phase);
|
||||||
|
onAdmissionChange?.(next);
|
||||||
|
};
|
||||||
|
const sourceReady = admissionPhase === "ready";
|
||||||
|
const overlaysPresented = sourceReady
|
||||||
|
&& presentedSeconds !== null
|
||||||
|
&& Math.abs(presentedSeconds - playback.currentSeconds) <= 0.25
|
||||||
|
&& (overlaySeconds === undefined || Math.abs(presentedSeconds - overlaySeconds) <= 0.25);
|
||||||
|
const handlePlaybackChange = (next: RecordedObservationPlayback) => {
|
||||||
|
setPresentedSeconds(next.currentSeconds);
|
||||||
|
// During a paused operator seek the existing media element can emit its old
|
||||||
|
// timestamp while the requested MSE window is being rebuilt. That stale
|
||||||
|
// callback must not undo the host target before the decoder reaches it.
|
||||||
|
if (!playback.playing && Math.abs(next.currentSeconds - playback.currentSeconds) > 0.35) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onPlaybackChange?.(next);
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<div className="recorded-evidence-video-scene">
|
<div className="recorded-evidence-video-scene">
|
||||||
<RecordedFmp4Player
|
<RecordedFmp4Player
|
||||||
@@ -57,29 +100,35 @@ export function RecordedEvidenceVideoScene({
|
|||||||
prepare
|
prepare
|
||||||
segmentSequence={segmentSequence}
|
segmentSequence={segmentSequence}
|
||||||
segmentCount={segmentCount}
|
segmentCount={segmentCount}
|
||||||
onPlaybackChange={onPlaybackChange}
|
onPlaybackChange={handlePlaybackChange}
|
||||||
onPlayingRejected={onPlayingRejected}
|
onPlayingRejected={onPlayingRejected}
|
||||||
|
onAdmissionChange={handleAdmissionChange}
|
||||||
|
playbackAuthority={playbackAuthority}
|
||||||
|
playbackTransport={playbackTransport}
|
||||||
|
recoverTimestampStalls={recoverTimestampStalls}
|
||||||
/>
|
/>
|
||||||
{semanticOverlay ? (
|
{overlaysPresented && semanticOverlay ? (
|
||||||
<RecordedEvidenceSemanticMaskOverlay
|
<RecordedEvidenceSemanticMaskOverlay
|
||||||
{...semanticOverlay}
|
{...semanticOverlay}
|
||||||
imageWidth={imageWidth}
|
imageWidth={imageWidth}
|
||||||
imageHeight={imageHeight}
|
imageHeight={imageHeight}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{pointCloudOverlay ? (
|
{overlaysPresented && pointCloudOverlay ? (
|
||||||
<RecordedEvidencePointCloudOverlay
|
<RecordedEvidencePointCloudOverlay
|
||||||
imageWidth={imageWidth}
|
imageWidth={imageWidth}
|
||||||
imageHeight={imageHeight}
|
imageHeight={imageHeight}
|
||||||
overlay={pointCloudOverlay}
|
overlay={pointCloudOverlay}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<RecordedEvidenceBoxOverlay
|
{overlaysPresented ? (
|
||||||
imageWidth={imageWidth}
|
<RecordedEvidenceBoxOverlay
|
||||||
imageHeight={imageHeight}
|
imageWidth={imageWidth}
|
||||||
boxes={boxes}
|
imageHeight={imageHeight}
|
||||||
ariaLabel={ariaLabel}
|
boxes={boxes}
|
||||||
/>
|
ariaLabel={ariaLabel}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
fetchCanonicalRecordedLabSpatialFrame,
|
||||||
|
type CanonicalRecordedLabSpatialFrame,
|
||||||
|
} from "../../core/laboratory/canonicalRecordedLabSpatial";
|
||||||
|
|
||||||
|
const FRAME_CACHE_LIMIT = 12;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared latest-request-wins scheduler for recorded LAB spatial evidence.
|
||||||
|
*
|
||||||
|
* A feature supplies only the sealed session identity and host-clock time.
|
||||||
|
* Cache ownership, identity fencing and stale-response suppression remain in
|
||||||
|
* the canonical instrument instead of being reimplemented per experiment.
|
||||||
|
*/
|
||||||
|
export function useCanonicalRecordedLabSpatialFrame({
|
||||||
|
sessionId,
|
||||||
|
generationSha256,
|
||||||
|
targetTimeNs,
|
||||||
|
}: {
|
||||||
|
sessionId: string;
|
||||||
|
generationSha256: string | null;
|
||||||
|
targetTimeNs: number;
|
||||||
|
}) {
|
||||||
|
const [frame, setFrame] = useState<CanonicalRecordedLabSpatialFrame | null>(null);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const desiredRef = useRef<number | null>(null);
|
||||||
|
const runningRef = useRef(false);
|
||||||
|
const mountedRef = useRef(true);
|
||||||
|
const identityRef = useRef("");
|
||||||
|
const cacheRef = useRef(new Map<number, CanonicalRecordedLabSpatialFrame>());
|
||||||
|
const pumpRef = useRef<() => void>(() => undefined);
|
||||||
|
const identity = `${sessionId}:${generationSha256 ?? "unavailable"}`;
|
||||||
|
identityRef.current = identity;
|
||||||
|
|
||||||
|
pumpRef.current = () => {
|
||||||
|
if (runningRef.current || desiredRef.current === null || !generationSha256) return;
|
||||||
|
runningRef.current = true;
|
||||||
|
const requestIdentity = identity;
|
||||||
|
let settledTimeNs: number | null = null;
|
||||||
|
void (async () => {
|
||||||
|
while (
|
||||||
|
mountedRef.current
|
||||||
|
&& identityRef.current === requestIdentity
|
||||||
|
&& desiredRef.current !== null
|
||||||
|
) {
|
||||||
|
const requestedTimeNs = desiredRef.current;
|
||||||
|
const cached = cacheRef.current.get(requestedTimeNs);
|
||||||
|
try {
|
||||||
|
const next = cached ?? await fetchCanonicalRecordedLabSpatialFrame(
|
||||||
|
sessionId,
|
||||||
|
generationSha256,
|
||||||
|
requestedTimeNs,
|
||||||
|
);
|
||||||
|
if (!mountedRef.current || identityRef.current !== requestIdentity) break;
|
||||||
|
if (!cached) {
|
||||||
|
cacheRef.current.set(requestedTimeNs, next);
|
||||||
|
while (cacheRef.current.size > FRAME_CACHE_LIMIT) {
|
||||||
|
const oldest = cacheRef.current.keys().next().value as number | undefined;
|
||||||
|
if (oldest === undefined) break;
|
||||||
|
cacheRef.current.delete(oldest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setFrame(next);
|
||||||
|
setError(null);
|
||||||
|
} catch (caught: unknown) {
|
||||||
|
if (!mountedRef.current || identityRef.current !== requestIdentity) break;
|
||||||
|
setError(caught instanceof Error
|
||||||
|
? caught.message
|
||||||
|
: "Spatial-слои записанной LAB недоступны.");
|
||||||
|
}
|
||||||
|
settledTimeNs = requestedTimeNs;
|
||||||
|
if (desiredRef.current === requestedTimeNs) break;
|
||||||
|
}
|
||||||
|
})().finally(() => {
|
||||||
|
runningRef.current = false;
|
||||||
|
if (
|
||||||
|
mountedRef.current
|
||||||
|
&& desiredRef.current !== null
|
||||||
|
&& (identityRef.current !== requestIdentity || desiredRef.current !== settledTimeNs)
|
||||||
|
) {
|
||||||
|
pumpRef.current();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
mountedRef.current = true;
|
||||||
|
return () => {
|
||||||
|
mountedRef.current = false;
|
||||||
|
desiredRef.current = null;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
cacheRef.current.clear();
|
||||||
|
desiredRef.current = null;
|
||||||
|
setFrame(null);
|
||||||
|
setError(null);
|
||||||
|
}, [identity]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!generationSha256) return;
|
||||||
|
desiredRef.current = targetTimeNs;
|
||||||
|
const cached = cacheRef.current.get(targetTimeNs);
|
||||||
|
if (cached) {
|
||||||
|
setFrame(cached);
|
||||||
|
setError(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pumpRef.current();
|
||||||
|
}, [generationSha256, identity, targetTimeNs]);
|
||||||
|
|
||||||
|
return { frame, error, loading: Boolean(generationSha256) && !frame && !error };
|
||||||
|
}
|
||||||
@@ -130,8 +130,12 @@ export function useRecordedEvidencePlayback(
|
|||||||
|
|
||||||
const synchronize = useCallback((next: RecordedObservationPlayback) => {
|
const synchronize = useCallback((next: RecordedObservationPlayback) => {
|
||||||
if (!validRange(range) || !Number.isFinite(next.currentSeconds)) return;
|
if (!validRange(range) || !Number.isFinite(next.currentSeconds)) return;
|
||||||
|
// Animation-clock mode is retained only for non-media diagnostics. A
|
||||||
|
// recorded LAB with video uses the external media clock so spatial and
|
||||||
|
// overlays never advance past the frame the decoder actually presented.
|
||||||
|
if (clock === "animation") return;
|
||||||
setPlayback((current) => synchronizeRecordedEvidencePlayback(current, next, range));
|
setPlayback((current) => synchronizeRecordedEvidencePlayback(current, next, range));
|
||||||
}, [range]);
|
}, [clock, range]);
|
||||||
|
|
||||||
return useMemo(() => ({
|
return useMemo(() => ({
|
||||||
playback,
|
playback,
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import type {MapVisualSettings} from '../../core/map/mapView';
|
||||||
|
import type {CesiumMapRendererProps} from '@nodedc/map-cesium-react';
|
||||||
|
|
||||||
|
export function mapPresentation(settings:MapVisualSettings):CesiumMapRendererProps['settings'] {
|
||||||
|
return {
|
||||||
|
atmosphere_enabled: settings.atmosphereEnabled,
|
||||||
|
atmosphere_hue: settings.atmosphereHue,
|
||||||
|
atmosphere_saturation:
|
||||||
|
settings.atmosphereSaturation,
|
||||||
|
atmosphere_brightness:
|
||||||
|
settings.atmosphereBrightness,
|
||||||
|
fog_enabled: settings.fogEnabled,
|
||||||
|
fog_density: settings.fogDensity,
|
||||||
|
sun_enabled: settings.sunEnabled,
|
||||||
|
sun_hour: settings.sunHour,
|
||||||
|
sun_intensity: settings.sunIntensity,
|
||||||
|
shadows_enabled: settings.shadowsEnabled,
|
||||||
|
monochrome_enabled: settings.monochromeEnabled,
|
||||||
|
monochrome_color: settings.monochromeColor,
|
||||||
|
imagery_brightness: settings.imageryBrightness,
|
||||||
|
imagery_contrast: settings.imageryContrast,
|
||||||
|
imagery_saturation: settings.imagerySaturation,
|
||||||
|
imagery_gamma: settings.imageryGamma,
|
||||||
|
imagery_hue: settings.imageryHue,
|
||||||
|
imagery_alpha: settings.imageryAlpha,
|
||||||
|
globe_color: settings.globeColor,
|
||||||
|
background_color: settings.backgroundColor,
|
||||||
|
terrain_exaggeration:
|
||||||
|
settings.terrainExaggeration,
|
||||||
|
buildings_color: settings.buildingsColor,
|
||||||
|
buildings_opacity: settings.buildingsOpacity,
|
||||||
|
buildings_maximum_screen_space_error:
|
||||||
|
settings.buildingsMaximumScreenSpaceError,
|
||||||
|
grid_lod_enabled: settings.gridLodEnabled,
|
||||||
|
grid_height_meters: settings.gridHeightMeters,
|
||||||
|
grid_lod_1_max_height_km:
|
||||||
|
settings.gridLod1MaxHeightKm,
|
||||||
|
grid_lod_1_step_km:
|
||||||
|
settings.gridLod1StepKm,
|
||||||
|
grid_lod_2_max_height_km:
|
||||||
|
settings.gridLod2MaxHeightKm,
|
||||||
|
grid_lod_2_step_km:
|
||||||
|
settings.gridLod2StepKm,
|
||||||
|
grid_lod_3_step_km:
|
||||||
|
settings.gridLod3StepKm,
|
||||||
|
grid_radius_km: settings.gridRadiusKm,
|
||||||
|
grid_line_width: settings.gridLineWidth,
|
||||||
|
grid_color: settings.gridColor,
|
||||||
|
grid_opacity: settings.gridOpacity,
|
||||||
|
grid_dots_enabled:
|
||||||
|
settings.gridDotsEnabled,
|
||||||
|
grid_dots_size: settings.gridDotsSize,
|
||||||
|
grid_dots_color: settings.gridDotsColor,
|
||||||
|
grid_dots_opacity:
|
||||||
|
settings.gridDotsOpacity,
|
||||||
|
camera_animation_enabled:
|
||||||
|
settings.cameraAnimationEnabled,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,261 @@
|
|||||||
|
import { useEffect, useMemo, useState } from "react";
|
||||||
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
Button,
|
||||||
|
FieldFrame,
|
||||||
|
Select,
|
||||||
|
StatusBadge,
|
||||||
|
Window,
|
||||||
|
WindowFooterActions,
|
||||||
|
} from "@nodedc/ui-react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
aiModuleIdForSetup,
|
||||||
|
fetchAIModuleCatalog,
|
||||||
|
fetchAICompositionRuns,
|
||||||
|
fetchAICompositionJobs,
|
||||||
|
saveAIComposition,
|
||||||
|
type AICompositionReceipt,
|
||||||
|
type AICompositionRun,
|
||||||
|
type AIGroupId,
|
||||||
|
type AIModuleCatalog,
|
||||||
|
} from "../../core/observatory/aiComposition";
|
||||||
|
import type { ObservatoryRecordedJob } from "../../core/observatory/recordedJobs";
|
||||||
|
|
||||||
|
const groups: readonly { id: AIGroupId; label: string; description: string }[] = [
|
||||||
|
{ id: "segmentation", label: "Сегментация", description: "Пиксельные маски сцены" },
|
||||||
|
{ id: "detection", label: "Объекты", description: "Рамки и классы объектов" },
|
||||||
|
{ id: "geometry", label: "Облако точек / TGS", description: "Опорная поверхность и карта проходимости" },
|
||||||
|
{ id: "range", label: "Дистанция", description: "Расстояния до объектов" },
|
||||||
|
{ id: "motion", label: "Движение", description: "Треки и динамика объектов" },
|
||||||
|
{ id: "policy", label: "Политика", description: "Теневые правила интерпретации" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export function AIConfigurationWindow({
|
||||||
|
open, sourceSessionId, sourceLabel, onClose, onCalculated,
|
||||||
|
}: {
|
||||||
|
readonly open: boolean;
|
||||||
|
readonly sourceSessionId: string;
|
||||||
|
readonly sourceLabel: string;
|
||||||
|
readonly onClose: () => void;
|
||||||
|
readonly onCalculated: (receipt: AICompositionReceipt) => void;
|
||||||
|
}) {
|
||||||
|
const [catalog, setCatalog] = useState<AIModuleCatalog | null>(null);
|
||||||
|
const [existingJobs, setExistingJobs] = useState<readonly ObservatoryRecordedJob[]>([]);
|
||||||
|
const [existingRuns, setExistingRuns] = useState<readonly AICompositionRun[]>([]);
|
||||||
|
const [selected, setSelected] = useState<Partial<Record<AIGroupId, string>>>({});
|
||||||
|
const [state, setState] = useState<"idle" | "loading" | "ready" | "saving" | "error">("idle");
|
||||||
|
const [message, setMessage] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!open) return;
|
||||||
|
const request = new AbortController();
|
||||||
|
setState("loading");
|
||||||
|
setMessage(null);
|
||||||
|
void Promise.all([
|
||||||
|
fetchAIModuleCatalog(request.signal),
|
||||||
|
fetchAICompositionJobs(sourceSessionId, request.signal),
|
||||||
|
fetchAICompositionRuns(sourceSessionId, request.signal),
|
||||||
|
]).then(([value, jobs, runs]) => {
|
||||||
|
if (request.signal.aborted) return;
|
||||||
|
setCatalog(value);
|
||||||
|
setExistingJobs(jobs);
|
||||||
|
setExistingRuns(runs);
|
||||||
|
setSelected({});
|
||||||
|
setState("ready");
|
||||||
|
}).catch((error: unknown) => {
|
||||||
|
if (request.signal.aborted) return;
|
||||||
|
setMessage(error instanceof Error ? error.message : "Каталог AI-модулей недоступен.");
|
||||||
|
setState("error");
|
||||||
|
});
|
||||||
|
return () => request.abort();
|
||||||
|
}, [open, sourceSessionId]);
|
||||||
|
|
||||||
|
const existingByModule = useMemo(() => {
|
||||||
|
const result = new Map<string, ObservatoryRecordedJob>();
|
||||||
|
for (const job of existingJobs) {
|
||||||
|
if (job.state === "failed" || job.publication.state === "failed") continue;
|
||||||
|
const moduleId = aiModuleIdForSetup(job.setupId);
|
||||||
|
if (moduleId && !result.has(moduleId)) result.set(moduleId, job);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}, [existingJobs]);
|
||||||
|
|
||||||
|
const selections = useMemo(() => groups.flatMap(({ id }) => {
|
||||||
|
const moduleId = selected[id];
|
||||||
|
const module = catalog?.groups.find((item) => item.group === id)?.modules.find(
|
||||||
|
(item) => item.moduleId === moduleId,
|
||||||
|
);
|
||||||
|
return module ? [{ group: id, module }] : [];
|
||||||
|
}), [catalog, selected]);
|
||||||
|
|
||||||
|
const selectedModuleIds = useMemo(
|
||||||
|
() => selections.map(({ module }) => module.moduleId).sort(),
|
||||||
|
[selections],
|
||||||
|
);
|
||||||
|
const matchingRun = useMemo(() => existingRuns.find((run) => (
|
||||||
|
run.state !== "failed"
|
||||||
|
&& [...run.moduleIds].sort().join("\0") === selectedModuleIds.join("\0")
|
||||||
|
)), [existingRuns, selectedModuleIds]);
|
||||||
|
const configurationAlreadyExists = selections.length > 0 && matchingRun !== undefined;
|
||||||
|
const configurationIsRunning = matchingRun?.state === "running";
|
||||||
|
|
||||||
|
function selectModule(group: AIGroupId, moduleId: string): void {
|
||||||
|
if (!catalog) return;
|
||||||
|
setSelected((current) => {
|
||||||
|
const next = { ...current, [group]: moduleId === "none" ? undefined : moduleId };
|
||||||
|
const modules = catalog.groups.flatMap((item) => item.modules);
|
||||||
|
const selectedIds = new Set(Object.values(next).filter((value): value is string => Boolean(value)));
|
||||||
|
if (moduleId !== "none") {
|
||||||
|
const module = modules.find((item) => item.moduleId === moduleId);
|
||||||
|
for (const requirement of module?.requires ?? []) {
|
||||||
|
if (requirement.startsWith("source.")) continue;
|
||||||
|
if (modules.some((item) => selectedIds.has(item.moduleId) && item.provides.includes(requirement))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const providers = modules.filter((item) => item.provides.includes(requirement));
|
||||||
|
if (providers.length === 1) {
|
||||||
|
const providerGroup = catalog.groups.find((item) => item.modules.some(
|
||||||
|
(candidate) => candidate.moduleId === providers[0]?.moduleId,
|
||||||
|
))?.group;
|
||||||
|
if (providerGroup) {
|
||||||
|
next[providerGroup] = providers[0]?.moduleId;
|
||||||
|
selectedIds.add(providers[0]?.moduleId ?? "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let changed = true;
|
||||||
|
while (changed) {
|
||||||
|
changed = false;
|
||||||
|
const capabilities = new Set(modules.filter((item) => Object.values(next).includes(item.moduleId))
|
||||||
|
.flatMap((item) => item.provides));
|
||||||
|
for (const item of modules.filter((candidate) => Object.values(next).includes(candidate.moduleId))) {
|
||||||
|
if (item.requires.some((requirement) => !requirement.startsWith("source.") && !capabilities.has(requirement))) {
|
||||||
|
const dependentGroup = catalog.groups.find((candidate) => candidate.modules.some(
|
||||||
|
(module) => module.moduleId === item.moduleId,
|
||||||
|
))?.group;
|
||||||
|
if (dependentGroup) {
|
||||||
|
next[dependentGroup] = undefined;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
setMessage(null);
|
||||||
|
if (state === "error") setState("ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function calculate(): Promise<void> {
|
||||||
|
if (selections.length === 0 || state === "saving") return;
|
||||||
|
if (configurationAlreadyExists) {
|
||||||
|
setMessage("Эта конфигурация уже рассчитана. Выберите другую конфигурацию.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState("saving");
|
||||||
|
setMessage(null);
|
||||||
|
try {
|
||||||
|
const receipt = await saveAIComposition(sourceSessionId, selections);
|
||||||
|
onCalculated(receipt);
|
||||||
|
onClose();
|
||||||
|
} catch (error) {
|
||||||
|
setMessage(error instanceof Error ? error.message : "Не удалось запустить расчёт.");
|
||||||
|
setState("error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Window
|
||||||
|
open={open}
|
||||||
|
className="observatory-ai-config-window"
|
||||||
|
title="Сконфигурировать AI-слой"
|
||||||
|
subtitle={sourceLabel}
|
||||||
|
size="lg"
|
||||||
|
closeOnBackdrop={state !== "saving"}
|
||||||
|
closeOnEscape={state !== "saving"}
|
||||||
|
onClose={onClose}
|
||||||
|
footer={<WindowFooterActions>
|
||||||
|
{configurationAlreadyExists ? (
|
||||||
|
<span className="observatory-ai-config__duplicate" role="status">
|
||||||
|
{configurationIsRunning
|
||||||
|
? "Текущая конфигурация уже рассчитывается."
|
||||||
|
: "Текущая конфигурация уже рассчитана."}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
<Button
|
||||||
|
variant="primary"
|
||||||
|
className={state === "saving" ? "observatory-ai-config__calculate--saving" : undefined}
|
||||||
|
disabled={state !== "ready" || selections.length === 0 || configurationAlreadyExists}
|
||||||
|
aria-busy={state === "saving"}
|
||||||
|
onClick={() => void calculate()}
|
||||||
|
>
|
||||||
|
{state === "saving" ? "Отправляем на Worker…" : "Рассчитать"}
|
||||||
|
</Button>
|
||||||
|
</WindowFooterActions>}
|
||||||
|
>
|
||||||
|
<div className="observatory-ai-config" aria-busy={state === "loading" || state === "saving"}>
|
||||||
|
<p className="observatory-ai-config__lead">
|
||||||
|
Выберите модуль в каждом нужном слое. Зависимые блоки подключаются явно в этой конфигурации.
|
||||||
|
</p>
|
||||||
|
{state === "loading" ? <ActivityIndicator label="Загружаем AI-модули" /> : null}
|
||||||
|
{catalog ? groups.map(({ id, label, description }) => {
|
||||||
|
const modules = catalog.groups.find((item) => item.group === id)?.modules ?? [];
|
||||||
|
const selectedModule = modules.find((item) => item.moduleId === selected[id]);
|
||||||
|
const selectedExisting = selectedModule
|
||||||
|
? existingByModule.get(selectedModule.moduleId)
|
||||||
|
: undefined;
|
||||||
|
const allCalculated = modules.length > 0 && modules.every(
|
||||||
|
(module) => existingByModule.has(module.moduleId),
|
||||||
|
);
|
||||||
|
return <section className="observatory-ai-module-group" key={id}>
|
||||||
|
<header>
|
||||||
|
<div>
|
||||||
|
<strong>{label}</strong>
|
||||||
|
<span>{description}</span>
|
||||||
|
</div>
|
||||||
|
<StatusBadge tone={selectedModule ? "accent" : "neutral"}>
|
||||||
|
{selectedExisting
|
||||||
|
? selectedExisting.state === "succeeded" ? "Уже рассчитано" : "В расчёте"
|
||||||
|
: selectedModule?.label ?? (allCalculated
|
||||||
|
? "Результат доступен"
|
||||||
|
: modules.length ? "Не используется" : "Не установлен")}
|
||||||
|
</StatusBadge>
|
||||||
|
</header>
|
||||||
|
<div className="observatory-ai-module-group__body">
|
||||||
|
<FieldFrame
|
||||||
|
label={`Модуль: ${label}`}
|
||||||
|
description={modules.length
|
||||||
|
? allCalculated
|
||||||
|
? "Готовый результат можно включить в новую конфигурацию без повторного расчёта."
|
||||||
|
: id === "segmentation"
|
||||||
|
? "DDRNet и EoMT — независимые альтернативы; выберите одну модель."
|
||||||
|
: id === "range"
|
||||||
|
? "Дистанция использует рамки детектора и синхронное облако точек."
|
||||||
|
: id === "geometry"
|
||||||
|
? "TGS обрабатывает LiDAR независимо от сегментации и детектора."
|
||||||
|
: "Связи с другими блоками определяются входами выбранного модуля."
|
||||||
|
: "Для этого слоя на Worker 006 пока нет установленного модуля."}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
label={`Выбрать модуль: ${label}`}
|
||||||
|
value={selected[id] ?? "none"}
|
||||||
|
options={[{ value: "none", label: "Не использовать" }, ...modules.map((module) => ({
|
||||||
|
value: module.moduleId,
|
||||||
|
label: module.label,
|
||||||
|
description: existingByModule.has(module.moduleId)
|
||||||
|
? "Готовый результат будет использован без повторного расчёта"
|
||||||
|
: module.dockerName,
|
||||||
|
}))]}
|
||||||
|
disabled={modules.length === 0 || state === "saving"}
|
||||||
|
onChange={(value) => selectModule(id, value)}
|
||||||
|
/>
|
||||||
|
</FieldFrame>
|
||||||
|
</div>
|
||||||
|
</section>;
|
||||||
|
}) : null}
|
||||||
|
{message ? <p className="observatory-ai-config__error" role="alert">{message}</p> : null}
|
||||||
|
</div>
|
||||||
|
</Window>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { WebViewer } from "@rerun-io/web-viewer";
|
||||||
|
|
||||||
|
import type { RerunFrameWindow } from "./recordedRerunProtocol";
|
||||||
|
import { createRecordedRerunOwner } from "./recordedRerunOwner";
|
||||||
|
|
||||||
|
// This entry is loaded only in the owned iframe. Upstream code is unmodified;
|
||||||
|
// destroying the frame also ends its timers, WASM, video and WebGPU lifetime.
|
||||||
|
const mount = document.getElementById("viewer");
|
||||||
|
if (!mount) throw new Error("Recorded viewer mount is missing");
|
||||||
|
(window as RerunFrameWindow).missionCoreRerun = createRecordedRerunOwner(() => new WebViewer(), mount);
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import { createRecordedRerunFacade, type RecordedRerunViewer } from "./recordedRerunFacade";
|
||||||
|
import type { RerunFrameWindow } from "./recordedRerunProtocol";
|
||||||
|
import { relayIsolatedRerunInput } from "./isolatedRerunInput";
|
||||||
|
|
||||||
|
/** Own the complete recorded-viewer realm, not just its canvas or SDK handle. */
|
||||||
|
export function createIsolatedRerunHost(host: HTMLElement) {
|
||||||
|
let frame: HTMLIFrameElement | null = host.ownerDocument.createElement("iframe");
|
||||||
|
frame.title = "Синхронизированная запись";
|
||||||
|
frame.className = "rerun-viewport__runtime";
|
||||||
|
frame.src = "/rerun-runtime.html";
|
||||||
|
let disposed = false;
|
||||||
|
let releaseFacade: (() => void) | null = null;
|
||||||
|
let releaseInput: (() => void) | null = null;
|
||||||
|
let frameWindow: RerunFrameWindow | null = null;
|
||||||
|
let rejectReady: ((reason: Error) => void) | null = null;
|
||||||
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
||||||
|
|
||||||
|
const dispose = () => {
|
||||||
|
if (disposed) return;
|
||||||
|
disposed = true;
|
||||||
|
clearTimeout(timer);
|
||||||
|
frame!.onload = null;
|
||||||
|
frame!.onerror = null;
|
||||||
|
releaseInput?.();
|
||||||
|
releaseInput = null;
|
||||||
|
try {
|
||||||
|
releaseFacade?.();
|
||||||
|
} catch {
|
||||||
|
// Realm teardown is required even if a partial native start failed.
|
||||||
|
} finally {
|
||||||
|
releaseFacade = null;
|
||||||
|
frameWindow = null;
|
||||||
|
frame!.remove();
|
||||||
|
frame = null;
|
||||||
|
rejectReady?.(new Error("Recorded viewer was disposed"));
|
||||||
|
rejectReady = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ready = new Promise<{ viewer: RecordedRerunViewer; mount: HTMLElement }>((resolve, reject) => {
|
||||||
|
rejectReady = reject;
|
||||||
|
frame!.onload = () => {
|
||||||
|
if (disposed) return;
|
||||||
|
try {
|
||||||
|
frameWindow = frame!.contentWindow as RerunFrameWindow | null;
|
||||||
|
const api = frameWindow?.missionCoreRerun;
|
||||||
|
if (!api || api.version !== 2) throw new Error("Recorded viewer host is unavailable");
|
||||||
|
const owner = createRecordedRerunFacade((request, bytes) => {
|
||||||
|
const current = frameWindow?.missionCoreRerun;
|
||||||
|
if (!current) throw new Error("Recorded viewer was disposed");
|
||||||
|
return current.invoke(request, bytes);
|
||||||
|
});
|
||||||
|
releaseFacade = owner.dispose;
|
||||||
|
api.connect(owner.notify);
|
||||||
|
releaseInput = relayIsolatedRerunInput(host, frameWindow, frame);
|
||||||
|
clearTimeout(timer);
|
||||||
|
rejectReady = null;
|
||||||
|
frame!.onload = null;
|
||||||
|
frame!.onerror = null;
|
||||||
|
resolve({ viewer: owner.facade, mount: host });
|
||||||
|
} catch (error) {
|
||||||
|
reject(new Error(String(error)));
|
||||||
|
dispose();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
frame!.onerror = () => {
|
||||||
|
reject(new Error("Recorded viewer host failed to load"));
|
||||||
|
dispose();
|
||||||
|
};
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
reject(new Error("Recorded viewer host timed out"));
|
||||||
|
dispose();
|
||||||
|
}, 30_000);
|
||||||
|
});
|
||||||
|
host.append(frame);
|
||||||
|
return { ready, dispose };
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/** Relay Escape from the isolated viewer realm to the owning application. */
|
||||||
|
export function relayIsolatedRerunInput(
|
||||||
|
host: HTMLElement,
|
||||||
|
frameWindow: Window | null,
|
||||||
|
_frame: HTMLIFrameElement | null,
|
||||||
|
) {
|
||||||
|
const parent = host.ownerDocument.defaultView;
|
||||||
|
const escape = (event: KeyboardEvent) => {
|
||||||
|
if (event.key !== "Escape" || event.defaultPrevented || !parent) return;
|
||||||
|
host.dispatchEvent(new parent.KeyboardEvent("keydown", { key: "Escape", bubbles: true }));
|
||||||
|
};
|
||||||
|
frameWindow?.addEventListener("keydown", escape);
|
||||||
|
return () => {
|
||||||
|
frameWindow?.removeEventListener("keydown", escape);
|
||||||
|
frameWindow = null;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,244 @@
|
|||||||
|
export type RecordedRerunCameraEye = {
|
||||||
|
readonly position: readonly [number, number, number];
|
||||||
|
readonly lookTarget: readonly [number, number, number];
|
||||||
|
readonly eyeUp: readonly [number, number, number];
|
||||||
|
};
|
||||||
|
|
||||||
|
type Vector3 = [number, number, number];
|
||||||
|
type DragMode = "rotate" | "pan" | "roll";
|
||||||
|
|
||||||
|
const ROTATION_RADIANS_PER_POINT = 0.004;
|
||||||
|
const RERUN_WEB_LINE_SCROLL_POINTS = 8;
|
||||||
|
const RERUN_ORBITAL_SCROLL_DIVISOR = 200;
|
||||||
|
const RERUN_WEB_PINCH_SCROLL_DIVISOR = 100;
|
||||||
|
const RERUN_MIN_ORBIT_DISTANCE = 0.02;
|
||||||
|
const DOM_WHEEL_DELTA_LINE = 1;
|
||||||
|
const DOM_WHEEL_DELTA_PAGE = 2;
|
||||||
|
|
||||||
|
export const RECORDED_RERUN_ORBITAL_EYE: RecordedRerunCameraEye = {
|
||||||
|
position: [16, -16, 18],
|
||||||
|
lookTarget: [0, 0, 0],
|
||||||
|
eyeUp: [0, 0, 1],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RECORDED_RERUN_PLAN_EYE: RecordedRerunCameraEye = {
|
||||||
|
position: [0, 0, 30],
|
||||||
|
lookTarget: [0, 0, 0],
|
||||||
|
eyeUp: [0, 1, 0],
|
||||||
|
};
|
||||||
|
|
||||||
|
const vector = (value: readonly [number, number, number]): Vector3 => [...value];
|
||||||
|
const add = (a: Vector3, b: Vector3): Vector3 => [a[0] + b[0], a[1] + b[1], a[2] + b[2]];
|
||||||
|
const subtract = (a: Vector3, b: Vector3): Vector3 => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
|
||||||
|
const scale = (value: Vector3, factor: number): Vector3 => [value[0] * factor, value[1] * factor, value[2] * factor];
|
||||||
|
const dot = (a: Vector3, b: Vector3) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
||||||
|
const cross = (a: Vector3, b: Vector3): Vector3 => [
|
||||||
|
a[1] * b[2] - a[2] * b[1],
|
||||||
|
a[2] * b[0] - a[0] * b[2],
|
||||||
|
a[0] * b[1] - a[1] * b[0],
|
||||||
|
];
|
||||||
|
const length = (value: Vector3) => Math.hypot(...value);
|
||||||
|
const normalize = (value: Vector3, fallback: Vector3): Vector3 => {
|
||||||
|
const magnitude = length(value);
|
||||||
|
return magnitude > Number.EPSILON ? scale(value, 1 / magnitude) : fallback;
|
||||||
|
};
|
||||||
|
const clamp = (value: number, minimum: number, maximum: number) => Math.max(minimum, Math.min(maximum, value));
|
||||||
|
const rotateAroundAxis = (value: Vector3, rawAxis: Vector3, angle: number): Vector3 => {
|
||||||
|
const axis = normalize(rawAxis, [0, 0, 1]);
|
||||||
|
const cosine = Math.cos(angle);
|
||||||
|
const sine = Math.sin(angle);
|
||||||
|
return add(
|
||||||
|
add(scale(value, cosine), scale(cross(axis, value), sine)),
|
||||||
|
scale(axis, dot(axis, value) * (1 - cosine)),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
function cloneEye(value: RecordedRerunCameraEye): {
|
||||||
|
position: Vector3;
|
||||||
|
lookTarget: Vector3;
|
||||||
|
eyeUp: Vector3;
|
||||||
|
} {
|
||||||
|
return {
|
||||||
|
position: vector(value.position),
|
||||||
|
lookTarget: vector(value.lookTarget),
|
||||||
|
eyeUp: vector(value.eyeUp),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mirrors Rerun 0.36.3's orbital eye math while the native viewer remains the
|
||||||
|
* renderer. Rerun writes operator navigation into its active blueprint clone;
|
||||||
|
* the WebViewer API cannot read that clone. Keeping the same three eye vectors
|
||||||
|
* here lets a later layer blueprint activate with the exact operator pose.
|
||||||
|
*/
|
||||||
|
export function createRecordedRerunCameraJournal(
|
||||||
|
canvas: HTMLCanvasElement,
|
||||||
|
scope: Window & typeof globalThis,
|
||||||
|
) {
|
||||||
|
let eye = cloneEye(RECORDED_RERUN_ORBITAL_EYE);
|
||||||
|
let spatialViewportStart = 0;
|
||||||
|
let pointerId: number | null = null;
|
||||||
|
let dragMode: DragMode | null = null;
|
||||||
|
let previousPointer: readonly [number, number] | null = null;
|
||||||
|
let latestPointer: readonly [number, number] | null = null;
|
||||||
|
let maxOrbitalRadius = 1.0e17;
|
||||||
|
|
||||||
|
const isSpatialPoint = (event: MouseEvent) => {
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
return rect.width > 0 && (event.clientX - rect.left) / rect.width >= spatialViewportStart;
|
||||||
|
};
|
||||||
|
const forward = () => normalize(subtract(eye.lookTarget, eye.position), [0, 1, 0]);
|
||||||
|
const usableUp = () => {
|
||||||
|
const fwd = forward();
|
||||||
|
const fallbackRight: Vector3 = Math.abs(dot(fwd, [0, 0, 1])) > 0.9999
|
||||||
|
? [1, 0, 0]
|
||||||
|
: cross(fwd, [0, 0, 1]);
|
||||||
|
const fallback = normalize(cross(fwd, fallbackRight), [0, 0, 1]);
|
||||||
|
const candidate = normalize(eye.eyeUp, fallback);
|
||||||
|
return Math.abs(dot(candidate, fwd)) > 0.9999 ? fallback : candidate;
|
||||||
|
};
|
||||||
|
const orbitRadius = () => length(subtract(eye.position, eye.lookTarget));
|
||||||
|
|
||||||
|
const rotate = (deltaX: number, deltaY: number) => {
|
||||||
|
const radius = orbitRadius();
|
||||||
|
const up = usableUp();
|
||||||
|
let fwd = forward();
|
||||||
|
const oldPitch = Math.asin(clamp(dot(fwd, up), -1, 1));
|
||||||
|
fwd = normalize(
|
||||||
|
rotateAroundAxis(fwd, up, -ROTATION_RADIANS_PER_POINT * deltaX),
|
||||||
|
fwd,
|
||||||
|
);
|
||||||
|
const right = normalize(cross(fwd, up), [1, 0, 0]);
|
||||||
|
const maxPitch = 0.99 * Math.PI / 2;
|
||||||
|
const nextPitch = clamp(
|
||||||
|
oldPitch - ROTATION_RADIANS_PER_POINT * deltaY,
|
||||||
|
-maxPitch,
|
||||||
|
maxPitch,
|
||||||
|
);
|
||||||
|
fwd = normalize(rotateAroundAxis(fwd, right, nextPitch - oldPitch), fwd);
|
||||||
|
eye.position = subtract(eye.lookTarget, scale(fwd, radius));
|
||||||
|
};
|
||||||
|
|
||||||
|
const pan = (deltaX: number, deltaY: number) => {
|
||||||
|
const fwd = forward();
|
||||||
|
const right = normalize(cross(fwd, usableUp()), [1, 0, 0]);
|
||||||
|
const screenUp = normalize(cross(right, fwd), [0, 0, 1]);
|
||||||
|
const speed = 0.001 * orbitRadius();
|
||||||
|
const translation = add(scale(right, -deltaX * speed), scale(screenUp, deltaY * speed));
|
||||||
|
eye.position = add(eye.position, translation);
|
||||||
|
eye.lookTarget = add(eye.lookTarget, translation);
|
||||||
|
};
|
||||||
|
|
||||||
|
const roll = (event: PointerEvent, deltaX: number, deltaY: number) => {
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
const left = rect.left + rect.width * spatialViewportStart;
|
||||||
|
const centerX = left + (rect.right - left) / 2;
|
||||||
|
const centerY = rect.top + rect.height / 2;
|
||||||
|
const relativeX = event.clientX - centerX;
|
||||||
|
const relativeY = event.clientY - centerY;
|
||||||
|
const divisor = relativeX * relativeX + relativeY * relativeY;
|
||||||
|
if (divisor <= Number.EPSILON) return;
|
||||||
|
const angle = (-deltaY * relativeX + deltaX * relativeY) / divisor;
|
||||||
|
eye.eyeUp = normalize(rotateAroundAxis(eye.eyeUp, scale(forward(), -1), angle), eye.eyeUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
const pointerDown = (event: PointerEvent) => {
|
||||||
|
latestPointer = [event.clientX, event.clientY];
|
||||||
|
if (!isSpatialPoint(event)) return;
|
||||||
|
pointerId = event.pointerId;
|
||||||
|
previousPointer = [event.clientX, event.clientY];
|
||||||
|
dragMode = event.button === 2 ? "pan" : event.button === 1 || event.altKey ? "roll" : "rotate";
|
||||||
|
};
|
||||||
|
const pointerMove = (event: PointerEvent) => {
|
||||||
|
latestPointer = [event.clientX, event.clientY];
|
||||||
|
if (event.pointerId !== pointerId || !previousPointer || !dragMode) return;
|
||||||
|
const deltaX = event.clientX - previousPointer[0];
|
||||||
|
const deltaY = event.clientY - previousPointer[1];
|
||||||
|
previousPointer = [event.clientX, event.clientY];
|
||||||
|
if (dragMode === "rotate") rotate(deltaX, deltaY);
|
||||||
|
else if (dragMode === "pan") pan(deltaX, deltaY);
|
||||||
|
else roll(event, deltaX, deltaY);
|
||||||
|
};
|
||||||
|
const pointerEnd = (event: PointerEvent) => {
|
||||||
|
if (event.pointerId !== pointerId) return;
|
||||||
|
pointerMove(event);
|
||||||
|
pointerId = null;
|
||||||
|
previousPointer = null;
|
||||||
|
dragMode = null;
|
||||||
|
};
|
||||||
|
const wheel = (event: WheelEvent) => {
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
const wheelX = event.clientX >= rect.left && event.clientX <= rect.right
|
||||||
|
? event.clientX
|
||||||
|
: latestPointer?.[0];
|
||||||
|
if (wheelX === undefined || rect.width <= 0) return;
|
||||||
|
if ((wheelX - rect.left) / rect.width < spatialViewportStart) return;
|
||||||
|
const unitMultiplier = event.deltaMode === DOM_WHEEL_DELTA_LINE
|
||||||
|
? RERUN_WEB_LINE_SCROLL_POINTS
|
||||||
|
: event.deltaMode === DOM_WHEEL_DELTA_PAGE
|
||||||
|
? rect.height
|
||||||
|
: 1;
|
||||||
|
const scrollPoints = (event.deltaX + event.deltaY) * unitMultiplier;
|
||||||
|
// eframe negates the DOM delta before Rerun applies
|
||||||
|
// radius / exp(smooth_scroll_delta / 200). Over a smoothed gesture the
|
||||||
|
// exponent products collapse to the raw accumulated DOM delta below.
|
||||||
|
const divisor = event.ctrlKey
|
||||||
|
? RERUN_WEB_PINCH_SCROLL_DIVISOR
|
||||||
|
: RERUN_ORBITAL_SCROLL_DIVISOR;
|
||||||
|
const radiusFactor = Math.exp(scrollPoints / divisor);
|
||||||
|
const offset = subtract(eye.position, eye.lookTarget);
|
||||||
|
const radius = length(offset);
|
||||||
|
// Keep the same orbital bounds as Rerun 0.36.3. An eye already outside
|
||||||
|
// the scene-derived cap is allowed to stay there and is never snapped in.
|
||||||
|
const nextRadius = clamp(
|
||||||
|
radius * radiusFactor,
|
||||||
|
RERUN_MIN_ORBIT_DISTANCE,
|
||||||
|
Math.max(radius, maxOrbitalRadius),
|
||||||
|
);
|
||||||
|
eye.position = add(
|
||||||
|
eye.lookTarget,
|
||||||
|
scale(offset, nextRadius / Math.max(radius, Number.EPSILON)),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
canvas.addEventListener("pointerdown", pointerDown, true);
|
||||||
|
scope.addEventListener("pointermove", pointerMove, true);
|
||||||
|
scope.addEventListener("pointerup", pointerEnd, true);
|
||||||
|
scope.addEventListener("pointercancel", pointerEnd, true);
|
||||||
|
scope.addEventListener("wheel", wheel, { capture: true, passive: true });
|
||||||
|
|
||||||
|
return {
|
||||||
|
current(): RecordedRerunCameraEye {
|
||||||
|
return {
|
||||||
|
position: [...eye.position],
|
||||||
|
lookTarget: [...eye.lookTarget],
|
||||||
|
eyeUp: [...eye.eyeUp],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
configure(nextEye: RecordedRerunCameraEye, nextSpatialViewportStart: number) {
|
||||||
|
eye = cloneEye(nextEye);
|
||||||
|
spatialViewportStart = clamp(nextSpatialViewportStart, 0, 0.9);
|
||||||
|
pointerId = null;
|
||||||
|
dragMode = null;
|
||||||
|
previousPointer = null;
|
||||||
|
},
|
||||||
|
setSpatialViewportStart(nextSpatialViewportStart: number) {
|
||||||
|
spatialViewportStart = clamp(nextSpatialViewportStart, 0, 0.9);
|
||||||
|
},
|
||||||
|
setMaxOrbitalRadius(nextMaxOrbitalRadius: number) {
|
||||||
|
if (
|
||||||
|
Number.isFinite(nextMaxOrbitalRadius) &&
|
||||||
|
nextMaxOrbitalRadius >= RERUN_MIN_ORBIT_DISTANCE
|
||||||
|
) {
|
||||||
|
maxOrbitalRadius = nextMaxOrbitalRadius;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dispose() {
|
||||||
|
canvas.removeEventListener("pointerdown", pointerDown, true);
|
||||||
|
scope.removeEventListener("pointermove", pointerMove, true);
|
||||||
|
scope.removeEventListener("pointerup", pointerEnd, true);
|
||||||
|
scope.removeEventListener("pointercancel", pointerEnd, true);
|
||||||
|
scope.removeEventListener("wheel", wheel, true);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import type { WebViewer } from "@rerun-io/web-viewer";
|
||||||
|
import type { RerunInvoke } from "./recordedRerunProtocol";
|
||||||
|
import type { RecordedRerunCameraEye } from "./recordedRerunCameraJournal";
|
||||||
|
|
||||||
|
type NativeChannel = ReturnType<WebViewer["open_channel"]>;
|
||||||
|
type Channel = Pick<NativeChannel, "ready" | "send_rrd" | "close">;
|
||||||
|
export type RecordedRerunViewer = Pick<WebViewer,
|
||||||
|
"start" | "stop" | "ready" | "on" | "open" | "close" | "override_panel_state" |
|
||||||
|
"get_active_recording_id" | "get_active_timeline" | "get_current_time" |
|
||||||
|
"get_playing" | "get_time_range" | "set_active_timeline" |
|
||||||
|
"set_current_time" | "set_playing"
|
||||||
|
> & {
|
||||||
|
open_channel: (name?: string) => Channel;
|
||||||
|
configure_camera_journal: (eye: RecordedRerunCameraEye, spatialViewportStart: number) => void;
|
||||||
|
get_camera_eye: () => RecordedRerunCameraEye;
|
||||||
|
set_camera_viewport_start: (spatialViewportStart: number) => void;
|
||||||
|
set_camera_max_orbital_radius: (maxOrbitalRadius: number) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Parent-owned values only. No SDK Promise or foreign prototype escapes. */
|
||||||
|
export function createRecordedRerunFacade(invoke: RerunInvoke | null) {
|
||||||
|
let nextId = 0;
|
||||||
|
const callbacks = new Map<number, (...args: unknown[]) => void>();
|
||||||
|
const starts = new Map<number, { resolve: () => void; reject: (error: Error) => void }>();
|
||||||
|
const command = (method: string, args: unknown[] = [], id?: number, bytes?: Uint8Array) => {
|
||||||
|
if (!invoke) throw new Error("Recorded viewer was disposed");
|
||||||
|
const response = JSON.parse(invoke(JSON.stringify({ method, args, id }), bytes));
|
||||||
|
if (response.error) throw new Error(response.error);
|
||||||
|
return response.result;
|
||||||
|
};
|
||||||
|
const dispose = () => {
|
||||||
|
callbacks.clear();
|
||||||
|
for (const pending of starts.values()) pending.reject(new Error("Recorded viewer was disposed"));
|
||||||
|
starts.clear();
|
||||||
|
try { if (invoke) command("stop"); } finally { invoke = null; }
|
||||||
|
};
|
||||||
|
const notify = (message: string) => {
|
||||||
|
if (!invoke) return;
|
||||||
|
const event = JSON.parse(message);
|
||||||
|
if (event.type === "event") callbacks.get(event.id)?.(...event.values);
|
||||||
|
else {
|
||||||
|
const pending = starts.get(event.id);
|
||||||
|
starts.delete(event.id);
|
||||||
|
if (event.type === "started") pending?.resolve();
|
||||||
|
else pending?.reject(new Error(event.message));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const facade: RecordedRerunViewer = {
|
||||||
|
get ready() { return invoke ? command("ready") : false; },
|
||||||
|
start(source, _parent, options) {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
const id = ++nextId;
|
||||||
|
starts.set(id, { resolve, reject });
|
||||||
|
try { command("start", [source, options], id); }
|
||||||
|
catch (error) { starts.delete(id); reject(error); }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
stop: dispose,
|
||||||
|
on: ((event: string, callback: (...args: unknown[]) => void) => {
|
||||||
|
const id = ++nextId;
|
||||||
|
callbacks.set(id, callback);
|
||||||
|
try { command("on", [event], id); }
|
||||||
|
catch (error) { callbacks.delete(id); throw error; }
|
||||||
|
return () => {
|
||||||
|
if (callbacks.delete(id) && invoke) command("off", [], id);
|
||||||
|
};
|
||||||
|
}) as WebViewer["on"],
|
||||||
|
open_channel(name) {
|
||||||
|
const id = ++nextId;
|
||||||
|
command("channel-open", name === undefined ? [] : [name], id);
|
||||||
|
let closed = false;
|
||||||
|
return {
|
||||||
|
get ready() { return !closed && invoke ? command("channel-ready", [], id) : false; },
|
||||||
|
send_rrd(bytes) { if (!closed && invoke) command("channel-send", [], id, bytes); },
|
||||||
|
close() {
|
||||||
|
if (closed) return;
|
||||||
|
closed = true;
|
||||||
|
if (invoke) command("channel-close", [], id);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
open: (...args) => command("open", args),
|
||||||
|
close: (...args) => { if (invoke) command("close", args); },
|
||||||
|
override_panel_state: (...args) => command("override_panel_state", args),
|
||||||
|
get_active_recording_id: () => command("get_active_recording_id"),
|
||||||
|
get_active_timeline: (...args) => command("get_active_timeline", args),
|
||||||
|
get_current_time: (...args) => command("get_current_time", args),
|
||||||
|
get_playing: (...args) => command("get_playing", args),
|
||||||
|
get_time_range: (...args) => command("get_time_range", args),
|
||||||
|
set_active_timeline: (...args) => command("set_active_timeline", args),
|
||||||
|
set_current_time: (...args) => command("set_current_time", args),
|
||||||
|
set_playing: (...args) => command("set_playing", args),
|
||||||
|
configure_camera_journal: (eye, spatialViewportStart) => command(
|
||||||
|
"configure-camera-journal", [eye, spatialViewportStart],
|
||||||
|
),
|
||||||
|
get_camera_eye: () => command("get-camera-eye"),
|
||||||
|
set_camera_viewport_start: (spatialViewportStart) => command(
|
||||||
|
"set-camera-viewport-start", [spatialViewportStart],
|
||||||
|
),
|
||||||
|
set_camera_max_orbital_radius: (maxOrbitalRadius) => command(
|
||||||
|
"set-camera-max-orbital-radius", [maxOrbitalRadius],
|
||||||
|
),
|
||||||
|
};
|
||||||
|
return { facade, dispose, notify };
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import type { WebViewer } from "@rerun-io/web-viewer";
|
||||||
|
import type { RerunFrameApi, RerunNotify } from "./recordedRerunProtocol";
|
||||||
|
import { createRecordedRerunCameraJournal } from "./recordedRerunCameraJournal";
|
||||||
|
|
||||||
|
/** Lives entirely inside the disposable iframe, including pending SDK starts. */
|
||||||
|
export function createRecordedRerunOwner(create: () => WebViewer, mount: HTMLElement): RerunFrameApi {
|
||||||
|
let native: WebViewer | null = null;
|
||||||
|
let notify: RerunNotify | null = null;
|
||||||
|
const subscriptions = new Map<number, () => void>();
|
||||||
|
const channels = new Map<number, ReturnType<WebViewer["open_channel"]>>();
|
||||||
|
let cameraJournal: ReturnType<typeof createRecordedRerunCameraJournal> | null = null;
|
||||||
|
const send = (message: object) => notify?.(JSON.stringify(message));
|
||||||
|
const stop = () => {
|
||||||
|
notify = null;
|
||||||
|
for (const unsubscribe of subscriptions.values()) {
|
||||||
|
try { unsubscribe(); } catch { /* Continue partial-start cleanup. */ }
|
||||||
|
}
|
||||||
|
subscriptions.clear();
|
||||||
|
for (const channel of channels.values()) {
|
||||||
|
try { channel.close(); } catch { /* Other channels must still close. */ }
|
||||||
|
}
|
||||||
|
channels.clear();
|
||||||
|
cameraJournal?.dispose();
|
||||||
|
cameraJournal = null;
|
||||||
|
const current = native;
|
||||||
|
native = null;
|
||||||
|
try { current?.stop(); } catch { /* Realm teardown remains authoritative. */ }
|
||||||
|
};
|
||||||
|
const required = () => {
|
||||||
|
if (!native) throw new Error("Recorded viewer was disposed");
|
||||||
|
return native;
|
||||||
|
};
|
||||||
|
const start = async (id: number, args: [string | string[] | null, Parameters<WebViewer["start"]>[2]]) => {
|
||||||
|
const starting = required();
|
||||||
|
try {
|
||||||
|
await starting.start(args[0], mount, args[1]);
|
||||||
|
if (native === starting && starting.canvas) {
|
||||||
|
cameraJournal?.dispose();
|
||||||
|
cameraJournal = createRecordedRerunCameraJournal(
|
||||||
|
starting.canvas,
|
||||||
|
window as Window & typeof globalThis,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (native === starting) send({ type: "started", id });
|
||||||
|
} catch (error) {
|
||||||
|
if (native === starting) send({ type: "start-failed", id, message: String(error) });
|
||||||
|
} finally {
|
||||||
|
if (native !== starting) {
|
||||||
|
try { starting.stop(); } catch { /* Late completion after disposal. */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
version: 2,
|
||||||
|
connect(callback) {
|
||||||
|
if (native) throw new Error("Recorded viewer already has an owner");
|
||||||
|
native = create();
|
||||||
|
notify = callback;
|
||||||
|
},
|
||||||
|
invoke(request, bytes) {
|
||||||
|
try {
|
||||||
|
const { method, args = [], id } = JSON.parse(request);
|
||||||
|
let result: unknown;
|
||||||
|
switch (method) {
|
||||||
|
case "stop": stop(); break;
|
||||||
|
case "start": required(); void start(id, args); break;
|
||||||
|
case "ready": result = native?.ready ?? false; break;
|
||||||
|
case "on": {
|
||||||
|
const subscribe = required().on as (event: string, callback: (...values: unknown[]) => void) => () => void;
|
||||||
|
subscriptions.set(id, subscribe.call(required(), args[0], (...values) => send({ type: "event", id, values })));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "off":
|
||||||
|
try { subscriptions.get(id)?.(); } finally { subscriptions.delete(id); }
|
||||||
|
break;
|
||||||
|
case "channel-open": {
|
||||||
|
const name = args[0] as string | undefined;
|
||||||
|
channels.set(id, required().open_channel(name));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "channel-ready": result = channels.get(id)?.ready ?? false; break;
|
||||||
|
case "channel-send": if (bytes) {
|
||||||
|
channels.get(id)?.send_rrd(bytes);
|
||||||
|
} break;
|
||||||
|
case "channel-close":
|
||||||
|
try { channels.get(id)?.close(); } finally {
|
||||||
|
channels.delete(id);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "configure-camera-journal": cameraJournal?.configure(args[0], args[1]); break;
|
||||||
|
case "get-camera-eye": result = cameraJournal?.current(); break;
|
||||||
|
case "set-camera-viewport-start": cameraJournal?.setSpatialViewportStart(args[0]); break;
|
||||||
|
case "set-camera-max-orbital-radius": cameraJournal?.setMaxOrbitalRadius(args[0]); break;
|
||||||
|
case "open": required().open(args[0]); break;
|
||||||
|
case "close": required().close(args[0]); break;
|
||||||
|
case "override_panel_state": required().override_panel_state(args[0], args[1]); break;
|
||||||
|
case "get_active_recording_id": result = required().get_active_recording_id(); break;
|
||||||
|
case "get_active_timeline": result = required().get_active_timeline(args[0]); break;
|
||||||
|
case "get_current_time": result = required().get_current_time(args[0], args[1]); break;
|
||||||
|
case "get_playing": result = required().get_playing(args[0]); break;
|
||||||
|
case "get_time_range": result = required().get_time_range(args[0], args[1]); break;
|
||||||
|
case "set_active_timeline": required().set_active_timeline(args[0], args[1]); break;
|
||||||
|
case "set_current_time": required().set_current_time(args[0], args[1], args[2]); break;
|
||||||
|
case "set_playing": required().set_playing(args[0], args[1]); break;
|
||||||
|
default: throw new Error("Unsupported recorded viewer command");
|
||||||
|
}
|
||||||
|
return JSON.stringify({ result });
|
||||||
|
} catch (error) {
|
||||||
|
// A native Error also has a foreign prototype. Return text, never throw
|
||||||
|
// that object into a parent Promise/callback retained after disposal.
|
||||||
|
return JSON.stringify({ error: String(error) });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/** Only parent-owned inputs and primitive strings cross the recorded realm.
|
||||||
|
* An SDK instance, Promise, DOM node or native callback never becomes a value
|
||||||
|
* retained by React. This is not a second playback clock.
|
||||||
|
*/
|
||||||
|
export type RerunInvoke = (request: string, bytes?: Uint8Array) => string;
|
||||||
|
export type RerunNotify = (message: string) => void;
|
||||||
|
export interface RerunFrameApi {
|
||||||
|
version: 2;
|
||||||
|
connect: (notify: RerunNotify) => void;
|
||||||
|
invoke: RerunInvoke;
|
||||||
|
}
|
||||||
|
export type RerunFrameWindow = Window & { missionCoreRerun?: RerunFrameApi };
|
||||||
@@ -1,25 +1,34 @@
|
|||||||
import {
|
import {
|
||||||
Application,
|
Application,
|
||||||
Asset,
|
Asset,
|
||||||
|
BLEND_NONE,
|
||||||
BLEND_NORMAL,
|
BLEND_NORMAL,
|
||||||
Color,
|
Color,
|
||||||
CULLFACE_NONE,
|
CULLFACE_NONE,
|
||||||
Entity,
|
Entity,
|
||||||
FILLMODE_NONE,
|
FILLMODE_NONE,
|
||||||
Mat4,
|
Mat4,
|
||||||
|
Quat,
|
||||||
RESOLUTION_AUTO,
|
RESOLUTION_AUTO,
|
||||||
StandardMaterial,
|
StandardMaterial,
|
||||||
Vec2,
|
Vec2,
|
||||||
Vec3,
|
Vec3,
|
||||||
WasmModule,
|
WasmModule,
|
||||||
type ContainerResource,
|
type ContainerResource,
|
||||||
type RenderComponent,
|
type ModelComponent,
|
||||||
type ScriptType,
|
type ScriptType,
|
||||||
} from "playcanvas";
|
} from "playcanvas";
|
||||||
import { CameraControls } from "playcanvas/scripts/esm/camera-controls.mjs";
|
import { CameraControls } from "playcanvas/scripts/esm/camera-controls.mjs";
|
||||||
|
|
||||||
|
import {
|
||||||
|
prepareSimulationPhysics,
|
||||||
|
SimulationUgvController,
|
||||||
|
type AmmoApi,
|
||||||
|
} from "./SimulationUgvController";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
SimulationTransformAxis,
|
SimulationEulerRotation,
|
||||||
|
SimulationUgvPreset,
|
||||||
SimulationViewerQuality,
|
SimulationViewerQuality,
|
||||||
SimulationWorldManifest,
|
SimulationWorldManifest,
|
||||||
} from "../../core/simulation/projects";
|
} from "../../core/simulation/projects";
|
||||||
@@ -28,46 +37,18 @@ export type SimulationViewMode = "visual" | "collision" | "combined";
|
|||||||
export type SimulationQuality = SimulationViewerQuality;
|
export type SimulationQuality = SimulationViewerQuality;
|
||||||
export type SimulationLayer = "visual" | "collision";
|
export type SimulationLayer = "visual" | "collision";
|
||||||
export type SimulationCameraAxis = "horizontal" | "vertical";
|
export type SimulationCameraAxis = "horizontal" | "vertical";
|
||||||
|
export type SimulationControlMode = "free" | "ugv";
|
||||||
|
|
||||||
export const PLAYCANVAS_IDENTITY_TRANSFORM = [
|
export function playCanvasEulerTransform(rotation: SimulationEulerRotation): number[] {
|
||||||
1, 0, 0, 0,
|
return Array.from(new Mat4().setFromEulerAngles(rotation.x, rotation.y, rotation.z).data);
|
||||||
0, 1, 0, 0,
|
|
||||||
0, 0, 1, 0,
|
|
||||||
0, 0, 0, 1,
|
|
||||||
];
|
|
||||||
|
|
||||||
export const PLAYCANVAS_X_180_TRANSFORM = [
|
|
||||||
1, 0, 0, 0,
|
|
||||||
0, -1, 0, 0,
|
|
||||||
0, 0, -1, 0,
|
|
||||||
0, 0, 0, 1,
|
|
||||||
];
|
|
||||||
|
|
||||||
export const PLAYCANVAS_Y_180_TRANSFORM = [
|
|
||||||
-1, 0, 0, 0,
|
|
||||||
0, 1, 0, 0,
|
|
||||||
0, 0, -1, 0,
|
|
||||||
0, 0, 0, 1,
|
|
||||||
];
|
|
||||||
|
|
||||||
export const PLAYCANVAS_Z_180_TRANSFORM = [
|
|
||||||
-1, 0, 0, 0,
|
|
||||||
0, -1, 0, 0,
|
|
||||||
0, 0, 1, 0,
|
|
||||||
0, 0, 0, 1,
|
|
||||||
];
|
|
||||||
|
|
||||||
export function playCanvasAxisTransform(axis: SimulationTransformAxis, inverted: boolean): number[] {
|
|
||||||
if (!inverted) return PLAYCANVAS_IDENTITY_TRANSFORM;
|
|
||||||
if (axis === "x") return PLAYCANVAS_X_180_TRANSFORM;
|
|
||||||
if (axis === "y") return PLAYCANVAS_Y_180_TRANSFORM;
|
|
||||||
return PLAYCANVAS_Z_180_TRANSFORM;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SimulationRuntime {
|
export interface SimulationRuntime {
|
||||||
mount(canvas: HTMLCanvasElement): Promise<void>;
|
mount(canvas: HTMLCanvasElement): Promise<void>;
|
||||||
loadWorld(manifest: SimulationWorldManifest): Promise<void>;
|
loadWorld(manifest: SimulationWorldManifest): Promise<void>;
|
||||||
setViewMode(mode: SimulationViewMode): Promise<void>;
|
setViewMode(mode: SimulationViewMode): Promise<void>;
|
||||||
|
setControlMode(mode: SimulationControlMode): Promise<void>;
|
||||||
|
setUgvPreset(preset: SimulationUgvPreset): void;
|
||||||
setQuality(quality: SimulationQuality): void;
|
setQuality(quality: SimulationQuality): void;
|
||||||
setLayerWorldTransform(layer: SimulationLayer, transform: number[]): void;
|
setLayerWorldTransform(layer: SimulationLayer, transform: number[]): void;
|
||||||
setCameraInversion(axis: SimulationCameraAxis, inverted: boolean): void;
|
setCameraInversion(axis: SimulationCameraAxis, inverted: boolean): void;
|
||||||
@@ -78,10 +59,6 @@ export interface SimulationRuntime {
|
|||||||
|
|
||||||
type DesktopCameraInput = {
|
type DesktopCameraInput = {
|
||||||
read(): { mouse: number[] } & Record<string, number[]>;
|
read(): { mouse: number[] } & Record<string, number[]>;
|
||||||
_pointerId?: number;
|
|
||||||
_button?: number[];
|
|
||||||
_keyNow?: number[];
|
|
||||||
_keyPrev?: number[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type CameraController = ScriptType & Pick<CameraControls, "reset" | "focus"> & {
|
type CameraController = ScriptType & Pick<CameraControls, "reset" | "focus"> & {
|
||||||
@@ -123,6 +100,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
private visualEntity: Entity | null = null;
|
private visualEntity: Entity | null = null;
|
||||||
private visualAsset: Asset | null = null;
|
private visualAsset: Asset | null = null;
|
||||||
private collisionEntity: Entity | null = null;
|
private collisionEntity: Entity | null = null;
|
||||||
|
private collisionVisuals: ModelComponent[] = [];
|
||||||
private collisionAsset: Asset | null = null;
|
private collisionAsset: Asset | null = null;
|
||||||
private collisionMaterial: StandardMaterial | null = null;
|
private collisionMaterial: StandardMaterial | null = null;
|
||||||
private collisionSource: { meshUrl: string; projectId: string; worldTransform: number[] } | null = null;
|
private collisionSource: { meshUrl: string; projectId: string; worldTransform: number[] } | null = null;
|
||||||
@@ -131,23 +109,44 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
private viewMode: SimulationViewMode = "visual";
|
private viewMode: SimulationViewMode = "visual";
|
||||||
private quality: SimulationQuality = "high";
|
private quality: SimulationQuality = "high";
|
||||||
private cameraInversion = { horizontal: true, vertical: false };
|
private cameraInversion = { horizontal: true, vertical: false };
|
||||||
|
private controlMode: SimulationControlMode = "free";
|
||||||
|
private controlRequestVersion = 0;
|
||||||
|
private ugvController: SimulationUgvController | null = null;
|
||||||
|
private readonly lastCameraPosition = new Vec3();
|
||||||
|
private readonly lastCameraRotation = new Quat();
|
||||||
|
private cameraSnapshotReady = false;
|
||||||
private renderIntervalMs = 1000 / 60;
|
private renderIntervalMs = 1000 / 60;
|
||||||
private renderAccumulatorMs = 0;
|
private renderAccumulatorMs = 0;
|
||||||
private disposed = false;
|
private disposed = false;
|
||||||
private scheduleFrame = (deltaMs: number): void => {
|
private requestGsplatFrame = (): void => {
|
||||||
if (!this.app || this.disposed) return;
|
this.requestRender();
|
||||||
this.renderAccumulatorMs += deltaMs;
|
};
|
||||||
|
private updateFrame = (deltaSeconds: number): void => {
|
||||||
|
if (!this.app || !this.camera || this.disposed) return;
|
||||||
|
this.ugvController?.update(deltaSeconds);
|
||||||
|
const position = this.camera.getPosition();
|
||||||
|
const rotation = this.camera.getRotation();
|
||||||
|
const cameraChanged = !this.cameraSnapshotReady
|
||||||
|
|| !position.equals(this.lastCameraPosition)
|
||||||
|
|| !rotation.equals(this.lastCameraRotation);
|
||||||
|
if (!cameraChanged && this.controlMode === "free") return;
|
||||||
|
this.lastCameraPosition.copy(position);
|
||||||
|
this.lastCameraRotation.copy(rotation);
|
||||||
|
this.cameraSnapshotReady = true;
|
||||||
|
this.renderAccumulatorMs += deltaSeconds * 1000;
|
||||||
if (this.renderAccumulatorMs < this.renderIntervalMs) return;
|
if (this.renderAccumulatorMs < this.renderIntervalMs) return;
|
||||||
this.renderAccumulatorMs %= this.renderIntervalMs;
|
this.renderAccumulatorMs %= this.renderIntervalMs;
|
||||||
this.app.renderNextFrame = true;
|
this.app.renderNextFrame = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
async mount(canvas: HTMLCanvasElement): Promise<void> {
|
async mount(canvas: HTMLCanvasElement): Promise<void> {
|
||||||
if (this.app) throw new Error("PlayCanvas runtime уже смонтирован.");
|
if (this.app) throw new Error("Runtime сцены уже запущен.");
|
||||||
this.disposed = false;
|
this.disposed = false;
|
||||||
this.renderAccumulatorMs = 0;
|
this.renderAccumulatorMs = 0;
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
configureDracoDecoder();
|
configureDracoDecoder();
|
||||||
|
const ammo: AmmoApi = await prepareSimulationPhysics();
|
||||||
|
if (this.disposed) return;
|
||||||
canvas.tabIndex = 0;
|
canvas.tabIndex = 0;
|
||||||
canvas.addEventListener("contextmenu", preventContextMenu);
|
canvas.addEventListener("contextmenu", preventContextMenu);
|
||||||
canvas.addEventListener("pointerdown", focusCanvas, true);
|
canvas.addEventListener("pointerdown", focusCanvas, true);
|
||||||
@@ -161,7 +160,8 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
});
|
});
|
||||||
this.app = app;
|
this.app = app;
|
||||||
app.autoRender = false;
|
app.autoRender = false;
|
||||||
app.on("frameupdate", this.scheduleFrame);
|
app.on("update", this.updateFrame);
|
||||||
|
app.systems.gsplat?.on("frame:request", this.requestGsplatFrame);
|
||||||
app.setCanvasFillMode(FILLMODE_NONE, 1, 1);
|
app.setCanvasFillMode(FILLMODE_NONE, 1, 1);
|
||||||
app.setCanvasResolution(RESOLUTION_AUTO);
|
app.setCanvasResolution(RESOLUTION_AUTO);
|
||||||
app.scene.ambientLight = new Color(0.35, 0.37, 0.42);
|
app.scene.ambientLight = new Color(0.35, 0.37, 0.42);
|
||||||
@@ -188,6 +188,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
app.root.addChild(camera);
|
app.root.addChild(camera);
|
||||||
this.camera = camera;
|
this.camera = camera;
|
||||||
this.cameraController = controller;
|
this.cameraController = controller;
|
||||||
|
this.ugvController = new SimulationUgvController(app, canvas, camera, ammo);
|
||||||
|
|
||||||
const light = new Entity("SimulationSun");
|
const light = new Entity("SimulationSun");
|
||||||
light.addComponent("light", {
|
light.addComponent("light", {
|
||||||
@@ -210,7 +211,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
const app = this.requiredApp();
|
const app = this.requiredApp();
|
||||||
this.unloadWorld();
|
this.unloadWorld();
|
||||||
const sourceUrl = manifest.visual.streamedSogUrl ?? manifest.visual.previewSogUrl;
|
const sourceUrl = manifest.visual.streamedSogUrl ?? manifest.visual.previewSogUrl;
|
||||||
if (!sourceUrl) throw new Error("World manifest не содержит визуальный SOG.");
|
if (!sourceUrl) throw new Error("Манифест локации не содержит визуальный слой.");
|
||||||
const asset = new Asset(`SimulationWorld:${manifest.projectId}`, "gsplat", { url: sourceUrl });
|
const asset = new Asset(`SimulationWorld:${manifest.projectId}`, "gsplat", { url: sourceUrl });
|
||||||
app.assets.add(asset);
|
app.assets.add(asset);
|
||||||
this.visualAsset = asset;
|
this.visualAsset = asset;
|
||||||
@@ -233,10 +234,11 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
this.visualEntity = visual;
|
this.visualEntity = visual;
|
||||||
this.applyQuality();
|
this.applyQuality();
|
||||||
this.applyViewMode();
|
this.applyViewMode();
|
||||||
|
this.requestRender();
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
const failed = (error: unknown) => {
|
const failed = (_error: unknown) => {
|
||||||
reject(new Error(error instanceof Error ? error.message : "PlayCanvas не загрузил SOG."));
|
reject(new Error("Визуальный слой не загрузился."));
|
||||||
};
|
};
|
||||||
asset.ready(ready);
|
asset.ready(ready);
|
||||||
asset.once("error", failed);
|
asset.once("error", failed);
|
||||||
@@ -254,7 +256,6 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async setViewMode(mode: SimulationViewMode): Promise<void> {
|
async setViewMode(mode: SimulationViewMode): Promise<void> {
|
||||||
this.resetCameraInteraction();
|
|
||||||
this.viewMode = mode;
|
this.viewMode = mode;
|
||||||
this.applyViewMode();
|
this.applyViewMode();
|
||||||
try {
|
try {
|
||||||
@@ -263,13 +264,58 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
this.applyViewMode();
|
this.applyViewMode();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
this.requestRender();
|
||||||
this.canvas?.focus({ preventScroll: true });
|
this.canvas?.focus({ preventScroll: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setControlMode(mode: SimulationControlMode): Promise<void> {
|
||||||
|
const requestVersion = ++this.controlRequestVersion;
|
||||||
|
const controller = this.ugvController;
|
||||||
|
if (!controller) throw new Error("Управление UGV ещё не готово.");
|
||||||
|
|
||||||
|
if (mode === "free") {
|
||||||
|
controller.disable();
|
||||||
|
this.controlMode = "free";
|
||||||
|
if (this.cameraController) this.cameraController.enabled = true;
|
||||||
|
this.home();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.controlMode === "ugv" && controller.enabled) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.ensureCollision();
|
||||||
|
if (requestVersion !== this.controlRequestVersion || this.disposed) return;
|
||||||
|
const collisionWorld = this.collisionEntity;
|
||||||
|
if (!collisionWorld) throw new Error("Физический слой UGV отсутствует.");
|
||||||
|
if (this.cameraController) this.cameraController.enabled = false;
|
||||||
|
await controller.enable(collisionWorld);
|
||||||
|
if (requestVersion !== this.controlRequestVersion || this.disposed) {
|
||||||
|
controller.disable();
|
||||||
|
if (this.cameraController) this.cameraController.enabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.controlMode = "ugv";
|
||||||
|
this.cameraSnapshotReady = false;
|
||||||
|
this.requestRender();
|
||||||
|
} catch (error) {
|
||||||
|
controller.disable();
|
||||||
|
this.controlMode = "free";
|
||||||
|
if (this.cameraController) this.cameraController.enabled = true;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setQuality(quality: SimulationQuality): void {
|
setQuality(quality: SimulationQuality): void {
|
||||||
this.quality = quality;
|
this.quality = quality;
|
||||||
this.applyQuality();
|
this.applyQuality();
|
||||||
|
this.requestRender();
|
||||||
|
}
|
||||||
|
|
||||||
|
setUgvPreset(preset: SimulationUgvPreset): void {
|
||||||
|
this.ugvController?.configure(preset);
|
||||||
|
this.cameraSnapshotReady = false;
|
||||||
|
this.requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
setLayerWorldTransform(layer: SimulationLayer, transform: number[]): void {
|
setLayerWorldTransform(layer: SimulationLayer, transform: number[]): void {
|
||||||
@@ -278,10 +324,13 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
}
|
}
|
||||||
if (layer === "visual") {
|
if (layer === "visual") {
|
||||||
if (this.visualEntity) applyWorldTransform(this.visualEntity, transform);
|
if (this.visualEntity) applyWorldTransform(this.visualEntity, transform);
|
||||||
|
this.requestRender();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.collisionSource) this.collisionSource.worldTransform = [...transform];
|
if (this.collisionSource) this.collisionSource.worldTransform = [...transform];
|
||||||
if (this.collisionEntity) applyWorldTransform(this.collisionEntity, transform);
|
if (this.collisionEntity) applyWorldTransform(this.collisionEntity, transform);
|
||||||
|
this.ugvController?.syncCollisionBodies();
|
||||||
|
this.requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
setCameraInversion(axis: SimulationCameraAxis, inverted: boolean): void {
|
setCameraInversion(axis: SimulationCameraAxis, inverted: boolean): void {
|
||||||
@@ -289,6 +338,12 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
home(): void {
|
home(): void {
|
||||||
|
if (this.controlMode === "ugv" && this.ugvController?.enabled) {
|
||||||
|
this.ugvController.reset();
|
||||||
|
this.cameraSnapshotReady = false;
|
||||||
|
this.requestRender();
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.resetCameraInteraction();
|
this.resetCameraInteraction();
|
||||||
if (this.cameraController) {
|
if (this.cameraController) {
|
||||||
this.cameraController.reset(HOME_FOCUS, HOME_POSITION);
|
this.cameraController.reset(HOME_FOCUS, HOME_POSITION);
|
||||||
@@ -296,6 +351,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
this.camera.setPosition(HOME_POSITION);
|
this.camera.setPosition(HOME_POSITION);
|
||||||
this.camera.lookAt(HOME_FOCUS);
|
this.camera.lookAt(HOME_FOCUS);
|
||||||
}
|
}
|
||||||
|
this.requestRender();
|
||||||
this.canvas?.focus({ preventScroll: true });
|
this.canvas?.focus({ preventScroll: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,6 +368,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
this.camera.setPosition(position);
|
this.camera.setPosition(position);
|
||||||
this.camera.lookAt(focus);
|
this.camera.lookAt(focus);
|
||||||
}
|
}
|
||||||
|
this.requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose(): void {
|
dispose(): void {
|
||||||
@@ -321,10 +378,13 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
this.resizeObserver = null;
|
this.resizeObserver = null;
|
||||||
const webgl = this.canvas?.getContext("webgl2") ?? this.canvas?.getContext("webgl");
|
const webgl = this.canvas?.getContext("webgl2") ?? this.canvas?.getContext("webgl");
|
||||||
this.unloadWorld();
|
this.unloadWorld();
|
||||||
|
this.ugvController?.dispose();
|
||||||
|
this.ugvController = null;
|
||||||
if (this.app) {
|
if (this.app) {
|
||||||
this.app.autoRender = false;
|
this.app.autoRender = false;
|
||||||
this.app.renderNextFrame = false;
|
this.app.renderNextFrame = false;
|
||||||
this.app.off("frameupdate", this.scheduleFrame);
|
this.app.systems.gsplat?.off("frame:request", this.requestGsplatFrame);
|
||||||
|
this.app.off("update", this.updateFrame);
|
||||||
this.app.destroy();
|
this.app.destroy();
|
||||||
}
|
}
|
||||||
webgl?.getExtension("WEBGL_lose_context")?.loseContext();
|
webgl?.getExtension("WEBGL_lose_context")?.loseContext();
|
||||||
@@ -340,19 +400,26 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
this.canvas = null;
|
this.canvas = null;
|
||||||
this.camera = null;
|
this.camera = null;
|
||||||
this.cameraController = null;
|
this.cameraController = null;
|
||||||
|
this.controlMode = "free";
|
||||||
|
this.cameraSnapshotReady = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private requiredApp(): Application {
|
private requiredApp(): Application {
|
||||||
if (!this.app) throw new Error("PlayCanvas runtime не смонтирован.");
|
if (!this.app) throw new Error("Runtime сцены не запущен.");
|
||||||
return this.app;
|
return this.app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private requestRender(): void {
|
||||||
|
if (this.app && !this.disposed) this.app.renderNextFrame = true;
|
||||||
|
}
|
||||||
|
|
||||||
private resize(): void {
|
private resize(): void {
|
||||||
if (!this.app || !this.canvas) return;
|
if (!this.app || !this.canvas) return;
|
||||||
const bounds = this.canvas.parentElement?.getBoundingClientRect();
|
const bounds = this.canvas.parentElement?.getBoundingClientRect();
|
||||||
const width = Math.max(1, Math.round(bounds?.width ?? this.canvas.clientWidth));
|
const width = Math.max(1, Math.round(bounds?.width ?? this.canvas.clientWidth));
|
||||||
const height = Math.max(1, Math.round(bounds?.height ?? this.canvas.clientHeight));
|
const height = Math.max(1, Math.round(bounds?.height ?? this.canvas.clientHeight));
|
||||||
this.app.resizeCanvas(width, height);
|
this.app.resizeCanvas(width, height);
|
||||||
|
this.requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
private applyViewMode(): void {
|
private applyViewMode(): void {
|
||||||
@@ -360,36 +427,27 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
this.visualEntity.enabled = this.viewMode !== "collision" || !this.collisionEntity;
|
this.visualEntity.enabled = this.viewMode !== "collision" || !this.collisionEntity;
|
||||||
}
|
}
|
||||||
if (this.collisionEntity) {
|
if (this.collisionEntity) {
|
||||||
this.collisionEntity.enabled = this.viewMode !== "visual";
|
// Physics bodies must stay active while their green debug geometry is hidden.
|
||||||
|
this.collisionEntity.enabled = true;
|
||||||
|
const visible = this.viewMode !== "visual";
|
||||||
|
for (const model of this.collisionVisuals) model.enabled = visible;
|
||||||
}
|
}
|
||||||
if (this.collisionMaterial) {
|
if (this.collisionMaterial) {
|
||||||
const combined = this.viewMode === "combined";
|
const combined = this.viewMode === "combined";
|
||||||
this.collisionMaterial.opacity = combined ? 0.34 : 0.82;
|
this.collisionMaterial.opacity = combined ? 0.34 : 1;
|
||||||
|
this.collisionMaterial.blendType = combined ? BLEND_NORMAL : BLEND_NONE;
|
||||||
this.collisionMaterial.depthWrite = !combined;
|
this.collisionMaterial.depthWrite = !combined;
|
||||||
this.collisionMaterial.update();
|
this.collisionMaterial.update();
|
||||||
}
|
}
|
||||||
|
this.requestRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
private resetCameraInteraction(): void {
|
private resetCameraInteraction(): void {
|
||||||
const controller = this.cameraController;
|
const controller = this.cameraController;
|
||||||
const canvas = this.canvas;
|
if (!controller) return;
|
||||||
if (!controller || !canvas) return;
|
// CameraControls owns pointer capture and its input-source lifecycle. Mutating
|
||||||
const input = controller._desktopInput;
|
// those private fields here left the source unable to claim the next drag
|
||||||
const pointerId = input?._pointerId ?? -1;
|
// after a layer switch. The documented state event drains pending deltas.
|
||||||
if (pointerId >= 0 && canvas.hasPointerCapture(pointerId)) {
|
|
||||||
try {
|
|
||||||
canvas.releasePointerCapture(pointerId);
|
|
||||||
} catch {
|
|
||||||
// The browser may already have released capture after a trackpad gesture.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (input) {
|
|
||||||
input._pointerId = -1;
|
|
||||||
input._button?.fill(0);
|
|
||||||
input._keyNow?.fill(0);
|
|
||||||
input._keyPrev?.fill(0);
|
|
||||||
input.read();
|
|
||||||
}
|
|
||||||
controller.fire("state");
|
controller.fire("state");
|
||||||
const state = controller._state;
|
const state = controller._state;
|
||||||
if (state) {
|
if (state) {
|
||||||
@@ -430,45 +488,52 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
this.collisionAsset = asset;
|
this.collisionAsset = asset;
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
asset.ready((loaded) => {
|
asset.ready((loaded) => {
|
||||||
if (this.disposed || !this.app) {
|
try {
|
||||||
loaded.unload();
|
if (this.disposed || !this.app) {
|
||||||
resolve();
|
loaded.unload();
|
||||||
return;
|
resolve();
|
||||||
}
|
return;
|
||||||
const resource = loaded.resource as ContainerResource | null;
|
|
||||||
if (!resource) {
|
|
||||||
reject(new Error("PlayCanvas не открыл collision GLB."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const entity = resource.instantiateRenderEntity({
|
|
||||||
castShadows: false,
|
|
||||||
receiveShadows: false,
|
|
||||||
});
|
|
||||||
entity.name = "CollisionWorld";
|
|
||||||
entity.enabled = false;
|
|
||||||
applyWorldTransform(entity, worldTransform);
|
|
||||||
app.root.addChild(entity);
|
|
||||||
|
|
||||||
const material = new StandardMaterial();
|
|
||||||
material.name = "SimulationCollisionMaterial";
|
|
||||||
material.diffuse = new Color(0.2, 0.95, 0.42);
|
|
||||||
material.emissive = new Color(0.08, 0.36, 0.14);
|
|
||||||
material.opacity = 0.34;
|
|
||||||
material.blendType = BLEND_NORMAL;
|
|
||||||
material.depthWrite = false;
|
|
||||||
material.cull = CULLFACE_NONE;
|
|
||||||
material.update();
|
|
||||||
for (const render of entity.findComponents("render") as RenderComponent[]) {
|
|
||||||
for (const meshInstance of render.meshInstances) {
|
|
||||||
meshInstance.material = material;
|
|
||||||
}
|
}
|
||||||
|
const resource = loaded.resource as ContainerResource | null;
|
||||||
|
if (!resource) throw new Error("Контейнер GLB не содержит ресурса.");
|
||||||
|
const entity = resource.instantiateModelEntity({
|
||||||
|
castShadows: false,
|
||||||
|
receiveShadows: false,
|
||||||
|
});
|
||||||
|
entity.name = "CollisionWorld";
|
||||||
|
entity.enabled = false;
|
||||||
|
applyWorldTransform(entity, worldTransform);
|
||||||
|
app.root.addChild(entity);
|
||||||
|
|
||||||
|
const material = new StandardMaterial();
|
||||||
|
material.name = "SimulationCollisionMaterial";
|
||||||
|
material.diffuse = new Color(0.2, 0.95, 0.42);
|
||||||
|
material.emissive = new Color(0.08, 0.36, 0.14);
|
||||||
|
material.opacity = 0.34;
|
||||||
|
material.blendType = BLEND_NORMAL;
|
||||||
|
material.depthWrite = false;
|
||||||
|
material.cull = CULLFACE_NONE;
|
||||||
|
material.update();
|
||||||
|
const models = entity.findComponents("model") as ModelComponent[];
|
||||||
|
for (const model of models) {
|
||||||
|
for (const meshInstance of model.meshInstances ?? []) {
|
||||||
|
meshInstance.material = material;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.collisionEntity = entity;
|
||||||
|
this.collisionVisuals = models;
|
||||||
|
this.collisionMaterial = material;
|
||||||
|
this.applyViewMode();
|
||||||
|
this.requestRender();
|
||||||
|
resolve();
|
||||||
|
} catch (error) {
|
||||||
|
reject(new Error(
|
||||||
|
`Слой коллизий не открылся: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
this.collisionEntity = entity;
|
|
||||||
this.collisionMaterial = material;
|
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
asset.once("error", (error: unknown) => {
|
asset.once("error", (error: unknown) => {
|
||||||
reject(new Error(error instanceof Error ? error.message : "PlayCanvas не загрузил collision GLB."));
|
reject(new Error(`Слой коллизий не загрузился: ${String(error)}`));
|
||||||
});
|
});
|
||||||
app.assets.load(asset);
|
app.assets.load(asset);
|
||||||
});
|
});
|
||||||
@@ -477,7 +542,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
private async ensureCollision(): Promise<void> {
|
private async ensureCollision(): Promise<void> {
|
||||||
if (this.collisionEntity) return;
|
if (this.collisionEntity) return;
|
||||||
if (!this.collisionSource) {
|
if (!this.collisionSource) {
|
||||||
throw new Error("Collision GLB отсутствует в world manifest.");
|
throw new Error("Слой коллизий отсутствует в манифесте локации.");
|
||||||
}
|
}
|
||||||
if (!this.collisionLoadPromise) {
|
if (!this.collisionLoadPromise) {
|
||||||
this.collisionLoadPromise = this.loadCollision(
|
this.collisionLoadPromise = this.loadCollision(
|
||||||
@@ -492,6 +557,10 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private unloadWorld(): void {
|
private unloadWorld(): void {
|
||||||
|
this.controlRequestVersion += 1;
|
||||||
|
this.ugvController?.disable();
|
||||||
|
this.controlMode = "free";
|
||||||
|
if (this.cameraController) this.cameraController.enabled = true;
|
||||||
if (this.visualEntity) {
|
if (this.visualEntity) {
|
||||||
this.visualEntity.destroy();
|
this.visualEntity.destroy();
|
||||||
this.visualEntity = null;
|
this.visualEntity = null;
|
||||||
@@ -505,6 +574,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
|
|||||||
this.collisionEntity.destroy();
|
this.collisionEntity.destroy();
|
||||||
this.collisionEntity = null;
|
this.collisionEntity = null;
|
||||||
}
|
}
|
||||||
|
this.collisionVisuals = [];
|
||||||
if (this.collisionAsset && this.app) {
|
if (this.collisionAsset && this.app) {
|
||||||
this.app.assets.remove(this.collisionAsset);
|
this.app.assets.remove(this.collisionAsset);
|
||||||
this.collisionAsset.unload();
|
this.collisionAsset.unload();
|
||||||
|
|||||||
@@ -0,0 +1,333 @@
|
|||||||
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
Button,
|
||||||
|
Icon,
|
||||||
|
IconButton,
|
||||||
|
StatusBadge,
|
||||||
|
} from "@nodedc/ui-react";
|
||||||
|
|
||||||
|
import type {
|
||||||
|
SimulationProject,
|
||||||
|
SimulationProjectStatus,
|
||||||
|
SimulationUploadProgress,
|
||||||
|
} from "../../core/simulation/projects";
|
||||||
|
import { estimateSimulationProcessing } from "../../core/simulation/projects";
|
||||||
|
|
||||||
|
const ACTIVE_STATUSES = new Set<SimulationProjectStatus>([
|
||||||
|
"uploading",
|
||||||
|
"queued",
|
||||||
|
"processing",
|
||||||
|
"importing",
|
||||||
|
]);
|
||||||
|
|
||||||
|
export function SimulationCatalog({
|
||||||
|
projects,
|
||||||
|
uploadProgress,
|
||||||
|
loading,
|
||||||
|
loadError,
|
||||||
|
queueError,
|
||||||
|
dropping,
|
||||||
|
onCreate,
|
||||||
|
onRetryLoad,
|
||||||
|
onSelect,
|
||||||
|
onEdit,
|
||||||
|
onDelete,
|
||||||
|
}: {
|
||||||
|
projects: SimulationProject[];
|
||||||
|
uploadProgress: ReadonlyMap<string, SimulationUploadProgress>;
|
||||||
|
loading: boolean;
|
||||||
|
loadError: string | null;
|
||||||
|
queueError: string | null;
|
||||||
|
dropping: boolean;
|
||||||
|
onCreate: () => void;
|
||||||
|
onRetryLoad: () => void;
|
||||||
|
onSelect: (project: SimulationProject) => void;
|
||||||
|
onEdit: (project: SimulationProject) => void;
|
||||||
|
onDelete: (project: SimulationProject) => void;
|
||||||
|
}) {
|
||||||
|
const sourceBytes = projects.reduce(
|
||||||
|
(sum, project) => sum + project.source.totalByteLength,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
className="simulation-catalog"
|
||||||
|
data-dropping={dropping ? "true" : undefined}
|
||||||
|
aria-label="Проекты симуляции"
|
||||||
|
>
|
||||||
|
<header className="simulation-catalog__summary">
|
||||||
|
<div><span>Проектов</span><strong>{projects.length.toLocaleString("ru-RU")}</strong></div>
|
||||||
|
<div><span>Визуал готов</span><strong>{countStatus(projects, "ready")}</strong></div>
|
||||||
|
<div><span>В работе</span><strong>{projects.filter((project) => ACTIVE_STATUSES.has(project.status)).length}</strong></div>
|
||||||
|
<div><span>Исходники</span><strong>{formatBytes(sourceBytes)}</strong></div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{queueError ? (
|
||||||
|
<div className="simulation-catalog__notice" role="alert">
|
||||||
|
<Icon name="alert" size={18} />
|
||||||
|
<span>{queueError}</span>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{loading && projects.length === 0 ? (
|
||||||
|
<div className="simulation-catalog__state" role="status">
|
||||||
|
<ActivityIndicator label="Загрузка каталога" />
|
||||||
|
<span>Читаем каталог сцен</span>
|
||||||
|
</div>
|
||||||
|
) : loadError && projects.length === 0 ? (
|
||||||
|
<div className="simulation-catalog__state" role="alert">
|
||||||
|
<Icon name="alert" size={20} />
|
||||||
|
<div><strong>Каталог недоступен</strong><span>{loadError}</span></div>
|
||||||
|
<Button size="compact" onClick={onRetryLoad}>Повторить</Button>
|
||||||
|
</div>
|
||||||
|
) : projects.length === 0 ? (
|
||||||
|
<div className="simulation-catalog__empty">
|
||||||
|
<span><Icon name="globe" size={20} /></span>
|
||||||
|
<strong>Перетащите архивы в это окно</strong>
|
||||||
|
<p>Каждый ZIP, RAR или 7z сразу станет отдельным проектом и встанет в очередь.</p>
|
||||||
|
<Button variant="primary" icon={<Icon name="plus" size={16} />} onClick={onCreate}>
|
||||||
|
Новый проект
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="simulation-catalog__table-wrap">
|
||||||
|
<table className="simulation-catalog__table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Проект</th>
|
||||||
|
<th>Источник</th>
|
||||||
|
<th>Размер</th>
|
||||||
|
<th>Сплаты</th>
|
||||||
|
<th>Статус</th>
|
||||||
|
<th>Обновлён</th>
|
||||||
|
<th aria-label="Действия" />
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{projects.map((project) => (
|
||||||
|
<SimulationCatalogRow
|
||||||
|
key={project.projectId}
|
||||||
|
project={project}
|
||||||
|
uploadProgress={uploadProgress.get(project.projectId)}
|
||||||
|
onSelect={() => onSelect(project)}
|
||||||
|
onEdit={() => onEdit(project)}
|
||||||
|
onDelete={() => onDelete(project)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{dropping ? (
|
||||||
|
<div className="simulation-catalog__drop-overlay" role="status">
|
||||||
|
<span className="simulation-catalog__drop-mark"><Icon name="plus" size={28} /></span>
|
||||||
|
<strong>Отпустите архивы</strong>
|
||||||
|
<span>ZIP · RAR · 7z · до 16 ГБ на проект</span>
|
||||||
|
<small>Внутри — сцена LCC или LCC2 и связанные файлы</small>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SimulationCatalogRow({
|
||||||
|
project,
|
||||||
|
uploadProgress,
|
||||||
|
onSelect,
|
||||||
|
onEdit,
|
||||||
|
onDelete,
|
||||||
|
}: {
|
||||||
|
project: SimulationProject;
|
||||||
|
uploadProgress?: SimulationUploadProgress;
|
||||||
|
onSelect: () => void;
|
||||||
|
onEdit: () => void;
|
||||||
|
onDelete: () => void;
|
||||||
|
}) {
|
||||||
|
const status = statusPresentation(project.status);
|
||||||
|
const active = ACTIVE_STATUSES.has(project.status);
|
||||||
|
return (
|
||||||
|
<tr data-status={project.status}>
|
||||||
|
<td>
|
||||||
|
<button type="button" onClick={onSelect}>
|
||||||
|
<span className="simulation-catalog__project-icon"><Icon name="globe" size={18} /></span>
|
||||||
|
<span>
|
||||||
|
<strong>{project.name}</strong>
|
||||||
|
<small>{sourceFileLabel(project)}</small>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
{active ? (
|
||||||
|
<td colSpan={3}>
|
||||||
|
<ProjectProgress project={project} localProgress={uploadProgress} />
|
||||||
|
</td>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<td>
|
||||||
|
{project.source.kind === "archive" ? "Архив" : "Папка"}
|
||||||
|
<small>{project.source.files.length} файл(ов)</small>
|
||||||
|
</td>
|
||||||
|
<td>{formatBytes(project.source.totalByteLength)}</td>
|
||||||
|
<td>—</td>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<td><StatusBadge tone={status.tone}>{status.label}</StatusBadge></td>
|
||||||
|
<td>{formatDate(project.updatedAtUtc)}</td>
|
||||||
|
<td>
|
||||||
|
<div className="simulation-catalog__actions">
|
||||||
|
<IconButton label={`Удалить ${project.name}`} onClick={onDelete}>
|
||||||
|
<Icon name="trash" size={16} />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton label={`Редактировать ${project.name}`} onClick={onEdit}>
|
||||||
|
<Icon name="edit" size={16} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ProjectProgress({
|
||||||
|
project,
|
||||||
|
localProgress,
|
||||||
|
}: {
|
||||||
|
project: SimulationProject;
|
||||||
|
localProgress?: SimulationUploadProgress;
|
||||||
|
}) {
|
||||||
|
const progress = progressPresentation(project, localProgress);
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="simulation-catalog__progress"
|
||||||
|
data-indeterminate={progress.indeterminate ? "true" : undefined}
|
||||||
|
role="progressbar"
|
||||||
|
aria-label={progress.label}
|
||||||
|
aria-valuemin={0}
|
||||||
|
aria-valuemax={100}
|
||||||
|
aria-valuenow={progress.indeterminate ? undefined : progress.percent}
|
||||||
|
>
|
||||||
|
{progress.meta ? <small className="simulation-catalog__progress-meta">{progress.meta}</small> : null}
|
||||||
|
<span className="simulation-catalog__progress-track">
|
||||||
|
<i style={{ width: `${progress.percent}%` }} />
|
||||||
|
</span>
|
||||||
|
<small className="simulation-catalog__progress-stage">{progress.label}</small>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function progressPresentation(
|
||||||
|
project: SimulationProject,
|
||||||
|
local?: SimulationUploadProgress,
|
||||||
|
): { percent: number; label: string; meta: string | null; indeterminate: boolean } {
|
||||||
|
if (project.status === "uploading") {
|
||||||
|
const uploaded = local?.uploadedBytes ?? project.source.uploadedByteLength;
|
||||||
|
const total = local?.totalBytes ?? project.source.totalByteLength;
|
||||||
|
const percent = total ? Math.min(100, uploaded / total * 100) : 0;
|
||||||
|
return {
|
||||||
|
percent,
|
||||||
|
label: `${formatBytes(uploaded)} / ${formatBytes(total)}`,
|
||||||
|
meta: progressMeta(percent, local?.estimatedSecondsRemaining),
|
||||||
|
indeterminate: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (project.status === "queued") {
|
||||||
|
return { percent: 0, label: "Ожидает обработки", meta: null, indeterminate: true };
|
||||||
|
}
|
||||||
|
const estimate = estimateSimulationProcessing(project);
|
||||||
|
if (estimate) {
|
||||||
|
return {
|
||||||
|
percent: estimate.percent,
|
||||||
|
label: processingLabel(project),
|
||||||
|
meta: progressMeta(estimate.percent, estimate.estimatedSecondsRemaining),
|
||||||
|
indeterminate: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const completed = project.provider.progress?.completed_steps;
|
||||||
|
const total = project.provider.progress?.total_steps;
|
||||||
|
if (typeof completed === "number" && typeof total === "number" && total > 0) {
|
||||||
|
const percent = Math.min(100, completed / total * 100);
|
||||||
|
return {
|
||||||
|
percent,
|
||||||
|
label: processingLabel(project),
|
||||||
|
meta: progressMeta(percent, null),
|
||||||
|
indeterminate: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
percent: project.status === "importing" ? 96 : 0,
|
||||||
|
label: processingLabel(project),
|
||||||
|
meta: project.status === "importing" ? progressMeta(96, null) : null,
|
||||||
|
indeterminate: project.status !== "importing",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function progressMeta(percent: number, seconds: number | null | undefined): string {
|
||||||
|
const roundedPercent = Math.min(100, Math.max(0, Math.floor(percent)));
|
||||||
|
return `Прогресс ${roundedPercent}% | ${remainingTimeLabel(seconds)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function remainingTimeLabel(seconds: number | null | undefined): string {
|
||||||
|
if (seconds === null || seconds === undefined) return "Время уточняется";
|
||||||
|
if (seconds <= 60) return "Осталось меньше минуты";
|
||||||
|
const minutes = Math.ceil(seconds / 60);
|
||||||
|
if (minutes < 60) return `Осталось примерно ${minutes} мин`;
|
||||||
|
const hours = Math.floor(minutes / 60);
|
||||||
|
const remainder = minutes % 60;
|
||||||
|
return `Осталось примерно ${hours} ч${remainder ? ` ${remainder} мин` : ""}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function processingLabel(project: SimulationProject): string {
|
||||||
|
if (project.status === "importing") return "Подготавливаем сцену";
|
||||||
|
const stage = project.provider.state;
|
||||||
|
if (stage === "verifying_source") return "Проверяем исходник";
|
||||||
|
if (stage === "inspecting") return "Анализируем локацию";
|
||||||
|
if (stage === "building_preview") return "Подготавливаем локацию";
|
||||||
|
if (stage === "building_streamed_sog") return "Собираем уровни детализации";
|
||||||
|
if (stage === "building_collision") return "Собираем коллизии";
|
||||||
|
return "Обрабатываем локацию";
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusPresentation(status: SimulationProjectStatus): {
|
||||||
|
label: string;
|
||||||
|
tone: "success" | "accent" | "warning" | "neutral";
|
||||||
|
} {
|
||||||
|
if (status === "ready") return { label: "Визуал готов", tone: "success" };
|
||||||
|
if (status === "failed") return { label: "Ошибка", tone: "warning" };
|
||||||
|
if (status === "uploading") return { label: "Загрузка", tone: "neutral" };
|
||||||
|
if (status === "queued") return { label: "В очереди", tone: "neutral" };
|
||||||
|
if (status === "importing") return { label: "Подготовка", tone: "accent" };
|
||||||
|
return { label: "Обработка", tone: "accent" };
|
||||||
|
}
|
||||||
|
|
||||||
|
function sourceFileLabel(project: SimulationProject): string {
|
||||||
|
const first = project.source.files[0]?.logicalPath;
|
||||||
|
if (project.source.kind === "archive" && first) return first;
|
||||||
|
return `${sceneTypeLabel(project.sceneType)} · ${project.source.files.length} файл(ов)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sceneTypeLabel(value: SimulationProject["sceneType"]): string {
|
||||||
|
if (value === "interior") return "Интерьер";
|
||||||
|
if (value === "object") return "Объект";
|
||||||
|
return "Улица";
|
||||||
|
}
|
||||||
|
|
||||||
|
function countStatus(projects: SimulationProject[], status: SimulationProjectStatus): number {
|
||||||
|
return projects.filter((project) => project.status === status).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDate(value: string): string {
|
||||||
|
const date = new Date(value);
|
||||||
|
return Number.isNaN(date.valueOf()) ? "—" : date.toLocaleString("ru-RU", {
|
||||||
|
day: "2-digit",
|
||||||
|
month: "short",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatBytes(value: number): string {
|
||||||
|
if (value < 1024) return `${value} Б`;
|
||||||
|
if (value < 1024 ** 2) return `${(value / 1024).toLocaleString("ru-RU", { maximumFractionDigits: 1 })} КБ`;
|
||||||
|
if (value < 1024 ** 3) return `${(value / 1024 ** 2).toLocaleString("ru-RU", { maximumFractionDigits: 1 })} МБ`;
|
||||||
|
return `${(value / 1024 ** 3).toLocaleString("ru-RU", { maximumFractionDigits: 2 })} ГБ`;
|
||||||
|
}
|
||||||
@@ -18,6 +18,10 @@ import {
|
|||||||
type SimulationUploadCandidate,
|
type SimulationUploadCandidate,
|
||||||
type SimulationUploadProgress,
|
type SimulationUploadProgress,
|
||||||
} from "../../core/simulation/projects";
|
} from "../../core/simulation/projects";
|
||||||
|
import {
|
||||||
|
candidatesFromDrop,
|
||||||
|
validateSimulationCandidates,
|
||||||
|
} from "../../core/simulation/sourceFiles";
|
||||||
|
|
||||||
export function SimulationProjectWindow({
|
export function SimulationProjectWindow({
|
||||||
open,
|
open,
|
||||||
@@ -55,7 +59,7 @@ export function SimulationProjectWindow({
|
|||||||
|
|
||||||
const acceptCandidates = (next: SimulationUploadCandidate[]) => {
|
const acceptCandidates = (next: SimulationUploadCandidate[]) => {
|
||||||
try {
|
try {
|
||||||
validateCandidates(next);
|
validateSimulationCandidates(next);
|
||||||
setCandidates(next);
|
setCandidates(next);
|
||||||
setError(null);
|
setError(null);
|
||||||
if (!name.trim() && next.length) {
|
if (!name.trim() && next.length) {
|
||||||
@@ -213,7 +217,10 @@ export function SimulationProjectWindow({
|
|||||||
<div className="simulation-upload-progress" role="status">
|
<div className="simulation-upload-progress" role="status">
|
||||||
<span><i style={{ width: `${Math.min(100, progress.totalBytes ? progress.uploadedBytes / progress.totalBytes * 100 : 0)}%` }} /></span>
|
<span><i style={{ width: `${Math.min(100, progress.totalBytes ? progress.uploadedBytes / progress.totalBytes * 100 : 0)}%` }} /></span>
|
||||||
<div>
|
<div>
|
||||||
<strong>{formatBytes(progress.uploadedBytes)} / {formatBytes(progress.totalBytes)}</strong>
|
<strong>
|
||||||
|
{formatBytes(progress.uploadedBytes)} / {formatBytes(progress.totalBytes)}
|
||||||
|
{" · "}{formatUploadEta(progress.estimatedSecondsRemaining)}
|
||||||
|
</strong>
|
||||||
<small>{progress.currentPath}</small>
|
<small>{progress.currentPath}</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -224,83 +231,15 @@ export function SimulationProjectWindow({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LegacyFileEntry {
|
function formatUploadEta(seconds: number | null): string {
|
||||||
isFile: boolean;
|
if (seconds === 0) return "загружено";
|
||||||
isDirectory: boolean;
|
if (seconds === null) return "считаем время";
|
||||||
name: string;
|
if (seconds < 60) return `осталось ≈ ${Math.max(1, seconds)} сек`;
|
||||||
fullPath: string;
|
const minutes = Math.ceil(seconds / 60);
|
||||||
file?: (callback: (file: File) => void, error?: (error: DOMException) => void) => void;
|
if (minutes < 60) return `осталось ≈ ${minutes} мин`;
|
||||||
createReader?: () => {
|
const hours = Math.floor(minutes / 60);
|
||||||
readEntries: (
|
const remainder = minutes % 60;
|
||||||
callback: (entries: LegacyFileEntry[]) => void,
|
return `осталось ≈ ${hours} ч${remainder ? ` ${remainder} мин` : ""}`;
|
||||||
error?: (error: DOMException) => void,
|
|
||||||
) => void;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function candidatesFromDrop(transfer: DataTransfer): Promise<SimulationUploadCandidate[]> {
|
|
||||||
const candidates: SimulationUploadCandidate[] = [];
|
|
||||||
for (const item of Array.from(transfer.items)) {
|
|
||||||
const entry = (item as DataTransferItem & {
|
|
||||||
webkitGetAsEntry?: () => LegacyFileEntry | null;
|
|
||||||
}).webkitGetAsEntry?.();
|
|
||||||
if (entry) {
|
|
||||||
await collectEntry(entry, candidates);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const file = item.getAsFile();
|
|
||||||
if (file) candidates.push({ file, logicalPath: file.name });
|
|
||||||
}
|
|
||||||
if (!candidates.length) {
|
|
||||||
for (const file of Array.from(transfer.files)) {
|
|
||||||
candidates.push({ file, logicalPath: file.name });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return candidates;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function collectEntry(entry: LegacyFileEntry, target: SimulationUploadCandidate[]): Promise<void> {
|
|
||||||
if (entry.isFile && entry.file) {
|
|
||||||
const file = await new Promise<File>((resolve, reject) => entry.file?.(resolve, reject));
|
|
||||||
target.push({ file, logicalPath: entry.fullPath.replace(/^\//, "") || file.name });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!entry.isDirectory || !entry.createReader) return;
|
|
||||||
const reader = entry.createReader();
|
|
||||||
while (true) {
|
|
||||||
const batch = await new Promise<LegacyFileEntry[]>((resolve, reject) => reader.readEntries(resolve, reject));
|
|
||||||
if (!batch.length) break;
|
|
||||||
for (const child of batch) await collectEntry(child, target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateCandidates(candidates: SimulationUploadCandidate[]): void {
|
|
||||||
if (!candidates.length) throw new Error("Выберите архив или папку с результатом LCC/LCC2.");
|
|
||||||
const archives = candidates.filter((candidate) => /\.(zip|rar|7z)$/i.test(candidate.logicalPath));
|
|
||||||
if (archives.length) {
|
|
||||||
if (candidates.length !== 1) throw new Error("Архив нужно загружать одним файлом.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const descriptors = candidates.filter((candidate) => /\.lcc2?$/i.test(candidate.logicalPath));
|
|
||||||
if (descriptors.length !== 1) {
|
|
||||||
throw new Error("В папке должна быть ровно одна сцена .lcc или .lcc2.");
|
|
||||||
}
|
|
||||||
const paths = new Set<string>();
|
|
||||||
for (const candidate of candidates) {
|
|
||||||
const parts = candidate.logicalPath.split("/");
|
|
||||||
if (
|
|
||||||
!candidate.logicalPath ||
|
|
||||||
candidate.logicalPath.startsWith("/") ||
|
|
||||||
candidate.logicalPath.includes("\\") ||
|
|
||||||
candidate.logicalPath.includes("\0") ||
|
|
||||||
parts.some((part) => part === "" || part === "." || part === "..")
|
|
||||||
) {
|
|
||||||
throw new Error("Папка содержит небезопасный путь.");
|
|
||||||
}
|
|
||||||
if (candidate.file.size <= 0) throw new Error(`Пустой файл: ${candidate.logicalPath}`);
|
|
||||||
if (paths.has(candidate.logicalPath)) throw new Error(`Повторяющийся путь: ${candidate.logicalPath}`);
|
|
||||||
paths.add(candidate.logicalPath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function summarizeCandidates(candidates: SimulationUploadCandidate[]): { title: string; detail: string } {
|
function summarizeCandidates(candidates: SimulationUploadCandidate[]): { title: string; detail: string } {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,191 @@
|
|||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Icon,
|
||||||
|
RangeControl,
|
||||||
|
Switch,
|
||||||
|
TextField,
|
||||||
|
} from "@nodedc/ui-react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS,
|
||||||
|
type SimulationUgvPreset,
|
||||||
|
} from "../../core/simulation/projects";
|
||||||
|
|
||||||
|
export type SimulationUgvPresetSaveState = "idle" | "saving" | "saved" | "failed";
|
||||||
|
|
||||||
|
export function SimulationUgvSettingsPanel({
|
||||||
|
value,
|
||||||
|
disabled,
|
||||||
|
saveState,
|
||||||
|
onChange,
|
||||||
|
onSave,
|
||||||
|
}: {
|
||||||
|
value: SimulationUgvPreset;
|
||||||
|
disabled: boolean;
|
||||||
|
saveState: SimulationUgvPresetSaveState;
|
||||||
|
onChange: (preset: SimulationUgvPreset) => void;
|
||||||
|
onSave: () => void;
|
||||||
|
}) {
|
||||||
|
const dimensions = value.dimensionsMeters;
|
||||||
|
const updateDimensions = (
|
||||||
|
patch: Partial<SimulationUgvPreset["dimensionsMeters"]>,
|
||||||
|
) => {
|
||||||
|
const next = { ...dimensions, ...patch };
|
||||||
|
next.groundClearance = Math.max(
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS.groundClearance.min,
|
||||||
|
Math.min(next.groundClearance, roundToHundredth(next.height - 0.05)),
|
||||||
|
);
|
||||||
|
onChange({ ...value, dimensionsMeters: next });
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="simulation-viewport__settings-group simulation-ugv-settings">
|
||||||
|
<div className="simulation-viewport__settings-group-head">
|
||||||
|
<strong>Физика UGV</strong>
|
||||||
|
<span>Дифференциальный привод</span>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
A/D без W/S разворачивают UGV вокруг собственного центра. Скорость и
|
||||||
|
динамика применяются сразу. Мышь вращает камеру вокруг машины, колесо
|
||||||
|
меняет дистанцию; после паузы камера плавно возвращается назад. Ползунок
|
||||||
|
работает в штатном диапазоне, точное значение можно ввести вручную.
|
||||||
|
</p>
|
||||||
|
<TextField
|
||||||
|
label="Название пресета"
|
||||||
|
value={value.presetName}
|
||||||
|
maxLength={80}
|
||||||
|
disabled={disabled}
|
||||||
|
spellCheck={false}
|
||||||
|
onChange={(event) => onChange({ ...value, presetName: event.currentTarget.value })}
|
||||||
|
/>
|
||||||
|
<RangeControl
|
||||||
|
label="Масса конструкции"
|
||||||
|
value={value.massKg}
|
||||||
|
min={10}
|
||||||
|
max={500}
|
||||||
|
exactValueBounds={SIMULATION_UGV_EXACT_VALUE_LIMITS.massKg}
|
||||||
|
step={5}
|
||||||
|
disabled={disabled}
|
||||||
|
formatValue={(mass) => `${Math.round(mass)} кг`}
|
||||||
|
onChange={(massKg) => onChange({ ...value, massKg })}
|
||||||
|
/>
|
||||||
|
<div className="simulation-ugv-settings__subhead">
|
||||||
|
<strong>Габариты</strong>
|
||||||
|
<span>В метрах</span>
|
||||||
|
</div>
|
||||||
|
<RangeControl
|
||||||
|
label="Длина"
|
||||||
|
value={dimensions.length}
|
||||||
|
min={0.4}
|
||||||
|
max={3}
|
||||||
|
exactValueBounds={SIMULATION_UGV_EXACT_VALUE_LIMITS.length}
|
||||||
|
step={0.05}
|
||||||
|
disabled={disabled}
|
||||||
|
formatValue={formatMeters}
|
||||||
|
onChange={(length) => updateDimensions({ length })}
|
||||||
|
/>
|
||||||
|
<RangeControl
|
||||||
|
label="Ширина"
|
||||||
|
value={dimensions.width}
|
||||||
|
min={0.3}
|
||||||
|
max={2}
|
||||||
|
exactValueBounds={SIMULATION_UGV_EXACT_VALUE_LIMITS.width}
|
||||||
|
step={0.05}
|
||||||
|
disabled={disabled}
|
||||||
|
formatValue={formatMeters}
|
||||||
|
onChange={(width) => updateDimensions({ width })}
|
||||||
|
/>
|
||||||
|
<RangeControl
|
||||||
|
label="Общая высота"
|
||||||
|
value={dimensions.height}
|
||||||
|
min={0.2}
|
||||||
|
max={2}
|
||||||
|
exactValueBounds={SIMULATION_UGV_EXACT_VALUE_LIMITS.height}
|
||||||
|
step={0.05}
|
||||||
|
disabled={disabled}
|
||||||
|
formatValue={formatMeters}
|
||||||
|
onChange={(height) => updateDimensions({ height })}
|
||||||
|
/>
|
||||||
|
<RangeControl
|
||||||
|
label="Клиренс"
|
||||||
|
value={dimensions.groundClearance}
|
||||||
|
min={0.03}
|
||||||
|
max={Math.max(0.03, Math.min(0.6, dimensions.height - 0.05))}
|
||||||
|
exactValueBounds={{
|
||||||
|
min: SIMULATION_UGV_EXACT_VALUE_LIMITS.groundClearance.min,
|
||||||
|
max: Math.min(
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS.groundClearance.max,
|
||||||
|
dimensions.height - 0.05,
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
step={0.01}
|
||||||
|
disabled={disabled}
|
||||||
|
formatValue={formatMeters}
|
||||||
|
onChange={(groundClearance) => updateDimensions({ groundClearance })}
|
||||||
|
/>
|
||||||
|
<div className="simulation-ugv-settings__subhead">
|
||||||
|
<strong>Динамика</strong>
|
||||||
|
<span>Ограничители движения</span>
|
||||||
|
</div>
|
||||||
|
<RangeControl
|
||||||
|
label="Скорость движения"
|
||||||
|
value={value.maxSpeedMetersPerSecond}
|
||||||
|
min={0.1}
|
||||||
|
max={8}
|
||||||
|
exactValueBounds={SIMULATION_UGV_EXACT_VALUE_LIMITS.maxSpeedMetersPerSecond}
|
||||||
|
step={0.1}
|
||||||
|
disabled={disabled}
|
||||||
|
formatValue={(speed) => `${formatDecimal(speed, 1)} м/с`}
|
||||||
|
onChange={(maxSpeedMetersPerSecond) => onChange({
|
||||||
|
...value,
|
||||||
|
maxSpeedMetersPerSecond,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<RangeControl
|
||||||
|
label="Скорость разворота"
|
||||||
|
value={value.maxTurnRateDegrees}
|
||||||
|
min={5}
|
||||||
|
max={180}
|
||||||
|
exactValueBounds={SIMULATION_UGV_EXACT_VALUE_LIMITS.maxTurnRateDegrees}
|
||||||
|
step={5}
|
||||||
|
disabled={disabled}
|
||||||
|
formatValue={(rate) => `${Math.round(rate)}°/с`}
|
||||||
|
onChange={(maxTurnRateDegrees) => onChange({ ...value, maxTurnRateDegrees })}
|
||||||
|
/>
|
||||||
|
<Switch
|
||||||
|
checked={value.invertSteering}
|
||||||
|
disabled={disabled}
|
||||||
|
label="Инверсия управления A/D"
|
||||||
|
onChange={(invertSteering) => onChange({ ...value, invertSteering })}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className="simulation-ugv-settings__save"
|
||||||
|
size="compact"
|
||||||
|
variant="secondary"
|
||||||
|
icon={<Icon name="save" size={16} />}
|
||||||
|
disabled={disabled || saveState === "saving" || value.presetName.trim() === ""}
|
||||||
|
onClick={onSave}
|
||||||
|
>
|
||||||
|
{saveState === "saving"
|
||||||
|
? "Сохраняем…"
|
||||||
|
: saveState === "saved"
|
||||||
|
? "Пресет сохранён"
|
||||||
|
: saveState === "failed"
|
||||||
|
? "Повторить сохранение"
|
||||||
|
: "Сохранить пресет"}
|
||||||
|
</Button>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMeters(value: number): string {
|
||||||
|
return `${formatDecimal(value, 2)} м`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDecimal(value: number, digits: number): string {
|
||||||
|
return value.toFixed(digits).replace(".", ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
function roundToHundredth(value: number): number {
|
||||||
|
return Math.round(value * 100) / 100;
|
||||||
|
}
|
||||||
@@ -2,18 +2,21 @@ import { useEffect, useId, useRef, useState } from "react";
|
|||||||
import {
|
import {
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Button,
|
Button,
|
||||||
|
GlassMaterialSurface,
|
||||||
Icon,
|
Icon,
|
||||||
IconButton,
|
IconButton,
|
||||||
SegmentedControl,
|
SegmentedControl,
|
||||||
Select,
|
Select,
|
||||||
StatusBadge,
|
StatusBadge,
|
||||||
Switch,
|
Switch,
|
||||||
|
TextField,
|
||||||
} from "@nodedc/ui-react";
|
} from "@nodedc/ui-react";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
saveSimulationViewerSettings,
|
saveSimulationViewerSettings,
|
||||||
|
type SimulationEulerRotation,
|
||||||
type SimulationProject,
|
type SimulationProject,
|
||||||
type SimulationTransformAxis,
|
type SimulationUgvPreset,
|
||||||
type SimulationViewerSettings,
|
type SimulationViewerSettings,
|
||||||
} from "../../core/simulation/projects";
|
} from "../../core/simulation/projects";
|
||||||
import {
|
import {
|
||||||
@@ -22,16 +25,17 @@ import {
|
|||||||
} from "../../core/runtime/latestAsyncCommitter";
|
} from "../../core/runtime/latestAsyncCommitter";
|
||||||
import {
|
import {
|
||||||
PlayCanvasRuntime,
|
PlayCanvasRuntime,
|
||||||
playCanvasAxisTransform,
|
playCanvasEulerTransform,
|
||||||
|
type SimulationControlMode,
|
||||||
type SimulationQuality,
|
type SimulationQuality,
|
||||||
type SimulationViewMode,
|
type SimulationViewMode,
|
||||||
} from "./PlayCanvasRuntime";
|
} from "./PlayCanvasRuntime";
|
||||||
|
import {
|
||||||
|
SimulationUgvSettingsPanel,
|
||||||
|
type SimulationUgvPresetSaveState,
|
||||||
|
} from "./SimulationUgvSettingsPanel";
|
||||||
|
|
||||||
const AXIS_OPTIONS: Array<{ value: SimulationTransformAxis; label: string }> = [
|
const ROTATION_AXES = ["x", "y", "z"] as const;
|
||||||
{ value: "x", label: "X" },
|
|
||||||
{ value: "y", label: "Y" },
|
|
||||||
{ value: "z", label: "Z" },
|
|
||||||
];
|
|
||||||
|
|
||||||
export function SimulationViewport({
|
export function SimulationViewport({
|
||||||
project,
|
project,
|
||||||
@@ -44,23 +48,34 @@ export function SimulationViewport({
|
|||||||
const runtimeRef = useRef<PlayCanvasRuntime | null>(null);
|
const runtimeRef = useRef<PlayCanvasRuntime | null>(null);
|
||||||
const viewerSettingsRef = useRef(project.viewerSettings);
|
const viewerSettingsRef = useRef(project.viewerSettings);
|
||||||
const settingsCommitterRef = useRef<LatestAsyncCommitter<SimulationViewerSettings> | null>(null);
|
const settingsCommitterRef = useRef<LatestAsyncCommitter<SimulationViewerSettings> | null>(null);
|
||||||
|
const settingsCommitSucceededRef = useRef(true);
|
||||||
|
const ugvSaveRequestRef = useRef(0);
|
||||||
|
const controlRequestRef = useRef(0);
|
||||||
const settingsId = useId();
|
const settingsId = useId();
|
||||||
const [state, setState] = useState<"mounting" | "loading" | "ready" | "failed">("mounting");
|
const [state, setState] = useState<"mounting" | "loading" | "ready" | "failed">("mounting");
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [viewMode, setViewMode] = useState<SimulationViewMode>("visual");
|
const [viewMode, setViewMode] = useState<SimulationViewMode>("visual");
|
||||||
|
const [controlMode, setControlMode] = useState<SimulationControlMode>("free");
|
||||||
|
const [controlState, setControlState] = useState<"idle" | "loading" | "ready" | "failed">("idle");
|
||||||
const [quality, setQuality] = useState<SimulationQuality>(project.viewerSettings.quality);
|
const [quality, setQuality] = useState<SimulationQuality>(project.viewerSettings.quality);
|
||||||
const [collisionState, setCollisionState] = useState<"idle" | "loading" | "ready" | "failed">("idle");
|
const [collisionState, setCollisionState] = useState<"idle" | "loading" | "ready" | "failed">("idle");
|
||||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||||
const [visualInverted, setVisualInverted] = useState(project.viewerSettings.visual.inverted);
|
const [visualRotation, setVisualRotation] = useState({
|
||||||
const [visualAxis, setVisualAxis] = useState(project.viewerSettings.visual.axis);
|
...project.viewerSettings.visual.rotationDegrees,
|
||||||
const [collisionInverted, setCollisionInverted] = useState(project.viewerSettings.collision.inverted);
|
});
|
||||||
const [collisionAxis, setCollisionAxis] = useState(project.viewerSettings.collision.axis);
|
const [collisionRotation, setCollisionRotation] = useState({
|
||||||
|
...project.viewerSettings.collision.rotationDegrees,
|
||||||
|
});
|
||||||
const [horizontalInverted, setHorizontalInverted] = useState(
|
const [horizontalInverted, setHorizontalInverted] = useState(
|
||||||
project.viewerSettings.camera.invertHorizontal,
|
project.viewerSettings.camera.invertHorizontal,
|
||||||
);
|
);
|
||||||
const [verticalInverted, setVerticalInverted] = useState(
|
const [verticalInverted, setVerticalInverted] = useState(
|
||||||
project.viewerSettings.camera.invertVertical,
|
project.viewerSettings.camera.invertVertical,
|
||||||
);
|
);
|
||||||
|
const [ugvDraft, setUgvDraft] = useState<SimulationUgvPreset>(() =>
|
||||||
|
cloneUgvPreset(project.viewerSettings.ugv),
|
||||||
|
);
|
||||||
|
const [ugvSaveState, setUgvSaveState] = useState<SimulationUgvPresetSaveState>("idle");
|
||||||
const [settingsError, setSettingsError] = useState<string | null>(null);
|
const [settingsError, setSettingsError] = useState<string | null>(null);
|
||||||
const worldManifestRevision = JSON.stringify(project.worldManifest);
|
const worldManifestRevision = JSON.stringify(project.worldManifest);
|
||||||
|
|
||||||
@@ -68,14 +83,18 @@ export function SimulationViewport({
|
|||||||
const settings = project.viewerSettings;
|
const settings = project.viewerSettings;
|
||||||
viewerSettingsRef.current = settings;
|
viewerSettingsRef.current = settings;
|
||||||
setQuality(settings.quality);
|
setQuality(settings.quality);
|
||||||
setVisualInverted(settings.visual.inverted);
|
setVisualRotation({ ...settings.visual.rotationDegrees });
|
||||||
setVisualAxis(settings.visual.axis);
|
setCollisionRotation({ ...settings.collision.rotationDegrees });
|
||||||
setCollisionInverted(settings.collision.inverted);
|
|
||||||
setCollisionAxis(settings.collision.axis);
|
|
||||||
setHorizontalInverted(settings.camera.invertHorizontal);
|
setHorizontalInverted(settings.camera.invertHorizontal);
|
||||||
setVerticalInverted(settings.camera.invertVertical);
|
setVerticalInverted(settings.camera.invertVertical);
|
||||||
|
setUgvDraft(cloneUgvPreset(settings.ugv));
|
||||||
|
setUgvSaveState("idle");
|
||||||
|
ugvSaveRequestRef.current += 1;
|
||||||
setSettingsError(null);
|
setSettingsError(null);
|
||||||
setSettingsOpen(false);
|
setSettingsOpen(false);
|
||||||
|
controlRequestRef.current += 1;
|
||||||
|
setControlMode("free");
|
||||||
|
setControlState("idle");
|
||||||
}, [project.projectId]);
|
}, [project.projectId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -84,9 +103,11 @@ export function SimulationViewport({
|
|||||||
async commit(settings) {
|
async commit(settings) {
|
||||||
try {
|
try {
|
||||||
const saved = await saveSimulationViewerSettings(project.projectId, settings);
|
const saved = await saveSimulationViewerSettings(project.projectId, settings);
|
||||||
|
settingsCommitSucceededRef.current = true;
|
||||||
if (active) onProjectChange?.(saved);
|
if (active) onProjectChange?.(saved);
|
||||||
return true;
|
return true;
|
||||||
} catch (caught) {
|
} catch (caught) {
|
||||||
|
settingsCommitSucceededRef.current = false;
|
||||||
if (active) {
|
if (active) {
|
||||||
setSettingsError(
|
setSettingsError(
|
||||||
caught instanceof Error ? caught.message : "Не удалось сохранить настройки сцены.",
|
caught instanceof Error ? caught.message : "Не удалось сохранить настройки сцены.",
|
||||||
@@ -122,15 +143,16 @@ export function SimulationViewport({
|
|||||||
runtime.setQuality(settings.quality);
|
runtime.setQuality(settings.quality);
|
||||||
runtime.setCameraInversion("horizontal", settings.camera.invertHorizontal);
|
runtime.setCameraInversion("horizontal", settings.camera.invertHorizontal);
|
||||||
runtime.setCameraInversion("vertical", settings.camera.invertVertical);
|
runtime.setCameraInversion("vertical", settings.camera.invertVertical);
|
||||||
|
runtime.setUgvPreset(settings.ugv);
|
||||||
setState("loading");
|
setState("loading");
|
||||||
await runtime.loadWorld(manifest);
|
await runtime.loadWorld(manifest);
|
||||||
runtime.setLayerWorldTransform(
|
runtime.setLayerWorldTransform(
|
||||||
"visual",
|
"visual",
|
||||||
playCanvasAxisTransform(settings.visual.axis, settings.visual.inverted),
|
playCanvasEulerTransform(settings.visual.rotationDegrees),
|
||||||
);
|
);
|
||||||
runtime.setLayerWorldTransform(
|
runtime.setLayerWorldTransform(
|
||||||
"collision",
|
"collision",
|
||||||
playCanvasAxisTransform(settings.collision.axis, settings.collision.inverted),
|
playCanvasEulerTransform(settings.collision.rotationDegrees),
|
||||||
);
|
);
|
||||||
if (!cancelled) setState("ready");
|
if (!cancelled) setState("ready");
|
||||||
}).catch((caught: unknown) => {
|
}).catch((caught: unknown) => {
|
||||||
@@ -140,6 +162,7 @@ export function SimulationViewport({
|
|||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
|
controlRequestRef.current += 1;
|
||||||
runtime.dispose();
|
runtime.dispose();
|
||||||
runtimeRef.current = null;
|
runtimeRef.current = null;
|
||||||
};
|
};
|
||||||
@@ -151,25 +174,89 @@ export function SimulationViewport({
|
|||||||
const collisionAvailable = project.worldManifest?.collision.available ?? false;
|
const collisionAvailable = project.worldManifest?.collision.available ?? false;
|
||||||
const commitViewerSettings = (settings: SimulationViewerSettings) => {
|
const commitViewerSettings = (settings: SimulationViewerSettings) => {
|
||||||
viewerSettingsRef.current = settings;
|
viewerSettingsRef.current = settings;
|
||||||
|
settingsCommitSucceededRef.current = false;
|
||||||
settingsCommitterRef.current?.enqueue(settings);
|
settingsCommitterRef.current?.enqueue(settings);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const changeUgvDraft = (next: SimulationUgvPreset) => {
|
||||||
|
setUgvDraft(cloneUgvPreset(next));
|
||||||
|
setUgvSaveState("idle");
|
||||||
|
ugvSaveRequestRef.current += 1;
|
||||||
|
runtimeRef.current?.setUgvPreset(next);
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveUgvPreset = () => {
|
||||||
|
const committer = settingsCommitterRef.current;
|
||||||
|
if (!committer) {
|
||||||
|
setUgvSaveState("failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const request = ++ugvSaveRequestRef.current;
|
||||||
|
const normalized = cloneUgvPreset({
|
||||||
|
...ugvDraft,
|
||||||
|
presetName: ugvDraft.presetName.trim(),
|
||||||
|
});
|
||||||
|
setUgvDraft(normalized);
|
||||||
|
setUgvSaveState("saving");
|
||||||
|
setSettingsError(null);
|
||||||
|
settingsCommitSucceededRef.current = false;
|
||||||
|
const settings = { ...viewerSettingsRef.current, ugv: normalized };
|
||||||
|
viewerSettingsRef.current = settings;
|
||||||
|
committer.enqueue(settings);
|
||||||
|
void committer.waitForIdle().then(() => {
|
||||||
|
if (request !== ugvSaveRequestRef.current) return;
|
||||||
|
setUgvSaveState(settingsCommitSucceededRef.current ? "saved" : "failed");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeControlMode = (next: SimulationControlMode) => {
|
||||||
|
const runtime = runtimeRef.current;
|
||||||
|
if (!runtime) return;
|
||||||
|
const request = ++controlRequestRef.current;
|
||||||
|
setControlMode(next);
|
||||||
|
setControlState(next === "ugv" ? "loading" : "idle");
|
||||||
|
if (next === "ugv" && collisionState !== "ready") setCollisionState("loading");
|
||||||
|
void runtime.setControlMode(next).then(() => {
|
||||||
|
if (request !== controlRequestRef.current) return;
|
||||||
|
if (next === "ugv") {
|
||||||
|
setControlState("ready");
|
||||||
|
setCollisionState("ready");
|
||||||
|
}
|
||||||
|
}).catch((caught: unknown) => {
|
||||||
|
if (request !== controlRequestRef.current) return;
|
||||||
|
setControlMode("free");
|
||||||
|
setControlState("failed");
|
||||||
|
setCollisionState("failed");
|
||||||
|
setError(caught instanceof Error ? caught.message : "Не удалось запустить UGV.");
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="simulation-viewport" aria-label={`Сцена ${project.name}`}>
|
<section
|
||||||
|
className="simulation-viewport"
|
||||||
|
data-control-mode={controlMode}
|
||||||
|
aria-label={`Сцена ${project.name}`}
|
||||||
|
>
|
||||||
<header className="simulation-viewport__toolbar">
|
<header className="simulation-viewport__toolbar">
|
||||||
<div>
|
<div>
|
||||||
<StatusBadge tone={state === "failed" || collisionState === "failed" ? "warning" : state === "ready" && collisionState !== "loading" ? "success" : "accent"}>
|
<StatusBadge tone={state === "failed" || collisionState === "failed" ? "warning" : state === "ready" && collisionState !== "loading" ? "success" : "accent"}>
|
||||||
{collisionState === "loading"
|
{controlState === "loading"
|
||||||
? "Загрузка collision"
|
? "Подготовка UGV"
|
||||||
|
: controlState === "ready"
|
||||||
|
? "UGV готов"
|
||||||
|
: controlState === "failed"
|
||||||
|
? "Ошибка UGV"
|
||||||
|
: collisionState === "loading"
|
||||||
|
? "Загрузка коллизий"
|
||||||
: collisionState === "failed"
|
: collisionState === "failed"
|
||||||
? "Ошибка collision"
|
? "Ошибка коллизий"
|
||||||
: state === "ready"
|
: state === "ready"
|
||||||
? "Runtime готов"
|
? "Runtime готов"
|
||||||
: state === "failed"
|
: state === "failed"
|
||||||
? "Ошибка runtime"
|
? "Ошибка runtime"
|
||||||
: "Загрузка сцены"}
|
: "Загрузка локации"}
|
||||||
</StatusBadge>
|
</StatusBadge>
|
||||||
<span>PlayCanvas Engine 2.21.4</span>
|
<span>Engine 2.21.4</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="simulation-viewport__controls">
|
<div className="simulation-viewport__controls">
|
||||||
<Select
|
<Select
|
||||||
@@ -190,6 +277,27 @@ export function SimulationViewport({
|
|||||||
minMenuWidth={230}
|
minMenuWidth={230}
|
||||||
menuWidth={230}
|
menuWidth={230}
|
||||||
/>
|
/>
|
||||||
|
<Select
|
||||||
|
label="Режим управления"
|
||||||
|
value={controlMode}
|
||||||
|
disabled={state !== "ready" || controlState === "loading"}
|
||||||
|
onChange={changeControlMode}
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
value: "free",
|
||||||
|
label: "Свободно",
|
||||||
|
description: "Свободная камера и навигация",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "ugv",
|
||||||
|
label: "UGV",
|
||||||
|
description: "WASD/стрелки · пробел — тормоз · R — домой",
|
||||||
|
disabled: !collisionAvailable,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
minMenuWidth={250}
|
||||||
|
menuWidth={250}
|
||||||
|
/>
|
||||||
<SegmentedControl
|
<SegmentedControl
|
||||||
label="Слой сцены"
|
label="Слой сцены"
|
||||||
value={viewMode}
|
value={viewMode}
|
||||||
@@ -204,7 +312,7 @@ export function SimulationViewport({
|
|||||||
if (next !== "visual") setCollisionState("ready");
|
if (next !== "visual") setCollisionState("ready");
|
||||||
}).catch((caught: unknown) => {
|
}).catch((caught: unknown) => {
|
||||||
setCollisionState("failed");
|
setCollisionState("failed");
|
||||||
setError(caught instanceof Error ? caught.message : "Не удалось открыть collision GLB.");
|
setError(caught instanceof Error ? caught.message : "Не удалось открыть слой коллизий.");
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
items={[
|
items={[
|
||||||
@@ -227,7 +335,7 @@ export function SimulationViewport({
|
|||||||
<Icon name="settings" size={18} />
|
<Icon name="settings" size={18} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
{settingsOpen ? (
|
{settingsOpen ? (
|
||||||
<div
|
<GlassMaterialSurface
|
||||||
id={settingsId}
|
id={settingsId}
|
||||||
className="simulation-viewport__settings"
|
className="simulation-viewport__settings"
|
||||||
role="dialog"
|
role="dialog"
|
||||||
@@ -236,100 +344,57 @@ export function SimulationViewport({
|
|||||||
<div className="simulation-viewport__settings-head">
|
<div className="simulation-viewport__settings-head">
|
||||||
<div>
|
<div>
|
||||||
<strong>Настройки сцены</strong>
|
<strong>Настройки сцены</strong>
|
||||||
<span>Gaussian-мир и камера</span>
|
<span>Локация, камера и UGV</span>
|
||||||
</div>
|
</div>
|
||||||
<IconButton label="Закрыть настройки" onClick={() => setSettingsOpen(false)}>
|
<IconButton label="Закрыть настройки" onClick={() => setSettingsOpen(false)}>
|
||||||
<Icon name="close" size={16} />
|
<Icon name="close" size={16} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
<section className="simulation-viewport__settings-group">
|
<div className="simulation-viewport__settings-scroll">
|
||||||
|
<section className="simulation-viewport__settings-group">
|
||||||
<div className="simulation-viewport__settings-group-head">
|
<div className="simulation-viewport__settings-group-head">
|
||||||
<strong>Система координат</strong>
|
<strong>Система координат</strong>
|
||||||
<span>Мир PlayCanvas · Y вверх</span>
|
<span>Мир сцены · Y вверх</span>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
Коррекция слоёв не меняет координаты камеры, навигации и будущей физики.
|
Коррекция слоёв не меняет координаты камеры, навигации и будущей физики.
|
||||||
</p>
|
</p>
|
||||||
<div className="simulation-viewport__settings-switches">
|
<div className="simulation-viewport__settings-rotations">
|
||||||
<div className="simulation-viewport__settings-layer-row">
|
<LayerRotationFields
|
||||||
<Switch
|
label="Гауссовый слой"
|
||||||
checked={visualInverted}
|
value={visualRotation}
|
||||||
disabled={state !== "ready"}
|
disabled={state !== "ready"}
|
||||||
label="Инверсия визуального слоя"
|
onChange={(next) => {
|
||||||
onChange={(checked) => {
|
setVisualRotation(next);
|
||||||
setVisualInverted(checked);
|
runtimeRef.current?.setLayerWorldTransform(
|
||||||
runtimeRef.current?.setLayerWorldTransform(
|
"visual",
|
||||||
"visual",
|
playCanvasEulerTransform(next),
|
||||||
playCanvasAxisTransform(visualAxis, checked),
|
);
|
||||||
);
|
commitViewerSettings({
|
||||||
commitViewerSettings({
|
...viewerSettingsRef.current,
|
||||||
...viewerSettingsRef.current,
|
visual: { rotationDegrees: next },
|
||||||
visual: { ...viewerSettingsRef.current.visual, inverted: checked },
|
});
|
||||||
});
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
<LayerRotationFields
|
||||||
<Select
|
label="Слой коллизий"
|
||||||
className="simulation-viewport__axis-select"
|
value={collisionRotation}
|
||||||
label="Ось инверсии визуального слоя"
|
disabled={state !== "ready"}
|
||||||
value={visualAxis}
|
onChange={(next) => {
|
||||||
options={AXIS_OPTIONS}
|
setCollisionRotation(next);
|
||||||
disabled={state !== "ready"}
|
runtimeRef.current?.setLayerWorldTransform(
|
||||||
minMenuWidth={72}
|
"collision",
|
||||||
menuWidth={72}
|
playCanvasEulerTransform(next),
|
||||||
onChange={(axis) => {
|
);
|
||||||
setVisualAxis(axis);
|
commitViewerSettings({
|
||||||
runtimeRef.current?.setLayerWorldTransform(
|
...viewerSettingsRef.current,
|
||||||
"visual",
|
collision: { rotationDegrees: next },
|
||||||
playCanvasAxisTransform(axis, visualInverted),
|
});
|
||||||
);
|
}}
|
||||||
commitViewerSettings({
|
/>
|
||||||
...viewerSettingsRef.current,
|
|
||||||
visual: { ...viewerSettingsRef.current.visual, axis },
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="simulation-viewport__settings-layer-row">
|
|
||||||
<Switch
|
|
||||||
checked={collisionInverted}
|
|
||||||
disabled={state !== "ready"}
|
|
||||||
label="Инверсия collision-слоя"
|
|
||||||
onChange={(checked) => {
|
|
||||||
setCollisionInverted(checked);
|
|
||||||
runtimeRef.current?.setLayerWorldTransform(
|
|
||||||
"collision",
|
|
||||||
playCanvasAxisTransform(collisionAxis, checked),
|
|
||||||
);
|
|
||||||
commitViewerSettings({
|
|
||||||
...viewerSettingsRef.current,
|
|
||||||
collision: { ...viewerSettingsRef.current.collision, inverted: checked },
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Select
|
|
||||||
className="simulation-viewport__axis-select"
|
|
||||||
label="Ось инверсии collision-слоя"
|
|
||||||
value={collisionAxis}
|
|
||||||
options={AXIS_OPTIONS}
|
|
||||||
disabled={state !== "ready"}
|
|
||||||
minMenuWidth={72}
|
|
||||||
menuWidth={72}
|
|
||||||
onChange={(axis) => {
|
|
||||||
setCollisionAxis(axis);
|
|
||||||
runtimeRef.current?.setLayerWorldTransform(
|
|
||||||
"collision",
|
|
||||||
playCanvasAxisTransform(axis, collisionInverted),
|
|
||||||
);
|
|
||||||
commitViewerSettings({
|
|
||||||
...viewerSettingsRef.current,
|
|
||||||
collision: { ...viewerSettingsRef.current.collision, axis },
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section className="simulation-viewport__settings-group">
|
<section className="simulation-viewport__settings-group">
|
||||||
<div className="simulation-viewport__settings-group-head">
|
<div className="simulation-viewport__settings-group-head">
|
||||||
<strong>Управление камерой</strong>
|
<strong>Управление камерой</strong>
|
||||||
<span>Мышь и тачпад</span>
|
<span>Мышь и тачпад</span>
|
||||||
@@ -362,39 +427,165 @@ export function SimulationViewport({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{settingsError ? (
|
<SimulationUgvSettingsPanel
|
||||||
<p className="simulation-viewport__settings-error" role="alert">
|
value={ugvDraft}
|
||||||
Настройки не сохранены: {settingsError}
|
disabled={state !== "ready"}
|
||||||
</p>
|
saveState={ugvSaveState}
|
||||||
) : null}
|
onChange={changeUgvDraft}
|
||||||
</div>
|
onSave={saveUgvPreset}
|
||||||
|
/>
|
||||||
|
{settingsError ? (
|
||||||
|
<p className="simulation-viewport__settings-error" role="alert">
|
||||||
|
Настройки не сохранены: {settingsError}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</GlassMaterialSurface>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span className="simulation-viewport__toolbar-balance" aria-hidden="true" />
|
<span className="simulation-viewport__toolbar-balance" aria-hidden="true" />
|
||||||
</header>
|
</header>
|
||||||
<div className="simulation-viewport__stage">
|
<div className="simulation-viewport__stage">
|
||||||
<canvas ref={canvasRef} aria-label={`PlayCanvas сцена ${project.name}`} />
|
<canvas ref={canvasRef} aria-label={`Сцена ${project.name}`} />
|
||||||
{state !== "ready" ? (
|
{state !== "ready" ? (
|
||||||
<div className="simulation-viewport__state" role={state === "failed" ? "alert" : "status"}>
|
<div className="simulation-viewport__state" role={state === "failed" ? "alert" : "status"}>
|
||||||
{state === "failed" ? null : <ActivityIndicator label="Загрузка сцены" />}
|
{state === "failed" ? null : <ActivityIndicator label="Загрузка локации" />}
|
||||||
<strong>{state === "failed" ? "Сцена не открылась" : "PlayCanvas загружает мир"}</strong>
|
<strong>{state === "failed" ? "Локация не открылась" : "Загружаем локацию"}</strong>
|
||||||
<p>{error ?? "Streamed SOG догружается по мере готовности runtime."}</p>
|
<p>{error ?? "LOD-уровни загружаются по мере готовности runtime."}</p>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{!collisionAvailable ? (
|
{!collisionAvailable ? (
|
||||||
<footer className="simulation-viewport__notice">
|
<footer className="simulation-viewport__notice">
|
||||||
<StatusBadge tone="warning">Collision недоступен</StatusBadge>
|
<StatusBadge tone="warning">Коллизии недоступны</StatusBadge>
|
||||||
<span>Для этой сборки collision GLB не был запрошен; визуальный слой настоящий и не подменяется.</span>
|
<span>Для этой сборки слой коллизий не создавался; визуальный слой остаётся доступен.</span>
|
||||||
</footer>
|
</footer>
|
||||||
) : collisionState === "failed" ? (
|
) : collisionState === "failed" ? (
|
||||||
<footer className="simulation-viewport__notice" role="alert">
|
<footer className="simulation-viewport__notice" role="alert">
|
||||||
<StatusBadge tone="warning">Collision не открылся</StatusBadge>
|
<StatusBadge tone="warning">Коллизии не открылись</StatusBadge>
|
||||||
<span>{error ?? "PlayCanvas не смог загрузить collision GLB."}</span>
|
<span>{error ?? "Слой коллизий не загрузился."}</span>
|
||||||
</footer>
|
</footer>
|
||||||
) : null}
|
) : null}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cloneUgvPreset(preset: SimulationUgvPreset): SimulationUgvPreset {
|
||||||
|
return {
|
||||||
|
...preset,
|
||||||
|
dimensionsMeters: { ...preset.dimensionsMeters },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function LayerRotationFields({
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
disabled,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
label: string;
|
||||||
|
value: SimulationEulerRotation;
|
||||||
|
disabled: boolean;
|
||||||
|
onChange: (rotation: SimulationEulerRotation) => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className="simulation-viewport__rotation-row">
|
||||||
|
<strong>{label}</strong>
|
||||||
|
<div className="simulation-viewport__rotation-fields">
|
||||||
|
{ROTATION_AXES.map((axis) => (
|
||||||
|
<RotationField
|
||||||
|
key={axis}
|
||||||
|
label={axis.toUpperCase()}
|
||||||
|
ariaLabel={`${label}: вращение ${axis.toUpperCase()}`}
|
||||||
|
value={value[axis]}
|
||||||
|
disabled={disabled}
|
||||||
|
onChange={(angle) => onChange({ ...value, [axis]: angle })}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RotationField({
|
||||||
|
label,
|
||||||
|
ariaLabel,
|
||||||
|
value,
|
||||||
|
disabled,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
label: string;
|
||||||
|
ariaLabel: string;
|
||||||
|
value: number;
|
||||||
|
disabled: boolean;
|
||||||
|
onChange: (angle: number) => void;
|
||||||
|
}) {
|
||||||
|
const [draft, setDraft] = useState(() => String(value));
|
||||||
|
const editingRef = useRef(false);
|
||||||
|
const cancelNextBlurRef = useRef(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!editingRef.current) setDraft(String(value));
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
const parsedDraft = () => Number(draft.trim().replace(",", "."));
|
||||||
|
const finishEditing = (commit: boolean) => {
|
||||||
|
const parsed = parsedDraft();
|
||||||
|
if (commit && draft.trim() !== "" && Number.isFinite(parsed)) {
|
||||||
|
const normalized = Math.max(-360, Math.min(360, parsed));
|
||||||
|
onChange(normalized);
|
||||||
|
setDraft(String(normalized));
|
||||||
|
} else {
|
||||||
|
setDraft(String(value));
|
||||||
|
}
|
||||||
|
editingRef.current = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TextField
|
||||||
|
label={label}
|
||||||
|
aria-label={ariaLabel}
|
||||||
|
fieldClassName="simulation-viewport__rotation-field"
|
||||||
|
className="simulation-viewport__rotation-input"
|
||||||
|
type="text"
|
||||||
|
inputMode="decimal"
|
||||||
|
value={draft}
|
||||||
|
disabled={disabled}
|
||||||
|
spellCheck={false}
|
||||||
|
onFocus={() => {
|
||||||
|
editingRef.current = true;
|
||||||
|
}}
|
||||||
|
onChange={(event) => {
|
||||||
|
// Treat the first input event as the start of editing as well. Programmatic
|
||||||
|
// focus (and some browser/trackpad paths) may not deliver React's focus
|
||||||
|
// event before the controlled value changes.
|
||||||
|
editingRef.current = true;
|
||||||
|
const nextDraft = event.currentTarget.value;
|
||||||
|
setDraft(nextDraft);
|
||||||
|
const parsed = Number(nextDraft.trim().replace(",", "."));
|
||||||
|
if (nextDraft.trim() !== "" && Number.isFinite(parsed) && parsed >= -360 && parsed <= 360) {
|
||||||
|
onChange(parsed);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
if (event.key === "Enter") event.currentTarget.blur();
|
||||||
|
if (event.key === "Escape") {
|
||||||
|
event.preventDefault();
|
||||||
|
cancelNextBlurRef.current = true;
|
||||||
|
finishEditing(false);
|
||||||
|
event.currentTarget.blur();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onBlur={() => {
|
||||||
|
if (cancelNextBlurRef.current) {
|
||||||
|
cancelNextBlurRef.current = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
finishEditing(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,19 +6,18 @@ interface TelemetrySeriesProps {
|
|||||||
ceiling?: number;
|
ceiling?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function linePoints(values: Array<number | null>, ceiling?: number): string {
|
export function lineSegments(values: Array<number | null>, ceiling?: number): string[] {
|
||||||
const finite = values.filter((value): value is number =>
|
const finite = values.filter((value): value is number => value !== null && Number.isFinite(value));
|
||||||
value !== null && Number.isFinite(value),
|
if (!finite.length) return [];
|
||||||
);
|
const maximum = Math.max(ceiling ?? 0, ...finite, 1), denominator = Math.max(1, values.length - 1);
|
||||||
if (finite.length === 0) return "";
|
const minimum = Math.min(0, ...finite), span = maximum - minimum;
|
||||||
const maximum = Math.max(ceiling ?? 0, ...finite, 1);
|
const segments:string[]=[];let segment:string[]=[];
|
||||||
const denominator = Math.max(1, values.length - 1);
|
values.forEach((value,index)=>{
|
||||||
return values.map((value, index) => {
|
if(value===null||!Number.isFinite(value)){if(segment.length)segments.push(segment.join(' '));segment=[];return;}
|
||||||
const x = index / denominator * 100;
|
segment.push(`${(index/denominator*100).toFixed(2)},${(36-(Math.min(maximum,value)-minimum)/span*34).toFixed(2)}`);
|
||||||
const normalized = value === null ? 0 : Math.max(0, Math.min(maximum, value));
|
});
|
||||||
const y = 36 - normalized / maximum * 34;
|
if(segment.length)segments.push(segment.join(' '));
|
||||||
return `${x.toFixed(2)},${y.toFixed(2)}`;
|
return segments;
|
||||||
}).join(" ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TelemetrySeries({
|
export function TelemetrySeries({
|
||||||
@@ -28,7 +27,7 @@ export function TelemetrySeries({
|
|||||||
resource,
|
resource,
|
||||||
ceiling,
|
ceiling,
|
||||||
}: TelemetrySeriesProps) {
|
}: TelemetrySeriesProps) {
|
||||||
const points = linePoints(values, ceiling);
|
const segments = lineSegments(values, ceiling);
|
||||||
return (
|
return (
|
||||||
<div className="system-telemetry-series">
|
<div className="system-telemetry-series">
|
||||||
<div>
|
<div>
|
||||||
@@ -45,7 +44,7 @@ export function TelemetrySeries({
|
|||||||
aria-label={`${label}: ${value}`}
|
aria-label={`${label}: ${value}`}
|
||||||
>
|
>
|
||||||
<path d="M0 37 H100" />
|
<path d="M0 37 H100" />
|
||||||
{points ? <polyline points={points} /> : null}
|
{segments.map((points,index)=><polyline key={index} points={points} />)}
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import type { WorkspaceDefinition } from "../productModel";
|
|||||||
|
|
||||||
interface ApplicationPanelActionsOptions {
|
interface ApplicationPanelActionsOptions {
|
||||||
definition: WorkspaceDefinition | null;
|
definition: WorkspaceDefinition | null;
|
||||||
|
onAddVehicle?: () => void;
|
||||||
refreshRuntime: () => void;
|
refreshRuntime: () => void;
|
||||||
resetConnectionScenario?: () => Promise<boolean>;
|
resetConnectionScenario?: () => Promise<boolean>;
|
||||||
connectionScenarioResetting: boolean;
|
connectionScenarioResetting: boolean;
|
||||||
@@ -43,6 +44,7 @@ export function deviceRuntimeUtilityAction({
|
|||||||
|
|
||||||
export function useApplicationPanelActions({
|
export function useApplicationPanelActions({
|
||||||
definition,
|
definition,
|
||||||
|
onAddVehicle,
|
||||||
refreshRuntime,
|
refreshRuntime,
|
||||||
resetConnectionScenario,
|
resetConnectionScenario,
|
||||||
connectionScenarioResetting,
|
connectionScenarioResetting,
|
||||||
@@ -52,6 +54,7 @@ export function useApplicationPanelActions({
|
|||||||
}: ApplicationPanelActionsOptions): ApplicationPanelUtilityAction[] {
|
}: ApplicationPanelActionsOptions): ApplicationPanelUtilityAction[] {
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const actions: ApplicationPanelUtilityAction[] = [];
|
const actions: ApplicationPanelUtilityAction[] = [];
|
||||||
|
if (definition?.kind === "vehicles" && onAddVehicle) actions.push({ label: "Добавить аппарат", icon: "plus", onClick: onAddVehicle });
|
||||||
if (definition?.kind === "device") {
|
if (definition?.kind === "device") {
|
||||||
actions.push(deviceRuntimeUtilityAction({
|
actions.push(deviceRuntimeUtilityAction({
|
||||||
refreshRuntime,
|
refreshRuntime,
|
||||||
@@ -71,6 +74,7 @@ export function useApplicationPanelActions({
|
|||||||
return actions;
|
return actions;
|
||||||
}, [
|
}, [
|
||||||
definition,
|
definition,
|
||||||
|
onAddVehicle,
|
||||||
refreshRuntime,
|
refreshRuntime,
|
||||||
resetConnectionScenario,
|
resetConnectionScenario,
|
||||||
connectionScenarioResetting,
|
connectionScenarioResetting,
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
import type { DeviceUiPlugin } from "../core/device-plugins/contracts";
|
import type { DeviceUiPlugin } from "../core/device-plugins/contracts";
|
||||||
import { xgridsK1Plugin } from "@xgrids-k1/frontend/plugin";
|
import { xgridsK1Plugin } from "@xgrids-k1/frontend/plugin";
|
||||||
|
import {insta360X4SensorUi} from '../../../../plugins/insta360-x4/frontend/src/plugin';
|
||||||
|
|
||||||
// Composition root: this is the only place where Mission Core chooses which
|
// Composition root: this is the only place where Mission Core chooses which
|
||||||
// statically reviewed device plugins are shipped in the current build.
|
// statically reviewed device plugins are shipped in the current build.
|
||||||
export const installedDevicePlugins: readonly DeviceUiPlugin[] = Object.freeze([
|
export const installedDevicePlugins: readonly DeviceUiPlugin[] = Object.freeze([
|
||||||
xgridsK1Plugin,
|
xgridsK1Plugin,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Node-executed camera controls use the existing sensor contribution contract;
|
||||||
|
// they do not register a desktop capture/AI runtime or a new product workspace.
|
||||||
|
export const installedNodeSensorContributions = Object.freeze([insta360X4SensorUi]);
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import {
|
|||||||
import { IdleMissionRuntimeProvider } from "../runtime/MissionRuntimeContext";
|
import { IdleMissionRuntimeProvider } from "../runtime/MissionRuntimeContext";
|
||||||
import type { DeviceUiPlugin, RegisteredDeviceModel } from "./contracts";
|
import type { DeviceUiPlugin, RegisteredDeviceModel } from "./contracts";
|
||||||
import { createDevicePluginRegistry, type DevicePluginRegistry } from "./registry";
|
import { createDevicePluginRegistry, type DevicePluginRegistry } from "./registry";
|
||||||
|
import type {SensorUiContribution} from '../../../../../packages/sensor-ui/src/extensions';
|
||||||
|
const NO_NODE_SENSORS: readonly SensorUiContribution[] = Object.freeze([]);
|
||||||
|
|
||||||
interface DevicePluginHostValue {
|
interface DevicePluginHostValue {
|
||||||
registry: DevicePluginRegistry;
|
registry: DevicePluginRegistry;
|
||||||
@@ -83,12 +85,14 @@ export function commitPersistedDeviceModelId(
|
|||||||
|
|
||||||
export function DevicePluginHostProvider({
|
export function DevicePluginHostProvider({
|
||||||
plugins,
|
plugins,
|
||||||
|
nodeSensorContributions = NO_NODE_SENSORS,
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
plugins: readonly DeviceUiPlugin[];
|
plugins: readonly DeviceUiPlugin[];
|
||||||
|
nodeSensorContributions?: readonly SensorUiContribution[];
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const registry = useMemo(() => createDevicePluginRegistry(plugins), [plugins]);
|
const registry = useMemo(() => createDevicePluginRegistry(plugins, nodeSensorContributions), [plugins, nodeSensorContributions]);
|
||||||
const selectionStorage = useMemo(browserDeviceModelSelectionStorage, []);
|
const selectionStorage = useMemo(browserDeviceModelSelectionStorage, []);
|
||||||
const [selectedModelId, setSelectedModelId] = useState<string | null>(() =>
|
const [selectedModelId, setSelectedModelId] = useState<string | null>(() =>
|
||||||
restorePersistedDeviceModelId(registry, selectionStorage)
|
restorePersistedDeviceModelId(registry, selectionStorage)
|
||||||
|
|||||||
@@ -103,6 +103,9 @@ export interface DevicePluginConnectionProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DeviceUiPlugin {
|
export interface DeviceUiPlugin {
|
||||||
|
sensorUi?: {
|
||||||
|
contributions: readonly import('../../../../../packages/sensor-ui/src/extensions').SensorUiContribution[];
|
||||||
|
};
|
||||||
manifest: DevicePluginManifest;
|
manifest: DevicePluginManifest;
|
||||||
RuntimeProvider: ComponentType<{
|
RuntimeProvider: ComponentType<{
|
||||||
activeModel: DeviceModelDefinition | null;
|
activeModel: DeviceModelDefinition | null;
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import {
|
|||||||
type DeviceUiPlugin,
|
type DeviceUiPlugin,
|
||||||
type RegisteredDeviceModel,
|
type RegisteredDeviceModel,
|
||||||
} from "./contracts";
|
} from "./contracts";
|
||||||
|
import type {SensorUiContribution} from '../../../../../packages/sensor-ui/src/extensions';
|
||||||
|
|
||||||
export interface DevicePluginRegistry {
|
export interface DevicePluginRegistry {
|
||||||
|
readonly sensorContributions: readonly SensorUiContribution[];
|
||||||
readonly plugins: readonly DeviceUiPlugin[];
|
readonly plugins: readonly DeviceUiPlugin[];
|
||||||
readonly models: readonly RegisteredDeviceModel[];
|
readonly models: readonly RegisteredDeviceModel[];
|
||||||
resolveModel: (modelId: string) => RegisteredDeviceModel | null;
|
resolveModel: (modelId: string) => RegisteredDeviceModel | null;
|
||||||
@@ -15,6 +17,7 @@ export interface DevicePluginRegistry {
|
|||||||
|
|
||||||
export function createDevicePluginRegistry(
|
export function createDevicePluginRegistry(
|
||||||
installedPlugins: readonly DeviceUiPlugin[],
|
installedPlugins: readonly DeviceUiPlugin[],
|
||||||
|
nodeSensorContributions: readonly SensorUiContribution[] = [],
|
||||||
): DevicePluginRegistry {
|
): DevicePluginRegistry {
|
||||||
const pluginIds = new Set<string>();
|
const pluginIds = new Set<string>();
|
||||||
const modelIds = new Set<string>();
|
const modelIds = new Set<string>();
|
||||||
@@ -133,7 +136,15 @@ export function createDevicePluginRegistry(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const modelById = new Map(models.map((registered) => [registered.model.id, registered]));
|
const modelById = new Map(models.map((registered) => [registered.model.id, registered]));
|
||||||
|
const sensorContributions = [
|
||||||
|
...installedPlugins.flatMap(plugin => plugin.sensorUi?.contributions ?? []),
|
||||||
|
...nodeSensorContributions,
|
||||||
|
];
|
||||||
|
if (new Set(sensorContributions.map(value => value.kind)).size !== sensorContributions.length) {
|
||||||
|
throw new Error('Плагины повторяют модель устройства БК.');
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
|
sensorContributions: Object.freeze(sensorContributions),
|
||||||
plugins: Object.freeze([...installedPlugins]),
|
plugins: Object.freeze([...installedPlugins]),
|
||||||
models: Object.freeze(models),
|
models: Object.freeze(models),
|
||||||
resolveModel: (modelId) => modelById.get(modelId) ?? null,
|
resolveModel: (modelId) => modelById.get(modelId) ?? null,
|
||||||
|
|||||||
@@ -1,32 +1,9 @@
|
|||||||
import { roots, type RootId } from "../../productModel";
|
import { roots, type RootId } from "../../productModel";
|
||||||
|
|
||||||
export type EnvironmentSurfaceId = "home" | RootId;
|
export type EnvironmentSurfaceId = "home" | RootId;
|
||||||
export type EnvironmentMediaKind = "image" | "video";
|
import { defaultEnvironmentImageDurationSeconds, maxEnvironmentMediaItems, type EnvironmentBackground, type EnvironmentMediaItem, type EnvironmentMediaKind, type EnvironmentMediaSource, type EnvironmentPage } from "@nodedc/ui-core";
|
||||||
export type EnvironmentMediaSource = "file" | "url";
|
export { createEnvironmentMediaItem, appendEnvironmentMediaItem, removeEnvironmentMediaItem, inferEnvironmentMediaKind, defaultEnvironmentImageDurationSeconds, maxEnvironmentMediaItems } from "@nodedc/ui-core";
|
||||||
|
export type { EnvironmentBackground, EnvironmentMediaItem, EnvironmentMediaKind, EnvironmentMediaSource, EnvironmentPage } from "@nodedc/ui-core";
|
||||||
export interface EnvironmentMediaItem {
|
|
||||||
id: string;
|
|
||||||
source: EnvironmentMediaSource;
|
|
||||||
url: string | null;
|
|
||||||
mediaKind: EnvironmentMediaKind | null;
|
|
||||||
fileName: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EnvironmentBackground {
|
|
||||||
enabled: boolean;
|
|
||||||
imageDurationSeconds: number;
|
|
||||||
items: EnvironmentMediaItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EnvironmentPage {
|
|
||||||
headerLabel: string;
|
|
||||||
eyebrow: string;
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
primaryWorkspaceId: string | null;
|
|
||||||
secondaryWorkspaceId: string | null;
|
|
||||||
background: EnvironmentBackground;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EnvironmentSettings {
|
export interface EnvironmentSettings {
|
||||||
revision: number;
|
revision: number;
|
||||||
@@ -54,72 +31,19 @@ const surfaceIds: readonly EnvironmentSurfaceId[] = [
|
|||||||
"polygon",
|
"polygon",
|
||||||
];
|
];
|
||||||
|
|
||||||
export const defaultEnvironmentImageDurationSeconds = 10;
|
function emptyBackground(): EnvironmentBackground { return { enabled: false, imageDurationSeconds: defaultEnvironmentImageDurationSeconds, items: [] }; }
|
||||||
export const maxEnvironmentMediaItems = 24;
|
|
||||||
|
|
||||||
function emptyBackground(): EnvironmentBackground {
|
|
||||||
return {
|
|
||||||
enabled: false,
|
|
||||||
imageDurationSeconds: defaultEnvironmentImageDurationSeconds,
|
|
||||||
items: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createEnvironmentMediaItem(): EnvironmentMediaItem {
|
|
||||||
return {
|
|
||||||
id: `media-${crypto.randomUUID()}`,
|
|
||||||
source: "file",
|
|
||||||
url: null,
|
|
||||||
mediaKind: null,
|
|
||||||
fileName: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function appendEnvironmentMediaItem(
|
|
||||||
background: EnvironmentBackground,
|
|
||||||
item: EnvironmentMediaItem = createEnvironmentMediaItem(),
|
|
||||||
): EnvironmentBackground {
|
|
||||||
if (
|
|
||||||
background.items.length >= maxEnvironmentMediaItems
|
|
||||||
|| background.items.some((candidate) => candidate.id === item.id)
|
|
||||||
) {
|
|
||||||
return background;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...background,
|
|
||||||
enabled: background.items.length === 0 ? true : background.enabled,
|
|
||||||
items: [...background.items, item],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeEnvironmentMediaItem(
|
|
||||||
background: EnvironmentBackground,
|
|
||||||
itemId: string,
|
|
||||||
): EnvironmentBackground {
|
|
||||||
const items = background.items.filter((item) => item.id !== itemId);
|
|
||||||
if (items.length === background.items.length) return background;
|
|
||||||
return {
|
|
||||||
...background,
|
|
||||||
enabled: items.length > 0 && background.enabled,
|
|
||||||
items,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function inferEnvironmentMediaKind(url: string): EnvironmentMediaKind {
|
|
||||||
return /\.(mp4|webm|mov)(?:[?#].*)?$/i.test(url) ? "video" : "image";
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultQuickActions: Record<
|
const defaultQuickActions: Record<
|
||||||
EnvironmentSurfaceId,
|
EnvironmentSurfaceId,
|
||||||
readonly [string | null, string | null]
|
readonly [string | null, string | null]
|
||||||
> = {
|
> = {
|
||||||
home: ["spatial-scene", "local-device"],
|
home: ["spatial-scene", "vehicles"],
|
||||||
fleet: ["contour-health", "local-device"],
|
fleet: ["vehicles", "contour-health"],
|
||||||
observation: ["spatial-scene", "cameras"],
|
observation: ["cameras", "world-map"],
|
||||||
missions: ["mission-planner", null],
|
missions: ["mission-planner", null],
|
||||||
data: ["recordings", "datasets"],
|
data: ["recordings", "datasets"],
|
||||||
system: ["modules", "integrations"],
|
system: ["modules", "integrations"],
|
||||||
polygon: ["lab-archive", null],
|
polygon: ["lab-archive", "local-device"],
|
||||||
};
|
};
|
||||||
|
|
||||||
export function defaultEnvironmentSettings(): EnvironmentSettings {
|
export function defaultEnvironmentSettings(): EnvironmentSettings {
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import {useEffect,useState} from 'react';
|
||||||
|
|
||||||
|
export interface MetricDefinition {label:string;group:string;resource:string;unit:string;reason:string|null}
|
||||||
|
export interface BoardMonitor {
|
||||||
|
available:boolean;fresh:boolean;storage:string;source_id:string;
|
||||||
|
latest:{seq:number;at:number;boot_id:string;values:Record<string,number|null>}|null;
|
||||||
|
definitions:Record<string,MetricDefinition>;received_at:number;backlog:number;
|
||||||
|
database_bytes:number|null;database_budget_bytes:number;archive_since:number|null;
|
||||||
|
start:number;end:number;bucket_seconds:number;
|
||||||
|
series:{at:number;min:number|null;max:number|null;mean:number|null;count:number}[];
|
||||||
|
events:{at:number;code:string;kind:string;locations:string[]}[];
|
||||||
|
}
|
||||||
|
export function useBoardMonitor(vehicle:string,metric:string,window:number,end:number|null){
|
||||||
|
const [value,setValue]=useState<BoardMonitor|null>(null),[error,setError]=useState('');
|
||||||
|
useEffect(()=>{
|
||||||
|
let active=true;let pending:AbortController|undefined;let timer:ReturnType<typeof setTimeout>|undefined;
|
||||||
|
setValue(null);setError('');
|
||||||
|
const read=async()=>{
|
||||||
|
const controller=new AbortController();pending=controller;
|
||||||
|
const deadline=setTimeout(()=>controller.abort(),12000);
|
||||||
|
try {
|
||||||
|
const query=new URLSearchParams({metric,window:String(window),...(end===null?{}:{end:String(end)})});
|
||||||
|
const response=await fetch(`/api/v1/fleet/${encodeURIComponent(vehicle)}/monitor?${query}`,{credentials:'same-origin',cache:'no-store',signal:controller.signal});
|
||||||
|
if(!response.ok)throw new Error('Мониторинг БК недоступен.');
|
||||||
|
const next=await response.json() as BoardMonitor;
|
||||||
|
if(active){setValue(next);setError('');}
|
||||||
|
}catch {if(active)setError('Не удалось обновить мониторинг БК.');}
|
||||||
|
finally {clearTimeout(deadline);if(active)timer=setTimeout(()=>void read(),5000);}
|
||||||
|
};
|
||||||
|
void read();return()=>{active=false;pending?.abort();clearTimeout(timer);};
|
||||||
|
},[vehicle,metric,window,end]);
|
||||||
|
return {value,error,dismissError:()=>setError('')};
|
||||||
|
}
|
||||||
|
export function monitorSeries(value:BoardMonitor,mode:'min'|'max'|'mean') {
|
||||||
|
const count=Math.ceil((value.end-value.start)/value.bucket_seconds)+1;
|
||||||
|
const values:Array<number|null>=Array(Math.min(count,302)).fill(null);
|
||||||
|
const first=Math.floor(value.start/value.bucket_seconds)*value.bucket_seconds;
|
||||||
|
for(const point of value.series){const index=Math.round((point.at-first)/value.bucket_seconds);if(index>=0&&index<values.length)values[index]=point[mode];}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
export function monitorValue(value:number|null|undefined,unit=''){
|
||||||
|
if(value==null||!Number.isFinite(value))return 'Нет измерения';
|
||||||
|
if(unit==='bytes'||unit==='bytes/s'){
|
||||||
|
const labels=unit==='bytes'?['Б','КиБ','МиБ','ГиБ']:['Б/с','КиБ/с','МиБ/с','ГиБ/с'];let index=0;
|
||||||
|
while(value>=1024&&index<3){value/=1024;index++;}
|
||||||
|
return `${value.toLocaleString('ru-RU',{maximumFractionDigits:1})} ${labels[index]}`;
|
||||||
|
}
|
||||||
|
return `${value.toLocaleString('ru-RU',{maximumFractionDigits:1})}${unit?' '+unit:''}`;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
export interface ObservationLayout {
|
||||||
|
version:1; order:string[]; hidden:string[]; layers:Record<string,string>;
|
||||||
|
splits:Record<string,number>; arrangement:'auto'|'columns'|'rows';
|
||||||
|
}
|
||||||
|
export const emptyObservationLayout=():ObservationLayout=>({version:1,order:[],hidden:[],layers:{},splits:{},arrangement:'auto'});
|
||||||
|
const record=(value:unknown):Record<string,unknown>=>value!==null&&typeof value==='object'&&!Array.isArray(value)?value as Record<string,unknown>:{};
|
||||||
|
export function decodeObservationLayout(value:unknown):ObservationLayout {
|
||||||
|
const source=record(value),result=emptyObservationLayout();if(source.version!==1)return result;
|
||||||
|
const ids=(value:unknown)=>Array.isArray(value)?[...new Set(value.filter((v):v is string=>typeof v==='string'&&v.length<512))].slice(0,128):[];
|
||||||
|
result.order=ids(source.order);result.hidden=ids(source.hidden);
|
||||||
|
result.layers=Object.fromEntries(Object.entries(record(source.layers)).filter(([k,v])=>k.length<512&&typeof v==='string'&&v.length<128).slice(0,128)) as Record<string,string>;
|
||||||
|
result.splits=Object.fromEntries(Object.entries(record(source.splits)).filter(([k,v])=>k.length<4096&&typeof v==='number'&&Number.isFinite(v)).slice(0,256).map(([k,v])=>[k,Math.min(80,Math.max(20,v as number))]));
|
||||||
|
if(source.arrangement==='rows'||source.arrangement==='columns')result.arrangement=source.arrangement;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
export function orderedObservationIDs(layout:ObservationLayout,available:string[]):string[]{
|
||||||
|
const present=new Set(available);return [...layout.order.filter(id=>present.has(id)),...available.filter(id=>!layout.order.includes(id))];
|
||||||
|
}
|
||||||
|
export function moveObservation(layout:ObservationLayout,available:string[],from:string,to:string):ObservationLayout {
|
||||||
|
const order=orderedObservationIDs(layout,available);const source=order.indexOf(from),target=order.indexOf(to);
|
||||||
|
if(source<0||target<0||source===target)return layout;
|
||||||
|
order.splice(source,1);order.splice(target,0,from);return {...layout,order};
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import type {SensorInventory, SensorTransport} from '../../../../../packages/sensor-ui/src/contracts';
|
||||||
|
import {fleetRequest} from './useFleet';
|
||||||
|
|
||||||
|
export function createFleetSensorTransport(vehicleID:string):SensorTransport {
|
||||||
|
const path=`/${encodeURIComponent(vehicleID)}/devices`;
|
||||||
|
const inventory=(fleet:{items:{id:string;connectivity:string;enrollment:string;sensor_state:SensorInventory}[]})=>{
|
||||||
|
const value=fleet.items.find(v=>v.id===vehicleID);
|
||||||
|
if(!value)throw new Error('Аппарат не найден.');
|
||||||
|
return {...value.sensor_state,fresh:value.connectivity==='online'&&value.enrollment==='paired'};
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
enrollment:{state:()=>fleetRequest(`${path}/enrollment`),submit:value=>fleetRequest(`${path}/enrollment/operations`,'POST',value),operation:id=>fleetRequest(`${path}/enrollment/operations/${encodeURIComponent(id)}`)},
|
||||||
|
inventory:async()=>inventory(await fleetRequest()),
|
||||||
|
subscribe:(receive,unavailable)=>{const events=new EventSource('/api/v1/fleet/events');events.onmessage=e=>{try{receive(inventory(JSON.parse(e.data)));}catch{unavailable();}};events.onerror=unavailable;return()=>events.close();},
|
||||||
|
submit:value=>fleetRequest(`${path}/operations`,'POST',value),
|
||||||
|
operation:id=>fleetRequest(`${path}/operations/${encodeURIComponent(id)}`),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export interface BoardHost {
|
||||||
|
hostname: string; os: string; architecture: string; cpus: number;
|
||||||
|
memory_kib: number | null; collected_at: string;
|
||||||
|
networks: { name: string; up: boolean; addresses: string[] }[];
|
||||||
|
usb: { port: string; product: string }[];
|
||||||
|
}
|
||||||
|
export interface Vehicle {
|
||||||
|
id: string; node_id: string; name: string; platform: string;
|
||||||
|
enrollment: "pending" | "paired" | "revoked" | "failed";
|
||||||
|
connectivity: "online" | "offline"; last_seen: number | null;
|
||||||
|
host: BoardHost | null; notice: string; revision: number;
|
||||||
|
}
|
||||||
|
export interface FleetPreview {
|
||||||
|
preview_id: string; node_id: string; name: string; host: BoardHost;
|
||||||
|
endpoint: string; expires_at: number;
|
||||||
|
}
|
||||||
|
export async function fleetRequest<T>(path = "", method = "GET", body?: unknown): Promise<T> {
|
||||||
|
const response = await fetch(`/api/v1/fleet${path}`, {
|
||||||
|
method, credentials: "same-origin", cache: "no-store",
|
||||||
|
headers: body === undefined ? {} : { "Content-Type": "application/json" },
|
||||||
|
body: body === undefined ? undefined : JSON.stringify(body), signal: AbortSignal.timeout(12000),
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
const data = await response.json().catch(() => ({}));
|
||||||
|
throw new Error(typeof data.detail === "string" ? data.detail : "Не удалось выполнить действие с аппаратом.");
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
|
export function useFleet() {
|
||||||
|
const [items, setItems] = useState<Vehicle[] | null>(null);
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
const refresh = useCallback(async () => {
|
||||||
|
const value = await fleetRequest<{ items: Vehicle[] }>();
|
||||||
|
setItems(value.items); setError(""); return value.items;
|
||||||
|
}, []);
|
||||||
|
useEffect(() => {
|
||||||
|
let active = true;
|
||||||
|
let fallback: ReturnType<typeof setInterval> | undefined;
|
||||||
|
const read = async () => {
|
||||||
|
try { const value = await fleetRequest<{ items: Vehicle[] }>(); if (active) { setItems(value.items); setError(""); } }
|
||||||
|
catch { if (active) setError("Реестр недоступен. Показаны последние полученные сведения; связь сейчас не подтверждена."); }
|
||||||
|
};
|
||||||
|
void read();
|
||||||
|
const events = new EventSource("/api/v1/fleet/events");
|
||||||
|
events.onmessage = event => {
|
||||||
|
if (!active) return;
|
||||||
|
try { const value = JSON.parse(event.data); setItems(value.items); setError(""); if (fallback) { clearInterval(fallback); fallback = undefined; } }
|
||||||
|
catch { unavailable(); }
|
||||||
|
};
|
||||||
|
const unavailable = () => {
|
||||||
|
if (!active) return;
|
||||||
|
setError("Связь обновляется. Показаны последние полученные сведения.");
|
||||||
|
if (!fallback) fallback = setInterval(() => void read(), 5000);
|
||||||
|
};
|
||||||
|
events.onerror = unavailable;
|
||||||
|
return () => { active = false; events.close(); if (fallback) clearInterval(fallback); };
|
||||||
|
}, []);
|
||||||
|
return { items, error, refresh };
|
||||||
|
}
|
||||||
@@ -48,9 +48,13 @@ import { fetchM48SFixedClassDetectorResult } from "./m48sFixedClassDetector";
|
|||||||
import { fetchM48TRiskQualityResult } from "./m48tRiskQuality";
|
import { fetchM48TRiskQualityResult } from "./m48tRiskQuality";
|
||||||
import { fetchM49TgsFailClosedResult } from "./m49TgsFailClosed";
|
import { fetchM49TgsFailClosedResult } from "./m49TgsFailClosed";
|
||||||
import { fetchM49TgsFullShadowResult } from "./m49TgsFullShadow";
|
import { fetchM49TgsFullShadowResult } from "./m49TgsFullShadow";
|
||||||
import { fetchVegetationShadowResult } from "./vegetationShadow";
|
import {
|
||||||
|
fetchVegetationBenchmarkResult,
|
||||||
|
fetchVegetationShadowResult,
|
||||||
|
} from "./vegetationShadow";
|
||||||
|
|
||||||
export type AdvancedLaboratoryWorkId =
|
export type AdvancedLaboratoryWorkId =
|
||||||
|
| "lab-v1-vegetation-benchmark"
|
||||||
| "lab-v1-vegetation-shadow"
|
| "lab-v1-vegetation-shadow"
|
||||||
| "m48-object-centric-quality"
|
| "m48-object-centric-quality"
|
||||||
| "m48-small-static-passage-regression"
|
| "m48-small-static-passage-regression"
|
||||||
@@ -102,6 +106,7 @@ export interface AdvancedLaboratoryIndexItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const WORK_IDS: readonly AdvancedLaboratoryWorkId[] = [
|
const WORK_IDS: readonly AdvancedLaboratoryWorkId[] = [
|
||||||
|
"lab-v1-vegetation-benchmark",
|
||||||
"lab-v1-vegetation-shadow",
|
"lab-v1-vegetation-shadow",
|
||||||
"m48-object-centric-quality",
|
"m48-object-centric-quality",
|
||||||
"m48-small-static-passage-regression",
|
"m48-small-static-passage-regression",
|
||||||
@@ -148,6 +153,7 @@ const WORK_IDS: readonly AdvancedLaboratoryWorkId[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const RESULT_PREFIX: Readonly<Record<AdvancedLaboratoryWorkId, string>> = {
|
const RESULT_PREFIX: Readonly<Record<AdvancedLaboratoryWorkId, string>> = {
|
||||||
|
"lab-v1-vegetation-benchmark": "lab-v1-vegetation-benchmark",
|
||||||
"lab-v1-vegetation-shadow": "lab-v1-vegetation-shadow",
|
"lab-v1-vegetation-shadow": "lab-v1-vegetation-shadow",
|
||||||
"m48-object-centric-quality": "m48-object-quality-(?:pack|result)",
|
"m48-object-centric-quality": "m48-object-quality-(?:pack|result)",
|
||||||
"m48-small-static-passage-regression": "m48-small-static-passage-regression",
|
"m48-small-static-passage-regression": "m48-small-static-passage-regression",
|
||||||
@@ -201,6 +207,7 @@ export function isAdvancedLaboratoryWorkId(
|
|||||||
|
|
||||||
export function emptyAdvancedLaboratoryResults(): AdvancedLaboratoryResults {
|
export function emptyAdvancedLaboratoryResults(): AdvancedLaboratoryResults {
|
||||||
return {
|
return {
|
||||||
|
vegetationBenchmark: null,
|
||||||
vegetationShadow: null,
|
vegetationShadow: null,
|
||||||
m47Graph: null,
|
m47Graph: null,
|
||||||
m48: null,
|
m48: null,
|
||||||
@@ -335,7 +342,8 @@ export function advancedLaboratoryResultAvailable(
|
|||||||
workId: AdvancedLaboratoryWorkId,
|
workId: AdvancedLaboratoryWorkId,
|
||||||
results: AdvancedLaboratoryResults,
|
results: AdvancedLaboratoryResults,
|
||||||
): boolean {
|
): boolean {
|
||||||
return workId === "lab-v1-vegetation-shadow" ? results.vegetationShadow !== null
|
return workId === "lab-v1-vegetation-benchmark" ? results.vegetationBenchmark !== null
|
||||||
|
: workId === "lab-v1-vegetation-shadow" ? results.vegetationShadow !== null
|
||||||
: workId === "m48-object-centric-quality" ? results.m48 !== null
|
: workId === "m48-object-centric-quality" ? results.m48 !== null
|
||||||
: workId === "m48-small-static-passage-regression" ? results.m48SmallStatic !== null
|
: workId === "m48-small-static-passage-regression" ? results.m48SmallStatic !== null
|
||||||
: workId === "m48-static-occupancy-qualification" ? results.m48StaticOccupancy !== null
|
: workId === "m48-static-occupancy-qualification" ? results.m48StaticOccupancy !== null
|
||||||
@@ -393,7 +401,10 @@ export async function fetchAdvancedLaboratoryResult(
|
|||||||
} = {},
|
} = {},
|
||||||
): Promise<AdvancedLaboratoryResults> {
|
): Promise<AdvancedLaboratoryResults> {
|
||||||
const results = emptyAdvancedLaboratoryResults();
|
const results = emptyAdvancedLaboratoryResults();
|
||||||
if (workId === "lab-v1-vegetation-shadow") {
|
if (workId === "lab-v1-vegetation-benchmark") {
|
||||||
|
if (!resultId) throw new AdvancedLaboratoryContractError("Vegetation benchmark identity не выбрана.");
|
||||||
|
results.vegetationBenchmark = await fetchVegetationBenchmarkResult(resultId, { fetcher, signal });
|
||||||
|
} else if (workId === "lab-v1-vegetation-shadow") {
|
||||||
if (!resultId) throw new AdvancedLaboratoryContractError("Vegetation LAB identity не выбрана.");
|
if (!resultId) throw new AdvancedLaboratoryContractError("Vegetation LAB identity не выбрана.");
|
||||||
results.vegetationShadow = await fetchVegetationShadowResult(resultId, { fetcher, signal });
|
results.vegetationShadow = await fetchVegetationShadowResult(resultId, { fetcher, signal });
|
||||||
} else if (workId === "m48-object-centric-quality") {
|
} else if (workId === "m48-object-centric-quality") {
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import type { M49TgsFullShadowResult } from "./m49TgsFullShadow";
|
|||||||
import type { VegetationShadowResult } from "./vegetationShadow";
|
import type { VegetationShadowResult } from "./vegetationShadow";
|
||||||
|
|
||||||
export interface AdvancedLaboratoryResults {
|
export interface AdvancedLaboratoryResults {
|
||||||
|
vegetationBenchmark: VegetationShadowResult | null;
|
||||||
vegetationShadow: VegetationShadowResult | null;
|
vegetationShadow: VegetationShadowResult | null;
|
||||||
m47Graph: M47ReferenceGraphLabResult | null;
|
m47Graph: M47ReferenceGraphLabResult | null;
|
||||||
m48: M48AdvancedResult | null;
|
m48: M48AdvancedResult | null;
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ export interface E31LaboratoryResult {
|
|||||||
limitations: readonly string[];
|
limitations: readonly string[];
|
||||||
access: "read-only";
|
access: "read-only";
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface E32LaboratoryResult {
|
export interface E32LaboratoryResult {
|
||||||
resultId: string;
|
resultId: string;
|
||||||
createdAtUtc: string | null;
|
createdAtUtc: string | null;
|
||||||
@@ -53,7 +52,6 @@ export interface E32LaboratoryResult {
|
|||||||
};
|
};
|
||||||
access: "read-only";
|
access: "read-only";
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface E33LaboratoryResult {
|
export interface E33LaboratoryResult {
|
||||||
resultId: string;
|
resultId: string;
|
||||||
createdAtUtc: string | null;
|
createdAtUtc: string | null;
|
||||||
@@ -967,6 +965,7 @@ export async function fetchAdvancedLaboratoryResults({
|
|||||||
const e39 = settledCatalogValue(settled[7]);
|
const e39 = settledCatalogValue(settled[7]);
|
||||||
const e40 = settledCatalogValue(settled[8]);
|
const e40 = settledCatalogValue(settled[8]);
|
||||||
return {
|
return {
|
||||||
|
vegetationBenchmark: null,
|
||||||
vegetationShadow: null,
|
vegetationShadow: null,
|
||||||
m47Graph: null, m48: null, m48SmallStatic: null, m48StaticOccupancy: null,
|
m47Graph: null, m48: null, m48SmallStatic: null, m48StaticOccupancy: null,
|
||||||
m48r3StaticOccupancy: null,
|
m48r3StaticOccupancy: null,
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
import type { ObservationSessionReplayLaunch } from "../observation/sessionArchive";
|
||||||
|
import type { RecordedRrdArtifactDescriptor } from "../observation/viewerProfile";
|
||||||
|
|
||||||
|
const SAFE_RESULT_ID = /^lab-v1-vegetation-shadow-[a-f0-9]{64}$/;
|
||||||
|
const SAFE_SESSION_SOURCE = /^\/api\/v1\/observation-sessions\/[A-Za-z0-9][A-Za-z0-9._:-]{0,127}\/recording\.rrd$/;
|
||||||
|
const MAX_CANONICAL_REPLAY_BYTES = 1024 * 1024 * 1024;
|
||||||
|
|
||||||
|
export interface CanonicalLabReplayDescriptor extends RecordedRrdArtifactDescriptor {
|
||||||
|
blueprintSourceUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the one immutable RRD used by the canonical recorded LAB.
|
||||||
|
*
|
||||||
|
* The server caches the merge of the sealed spatial recording and the LAB AI
|
||||||
|
* evidence. Rerun therefore opens one source and cannot present the base store
|
||||||
|
* before a second receiver has finished decoding the semantic layer.
|
||||||
|
*/
|
||||||
|
export async function resolveCanonicalLabReplay(
|
||||||
|
resultId: string,
|
||||||
|
launch: ObservationSessionReplayLaunch,
|
||||||
|
{
|
||||||
|
origin = window.location.origin,
|
||||||
|
signal,
|
||||||
|
fetcher = globalThis.fetch,
|
||||||
|
sourceKind = "legacy-vegetation",
|
||||||
|
}: {
|
||||||
|
origin?: string;
|
||||||
|
signal?: AbortSignal;
|
||||||
|
fetcher?: typeof globalThis.fetch;
|
||||||
|
sourceKind?: "legacy-vegetation" | "portable-tgs" | "portable-semantic" | "portable-objects";
|
||||||
|
} = {},
|
||||||
|
): Promise<CanonicalLabReplayDescriptor> {
|
||||||
|
const base = new URL(origin);
|
||||||
|
if (
|
||||||
|
!(sourceKind === "portable-tgs"
|
||||||
|
? /^m49-tgs-portable-review-[a-f0-9]{64}$/.test(resultId)
|
||||||
|
: sourceKind === "portable-semantic"
|
||||||
|
? /^(?:lab-v1-eomt-ddrnet|ai-layer-(?:ddrnet|eomt))-[a-f0-9]{64}$/.test(resultId)
|
||||||
|
: sourceKind === "portable-objects"
|
||||||
|
? /^ai-layer-(?:rf-detr|object-distance)-[a-f0-9]{64}$/.test(resultId)
|
||||||
|
: SAFE_RESULT_ID.test(resultId))
|
||||||
|
|| !SAFE_SESSION_SOURCE.test(launch.sourceUrl)
|
||||||
|
|| launch.viewerSourceUrl !== `${launch.sourceUrl}?generation=${launch.sha256}`
|
||||||
|
|| !/^[a-f0-9]{64}$/.test(launch.sha256)
|
||||||
|
) {
|
||||||
|
throw new Error("Канонический replay LAB имеет небезопасный descriptor.");
|
||||||
|
}
|
||||||
|
const sourceUrl = sourceKind !== "legacy-vegetation"
|
||||||
|
? `/api/v1/observatory/portable-results/${resultId}/replays/${launch.sha256}/recording.rrd`
|
||||||
|
: `/api/v1/laboratory/vegetation-shadow/${encodeURIComponent(resultId)}/canonical-replay.rrd`;
|
||||||
|
const descriptorUrl = new URL(sourceUrl, `${base.origin}/`);
|
||||||
|
if (sourceKind === "legacy-vegetation") descriptorUrl.searchParams.set("base_generation", launch.sha256);
|
||||||
|
if (descriptorUrl.origin !== base.origin) {
|
||||||
|
throw new Error("Канонический replay LAB должен быть same-origin.");
|
||||||
|
}
|
||||||
|
const response = await fetcher(descriptorUrl.href, {
|
||||||
|
method: "HEAD",
|
||||||
|
credentials: "same-origin",
|
||||||
|
headers: { Accept: "application/vnd.rerun.rrd" },
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
const contentType = response.headers.get("Content-Type")?.split(";", 1)[0].trim();
|
||||||
|
const byteLength = Number(response.headers.get("Content-Length"));
|
||||||
|
const sha256 = response.headers.get("ETag")?.match(/^"([a-f0-9]{64})"$/)?.[1];
|
||||||
|
if (
|
||||||
|
response.status !== 200
|
||||||
|
|| contentType !== "application/vnd.rerun.rrd"
|
||||||
|
|| response.headers.get("X-Rerun-Format") !== "RRF2"
|
||||||
|
|| !sha256
|
||||||
|
|| !Number.isSafeInteger(byteLength)
|
||||||
|
|| byteLength < 4
|
||||||
|
|| byteLength > MAX_CANONICAL_REPLAY_BYTES
|
||||||
|
) {
|
||||||
|
throw new Error("Единый replay LAB не прошёл проверку.");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
sourceUrl,
|
||||||
|
viewerSourceUrl: `${sourceUrl}?generation=${sha256}`,
|
||||||
|
byteLength,
|
||||||
|
sha256,
|
||||||
|
blueprintSourceUrl: launch.sourceUrl.replace(/\/recording\.rrd$/, "/blueprint.rrd"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resolveAICompositionReplay(
|
||||||
|
runId: string,
|
||||||
|
launch: ObservationSessionReplayLaunch,
|
||||||
|
{
|
||||||
|
origin = window.location.origin,
|
||||||
|
signal,
|
||||||
|
fetcher = globalThis.fetch,
|
||||||
|
}: {
|
||||||
|
origin?: string;
|
||||||
|
signal?: AbortSignal;
|
||||||
|
fetcher?: typeof globalThis.fetch;
|
||||||
|
} = {},
|
||||||
|
): Promise<CanonicalLabReplayDescriptor> {
|
||||||
|
const base = new URL(origin);
|
||||||
|
if (
|
||||||
|
!/^ai-composition-[a-f0-9]{64}$/.test(runId)
|
||||||
|
|| !SAFE_SESSION_SOURCE.test(launch.sourceUrl)
|
||||||
|
|| launch.viewerSourceUrl !== `${launch.sourceUrl}?generation=${launch.sha256}`
|
||||||
|
|| !/^[a-f0-9]{64}$/.test(launch.sha256)
|
||||||
|
) {
|
||||||
|
throw new Error("Составной replay LAB имеет небезопасный descriptor.");
|
||||||
|
}
|
||||||
|
const sourceUrl = `/api/v1/observatory/ai-composition-runs/${runId}/replays/${launch.sha256}/recording.rrd`;
|
||||||
|
const descriptorUrl = new URL(sourceUrl, `${base.origin}/`);
|
||||||
|
const response = await fetcher(descriptorUrl.href, {
|
||||||
|
method: "HEAD",
|
||||||
|
credentials: "same-origin",
|
||||||
|
headers: { Accept: "application/vnd.rerun.rrd" },
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
const contentType = response.headers.get("Content-Type")?.split(";", 1)[0].trim();
|
||||||
|
const byteLength = Number(response.headers.get("Content-Length"));
|
||||||
|
const sha256 = response.headers.get("ETag")?.match(/^"([a-f0-9]{64})"$/)?.[1];
|
||||||
|
if (
|
||||||
|
response.status !== 200 || contentType !== "application/vnd.rerun.rrd"
|
||||||
|
|| response.headers.get("X-Rerun-Format") !== "RRF2" || !sha256
|
||||||
|
|| !Number.isSafeInteger(byteLength) || byteLength < 4
|
||||||
|
|| byteLength > MAX_CANONICAL_REPLAY_BYTES
|
||||||
|
) {
|
||||||
|
throw new Error("Составной replay LAB не прошёл проверку.");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
sourceUrl,
|
||||||
|
viewerSourceUrl: `${sourceUrl}?generation=${sha256}`,
|
||||||
|
byteLength,
|
||||||
|
sha256,
|
||||||
|
blueprintSourceUrl: launch.sourceUrl.replace(/\/recording\.rrd$/, "/blueprint.rrd"),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
export const CANONICAL_RECORDED_LAB_TGS_HISTORY_SECONDS = 1;
|
||||||
|
export const CANONICAL_RECORDED_LAB_SPATIAL_PROFILE = "source-paced-ground-v3";
|
||||||
|
|
||||||
|
export interface CanonicalRecordedLabPackedCellEvidence {
|
||||||
|
centersBodyXyM: Float32Array;
|
||||||
|
zBoundsM: Float32Array;
|
||||||
|
stateCodes: Uint8Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CanonicalRecordedLabBodyGroundFrame {
|
||||||
|
originMapXyzM: readonly [number, number, number];
|
||||||
|
sensorOriginMapXyzM: readonly [number, number, number];
|
||||||
|
basisMapFromBody: readonly [
|
||||||
|
readonly [number, number, number],
|
||||||
|
readonly [number, number, number],
|
||||||
|
readonly [number, number, number],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CanonicalRecordedLabTgsCostmap {
|
||||||
|
centersXyM: readonly (readonly [number, number])[];
|
||||||
|
stateCodes: readonly number[];
|
||||||
|
zBoundsM: readonly (readonly [number | null, number | null])[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canonicalRecordedLabTgsIsCurrent(
|
||||||
|
currentTimeNs: number,
|
||||||
|
anchorTimeNs: number,
|
||||||
|
historySeconds = CANONICAL_RECORDED_LAB_TGS_HISTORY_SECONDS,
|
||||||
|
): boolean {
|
||||||
|
if (
|
||||||
|
!Number.isSafeInteger(currentTimeNs)
|
||||||
|
|| !Number.isSafeInteger(anchorTimeNs)
|
||||||
|
|| !Number.isFinite(historySeconds)
|
||||||
|
|| historySeconds <= 0
|
||||||
|
) return false;
|
||||||
|
const ageNs = currentTimeNs - anchorTimeNs;
|
||||||
|
return ageNs >= 0 && ageNs <= Math.round(historySeconds * 1_000_000_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canonicalMapGravityLocalPointToBodyGround(
|
||||||
|
point: readonly [number, number, number],
|
||||||
|
anchor: CanonicalRecordedLabBodyGroundFrame,
|
||||||
|
current: CanonicalRecordedLabBodyGroundFrame,
|
||||||
|
): readonly [number, number, number] {
|
||||||
|
// TGS is translation-only map-gravity-local: its axes are map axes and its
|
||||||
|
// origin is the LiDAR at the source frame. It is not an anchor body frame.
|
||||||
|
const map: readonly [number, number, number] = [
|
||||||
|
anchor.sensorOriginMapXyzM[0] + point[0],
|
||||||
|
anchor.sensorOriginMapXyzM[1] + point[1],
|
||||||
|
anchor.sensorOriginMapXyzM[2] + point[2],
|
||||||
|
];
|
||||||
|
const delta: readonly [number, number, number] = [
|
||||||
|
map[0] - current.originMapXyzM[0],
|
||||||
|
map[1] - current.originMapXyzM[1],
|
||||||
|
map[2] - current.originMapXyzM[2],
|
||||||
|
];
|
||||||
|
return [
|
||||||
|
current.basisMapFromBody[0][0] * delta[0]
|
||||||
|
+ current.basisMapFromBody[1][0] * delta[1]
|
||||||
|
+ current.basisMapFromBody[2][0] * delta[2],
|
||||||
|
current.basisMapFromBody[0][1] * delta[0]
|
||||||
|
+ current.basisMapFromBody[1][1] * delta[1]
|
||||||
|
+ current.basisMapFromBody[2][1] * delta[2],
|
||||||
|
current.basisMapFromBody[0][2] * delta[0]
|
||||||
|
+ current.basisMapFromBody[1][2] * delta[1]
|
||||||
|
+ current.basisMapFromBody[2][2] * delta[2],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canonicalRecordedLabPackedTgsCells(
|
||||||
|
costmap: CanonicalRecordedLabTgsCostmap,
|
||||||
|
anchor: CanonicalRecordedLabBodyGroundFrame,
|
||||||
|
current: CanonicalRecordedLabBodyGroundFrame,
|
||||||
|
): CanonicalRecordedLabPackedCellEvidence {
|
||||||
|
if (
|
||||||
|
costmap.centersXyM.length !== costmap.stateCodes.length
|
||||||
|
|| costmap.centersXyM.length !== costmap.zBoundsM.length
|
||||||
|
) throw new Error("Canonical recorded LAB TGS accounting changed");
|
||||||
|
const centers: number[] = [];
|
||||||
|
const zBounds: number[] = [];
|
||||||
|
costmap.centersXyM.forEach(([x, y], index) => {
|
||||||
|
const bounds = costmap.zBoundsM[index] ?? [null, null];
|
||||||
|
const center = canonicalMapGravityLocalPointToBodyGround([x, y, 0], anchor, current);
|
||||||
|
centers.push(center[0], center[1]);
|
||||||
|
if (bounds[0] === null || bounds[1] === null) {
|
||||||
|
zBounds.push(Number.NaN, Number.NaN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const bottom = canonicalMapGravityLocalPointToBodyGround([x, y, bounds[0]], anchor, current);
|
||||||
|
const top = canonicalMapGravityLocalPointToBodyGround([x, y, bounds[1]], anchor, current);
|
||||||
|
zBounds.push(Math.min(bottom[2], top[2]), Math.max(bottom[2], top[2]));
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
centersBodyXyM: Float32Array.from(centers),
|
||||||
|
zBoundsM: Float32Array.from(zBounds),
|
||||||
|
stateCodes: Uint8Array.from(costmap.stateCodes),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,259 @@
|
|||||||
|
import type { LaboratoryFetch } from "./advancedResults";
|
||||||
|
import { CANONICAL_RECORDED_LAB_SPATIAL_PROFILE } from "./canonicalRecordedLab";
|
||||||
|
|
||||||
|
const SAFE_SESSION_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
||||||
|
const SHA256 = /^[a-f0-9]{64}$/;
|
||||||
|
|
||||||
|
export class CanonicalRecordedLabSpatialContractError extends Error {}
|
||||||
|
|
||||||
|
export interface CanonicalRecordedLabSpatialFrame {
|
||||||
|
targetTimeNs: number;
|
||||||
|
sourceTimeNs: number;
|
||||||
|
poseTimeNs: number;
|
||||||
|
trajectoryTimeNs: number;
|
||||||
|
sourcePointCount: number;
|
||||||
|
coordinateFrame: "body-ground";
|
||||||
|
sensorHeight: {
|
||||||
|
meters: number;
|
||||||
|
source: "local-source-cloud-ground-quantile-median" | "session-source-cloud-fallback";
|
||||||
|
sampleCount: number;
|
||||||
|
madM: number;
|
||||||
|
authority: "visual-derived";
|
||||||
|
};
|
||||||
|
spatialProfile: {
|
||||||
|
profileId: typeof CANONICAL_RECORDED_LAB_SPATIAL_PROFILE;
|
||||||
|
localSlamHistorySeconds: number;
|
||||||
|
localSlamRadiusM: number;
|
||||||
|
localSlamVoxelSizeM: number;
|
||||||
|
localSlamPointLimit: number;
|
||||||
|
};
|
||||||
|
bodyFrame: {
|
||||||
|
originMapXyzM: readonly [number, number, number];
|
||||||
|
sensorOriginMapXyzM: readonly [number, number, number];
|
||||||
|
basisMapFromBody: readonly [
|
||||||
|
readonly [number, number, number],
|
||||||
|
readonly [number, number, number],
|
||||||
|
readonly [number, number, number],
|
||||||
|
];
|
||||||
|
};
|
||||||
|
sourcePointsBodyXyzM: readonly (readonly [number, number, number])[];
|
||||||
|
localSlamSourceFrameCount: number;
|
||||||
|
localSlamSourcePointCount: number;
|
||||||
|
localSlamBodyXyzM: readonly (readonly [number, number, number])[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function objectValue(value: unknown, label: string): Record<string, unknown> {
|
||||||
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||||
|
throw new CanonicalRecordedLabSpatialContractError(`${label}: ожидался объект.`);
|
||||||
|
}
|
||||||
|
return value as Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function arrayValue(value: unknown, label: string): unknown[] {
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
throw new CanonicalRecordedLabSpatialContractError(`${label}: ожидался массив.`);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function exact(value: unknown, expected: unknown, label: string): void {
|
||||||
|
if (value !== expected) {
|
||||||
|
throw new CanonicalRecordedLabSpatialContractError(`${label}: контракт изменён.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function numberValue(value: unknown, label: string): number {
|
||||||
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
||||||
|
throw new CanonicalRecordedLabSpatialContractError(`${label}: ожидалось число.`);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function integerValue(value: unknown, label: string): number {
|
||||||
|
const parsed = numberValue(value, label);
|
||||||
|
if (!Number.isSafeInteger(parsed) || parsed < 0) {
|
||||||
|
throw new CanonicalRecordedLabSpatialContractError(`${label}: ожидалось целое значение.`);
|
||||||
|
}
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pointList(
|
||||||
|
value: unknown,
|
||||||
|
label: string,
|
||||||
|
): readonly (readonly [number, number, number])[] {
|
||||||
|
return arrayValue(value, label).map((entry, index) => {
|
||||||
|
const point = arrayValue(entry, `${label}[${index}]`).map(
|
||||||
|
(channel, channelIndex) => numberValue(channel, `${label}[${index}][${channelIndex}]`),
|
||||||
|
);
|
||||||
|
if (point.length !== 3) {
|
||||||
|
throw new CanonicalRecordedLabSpatialContractError(`${label}[${index}]: размер изменён.`);
|
||||||
|
}
|
||||||
|
return [point[0]!, point[1]!, point[2]!] as const;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchCanonicalRecordedLabSpatialFrame(
|
||||||
|
sessionId: string,
|
||||||
|
generationSha256: string,
|
||||||
|
targetTimeNs: number,
|
||||||
|
{
|
||||||
|
fetcher = fetch,
|
||||||
|
signal,
|
||||||
|
}: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
|
||||||
|
): Promise<CanonicalRecordedLabSpatialFrame> {
|
||||||
|
if (
|
||||||
|
!SAFE_SESSION_ID.test(sessionId)
|
||||||
|
|| !SHA256.test(generationSha256)
|
||||||
|
|| !Number.isSafeInteger(targetTimeNs)
|
||||||
|
|| targetTimeNs < 0
|
||||||
|
) {
|
||||||
|
throw new CanonicalRecordedLabSpatialContractError(
|
||||||
|
"Canonical LAB spatial identity недопустима.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const query = new URLSearchParams({
|
||||||
|
generation: generationSha256,
|
||||||
|
time_ns: String(targetTimeNs),
|
||||||
|
profile: CANONICAL_RECORDED_LAB_SPATIAL_PROFILE,
|
||||||
|
});
|
||||||
|
const response = await fetcher(
|
||||||
|
`/api/v1/observation-sessions/${encodeURIComponent(sessionId)}`
|
||||||
|
+ `/canonical-lab/spatial-frame?${query.toString()}`,
|
||||||
|
{ method: "GET", headers: { Accept: "application/json" }, signal },
|
||||||
|
);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new CanonicalRecordedLabSpatialContractError(
|
||||||
|
`Canonical LAB spatial frame недоступен: HTTP ${response.status}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const payload = objectValue(await response.json(), "canonical_lab.spatial_frame");
|
||||||
|
exact(
|
||||||
|
payload.schema_version,
|
||||||
|
"missioncore.canonical-recorded-lab-spatial-frame/v3",
|
||||||
|
"canonical_lab.spatial_frame.schema_version",
|
||||||
|
);
|
||||||
|
exact(payload.coordinate_frame, "body-ground", "canonical_lab.spatial_frame.coordinate_frame");
|
||||||
|
exact(payload.target_time_ns, targetTimeNs, "canonical_lab.spatial_frame.target_time_ns");
|
||||||
|
const sourcePoints = pointList(
|
||||||
|
payload.source_points_body_xyz_m,
|
||||||
|
"canonical_lab.spatial_frame.source_points",
|
||||||
|
);
|
||||||
|
const localSlam = pointList(
|
||||||
|
payload.local_slam_body_xyz_m,
|
||||||
|
"canonical_lab.spatial_frame.local_slam",
|
||||||
|
);
|
||||||
|
const sourcePointCount = integerValue(
|
||||||
|
payload.source_point_count,
|
||||||
|
"canonical_lab.spatial_frame.source_point_count",
|
||||||
|
);
|
||||||
|
const localSlamPointCount = integerValue(
|
||||||
|
payload.local_slam_point_count,
|
||||||
|
"canonical_lab.spatial_frame.local_slam_point_count",
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
sourcePointCount !== sourcePoints.length
|
||||||
|
|| sourcePointCount > 100_000
|
||||||
|
|| localSlamPointCount !== localSlam.length
|
||||||
|
|| localSlam.length > 27_000
|
||||||
|
) {
|
||||||
|
throw new CanonicalRecordedLabSpatialContractError(
|
||||||
|
"Canonical LAB spatial accounting изменён.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const bodyFrame = objectValue(payload.body_frame, "canonical_lab.spatial_frame.body_frame");
|
||||||
|
const origin = pointList(
|
||||||
|
[bodyFrame.origin_map_xyz_m],
|
||||||
|
"canonical_lab.spatial_frame.body_frame.origin",
|
||||||
|
)[0]!;
|
||||||
|
const sensorOrigin = pointList(
|
||||||
|
[bodyFrame.sensor_origin_map_xyz_m],
|
||||||
|
"canonical_lab.spatial_frame.body_frame.sensor_origin",
|
||||||
|
)[0]!;
|
||||||
|
const basisRows = pointList(
|
||||||
|
bodyFrame.basis_map_from_body,
|
||||||
|
"canonical_lab.spatial_frame.body_frame.basis",
|
||||||
|
);
|
||||||
|
if (basisRows.length !== 3) {
|
||||||
|
throw new CanonicalRecordedLabSpatialContractError(
|
||||||
|
"Canonical LAB spatial basis изменён.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const sensorHeight = objectValue(payload.sensor_height, "canonical_lab.spatial_frame.sensor_height");
|
||||||
|
if (
|
||||||
|
sensorHeight.source !== "local-source-cloud-ground-quantile-median"
|
||||||
|
&& sensorHeight.source !== "session-source-cloud-fallback"
|
||||||
|
) {
|
||||||
|
throw new CanonicalRecordedLabSpatialContractError(
|
||||||
|
"canonical_lab.spatial_frame.sensor_height.source: контракт изменён.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
exact(
|
||||||
|
sensorHeight.authority,
|
||||||
|
"visual-derived",
|
||||||
|
"canonical_lab.spatial_frame.sensor_height.authority",
|
||||||
|
);
|
||||||
|
const spatialProfile = objectValue(
|
||||||
|
payload.spatial_profile,
|
||||||
|
"canonical_lab.spatial_frame.spatial_profile",
|
||||||
|
);
|
||||||
|
exact(
|
||||||
|
spatialProfile.profile_id,
|
||||||
|
CANONICAL_RECORDED_LAB_SPATIAL_PROFILE,
|
||||||
|
"canonical_lab.spatial_frame.spatial_profile.profile_id",
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
targetTimeNs,
|
||||||
|
sourceTimeNs: integerValue(payload.source_time_ns, "canonical_lab.spatial_frame.source_time_ns"),
|
||||||
|
poseTimeNs: integerValue(payload.pose_time_ns, "canonical_lab.spatial_frame.pose_time_ns"),
|
||||||
|
trajectoryTimeNs: integerValue(
|
||||||
|
payload.trajectory_time_ns,
|
||||||
|
"canonical_lab.spatial_frame.trajectory_time_ns",
|
||||||
|
),
|
||||||
|
sourcePointCount,
|
||||||
|
coordinateFrame: "body-ground",
|
||||||
|
sensorHeight: {
|
||||||
|
meters: numberValue(sensorHeight.meters, "canonical_lab.spatial_frame.sensor_height.meters"),
|
||||||
|
source: sensorHeight.source,
|
||||||
|
sampleCount: integerValue(
|
||||||
|
sensorHeight.sample_count,
|
||||||
|
"canonical_lab.spatial_frame.sensor_height.sample_count",
|
||||||
|
),
|
||||||
|
madM: numberValue(sensorHeight.mad_m, "canonical_lab.spatial_frame.sensor_height.mad_m"),
|
||||||
|
authority: "visual-derived",
|
||||||
|
},
|
||||||
|
spatialProfile: {
|
||||||
|
profileId: CANONICAL_RECORDED_LAB_SPATIAL_PROFILE,
|
||||||
|
localSlamHistorySeconds: numberValue(
|
||||||
|
spatialProfile.local_slam_history_seconds,
|
||||||
|
"canonical_lab.spatial_frame.spatial_profile.history",
|
||||||
|
),
|
||||||
|
localSlamRadiusM: numberValue(
|
||||||
|
spatialProfile.local_slam_radius_m,
|
||||||
|
"canonical_lab.spatial_frame.spatial_profile.radius",
|
||||||
|
),
|
||||||
|
localSlamVoxelSizeM: numberValue(
|
||||||
|
spatialProfile.local_slam_voxel_size_m,
|
||||||
|
"canonical_lab.spatial_frame.spatial_profile.voxel",
|
||||||
|
),
|
||||||
|
localSlamPointLimit: integerValue(
|
||||||
|
spatialProfile.local_slam_point_limit,
|
||||||
|
"canonical_lab.spatial_frame.spatial_profile.limit",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
bodyFrame: {
|
||||||
|
originMapXyzM: origin,
|
||||||
|
sensorOriginMapXyzM: sensorOrigin,
|
||||||
|
basisMapFromBody: [basisRows[0]!, basisRows[1]!, basisRows[2]!],
|
||||||
|
},
|
||||||
|
sourcePointsBodyXyzM: sourcePoints,
|
||||||
|
localSlamSourceFrameCount: integerValue(
|
||||||
|
payload.local_slam_source_frame_count,
|
||||||
|
"canonical_lab.spatial_frame.local_slam_source_frames",
|
||||||
|
),
|
||||||
|
localSlamSourcePointCount: integerValue(
|
||||||
|
payload.local_slam_source_point_count,
|
||||||
|
"canonical_lab.spatial_frame.local_slam_source_points",
|
||||||
|
),
|
||||||
|
localSlamBodyXyzM: localSlam,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -154,6 +154,9 @@ export interface M4ThreatTimelineFrame {
|
|||||||
pointCloudSourceCount: number;
|
pointCloudSourceCount: number;
|
||||||
pointCloudSampleCount: number;
|
pointCloudSampleCount: number;
|
||||||
pointCloudLayer: "current-increment";
|
pointCloudLayer: "current-increment";
|
||||||
|
localSlamBodyXyzM?: readonly M4Point3[];
|
||||||
|
localSlamSourceFrameCount?: number;
|
||||||
|
localSlamSourcePointCount?: number;
|
||||||
cameraProjectedPointsXyd: readonly (readonly [number, number, number])[];
|
cameraProjectedPointsXyd: readonly (readonly [number, number, number])[];
|
||||||
cameraProjectedSourceCount: number;
|
cameraProjectedSourceCount: number;
|
||||||
cameraProjectedPointCount: number;
|
cameraProjectedPointCount: number;
|
||||||
@@ -168,10 +171,11 @@ export interface M4ThreatTimelineFrame {
|
|||||||
|
|
||||||
export interface M4ThreatTimeline {
|
export interface M4ThreatTimeline {
|
||||||
resultId: string;
|
resultId: string;
|
||||||
recordedSourceSessionId: "20260720T065719Z_viewer_live";
|
recordedSourceSessionId: string;
|
||||||
|
recordedSourceId: string;
|
||||||
imageWidth: 800;
|
imageWidth: 800;
|
||||||
imageHeight: 600;
|
imageHeight: 600;
|
||||||
frameCount: 4489;
|
frameCount: number;
|
||||||
frameTimesNs: readonly number[];
|
frameTimesNs: readonly number[];
|
||||||
timelineStartSeconds: number;
|
timelineStartSeconds: number;
|
||||||
timelineEndSeconds: number;
|
timelineEndSeconds: number;
|
||||||
@@ -234,7 +238,7 @@ export interface M4ThreatPlaybackProgress {
|
|||||||
|
|
||||||
export interface M4ThreatPlaybackPointPack {
|
export interface M4ThreatPlaybackPointPack {
|
||||||
resultId: string;
|
resultId: string;
|
||||||
frameCount: 4489;
|
frameCount: number;
|
||||||
pointCount: number;
|
pointCount: number;
|
||||||
pointOffsets: Uint32Array;
|
pointOffsets: Uint32Array;
|
||||||
pointsMapXyzM: Float32Array;
|
pointsMapXyzM: Float32Array;
|
||||||
@@ -257,7 +261,7 @@ export interface M4ThreatPlaybackChunkDescriptor {
|
|||||||
|
|
||||||
export interface M4ThreatPlaybackManifest {
|
export interface M4ThreatPlaybackManifest {
|
||||||
resultId: string;
|
resultId: string;
|
||||||
frameCount: 4489;
|
frameCount: number;
|
||||||
pointCount: number;
|
pointCount: number;
|
||||||
pointOffsets: Uint32Array;
|
pointOffsets: Uint32Array;
|
||||||
chunkFrameCount: 24;
|
chunkFrameCount: 24;
|
||||||
@@ -337,7 +341,7 @@ const motion = (value: unknown): M4ThreatMotion => {
|
|||||||
};
|
};
|
||||||
const resultId = (value: unknown): string => {
|
const resultId = (value: unknown): string => {
|
||||||
const parsed = text(value, "M4.6 result id");
|
const parsed = text(value, "M4.6 result id");
|
||||||
if (!/^m4-threat-replay-[a-f0-9]{64}$/.test(parsed)) {
|
if (!/^[a-z0-9][a-z0-9-]{0,127}-[a-f0-9]{64}$/.test(parsed)) {
|
||||||
throw new M4ThreatContractError("M4.6 result id: нарушена идентичность.");
|
throw new M4ThreatContractError("M4.6 result id: нарушена идентичность.");
|
||||||
}
|
}
|
||||||
return parsed;
|
return parsed;
|
||||||
@@ -704,12 +708,8 @@ export async function fetchM4ThreatTimeline(
|
|||||||
exact(payload.result_id, result, "M4.6 timeline result");
|
exact(payload.result_id, result, "M4.6 timeline result");
|
||||||
exact(payload.authority, "replay-simulated", "M4.6 timeline authority");
|
exact(payload.authority, "replay-simulated", "M4.6 timeline authority");
|
||||||
const recorded = object(payload.recorded_source, "M4.6 recorded source");
|
const recorded = object(payload.recorded_source, "M4.6 recorded source");
|
||||||
exact(
|
const recordedSessionId = text(recorded.session_id, "M4.6 recorded session");
|
||||||
recorded.session_id,
|
const recordedSourceId = text(recorded.source_id, "M4.6 recorded source id");
|
||||||
"20260720T065719Z_viewer_live",
|
|
||||||
"M4.6 recorded session",
|
|
||||||
);
|
|
||||||
exact(recorded.source_id, "RAVNOVES00", "M4.6 recorded source id");
|
|
||||||
exact(
|
exact(
|
||||||
recorded.representation_id,
|
recorded.representation_id,
|
||||||
"registered-map-increment-v1",
|
"registered-map-increment-v1",
|
||||||
@@ -720,7 +720,10 @@ export async function fetchM4ThreatTimeline(
|
|||||||
"host-arrival-best-effort",
|
"host-arrival-best-effort",
|
||||||
"M4.6 recorded synchronization",
|
"M4.6 recorded synchronization",
|
||||||
);
|
);
|
||||||
const frameCount = exact(payload.frame_count, 4489, "M4.6 timeline frame count");
|
const frameCount = integer(payload.frame_count, "M4.6 timeline frame count");
|
||||||
|
if (frameCount < 1) {
|
||||||
|
throw new M4ThreatContractError("M4.6 timeline frame count: пустой timeline.");
|
||||||
|
}
|
||||||
const frameTimesNs = array(payload.frame_times_ns, "M4.6 timeline index").map(
|
const frameTimesNs = array(payload.frame_times_ns, "M4.6 timeline index").map(
|
||||||
(value) => integer(value, "M4.6 timeline time"),
|
(value) => integer(value, "M4.6 timeline time"),
|
||||||
);
|
);
|
||||||
@@ -738,7 +741,8 @@ export async function fetchM4ThreatTimeline(
|
|||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
resultId: result,
|
resultId: result,
|
||||||
recordedSourceSessionId: "20260720T065719Z_viewer_live",
|
recordedSourceSessionId: recordedSessionId,
|
||||||
|
recordedSourceId,
|
||||||
imageWidth: exact(payload.image_width, 800, "M4.6 image width"),
|
imageWidth: exact(payload.image_width, 800, "M4.6 image width"),
|
||||||
imageHeight: exact(payload.image_height, 600, "M4.6 image height"),
|
imageHeight: exact(payload.image_height, 600, "M4.6 image height"),
|
||||||
frameCount,
|
frameCount,
|
||||||
@@ -849,12 +853,14 @@ export async function fetchM4ThreatTimelineChunk(
|
|||||||
endpointRoot = M4_THREAT_TIMELINE_ENDPOINT_ROOT,
|
endpointRoot = M4_THREAT_TIMELINE_ENDPOINT_ROOT,
|
||||||
cameraObstacleProjectionDelivery = null,
|
cameraObstacleProjectionDelivery = null,
|
||||||
playbackPointPack,
|
playbackPointPack,
|
||||||
|
includePoints = true,
|
||||||
}: {
|
}: {
|
||||||
fetcher?: LaboratoryFetch;
|
fetcher?: LaboratoryFetch;
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
endpointRoot?: string;
|
endpointRoot?: string;
|
||||||
cameraObstacleProjectionDelivery?: M4ThreatTimeline["cameraObstacleProjectionDelivery"];
|
cameraObstacleProjectionDelivery?: M4ThreatTimeline["cameraObstacleProjectionDelivery"];
|
||||||
playbackPointPack?: M4ThreatPlaybackPointPack;
|
playbackPointPack?: M4ThreatPlaybackPointPack;
|
||||||
|
includePoints?: boolean;
|
||||||
} = {},
|
} = {},
|
||||||
): Promise<M4ThreatTimelineChunk> {
|
): Promise<M4ThreatTimelineChunk> {
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
@@ -864,7 +870,7 @@ export async function fetchM4ThreatTimelineChunk(
|
|||||||
if (cameraObstacleProjectionDelivery !== null) {
|
if (cameraObstacleProjectionDelivery !== null) {
|
||||||
params.set("obstacle_projection", cameraObstacleProjectionDelivery);
|
params.set("obstacle_projection", cameraObstacleProjectionDelivery);
|
||||||
}
|
}
|
||||||
if (playbackPointPack) params.set("include_points", "false");
|
if (playbackPointPack || !includePoints) params.set("include_points", "false");
|
||||||
const response = await fetcher(
|
const response = await fetcher(
|
||||||
`${endpointRoot}/${result}/timeline/chunk?${params}`,
|
`${endpointRoot}/${result}/timeline/chunk?${params}`,
|
||||||
{ headers: { Accept: "application/json" }, signal },
|
{ headers: { Accept: "application/json" }, signal },
|
||||||
@@ -995,7 +1001,10 @@ export async function fetchM4ThreatPlaybackManifest(
|
|||||||
exact(manifest.result_id, result, "M4.6 playback result");
|
exact(manifest.result_id, result, "M4.6 playback result");
|
||||||
exact(manifest.coordinate_frame, "map", "M4.6 playback coordinate frame");
|
exact(manifest.coordinate_frame, "map", "M4.6 playback coordinate frame");
|
||||||
exact(manifest.access, "read-only-sealed-binary-playback", "M4.6 playback access");
|
exact(manifest.access, "read-only-sealed-binary-playback", "M4.6 playback access");
|
||||||
const frameCount = exact(integer(manifest.frame_count, "M4.6 playback frames"), 4489, "M4.6 playback frames");
|
const frameCount = integer(manifest.frame_count, "M4.6 playback frames");
|
||||||
|
if (frameCount < 1) {
|
||||||
|
throw new M4ThreatContractError("M4.6 playback frames: пустой playback недопустим.");
|
||||||
|
}
|
||||||
const pointCount = integer(manifest.point_count, "M4.6 playback points");
|
const pointCount = integer(manifest.point_count, "M4.6 playback points");
|
||||||
const offsetsRaw = array(manifest.point_offsets, "M4.6 playback offsets");
|
const offsetsRaw = array(manifest.point_offsets, "M4.6 playback offsets");
|
||||||
if (offsetsRaw.length !== frameCount + 1) {
|
if (offsetsRaw.length !== frameCount + 1) {
|
||||||
@@ -1332,6 +1341,17 @@ function parseTimelineFrame(
|
|||||||
"current-increment",
|
"current-increment",
|
||||||
"M4.6 timeline point layer",
|
"M4.6 timeline point layer",
|
||||||
),
|
),
|
||||||
|
localSlamBodyXyzM: item.local_slam_body_xyz_m === undefined
|
||||||
|
? []
|
||||||
|
: array(item.local_slam_body_xyz_m, "M4.6 local SLAM points").map(
|
||||||
|
(point) => vector(point, 3, "M4.6 local SLAM point") as [number, number, number],
|
||||||
|
),
|
||||||
|
localSlamSourceFrameCount: item.local_slam_source_frame_count === undefined
|
||||||
|
? undefined
|
||||||
|
: integer(item.local_slam_source_frame_count, "M4.6 local SLAM source frames"),
|
||||||
|
localSlamSourcePointCount: item.local_slam_source_point_count === undefined
|
||||||
|
? undefined
|
||||||
|
: integer(item.local_slam_source_point_count, "M4.6 local SLAM source points"),
|
||||||
cameraProjectedPointsXyd: item.camera_projected_points_xyd === undefined
|
cameraProjectedPointsXyd: item.camera_projected_points_xyd === undefined
|
||||||
? []
|
? []
|
||||||
: array(item.camera_projected_points_xyd, "M4.6 camera points").map(
|
: array(item.camera_projected_points_xyd, "M4.6 camera points").map(
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import {
|
||||||
|
LABORATORY_RECORDED_EVIDENCE_VIEWER_PROFILE,
|
||||||
|
type LaboratoryRecordedEvidenceViewerProfile,
|
||||||
|
} from "../observation/viewerProfile";
|
||||||
|
|
||||||
|
export type LaboratoryRecordedMediaMode = "video" | "camera" | null;
|
||||||
|
export type LaboratoryRecordedSpatialMode = "3d" | "plan" | null;
|
||||||
|
export type LaboratoryClassifiedSpatialMode = "none" | "overlay" | "replace-source";
|
||||||
|
|
||||||
|
export interface LaboratoryRecordedEvidenceVisibility {
|
||||||
|
mediaMode: LaboratoryRecordedMediaMode;
|
||||||
|
spatialMode: LaboratoryRecordedSpatialMode;
|
||||||
|
showMediaSemantic: boolean;
|
||||||
|
showSpatialSemantic: boolean;
|
||||||
|
showMediaPoints: boolean;
|
||||||
|
classifiedSpatialMode: LaboratoryClassifiedSpatialMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LaboratoryRecordedEvidenceDemand {
|
||||||
|
sourceTimelineMetadata: true;
|
||||||
|
recordedVideo: boolean;
|
||||||
|
exactCameraFrame: boolean;
|
||||||
|
sourceSpatialPoints: boolean;
|
||||||
|
cameraPointOverlay: boolean;
|
||||||
|
selectedSemanticMask: boolean;
|
||||||
|
selectedSemanticPoints: boolean;
|
||||||
|
classifiedSpatial: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate the already-visible M4 evidence composition into explicit data
|
||||||
|
* demand. The profile never starts a hidden point, semantic or camera channel
|
||||||
|
* merely because the selected LAB happens to publish that artifact.
|
||||||
|
*/
|
||||||
|
export function laboratoryRecordedEvidenceDemand(
|
||||||
|
visibility: LaboratoryRecordedEvidenceVisibility,
|
||||||
|
profile: LaboratoryRecordedEvidenceViewerProfile =
|
||||||
|
LABORATORY_RECORDED_EVIDENCE_VIEWER_PROFILE,
|
||||||
|
): LaboratoryRecordedEvidenceDemand {
|
||||||
|
if (profile.loadPolicy !== "explicit-legacy-comparison-only") {
|
||||||
|
throw new Error("Unsupported LAB recorded evidence load policy");
|
||||||
|
}
|
||||||
|
const mediaVisible = visibility.mediaMode !== null;
|
||||||
|
const spatialVisible = visibility.spatialMode !== null;
|
||||||
|
return {
|
||||||
|
sourceTimelineMetadata: true,
|
||||||
|
recordedVideo: visibility.mediaMode === "video",
|
||||||
|
exactCameraFrame: visibility.mediaMode === "camera",
|
||||||
|
sourceSpatialPoints:
|
||||||
|
spatialVisible && visibility.classifiedSpatialMode !== "replace-source",
|
||||||
|
cameraPointOverlay: mediaVisible && visibility.showMediaPoints,
|
||||||
|
selectedSemanticMask: mediaVisible && visibility.showMediaSemantic,
|
||||||
|
selectedSemanticPoints: spatialVisible && visibility.showSpatialSemantic,
|
||||||
|
classifiedSpatial:
|
||||||
|
spatialVisible && visibility.classifiedSpatialMode !== "none",
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { LaboratoryFetch } from "./advancedResults";
|
import type { LaboratoryFetch } from "./advancedResults";
|
||||||
|
|
||||||
const RESULT_ID = /^lab-v1-vegetation-shadow-[a-f0-9]{64}$/;
|
const RESULT_ID = /^lab-v1-vegetation-shadow-[a-f0-9]{64}$/;
|
||||||
|
const BENCHMARK_RESULT_ID = /^lab-v1-vegetation-benchmark-[a-f0-9]{64}$/;
|
||||||
const SHA256 = /^[a-f0-9]{64}$/;
|
const SHA256 = /^[a-f0-9]{64}$/;
|
||||||
const CANDIDATES = ["ddrnet", "ppliteseg"] as const;
|
const CANDIDATES = ["ddrnet", "ppliteseg"] as const;
|
||||||
const ROUTE_MODES = ["source", "ddrnet", "ppliteseg", "urban", "rural", "offroad"] as const;
|
const ROUTE_MODES = ["source", "ddrnet", "ppliteseg", "urban", "rural", "offroad"] as const;
|
||||||
@@ -55,7 +56,9 @@ export interface VegetationVideoSemanticClass {
|
|||||||
classId: number;
|
classId: number;
|
||||||
label: string;
|
label: string;
|
||||||
colorRgb: readonly [number, number, number];
|
colorRgb: readonly [number, number, number];
|
||||||
disposition: "prediction" | "undefined";
|
disposition: "labeled" | "ambiguous" | "prediction" | "undefined";
|
||||||
|
materialClass: string | null;
|
||||||
|
evidenceState: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VegetationRouteVideo {
|
export interface VegetationRouteVideo {
|
||||||
@@ -67,8 +70,90 @@ export interface VegetationRouteVideo {
|
|||||||
height: 600;
|
height: 600;
|
||||||
centerCropXyxy: readonly [100, 0, 700, 600];
|
centerCropXyxy: readonly [100, 0, 700, 600];
|
||||||
outsideCropState: "undefined";
|
outsideCropState: "undefined";
|
||||||
|
viewKind: "fine-semantic-prediction" | "coarse-material-policy-review";
|
||||||
|
linkedTgsResultId: string | null;
|
||||||
taxonomy: readonly VegetationVideoSemanticClass[];
|
taxonomy: readonly VegetationVideoSemanticClass[];
|
||||||
aggregatePredictionPixels: readonly number[];
|
aggregatePredictionPixels: readonly number[];
|
||||||
|
policyPresets: Readonly<Record<string, Readonly<Record<string, string>>>> | null;
|
||||||
|
fusionMode: "synchronised-multilayer-review" | null;
|
||||||
|
validFovMaskSha256: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VegetationMixedRouteCase {
|
||||||
|
caseId: string;
|
||||||
|
phase: "rural" | "transition" | "urban";
|
||||||
|
sourceSequence: number;
|
||||||
|
sessionSeconds: number;
|
||||||
|
assets: Readonly<Record<"source" | "city" | "vegetation" | "tgs", string>>;
|
||||||
|
tgs: {
|
||||||
|
groundCells: number;
|
||||||
|
occupiedCells: number;
|
||||||
|
rejectedCells: number;
|
||||||
|
unobservedCells: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VegetationMixedRouteReview {
|
||||||
|
sourceId: "RAVNOVES004TREE";
|
||||||
|
sessionId: string;
|
||||||
|
packId: string;
|
||||||
|
frameCount: 10;
|
||||||
|
models: {
|
||||||
|
city: { name: string; inferenceFps: number; endToEndP95Ms: number };
|
||||||
|
vegetation: { name: string; latencyP95Ms: number };
|
||||||
|
tgs: { name: string; latencyP95Ms: number; cellSizeM: number; radiusM: number };
|
||||||
|
};
|
||||||
|
cases: readonly VegetationMixedRouteCase[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VegetationRouteTgsAnchor {
|
||||||
|
sourceSequence: number;
|
||||||
|
slot: number;
|
||||||
|
currentPointsXyzM: readonly (readonly [number, number, number])[];
|
||||||
|
costmap: {
|
||||||
|
cellSizeM: 0.45;
|
||||||
|
centersXyM: readonly (readonly [number, number])[];
|
||||||
|
stateCodes: readonly number[];
|
||||||
|
zBoundsM: readonly (readonly [number | null, number | null])[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VegetationFullRouteLayer {
|
||||||
|
name: string;
|
||||||
|
resultId: string;
|
||||||
|
frameCount: 6830;
|
||||||
|
taxonomy: readonly VegetationVideoSemanticClass[];
|
||||||
|
inferenceFps: number;
|
||||||
|
latencyP95Ms: number;
|
||||||
|
peakReservedVramBytes: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VegetationFullRouteReview {
|
||||||
|
sourceId: "RAVNOVES004TREE";
|
||||||
|
sessionId: "20260828T130511Z_viewer_live";
|
||||||
|
linkedRouteReviewResultId: string;
|
||||||
|
sourceJobId: "recorded-camera-eb2783c5480d56bda07c8af0";
|
||||||
|
sourceJobInputSha256: string;
|
||||||
|
sourceStreamSha256: string;
|
||||||
|
recordedMediaSourceId: "recorded.camera.6a3945242828a038";
|
||||||
|
recordedMediaGenerationSha256: string;
|
||||||
|
frameCount: 6830;
|
||||||
|
width: 800;
|
||||||
|
height: 600;
|
||||||
|
timelineStartSeconds: number;
|
||||||
|
timelineEndSeconds: number;
|
||||||
|
timelineArtifact: {
|
||||||
|
sha256: string;
|
||||||
|
byteLength: number;
|
||||||
|
};
|
||||||
|
frameSourceTimesNs: readonly number[];
|
||||||
|
decodeRepair: {
|
||||||
|
repairedFrameCount: 1;
|
||||||
|
sequence: 6092;
|
||||||
|
method: "duplicate-previous-decoded-frame";
|
||||||
|
};
|
||||||
|
city: VegetationFullRouteLayer;
|
||||||
|
vegetation: VegetationFullRouteLayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VegetationShadowResult {
|
export interface VegetationShadowResult {
|
||||||
@@ -80,6 +165,8 @@ export interface VegetationShadowResult {
|
|||||||
routeCases: readonly VegetationVisualCase[];
|
routeCases: readonly VegetationVisualCase[];
|
||||||
validationCases: readonly VegetationVisualCase[];
|
validationCases: readonly VegetationVisualCase[];
|
||||||
routeVideo: VegetationRouteVideo | null;
|
routeVideo: VegetationRouteVideo | null;
|
||||||
|
routeReview: VegetationMixedRouteReview | null;
|
||||||
|
routeFullReview: VegetationFullRouteReview | null;
|
||||||
limitations: readonly string[];
|
limitations: readonly string[];
|
||||||
visualShadowReady: true;
|
visualShadowReady: true;
|
||||||
missionPolicyReadyForConfiguration: true;
|
missionPolicyReadyForConfiguration: true;
|
||||||
@@ -186,6 +273,7 @@ function visualCaseValue(
|
|||||||
value: unknown,
|
value: unknown,
|
||||||
resultId: string,
|
resultId: string,
|
||||||
expectedKind: "goose" | "ravnoves",
|
expectedKind: "goose" | "ravnoves",
|
||||||
|
endpointRoot: string,
|
||||||
): VegetationVisualCase {
|
): VegetationVisualCase {
|
||||||
const row = objectValue(value, `vegetation.${expectedKind}.case`);
|
const row = objectValue(value, `vegetation.${expectedKind}.case`);
|
||||||
exact(row.source_kind, expectedKind, "vegetation.case.source_kind");
|
exact(row.source_kind, expectedKind, "vegetation.case.source_kind");
|
||||||
@@ -204,7 +292,7 @@ function visualCaseValue(
|
|||||||
if (!SHA256.test(sha256) || !path.startsWith(`visual/${expectedKind}/${caseId}/`)) {
|
if (!SHA256.test(sha256) || !path.startsWith(`visual/${expectedKind}/${caseId}/`)) {
|
||||||
throw new VegetationShadowContractError(`vegetation.case.assets.${key}: proof invalid.`);
|
throw new VegetationShadowContractError(`vegetation.case.assets.${key}: proof invalid.`);
|
||||||
}
|
}
|
||||||
projected[key] = `/api/v1/laboratory/vegetation-shadow/${encodeURIComponent(resultId)}/assets/${path
|
projected[key] = `${endpointRoot}/${encodeURIComponent(resultId)}/assets/${path
|
||||||
.split("/")
|
.split("/")
|
||||||
.map(encodeURIComponent)
|
.map(encodeURIComponent)
|
||||||
.join("/")}`;
|
.join("/")}`;
|
||||||
@@ -257,6 +345,9 @@ function routeVideoValue(value: unknown): VegetationRouteVideo | null {
|
|||||||
"vegetation.route_video.m47_reference_graph_result_id",
|
"vegetation.route_video.m47_reference_graph_result_id",
|
||||||
);
|
);
|
||||||
const baseM4ResultId = textValue(row.base_m4_result_id, "vegetation.route_video.base_m4_result_id");
|
const baseM4ResultId = textValue(row.base_m4_result_id, "vegetation.route_video.base_m4_result_id");
|
||||||
|
const viewKind = row.view_kind === undefined
|
||||||
|
? "fine-semantic-prediction"
|
||||||
|
: textValue(row.view_kind, "vegetation.route_video.view_kind");
|
||||||
if (
|
if (
|
||||||
!/^lab-v1-ravnoves-video-ddrnet-[a-f0-9]{64}$/.test(workerResultId)
|
!/^lab-v1-ravnoves-video-ddrnet-[a-f0-9]{64}$/.test(workerResultId)
|
||||||
|| !/^m47-reference-graph-lab-[a-f0-9]{64}$/.test(m47ReferenceGraphResultId)
|
|| !/^m47-reference-graph-lab-[a-f0-9]{64}$/.test(m47ReferenceGraphResultId)
|
||||||
@@ -264,6 +355,15 @@ function routeVideoValue(value: unknown): VegetationRouteVideo | null {
|
|||||||
) {
|
) {
|
||||||
throw new VegetationShadowContractError("vegetation.route_video: identity invalid.");
|
throw new VegetationShadowContractError("vegetation.route_video: identity invalid.");
|
||||||
}
|
}
|
||||||
|
if (viewKind !== "fine-semantic-prediction" && viewKind !== "coarse-material-policy-review") {
|
||||||
|
throw new VegetationShadowContractError("vegetation.route_video: view kind invalid.");
|
||||||
|
}
|
||||||
|
const linkedTgsResultId = viewKind === "coarse-material-policy-review"
|
||||||
|
? textValue(row.linked_tgs_result_id, "vegetation.route_video.linked_tgs_result_id")
|
||||||
|
: null;
|
||||||
|
if (linkedTgsResultId && !/^m49-tgs-full-shadow-[a-f0-9]{64}$/.test(linkedTgsResultId)) {
|
||||||
|
throw new VegetationShadowContractError("vegetation.route_video: TGS identity invalid.");
|
||||||
|
}
|
||||||
exact(row.frame_count, 4489, "vegetation.route_video.frame_count");
|
exact(row.frame_count, 4489, "vegetation.route_video.frame_count");
|
||||||
exact(row.width, 800, "vegetation.route_video.width");
|
exact(row.width, 800, "vegetation.route_video.width");
|
||||||
exact(row.height, 600, "vegetation.route_video.height");
|
exact(row.height, 600, "vegetation.route_video.height");
|
||||||
@@ -279,11 +379,9 @@ function routeVideoValue(value: unknown): VegetationRouteVideo | null {
|
|||||||
throw new VegetationShadowContractError("vegetation.route_video: crop contract changed.");
|
throw new VegetationShadowContractError("vegetation.route_video: crop contract changed.");
|
||||||
}
|
}
|
||||||
const taxonomy = objectValue(row.taxonomy, "vegetation.route_video.taxonomy");
|
const taxonomy = objectValue(row.taxonomy, "vegetation.route_video.taxonomy");
|
||||||
exact(
|
exact(taxonomy.schema_version, viewKind === "coarse-material-policy-review"
|
||||||
taxonomy.schema_version,
|
? "missioncore.lab-v1-terrain-policy-taxonomy/v1"
|
||||||
"missioncore.lab-v1-vegetation-taxonomy/v1",
|
: "missioncore.lab-v1-vegetation-taxonomy/v1", "vegetation.route_video.taxonomy.schema");
|
||||||
"vegetation.route_video.taxonomy.schema",
|
|
||||||
);
|
|
||||||
const classes = arrayValue(taxonomy.classes, "vegetation.route_video.taxonomy.classes")
|
const classes = arrayValue(taxonomy.classes, "vegetation.route_video.taxonomy.classes")
|
||||||
.map((value, expectedId): VegetationVideoSemanticClass => {
|
.map((value, expectedId): VegetationVideoSemanticClass => {
|
||||||
const item = objectValue(value, `vegetation.route_video.taxonomy[${expectedId}]`);
|
const item = objectValue(value, `vegetation.route_video.taxonomy[${expectedId}]`);
|
||||||
@@ -296,36 +394,95 @@ function routeVideoValue(value: unknown): VegetationRouteVideo | null {
|
|||||||
if (color.length !== 3 || color.some((channel) => channel > 255)) {
|
if (color.length !== 3 || color.some((channel) => channel > 255)) {
|
||||||
throw new VegetationShadowContractError("vegetation.route_video: taxonomy color invalid.");
|
throw new VegetationShadowContractError("vegetation.route_video: taxonomy color invalid.");
|
||||||
}
|
}
|
||||||
const disposition: VegetationVideoSemanticClass["disposition"] = expectedId === 0
|
const disposition = item.disposition;
|
||||||
? "undefined"
|
if (
|
||||||
: "prediction";
|
disposition !== "labeled"
|
||||||
if (item.disposition !== disposition) {
|
&& disposition !== "ambiguous"
|
||||||
|
&& disposition !== "prediction"
|
||||||
|
&& disposition !== "undefined"
|
||||||
|
) {
|
||||||
throw new VegetationShadowContractError("vegetation.route_video: taxonomy disposition changed.");
|
throw new VegetationShadowContractError("vegetation.route_video: taxonomy disposition changed.");
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
viewKind === "fine-semantic-prediction"
|
||||||
|
&& disposition !== (expectedId === 0 ? "undefined" : "prediction")
|
||||||
|
) {
|
||||||
|
throw new VegetationShadowContractError("vegetation.route_video: fine taxonomy disposition changed.");
|
||||||
|
}
|
||||||
|
const materialClass = item.material_class === null || item.material_class === undefined
|
||||||
|
? null
|
||||||
|
: textValue(item.material_class, `vegetation.route_video.material[${expectedId}]`);
|
||||||
|
const evidenceState = item.evidence_state === null || item.evidence_state === undefined
|
||||||
|
? null
|
||||||
|
: textValue(item.evidence_state, `vegetation.route_video.evidence[${expectedId}]`);
|
||||||
return {
|
return {
|
||||||
classId,
|
classId,
|
||||||
label: textValue(item.label, `vegetation.route_video.label[${expectedId}]`),
|
label: textValue(item.label, `vegetation.route_video.label[${expectedId}]`),
|
||||||
colorRgb: color as unknown as readonly [number, number, number],
|
colorRgb: color as unknown as readonly [number, number, number],
|
||||||
disposition,
|
disposition,
|
||||||
|
materialClass,
|
||||||
|
evidenceState,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
if (classes.length !== 64) {
|
const expectedClassCount = viewKind === "coarse-material-policy-review" ? 10 : 64;
|
||||||
throw new VegetationShadowContractError("vegetation.route_video: taxonomy must contain 64 classes.");
|
if (classes.length !== expectedClassCount) {
|
||||||
|
throw new VegetationShadowContractError("vegetation.route_video: taxonomy size changed.");
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
viewKind === "coarse-material-policy-review"
|
||||||
|
&& (classes[9]?.disposition !== "undefined" || classes[9]?.evidenceState !== "UNOBSERVED")
|
||||||
|
) {
|
||||||
|
throw new VegetationShadowContractError("vegetation.route_video: valid-FOV class changed.");
|
||||||
}
|
}
|
||||||
const aggregatePredictionPixels = arrayValue(
|
const aggregatePredictionPixels = arrayValue(
|
||||||
row.aggregate_prediction_pixels,
|
row.aggregate_prediction_pixels,
|
||||||
"vegetation.route_video.aggregate_prediction_pixels",
|
"vegetation.route_video.aggregate_prediction_pixels",
|
||||||
).map((value, index) => integerValue(value, `vegetation.route_video.pixels[${index}]`));
|
).map((value, index) => integerValue(value, `vegetation.route_video.pixels[${index}]`));
|
||||||
if (aggregatePredictionPixels.length !== 64) {
|
if (aggregatePredictionPixels.length !== expectedClassCount) {
|
||||||
throw new VegetationShadowContractError("vegetation.route_video: class accounting changed.");
|
throw new VegetationShadowContractError("vegetation.route_video: class accounting changed.");
|
||||||
}
|
}
|
||||||
const maskArchive = objectValue(row.mask_archive, "vegetation.route_video.mask_archive");
|
const maskArchive = objectValue(row.mask_archive, "vegetation.route_video.mask_archive");
|
||||||
exact(maskArchive.path, "video/ddrnet-semantic-masks.zip", "vegetation.route_video.mask_archive.path");
|
exact(maskArchive.path, viewKind === "coarse-material-policy-review"
|
||||||
|
? "video/coarse-material-policy-masks.zip"
|
||||||
|
: "video/ddrnet-semantic-masks.zip", "vegetation.route_video.mask_archive.path");
|
||||||
const archiveSha256 = textValue(maskArchive.sha256, "vegetation.route_video.mask_archive.sha256");
|
const archiveSha256 = textValue(maskArchive.sha256, "vegetation.route_video.mask_archive.sha256");
|
||||||
if (!SHA256.test(archiveSha256)) {
|
if (!SHA256.test(archiveSha256)) {
|
||||||
throw new VegetationShadowContractError("vegetation.route_video: archive digest invalid.");
|
throw new VegetationShadowContractError("vegetation.route_video: archive digest invalid.");
|
||||||
}
|
}
|
||||||
integerValue(maskArchive.byte_length, "vegetation.route_video.mask_archive.byte_length");
|
integerValue(maskArchive.byte_length, "vegetation.route_video.mask_archive.byte_length");
|
||||||
|
let policyPresets: VegetationRouteVideo["policyPresets"] = null;
|
||||||
|
let fusionMode: VegetationRouteVideo["fusionMode"] = null;
|
||||||
|
let validFovMaskSha256: string | null = null;
|
||||||
|
if (viewKind === "coarse-material-policy-review") {
|
||||||
|
const validFov = objectValue(row.valid_fov, "vegetation.route_video.valid_fov");
|
||||||
|
exact(validFov.mask_path, "video/valid-fov-mask.png", "vegetation.route_video.valid_fov.path");
|
||||||
|
validFovMaskSha256 = textValue(
|
||||||
|
validFov.mask_sha256,
|
||||||
|
"vegetation.route_video.valid_fov.sha256",
|
||||||
|
);
|
||||||
|
if (!SHA256.test(validFovMaskSha256)) {
|
||||||
|
throw new VegetationShadowContractError("vegetation.route_video: valid-FOV digest invalid.");
|
||||||
|
}
|
||||||
|
exact(validFov.outside_valid_fov_class_id, 9, "vegetation.route_video.valid_fov.class_id");
|
||||||
|
const policy = objectValue(row.policy, "vegetation.route_video.policy");
|
||||||
|
const presets = objectValue(policy.presets, "vegetation.route_video.policy.presets");
|
||||||
|
policyPresets = Object.fromEntries(Object.entries(presets).map(([presetId, rawRules]) => {
|
||||||
|
const rules = objectValue(rawRules, `vegetation.route_video.policy.${presetId}`);
|
||||||
|
return [presetId, Object.fromEntries(Object.entries(rules).map(([material, action]) => [
|
||||||
|
material,
|
||||||
|
textValue(action, `vegetation.route_video.policy.${presetId}.${material}`),
|
||||||
|
]))];
|
||||||
|
}));
|
||||||
|
const fusion = objectValue(row.fusion, "vegetation.route_video.fusion");
|
||||||
|
exact(fusion.pixel_raster_fusion, false, "vegetation.route_video.fusion.pixel_raster_fusion");
|
||||||
|
exact(fusion.camera_semantic_temporal_filter, "none", "vegetation.route_video.fusion.camera_filter");
|
||||||
|
exact(
|
||||||
|
fusion.mode,
|
||||||
|
"synchronised-multilayer-review",
|
||||||
|
"vegetation.route_video.fusion.mode",
|
||||||
|
);
|
||||||
|
fusionMode = "synchronised-multilayer-review";
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
workerResultId,
|
workerResultId,
|
||||||
m47ReferenceGraphResultId,
|
m47ReferenceGraphResultId,
|
||||||
@@ -335,12 +492,374 @@ function routeVideoValue(value: unknown): VegetationRouteVideo | null {
|
|||||||
height: 600,
|
height: 600,
|
||||||
centerCropXyxy: [100, 0, 700, 600],
|
centerCropXyxy: [100, 0, 700, 600],
|
||||||
outsideCropState: "undefined",
|
outsideCropState: "undefined",
|
||||||
|
viewKind,
|
||||||
|
linkedTgsResultId,
|
||||||
taxonomy: classes,
|
taxonomy: classes,
|
||||||
aggregatePredictionPixels,
|
aggregatePredictionPixels,
|
||||||
|
policyPresets,
|
||||||
|
fusionMode,
|
||||||
|
validFovMaskSha256,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseResult(value: unknown, resultId: string): VegetationShadowResult {
|
function mixedRouteReviewValue(
|
||||||
|
value: unknown,
|
||||||
|
resultId: string,
|
||||||
|
endpointRoot: string,
|
||||||
|
): VegetationMixedRouteReview | null {
|
||||||
|
if (value === null || value === undefined) return null;
|
||||||
|
const row = objectValue(value, "vegetation.route_review");
|
||||||
|
exact(row.source_id, "RAVNOVES004TREE", "vegetation.route_review.source_id");
|
||||||
|
exact(row.frame_count, 10, "vegetation.route_review.frame_count");
|
||||||
|
exact(row.ground_truth, false, "vegetation.route_review.ground_truth");
|
||||||
|
exact(
|
||||||
|
row.selection_policy,
|
||||||
|
"same-scene-camera-lidar-aligned-review-islands/v1",
|
||||||
|
"vegetation.route_review.selection_policy",
|
||||||
|
);
|
||||||
|
const packId = textValue(row.pack_id, "vegetation.route_review.pack_id");
|
||||||
|
if (!/^mixed-route-review-pack-[a-f0-9]{64}$/.test(packId)) {
|
||||||
|
throw new VegetationShadowContractError("vegetation.route_review.pack_id: identity invalid.");
|
||||||
|
}
|
||||||
|
const models = objectValue(row.models, "vegetation.route_review.models");
|
||||||
|
const city = objectValue(models.city, "vegetation.route_review.models.city");
|
||||||
|
const vegetation = objectValue(models.vegetation, "vegetation.route_review.models.vegetation");
|
||||||
|
const tgsModel = objectValue(models.tgs, "vegetation.route_review.models.tgs");
|
||||||
|
exact(city.frames, 10, "vegetation.route_review.models.city.frames");
|
||||||
|
exact(vegetation.frames, 10, "vegetation.route_review.models.vegetation.frames");
|
||||||
|
exact(tgsModel.frames, 10, "vegetation.route_review.models.tgs.frames");
|
||||||
|
const cases = arrayValue(row.cases, "vegetation.route_review.cases").map((raw, index) => {
|
||||||
|
const item = objectValue(raw, `vegetation.route_review.cases[${index}]`);
|
||||||
|
const caseId = textValue(item.case_id, `vegetation.route_review.cases[${index}].case_id`);
|
||||||
|
if (caseId !== `route-${String(index + 1).padStart(2, "0")}`) {
|
||||||
|
throw new VegetationShadowContractError("vegetation.route_review.case order changed.");
|
||||||
|
}
|
||||||
|
const phaseValue = item.phase;
|
||||||
|
if (phaseValue !== "rural" && phaseValue !== "transition" && phaseValue !== "urban") {
|
||||||
|
throw new VegetationShadowContractError("vegetation.route_review.phase changed.");
|
||||||
|
}
|
||||||
|
const phase: VegetationMixedRouteCase["phase"] = phaseValue;
|
||||||
|
const assets = objectValue(item.assets, `vegetation.route_review.cases[${index}].assets`);
|
||||||
|
const projected = Object.fromEntries(["source", "city", "vegetation", "tgs"].map((key) => {
|
||||||
|
const descriptor = objectValue(assets[key], `vegetation.route_review.assets.${key}`);
|
||||||
|
const path = textValue(descriptor.path, `vegetation.route_review.assets.${key}.path`);
|
||||||
|
const digest = textValue(descriptor.sha256, `vegetation.route_review.assets.${key}.sha256`);
|
||||||
|
if (!SHA256.test(digest) || !path.startsWith(`route-review/${caseId}/`)) {
|
||||||
|
throw new VegetationShadowContractError(`vegetation.route_review.assets.${key}: proof invalid.`);
|
||||||
|
}
|
||||||
|
return [key, `${endpointRoot}/${encodeURIComponent(resultId)}/assets/${path
|
||||||
|
.split("/").map(encodeURIComponent).join("/")}`];
|
||||||
|
})) as Record<"source" | "city" | "vegetation" | "tgs", string>;
|
||||||
|
const tgs = objectValue(item.tgs, `vegetation.route_review.cases[${index}].tgs`);
|
||||||
|
const groundCells = integerValue(tgs.ground_cells, "vegetation.route_review.tgs.ground");
|
||||||
|
const occupiedCells = integerValue(tgs.occupied_cells, "vegetation.route_review.tgs.occupied");
|
||||||
|
const rejectedCells = integerValue(tgs.rejected_cells, "vegetation.route_review.tgs.rejected");
|
||||||
|
const unobservedCells = integerValue(tgs.unobserved_cells, "vegetation.route_review.tgs.unobserved");
|
||||||
|
if (groundCells + occupiedCells + rejectedCells + unobservedCells !== 2244) {
|
||||||
|
throw new VegetationShadowContractError("vegetation.route_review.tgs cell accounting changed.");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
caseId,
|
||||||
|
phase,
|
||||||
|
sourceSequence: integerValue(item.source_sequence, "vegetation.route_review.source_sequence"),
|
||||||
|
sessionSeconds: numberValue(item.session_seconds, "vegetation.route_review.session_seconds"),
|
||||||
|
assets: projected,
|
||||||
|
tgs: { groundCells, occupiedCells, rejectedCells, unobservedCells },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
if (cases.length !== 10) {
|
||||||
|
throw new VegetationShadowContractError("vegetation.route_review.cases: expected 10 aligned islands.");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
sourceId: "RAVNOVES004TREE",
|
||||||
|
sessionId: textValue(row.session_id, "vegetation.route_review.session_id"),
|
||||||
|
packId,
|
||||||
|
frameCount: 10,
|
||||||
|
models: {
|
||||||
|
city: {
|
||||||
|
name: textValue(city.name, "vegetation.route_review.models.city.name"),
|
||||||
|
inferenceFps: numberValue(city.inference_fps, "vegetation.route_review.models.city.fps"),
|
||||||
|
endToEndP95Ms: numberValue(city.end_to_end_p95_ms, "vegetation.route_review.models.city.p95"),
|
||||||
|
},
|
||||||
|
vegetation: {
|
||||||
|
name: textValue(vegetation.name, "vegetation.route_review.models.vegetation.name"),
|
||||||
|
latencyP95Ms: numberValue(vegetation.latency_p95_ms, "vegetation.route_review.models.vegetation.p95"),
|
||||||
|
},
|
||||||
|
tgs: {
|
||||||
|
name: textValue(tgsModel.name, "vegetation.route_review.models.tgs.name"),
|
||||||
|
latencyP95Ms: numberValue(tgsModel.latency_p95_ms, "vegetation.route_review.models.tgs.p95"),
|
||||||
|
cellSizeM: numberValue(tgsModel.cell_size_m, "vegetation.route_review.models.tgs.cell"),
|
||||||
|
radiusM: numberValue(tgsModel.radius_m, "vegetation.route_review.models.tgs.radius"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
cases,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function fullRouteTaxonomyValue(
|
||||||
|
value: unknown,
|
||||||
|
label: string,
|
||||||
|
schema: string,
|
||||||
|
classCount: number,
|
||||||
|
): readonly VegetationVideoSemanticClass[] {
|
||||||
|
const taxonomy = objectValue(value, `${label}.taxonomy`);
|
||||||
|
exact(taxonomy.schema_version, schema, `${label}.taxonomy.schema`);
|
||||||
|
const classes = arrayValue(taxonomy.classes, `${label}.taxonomy.classes`).map(
|
||||||
|
(raw, expectedId): VegetationVideoSemanticClass => {
|
||||||
|
const item = objectValue(raw, `${label}.taxonomy[${expectedId}]`);
|
||||||
|
const classId = integerValue(item.class_id, `${label}.class_id[${expectedId}]`);
|
||||||
|
if (classId !== expectedId) {
|
||||||
|
throw new VegetationShadowContractError(`${label}: taxonomy order changed.`);
|
||||||
|
}
|
||||||
|
const color = arrayValue(item.color_rgb, `${label}.color[${expectedId}]`)
|
||||||
|
.map((channel, index) => integerValue(channel, `${label}.color[${expectedId}][${index}]`));
|
||||||
|
if (color.length !== 3 || color.some((channel) => channel > 255)) {
|
||||||
|
throw new VegetationShadowContractError(`${label}: taxonomy color invalid.`);
|
||||||
|
}
|
||||||
|
const disposition = item.disposition;
|
||||||
|
if (
|
||||||
|
disposition !== "labeled"
|
||||||
|
&& disposition !== "ambiguous"
|
||||||
|
&& disposition !== "prediction"
|
||||||
|
&& disposition !== "undefined"
|
||||||
|
) {
|
||||||
|
throw new VegetationShadowContractError(`${label}: taxonomy disposition changed.`);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
classId,
|
||||||
|
label: textValue(item.label, `${label}.label[${expectedId}]`),
|
||||||
|
colorRgb: color as unknown as readonly [number, number, number],
|
||||||
|
disposition,
|
||||||
|
materialClass: item.material_class === null || item.material_class === undefined
|
||||||
|
? null
|
||||||
|
: textValue(item.material_class, `${label}.material[${expectedId}]`),
|
||||||
|
evidenceState: item.evidence_state === null || item.evidence_state === undefined
|
||||||
|
? null
|
||||||
|
: textValue(item.evidence_state, `${label}.evidence[${expectedId}]`),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (classes.length !== classCount) {
|
||||||
|
throw new VegetationShadowContractError(`${label}: taxonomy size changed.`);
|
||||||
|
}
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fullRouteLayerValue(
|
||||||
|
value: unknown,
|
||||||
|
layer: "city" | "vegetation",
|
||||||
|
): VegetationFullRouteLayer {
|
||||||
|
const label = `vegetation.route_full_review.layers.${layer}`;
|
||||||
|
const row = objectValue(value, label);
|
||||||
|
const resultId = textValue(row.result_id, `${label}.result_id`);
|
||||||
|
const identity = layer === "city"
|
||||||
|
? /^result-[a-f0-9]{64}$/
|
||||||
|
: /^lab-v1-ravnoves-video-ddrnet-[a-f0-9]{64}$/;
|
||||||
|
if (!identity.test(resultId)) {
|
||||||
|
throw new VegetationShadowContractError(`${label}: identity invalid.`);
|
||||||
|
}
|
||||||
|
exact(row.frame_count, 6830, `${label}.frame_count`);
|
||||||
|
const archive = objectValue(row.mask_archive, `${label}.mask_archive`);
|
||||||
|
exact(
|
||||||
|
archive.path,
|
||||||
|
layer === "city" ? "video/eomt-semantic-masks.zip" : "video/ddrnet-semantic-masks.zip",
|
||||||
|
`${label}.mask_archive.path`,
|
||||||
|
);
|
||||||
|
const digest = textValue(archive.sha256, `${label}.mask_archive.sha256`);
|
||||||
|
if (!SHA256.test(digest)) {
|
||||||
|
throw new VegetationShadowContractError(`${label}: archive digest invalid.`);
|
||||||
|
}
|
||||||
|
integerValue(archive.byte_length, `${label}.mask_archive.byte_length`);
|
||||||
|
return {
|
||||||
|
name: textValue(row.name, `${label}.name`),
|
||||||
|
resultId,
|
||||||
|
frameCount: 6830,
|
||||||
|
taxonomy: fullRouteTaxonomyValue(
|
||||||
|
row.taxonomy,
|
||||||
|
label,
|
||||||
|
layer === "city"
|
||||||
|
? "missioncore.recorded-eomt-taxonomy/v1"
|
||||||
|
: "missioncore.lab-v1-vegetation-taxonomy/v1",
|
||||||
|
layer === "city" ? 16 : 64,
|
||||||
|
),
|
||||||
|
inferenceFps: numberValue(row.inference_fps, `${label}.inference_fps`),
|
||||||
|
latencyP95Ms: numberValue(row.latency_p95_ms, `${label}.latency_p95_ms`),
|
||||||
|
peakReservedVramBytes: integerValue(
|
||||||
|
row.peak_reserved_vram_bytes,
|
||||||
|
`${label}.peak_reserved_vram_bytes`,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function fullRouteReviewValue(value: unknown): VegetationFullRouteReview | null {
|
||||||
|
if (value === null || value === undefined) return null;
|
||||||
|
const row = objectValue(value, "vegetation.route_full_review");
|
||||||
|
exact(row.source_id, "RAVNOVES004TREE", "vegetation.route_full_review.source_id");
|
||||||
|
exact(
|
||||||
|
row.session_id,
|
||||||
|
"20260828T130511Z_viewer_live",
|
||||||
|
"vegetation.route_full_review.session_id",
|
||||||
|
);
|
||||||
|
exact(
|
||||||
|
row.source_job_id,
|
||||||
|
"recorded-camera-eb2783c5480d56bda07c8af0",
|
||||||
|
"vegetation.route_full_review.source_job_id",
|
||||||
|
);
|
||||||
|
const linkedRouteReviewResultId = textValue(
|
||||||
|
row.linked_route_review_result_id,
|
||||||
|
"vegetation.route_full_review.linked_route_review_result_id",
|
||||||
|
);
|
||||||
|
if (!RESULT_ID.test(linkedRouteReviewResultId)) {
|
||||||
|
throw new VegetationShadowContractError(
|
||||||
|
"vegetation.route_full_review: linked route review identity invalid.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
exact(row.frame_count, 6830, "vegetation.route_full_review.frame_count");
|
||||||
|
exact(row.width, 800, "vegetation.route_full_review.width");
|
||||||
|
exact(row.height, 600, "vegetation.route_full_review.height");
|
||||||
|
exact(row.ground_truth, false, "vegetation.route_full_review.ground_truth");
|
||||||
|
const sourceJobInputSha256 = textValue(
|
||||||
|
row.source_job_input_sha256,
|
||||||
|
"vegetation.route_full_review.source_job_input_sha256",
|
||||||
|
);
|
||||||
|
const sourceStreamSha256 = textValue(
|
||||||
|
row.source_stream_sha256,
|
||||||
|
"vegetation.route_full_review.source_stream_sha256",
|
||||||
|
);
|
||||||
|
exact(
|
||||||
|
sourceJobInputSha256,
|
||||||
|
"eb2783c5480d56bda07c8af008dff5344d19dc550ef70fe2075d6f098f7cc715",
|
||||||
|
"vegetation.route_full_review.source_job_input_sha256",
|
||||||
|
);
|
||||||
|
exact(
|
||||||
|
sourceStreamSha256,
|
||||||
|
"e5eb017e2cc0f546736eda5235ca157b501913093cb64af5e548e335417e1bac",
|
||||||
|
"vegetation.route_full_review.source_stream_sha256",
|
||||||
|
);
|
||||||
|
exact(
|
||||||
|
row.recorded_media_source_id,
|
||||||
|
"recorded.camera.6a3945242828a038",
|
||||||
|
"vegetation.route_full_review.recorded_media_source_id",
|
||||||
|
);
|
||||||
|
const recordedMediaGenerationSha256 = textValue(
|
||||||
|
row.recorded_media_generation_sha256,
|
||||||
|
"vegetation.route_full_review.recorded_media_generation_sha256",
|
||||||
|
);
|
||||||
|
exact(
|
||||||
|
recordedMediaGenerationSha256,
|
||||||
|
"b073ea1e7babf1c77a664e1a5b95e3702d0e05b0e34c1e85a7c67a6f8b392ded",
|
||||||
|
"vegetation.route_full_review.recorded_media_generation_sha256",
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
!SHA256.test(sourceJobInputSha256)
|
||||||
|
|| !SHA256.test(sourceStreamSha256)
|
||||||
|
|| !SHA256.test(recordedMediaGenerationSha256)
|
||||||
|
) {
|
||||||
|
throw new VegetationShadowContractError("vegetation.route_full_review: source digest invalid.");
|
||||||
|
}
|
||||||
|
const timelineStartSeconds = numberValue(
|
||||||
|
row.timeline_start_seconds,
|
||||||
|
"vegetation.route_full_review.timeline_start_seconds",
|
||||||
|
);
|
||||||
|
const timelineEndSeconds = numberValue(
|
||||||
|
row.timeline_end_seconds,
|
||||||
|
"vegetation.route_full_review.timeline_end_seconds",
|
||||||
|
);
|
||||||
|
if (timelineEndSeconds <= timelineStartSeconds) {
|
||||||
|
throw new VegetationShadowContractError("vegetation.route_full_review: timeline invalid.");
|
||||||
|
}
|
||||||
|
const timeline = objectValue(row.timeline, "vegetation.route_full_review.timeline");
|
||||||
|
exact(
|
||||||
|
timeline.path,
|
||||||
|
"video/frame-source-times-ns.bin",
|
||||||
|
"vegetation.route_full_review.timeline.path",
|
||||||
|
);
|
||||||
|
exact(
|
||||||
|
timeline.encoding,
|
||||||
|
"uint64-le-nanoseconds",
|
||||||
|
"vegetation.route_full_review.timeline.encoding",
|
||||||
|
);
|
||||||
|
exact(timeline.frame_count, 6830, "vegetation.route_full_review.timeline.frame_count");
|
||||||
|
const timelineSha256 = textValue(
|
||||||
|
timeline.sha256,
|
||||||
|
"vegetation.route_full_review.timeline.sha256",
|
||||||
|
);
|
||||||
|
if (!SHA256.test(timelineSha256)) {
|
||||||
|
throw new VegetationShadowContractError("vegetation.route_full_review: timeline digest invalid.");
|
||||||
|
}
|
||||||
|
const timelineByteLength = integerValue(
|
||||||
|
timeline.byte_length,
|
||||||
|
"vegetation.route_full_review.timeline.byte_length",
|
||||||
|
);
|
||||||
|
exact(timelineByteLength, 6830 * 8, "vegetation.route_full_review.timeline.byte_length");
|
||||||
|
const decodeRepair = objectValue(
|
||||||
|
row.decode_repair,
|
||||||
|
"vegetation.route_full_review.decode_repair",
|
||||||
|
);
|
||||||
|
exact(decodeRepair.repaired_frame_count, 1, "vegetation.route_full_review.decode_repair.count");
|
||||||
|
exact(decodeRepair.sequence, 6092, "vegetation.route_full_review.decode_repair.sequence");
|
||||||
|
exact(
|
||||||
|
decodeRepair.method,
|
||||||
|
"duplicate-previous-decoded-frame",
|
||||||
|
"vegetation.route_full_review.decode_repair.method",
|
||||||
|
);
|
||||||
|
const repairProofs = objectValue(
|
||||||
|
decodeRepair.proofs,
|
||||||
|
"vegetation.route_full_review.decode_repair.proofs",
|
||||||
|
);
|
||||||
|
for (const [key, expectedPath] of Object.entries({
|
||||||
|
eomt: "proofs/decode_repair.json",
|
||||||
|
ddrnet: "proofs/ddrnet_decode_repair.json",
|
||||||
|
})) {
|
||||||
|
const proof = objectValue(
|
||||||
|
repairProofs[key],
|
||||||
|
`vegetation.route_full_review.decode_repair.proofs.${key}`,
|
||||||
|
);
|
||||||
|
exact(
|
||||||
|
proof.path,
|
||||||
|
expectedPath,
|
||||||
|
`vegetation.route_full_review.decode_repair.proofs.${key}.path`,
|
||||||
|
);
|
||||||
|
const digest = textValue(
|
||||||
|
proof.sha256,
|
||||||
|
`vegetation.route_full_review.decode_repair.proofs.${key}.sha256`,
|
||||||
|
);
|
||||||
|
if (!SHA256.test(digest)) {
|
||||||
|
throw new VegetationShadowContractError("vegetation.route_full_review: repair proof invalid.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const layers = objectValue(row.layers, "vegetation.route_full_review.layers");
|
||||||
|
return {
|
||||||
|
sourceId: "RAVNOVES004TREE",
|
||||||
|
sessionId: "20260828T130511Z_viewer_live",
|
||||||
|
linkedRouteReviewResultId,
|
||||||
|
sourceJobId: "recorded-camera-eb2783c5480d56bda07c8af0",
|
||||||
|
sourceJobInputSha256,
|
||||||
|
sourceStreamSha256,
|
||||||
|
recordedMediaSourceId: "recorded.camera.6a3945242828a038",
|
||||||
|
recordedMediaGenerationSha256,
|
||||||
|
frameCount: 6830,
|
||||||
|
width: 800,
|
||||||
|
height: 600,
|
||||||
|
timelineStartSeconds,
|
||||||
|
timelineEndSeconds,
|
||||||
|
timelineArtifact: { sha256: timelineSha256, byteLength: timelineByteLength },
|
||||||
|
frameSourceTimesNs: [],
|
||||||
|
decodeRepair: {
|
||||||
|
repairedFrameCount: 1,
|
||||||
|
sequence: 6092,
|
||||||
|
method: "duplicate-previous-decoded-frame",
|
||||||
|
},
|
||||||
|
city: fullRouteLayerValue(layers.city, "city"),
|
||||||
|
vegetation: fullRouteLayerValue(layers.vegetation, "vegetation"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseResult(
|
||||||
|
value: unknown,
|
||||||
|
resultId: string,
|
||||||
|
endpointRoot: string,
|
||||||
|
): VegetationShadowResult {
|
||||||
const payload = objectValue(value, "Vegetation LAB");
|
const payload = objectValue(value, "Vegetation LAB");
|
||||||
exact(payload.schema_version, "missioncore.lab-v1-vegetation-shadow/v1", "vegetation.schema");
|
exact(payload.schema_version, "missioncore.lab-v1-vegetation-shadow/v1", "vegetation.schema");
|
||||||
exact(payload.result_id, resultId, "vegetation.result_id");
|
exact(payload.result_id, resultId, "vegetation.result_id");
|
||||||
@@ -376,10 +895,15 @@ function parseResult(value: unknown, resultId: string): VegetationShadowResult {
|
|||||||
"vegetation.authority.camera_semantics_can_clear_rigid_geometry",
|
"vegetation.authority.camera_semantics_can_clear_rigid_geometry",
|
||||||
);
|
);
|
||||||
const routeCases = arrayValue(catalogs.ravnoves, "vegetation.catalogs.ravnoves")
|
const routeCases = arrayValue(catalogs.ravnoves, "vegetation.catalogs.ravnoves")
|
||||||
.map((item) => visualCaseValue(item, resultId, "ravnoves"));
|
.map((item) => visualCaseValue(item, resultId, "ravnoves", endpointRoot));
|
||||||
const validationCases = arrayValue(catalogs.goose, "vegetation.catalogs.goose")
|
const validationCases = arrayValue(catalogs.goose, "vegetation.catalogs.goose")
|
||||||
.map((item) => visualCaseValue(item, resultId, "goose"));
|
.map((item) => visualCaseValue(item, resultId, "goose", endpointRoot));
|
||||||
if (routeCases.length !== 0 || validationCases.length !== 12) {
|
const routeReview = mixedRouteReviewValue(payload.route_review, resultId, endpointRoot);
|
||||||
|
const routeFullReview = fullRouteReviewValue(payload.route_full_review);
|
||||||
|
if (
|
||||||
|
routeCases.length !== 0
|
||||||
|
|| (routeReview || routeFullReview ? validationCases.length !== 0 : validationCases.length !== 12)
|
||||||
|
) {
|
||||||
throw new VegetationShadowContractError("vegetation.catalogs: ожидалось 12 truth-backed GOOSE случаев без route viewer.");
|
throw new VegetationShadowContractError("vegetation.catalogs: ожидалось 12 truth-backed GOOSE случаев без route viewer.");
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -391,6 +915,8 @@ function parseResult(value: unknown, resultId: string): VegetationShadowResult {
|
|||||||
routeCases,
|
routeCases,
|
||||||
validationCases,
|
validationCases,
|
||||||
routeVideo: routeVideoValue(payload.route_video),
|
routeVideo: routeVideoValue(payload.route_video),
|
||||||
|
routeReview,
|
||||||
|
routeFullReview,
|
||||||
limitations: arrayValue(payload.limitations, "vegetation.limitations")
|
limitations: arrayValue(payload.limitations, "vegetation.limitations")
|
||||||
.map((item, index) => textValue(item, `vegetation.limitations[${index}]`)),
|
.map((item, index) => textValue(item, `vegetation.limitations[${index}]`)),
|
||||||
visualShadowReady: true,
|
visualShadowReady: true,
|
||||||
@@ -411,7 +937,96 @@ export function vegetationVideoMaskUrl(resultId: string, sequence: number): stri
|
|||||||
return `/api/v1/laboratory/vegetation-shadow/${encodeURIComponent(resultId)}/masks/${sequence}`;
|
return `/api/v1/laboratory/vegetation-shadow/${encodeURIComponent(resultId)}/masks/${sequence}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchVegetationShadowResult(
|
export function vegetationFullRouteMaskUrl(
|
||||||
|
resultId: string,
|
||||||
|
layer: "city" | "vegetation",
|
||||||
|
sequence: number,
|
||||||
|
): string {
|
||||||
|
if (
|
||||||
|
!RESULT_ID.test(resultId)
|
||||||
|
|| (layer !== "city" && layer !== "vegetation")
|
||||||
|
|| !Number.isInteger(sequence)
|
||||||
|
|| sequence < 0
|
||||||
|
|| sequence >= 6830
|
||||||
|
) {
|
||||||
|
throw new VegetationShadowContractError("Vegetation full-route mask identity недопустима.");
|
||||||
|
}
|
||||||
|
return `/api/v1/laboratory/vegetation-shadow/${encodeURIComponent(resultId)}/route-masks/${layer}/${sequence}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchVegetationRouteTgsAnchor(
|
||||||
|
resultId: string,
|
||||||
|
sourceSequence: number,
|
||||||
|
{
|
||||||
|
fetcher = fetch,
|
||||||
|
signal,
|
||||||
|
}: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
|
||||||
|
): Promise<VegetationRouteTgsAnchor> {
|
||||||
|
if (!RESULT_ID.test(resultId) || !Number.isInteger(sourceSequence) || sourceSequence < 1) {
|
||||||
|
throw new VegetationShadowContractError("Vegetation TGS anchor identity недопустима.");
|
||||||
|
}
|
||||||
|
const response = await fetcher(
|
||||||
|
`/api/v1/laboratory/vegetation-shadow/${encodeURIComponent(resultId)}`
|
||||||
|
+ `/route-tgs-anchor/${sourceSequence}`,
|
||||||
|
{ method: "GET", headers: { Accept: "application/json" }, signal },
|
||||||
|
);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new VegetationShadowContractError(`Vegetation TGS anchor недоступен: HTTP ${response.status}.`);
|
||||||
|
}
|
||||||
|
const payload = objectValue(await response.json(), "vegetation.route_tgs_anchor");
|
||||||
|
exact(
|
||||||
|
payload.schema_version,
|
||||||
|
"missioncore.lab-v1-route-tgs-anchor/v1",
|
||||||
|
"vegetation.route_tgs_anchor.schema_version",
|
||||||
|
);
|
||||||
|
exact(payload.source_sequence, sourceSequence, "vegetation.route_tgs_anchor.source_sequence");
|
||||||
|
const pointValue = (value: unknown, label: string): readonly number[] => {
|
||||||
|
const point = arrayValue(value, label).map((item, index) => numberValue(item, `${label}[${index}]`));
|
||||||
|
if (point.length !== 2 && point.length !== 3) {
|
||||||
|
throw new VegetationShadowContractError(`${label}: размер изменён.`);
|
||||||
|
}
|
||||||
|
return point;
|
||||||
|
};
|
||||||
|
const points = arrayValue(payload.current_points_xyz_m, "vegetation.route_tgs_anchor.points")
|
||||||
|
.map((value, index) => pointValue(value, `vegetation.route_tgs_anchor.points[${index}]`));
|
||||||
|
const costmap = objectValue(payload.costmap, "vegetation.route_tgs_anchor.costmap");
|
||||||
|
exact(costmap.cell_size_m, 0.45, "vegetation.route_tgs_anchor.costmap.cell_size_m");
|
||||||
|
const centers = arrayValue(costmap.centers_xy_m, "vegetation.route_tgs_anchor.costmap.centers")
|
||||||
|
.map((value, index) => pointValue(value, `vegetation.route_tgs_anchor.costmap.centers[${index}]`));
|
||||||
|
const stateCodes = arrayValue(costmap.state_codes, "vegetation.route_tgs_anchor.costmap.states")
|
||||||
|
.map((value, index) => integerValue(value, `vegetation.route_tgs_anchor.costmap.states[${index}]`));
|
||||||
|
const zBounds = arrayValue(costmap.z_bounds_m, "vegetation.route_tgs_anchor.costmap.z_bounds")
|
||||||
|
.map((value, index) => {
|
||||||
|
const row = arrayValue(value, `vegetation.route_tgs_anchor.costmap.z_bounds[${index}]`);
|
||||||
|
if (row.length !== 2 || row.some((item) => item !== null && (typeof item !== "number" || !Number.isFinite(item)))) {
|
||||||
|
throw new VegetationShadowContractError("vegetation.route_tgs_anchor.costmap.z_bounds: контракт изменён.");
|
||||||
|
}
|
||||||
|
return row as readonly [number | null, number | null];
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
centers.length !== 2244
|
||||||
|
|| stateCodes.length !== 2244
|
||||||
|
|| zBounds.length !== 2244
|
||||||
|
|| stateCodes.some((value) => value > 3)
|
||||||
|
|| points.some((point) => point.length !== 3)
|
||||||
|
|| centers.some((point) => point.length !== 2)
|
||||||
|
) {
|
||||||
|
throw new VegetationShadowContractError("Vegetation TGS anchor shape изменён.");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
sourceSequence,
|
||||||
|
slot: integerValue(payload.slot, "vegetation.route_tgs_anchor.slot"),
|
||||||
|
currentPointsXyzM: points.map((point) => [point[0]!, point[1]!, point[2]!] as const),
|
||||||
|
costmap: {
|
||||||
|
cellSizeM: 0.45,
|
||||||
|
centersXyM: centers.map((point) => [point[0]!, point[1]!] as const),
|
||||||
|
stateCodes,
|
||||||
|
zBoundsM: zBounds,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchVegetationShadowResultMetadata(
|
||||||
resultId: string,
|
resultId: string,
|
||||||
{
|
{
|
||||||
fetcher = fetch,
|
fetcher = fetch,
|
||||||
@@ -428,5 +1043,85 @@ export async function fetchVegetationShadowResult(
|
|||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new VegetationShadowContractError(`Vegetation LAB недоступна: HTTP ${response.status}.`);
|
throw new VegetationShadowContractError(`Vegetation LAB недоступна: HTTP ${response.status}.`);
|
||||||
}
|
}
|
||||||
return parseResult(await response.json(), resultId);
|
const result = parseResult(
|
||||||
|
await response.json(),
|
||||||
|
resultId,
|
||||||
|
"/api/v1/laboratory/vegetation-shadow",
|
||||||
|
);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchVegetationShadowResult(
|
||||||
|
resultId: string,
|
||||||
|
{
|
||||||
|
fetcher = fetch,
|
||||||
|
signal,
|
||||||
|
}: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
|
||||||
|
): Promise<VegetationShadowResult> {
|
||||||
|
const result = await fetchVegetationShadowResultMetadata(resultId, { fetcher, signal });
|
||||||
|
if (!result.routeFullReview) return result;
|
||||||
|
const timelineResponse = await fetcher(
|
||||||
|
`/api/v1/laboratory/vegetation-shadow/${encodeURIComponent(resultId)}/route-timeline`,
|
||||||
|
{ method: "GET", headers: { Accept: "application/octet-stream" }, signal },
|
||||||
|
);
|
||||||
|
if (!timelineResponse.ok) {
|
||||||
|
throw new VegetationShadowContractError(
|
||||||
|
`Vegetation LAB timeline недоступна: HTTP ${timelineResponse.status}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
timelineResponse.headers.get("etag")
|
||||||
|
!== `"${result.routeFullReview.timelineArtifact.sha256}"`
|
||||||
|
) {
|
||||||
|
throw new VegetationShadowContractError("Vegetation LAB timeline digest изменён.");
|
||||||
|
}
|
||||||
|
const timelinePayload = await timelineResponse.arrayBuffer();
|
||||||
|
if (timelinePayload.byteLength !== result.routeFullReview.timelineArtifact.byteLength) {
|
||||||
|
throw new VegetationShadowContractError("Vegetation LAB timeline size изменён.");
|
||||||
|
}
|
||||||
|
const timelineView = new DataView(timelinePayload);
|
||||||
|
const frameSourceTimesNs = Array.from({ length: result.routeFullReview.frameCount }, (_, index) => {
|
||||||
|
const value = Number(timelineView.getBigUint64(index * 8, true));
|
||||||
|
if (!Number.isSafeInteger(value)) {
|
||||||
|
throw new VegetationShadowContractError("Vegetation LAB timeline содержит unsafe time.");
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
frameSourceTimesNs[0] !== Math.round(result.routeFullReview.timelineStartSeconds * 1_000_000_000)
|
||||||
|
|| frameSourceTimesNs.some((time, index) => index > 0 && time <= frameSourceTimesNs[index - 1]!)
|
||||||
|
) {
|
||||||
|
throw new VegetationShadowContractError("Vegetation LAB timeline нарушена.");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
routeFullReview: { ...result.routeFullReview, frameSourceTimesNs },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchVegetationBenchmarkResult(
|
||||||
|
resultId: string,
|
||||||
|
{
|
||||||
|
fetcher = fetch,
|
||||||
|
signal,
|
||||||
|
}: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
|
||||||
|
): Promise<VegetationShadowResult> {
|
||||||
|
if (!BENCHMARK_RESULT_ID.test(resultId)) {
|
||||||
|
throw new VegetationShadowContractError("Vegetation benchmark identity недопустима.");
|
||||||
|
}
|
||||||
|
const endpointRoot = "/api/v1/laboratory/vegetation-benchmark";
|
||||||
|
const response = await fetcher(
|
||||||
|
`${endpointRoot}/${encodeURIComponent(resultId)}`,
|
||||||
|
{ method: "GET", headers: { Accept: "application/json" }, signal },
|
||||||
|
);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new VegetationShadowContractError(
|
||||||
|
`Vegetation benchmark недоступен: HTTP ${response.status}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const result = parseResult(await response.json(), resultId, endpointRoot);
|
||||||
|
if (result.routeVideo) {
|
||||||
|
throw new VegetationShadowContractError("Vegetation benchmark содержит route video.");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
export const OBSERVATION_LAB_CALCULATION_PROFILE_SCHEMA =
|
||||||
|
"missioncore.observatory-calculation-profile/v1" as const;
|
||||||
|
|
||||||
|
export type ObservationLabCalculationProfileOrigin =
|
||||||
|
| "archived-definition"
|
||||||
|
| "existing-result";
|
||||||
|
|
||||||
|
export interface ObservationLabCalculationProfile {
|
||||||
|
readonly schemaVersion: typeof OBSERVATION_LAB_CALCULATION_PROFILE_SCHEMA;
|
||||||
|
readonly setupId: string;
|
||||||
|
readonly displayName: string;
|
||||||
|
readonly origin: ObservationLabCalculationProfileOrigin;
|
||||||
|
readonly definitionId: string | null;
|
||||||
|
readonly definitionVersion: number | null;
|
||||||
|
readonly definitionSha256: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ObservationLabCalculationProfileContractError extends Error {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
this.name = "ObservationLabCalculationProfileContractError";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PROFILE_KEYS = new Set([
|
||||||
|
"schema_version",
|
||||||
|
"setup_id",
|
||||||
|
"display_name",
|
||||||
|
"origin",
|
||||||
|
"definition_id",
|
||||||
|
"definition_version",
|
||||||
|
"definition_sha256",
|
||||||
|
]);
|
||||||
|
const IDENTIFIER = /^[a-z][a-z0-9-]{2,95}$/;
|
||||||
|
const SHA256 = /^[a-f0-9]{64}$/;
|
||||||
|
|
||||||
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function requireText(value: unknown, field: string, maximum: number): string {
|
||||||
|
if (
|
||||||
|
typeof value !== "string"
|
||||||
|
|| value.length === 0
|
||||||
|
|| value.length > maximum
|
||||||
|
|| value !== value.trim()
|
||||||
|
) {
|
||||||
|
throw new ObservationLabCalculationProfileContractError(
|
||||||
|
`Поле calculation_profile.${field} должно быть непустой строкой.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function requireIdentifier(value: unknown, field: string): string {
|
||||||
|
const identifier = requireText(value, field, 96);
|
||||||
|
if (!IDENTIFIER.test(identifier)) {
|
||||||
|
throw new ObservationLabCalculationProfileContractError(
|
||||||
|
`Поле calculation_profile.${field} содержит некорректный идентификатор.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeObservationLabCalculationProfile(
|
||||||
|
value: unknown,
|
||||||
|
sessionId: string,
|
||||||
|
): ObservationLabCalculationProfile | null {
|
||||||
|
if (value === null) return null;
|
||||||
|
if (!isRecord(value) || Object.keys(value).some((key) => !PROFILE_KEYS.has(key))) {
|
||||||
|
throw new ObservationLabCalculationProfileContractError(
|
||||||
|
`Профиль расчёта LAB-сессии ${sessionId} нарушил контракт полей.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
Object.keys(value).length !== PROFILE_KEYS.size
|
||||||
|
|| value.schema_version !== OBSERVATION_LAB_CALCULATION_PROFILE_SCHEMA
|
||||||
|
) {
|
||||||
|
throw new ObservationLabCalculationProfileContractError(
|
||||||
|
`Профиль расчёта LAB-сессии ${sessionId} имеет неизвестную версию.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const origin = value.origin;
|
||||||
|
if (origin !== "archived-definition" && origin !== "existing-result") {
|
||||||
|
throw new ObservationLabCalculationProfileContractError(
|
||||||
|
`Профиль расчёта LAB-сессии ${sessionId} имеет неизвестное происхождение.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const definitionId = value.definition_id === null
|
||||||
|
? null
|
||||||
|
: requireIdentifier(value.definition_id, "definition_id");
|
||||||
|
const definitionVersion = value.definition_version === null
|
||||||
|
? null
|
||||||
|
: value.definition_version;
|
||||||
|
if (
|
||||||
|
definitionVersion !== null
|
||||||
|
&& (
|
||||||
|
typeof definitionVersion !== "number"
|
||||||
|
|| !Number.isInteger(definitionVersion)
|
||||||
|
|| definitionVersion < 1
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new ObservationLabCalculationProfileContractError(
|
||||||
|
`Профиль расчёта LAB-сессии ${sessionId} содержит некорректную версию определения.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const definitionSha256 = value.definition_sha256 === null
|
||||||
|
? null
|
||||||
|
: requireText(value.definition_sha256, "definition_sha256", 64);
|
||||||
|
if (definitionSha256 !== null && !SHA256.test(definitionSha256)) {
|
||||||
|
throw new ObservationLabCalculationProfileContractError(
|
||||||
|
`Профиль расчёта LAB-сессии ${sessionId} не содержит SHA-256 определения.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const definitionFieldCount = [
|
||||||
|
definitionId,
|
||||||
|
definitionVersion,
|
||||||
|
definitionSha256,
|
||||||
|
].filter((entry) => entry !== null).length;
|
||||||
|
if (
|
||||||
|
(origin === "existing-result" && definitionFieldCount !== 0)
|
||||||
|
|| (origin === "archived-definition" && definitionFieldCount !== 3)
|
||||||
|
) {
|
||||||
|
throw new ObservationLabCalculationProfileContractError(
|
||||||
|
`Профиль расчёта LAB-сессии ${sessionId} содержит неполную идентичность определения.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
schemaVersion: OBSERVATION_LAB_CALCULATION_PROFILE_SCHEMA,
|
||||||
|
setupId: requireIdentifier(value.setup_id, "setup_id"),
|
||||||
|
displayName: requireText(value.display_name, "display_name", 256),
|
||||||
|
origin,
|
||||||
|
definitionId,
|
||||||
|
definitionVersion,
|
||||||
|
definitionSha256,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,280 @@
|
|||||||
|
export interface ObservationCanonicalLabReplayCapability {
|
||||||
|
schemaVersion: "missioncore.observation-lab-replay-capability/v1";
|
||||||
|
kind: "canonical-recorded-rerun";
|
||||||
|
viewerProfile: "recorded-session";
|
||||||
|
timeline: "session_time";
|
||||||
|
activation: "explicit";
|
||||||
|
commandsEnabled: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ObservationPortableLabReplayCapability {
|
||||||
|
schemaVersion: "missioncore.observation-lab-replay-capability/v2";
|
||||||
|
kind: "portable-result-review";
|
||||||
|
viewerProfile: "portable-result";
|
||||||
|
timeline: "result-defined";
|
||||||
|
activation: "explicit";
|
||||||
|
commandsEnabled: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ObservationLabReplayCapability =
|
||||||
|
| ObservationCanonicalLabReplayCapability
|
||||||
|
| ObservationPortableLabReplayCapability;
|
||||||
|
|
||||||
|
export class ObservationLabReplayCapabilityContractError extends Error {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
this.name = "ObservationLabReplayCapabilityContractError";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SHA256 = /^[a-f0-9]{64}$/;
|
||||||
|
const CAPABILITY_KEYS = new Set([
|
||||||
|
"schema_version",
|
||||||
|
"kind",
|
||||||
|
"viewer_profile",
|
||||||
|
"timeline",
|
||||||
|
"activation",
|
||||||
|
"commands_enabled",
|
||||||
|
]);
|
||||||
|
const CANONICAL_PROVENANCE_KEYS = new Set([
|
||||||
|
"schema_version",
|
||||||
|
"evidence_identity_sha256",
|
||||||
|
"result_document_sha256",
|
||||||
|
"replay_capability",
|
||||||
|
"authority",
|
||||||
|
"method",
|
||||||
|
]);
|
||||||
|
const PORTABLE_PROVENANCE_KEYS = new Set([
|
||||||
|
"schema_version",
|
||||||
|
"authority",
|
||||||
|
"calculation_profile",
|
||||||
|
"calculation_profile_sha256",
|
||||||
|
"job",
|
||||||
|
"source",
|
||||||
|
"run_definition",
|
||||||
|
"result_package",
|
||||||
|
"replay_capability",
|
||||||
|
"storage",
|
||||||
|
"method",
|
||||||
|
]);
|
||||||
|
const AUTHORITY_KEYS = new Set([
|
||||||
|
"commands_enabled",
|
||||||
|
"navigation_or_safety_accepted",
|
||||||
|
"actuation_accepted",
|
||||||
|
]);
|
||||||
|
const METHOD_KEYS = new Set([
|
||||||
|
"schema_version",
|
||||||
|
"completeness",
|
||||||
|
"execution_class",
|
||||||
|
"pipeline_id",
|
||||||
|
"components",
|
||||||
|
]);
|
||||||
|
const METHOD_COMPONENT_KEYS = new Set([
|
||||||
|
"kind",
|
||||||
|
"name",
|
||||||
|
"version",
|
||||||
|
"role",
|
||||||
|
"identity_sha256",
|
||||||
|
]);
|
||||||
|
|
||||||
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertExactKeys(
|
||||||
|
value: Record<string, unknown>,
|
||||||
|
expected: ReadonlySet<string>,
|
||||||
|
label: string,
|
||||||
|
): void {
|
||||||
|
const keys = Object.keys(value);
|
||||||
|
if (keys.length !== expected.size || keys.some((key) => !expected.has(key))) {
|
||||||
|
throw new ObservationLabReplayCapabilityContractError(
|
||||||
|
`${label} содержит неизвестные или пропущенные поля.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeLabReplayCapability(
|
||||||
|
value: unknown,
|
||||||
|
sessionId: string,
|
||||||
|
): ObservationLabReplayCapability | null {
|
||||||
|
if (value === null || value === undefined) return null;
|
||||||
|
if (!isRecord(value)) {
|
||||||
|
throw new ObservationLabReplayCapabilityContractError(
|
||||||
|
`Replay-возможность LAB-сессии ${sessionId} должна быть объектом или null.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assertExactKeys(value, CAPABILITY_KEYS, `Replay-возможность LAB-сессии ${sessionId}`);
|
||||||
|
if (
|
||||||
|
value.activation !== "explicit"
|
||||||
|
|| value.commands_enabled !== false
|
||||||
|
) {
|
||||||
|
throw new ObservationLabReplayCapabilityContractError(
|
||||||
|
`Replay-возможность LAB-сессии ${sessionId} нарушает observation-only контракт.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
value.schema_version === "missioncore.observation-lab-replay-capability/v1"
|
||||||
|
&& value.kind === "canonical-recorded-rerun"
|
||||||
|
&& value.viewer_profile === "recorded-session"
|
||||||
|
&& value.timeline === "session_time"
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
schemaVersion: "missioncore.observation-lab-replay-capability/v1",
|
||||||
|
kind: "canonical-recorded-rerun",
|
||||||
|
viewerProfile: "recorded-session",
|
||||||
|
timeline: "session_time",
|
||||||
|
activation: "explicit",
|
||||||
|
commandsEnabled: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
value.schema_version === "missioncore.observation-lab-replay-capability/v2"
|
||||||
|
&& value.kind === "portable-result-review"
|
||||||
|
&& value.viewer_profile === "portable-result"
|
||||||
|
&& value.timeline === "result-defined"
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
schemaVersion: "missioncore.observation-lab-replay-capability/v2",
|
||||||
|
kind: "portable-result-review",
|
||||||
|
viewerProfile: "portable-result",
|
||||||
|
timeline: "result-defined",
|
||||||
|
activation: "explicit",
|
||||||
|
commandsEnabled: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
throw new ObservationLabReplayCapabilityContractError(
|
||||||
|
`Replay-возможность LAB-сессии ${sessionId} содержит неизвестный viewer capability.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeCanonicalLabReplayCapabilityProvenance(
|
||||||
|
provenance: Readonly<Record<string, unknown>>,
|
||||||
|
sessionId: string,
|
||||||
|
resultId: string,
|
||||||
|
): ObservationCanonicalLabReplayCapability {
|
||||||
|
assertExactKeys(provenance, CANONICAL_PROVENANCE_KEYS, `Canonical provenance LAB-сессии ${sessionId}`);
|
||||||
|
if (
|
||||||
|
provenance.schema_version !== "missioncore.canonical-recorded-lab-projection/v1"
|
||||||
|
|| typeof provenance.evidence_identity_sha256 !== "string"
|
||||||
|
|| !SHA256.test(provenance.evidence_identity_sha256)
|
||||||
|
|| resultId !== `lab-v1-vegetation-shadow-${provenance.evidence_identity_sha256}`
|
||||||
|
|| typeof provenance.result_document_sha256 !== "string"
|
||||||
|
|| !SHA256.test(provenance.result_document_sha256)
|
||||||
|
) {
|
||||||
|
throw new ObservationLabReplayCapabilityContractError(
|
||||||
|
`Canonical provenance LAB-сессии ${sessionId} потеряла immutable identity.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!isRecord(provenance.authority)) {
|
||||||
|
throw new ObservationLabReplayCapabilityContractError(
|
||||||
|
`Canonical provenance LAB-сессии ${sessionId} не содержит authority.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assertExactKeys(provenance.authority, AUTHORITY_KEYS, `Canonical authority LAB-сессии ${sessionId}`);
|
||||||
|
if (
|
||||||
|
provenance.authority.commands_enabled !== false
|
||||||
|
|| provenance.authority.navigation_or_safety_accepted !== false
|
||||||
|
|| provenance.authority.actuation_accepted !== false
|
||||||
|
) {
|
||||||
|
throw new ObservationLabReplayCapabilityContractError(
|
||||||
|
`Canonical provenance LAB-сессии ${sessionId} нарушает observation-only authority.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!isRecord(provenance.method)) {
|
||||||
|
throw new ObservationLabReplayCapabilityContractError(
|
||||||
|
`Canonical provenance LAB-сессии ${sessionId} не содержит method manifest.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assertExactKeys(provenance.method, METHOD_KEYS, `Canonical method LAB-сессии ${sessionId}`);
|
||||||
|
if (
|
||||||
|
provenance.method.schema_version !== "missioncore.laboratory-method/v1"
|
||||||
|
|| provenance.method.completeness !== "legacy-partial"
|
||||||
|
|| provenance.method.execution_class !== "ai-inference"
|
||||||
|
|| provenance.method.pipeline_id
|
||||||
|
!== "ravnoves004tree-full-eomt-ddrnet-recorded-review/v1"
|
||||||
|
|| !Array.isArray(provenance.method.components)
|
||||||
|
|| provenance.method.components.length !== 1
|
||||||
|
) {
|
||||||
|
throw new ObservationLabReplayCapabilityContractError(
|
||||||
|
`Canonical method LAB-сессии ${sessionId} не соответствует записанному прогону.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const component = provenance.method.components[0];
|
||||||
|
if (!isRecord(component)) {
|
||||||
|
throw new ObservationLabReplayCapabilityContractError(
|
||||||
|
`Canonical method LAB-сессии ${sessionId} не содержит source component.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assertExactKeys(
|
||||||
|
component,
|
||||||
|
METHOD_COMPONENT_KEYS,
|
||||||
|
`Canonical source component LAB-сессии ${sessionId}`,
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
component.kind !== "source"
|
||||||
|
|| component.name !== "sealed full-route LAB result"
|
||||||
|
|| component.version !== "missioncore.lab-v1-vegetation-shadow/v1"
|
||||||
|
|| component.role !== "immutable Session catalog projection"
|
||||||
|
|| component.identity_sha256 !== provenance.evidence_identity_sha256
|
||||||
|
) {
|
||||||
|
throw new ObservationLabReplayCapabilityContractError(
|
||||||
|
`Canonical source component LAB-сессии ${sessionId} потерял immutable identity.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const capability = decodeLabReplayCapability(provenance.replay_capability, sessionId);
|
||||||
|
if (capability === null || capability.kind !== "canonical-recorded-rerun") {
|
||||||
|
throw new ObservationLabReplayCapabilityContractError(
|
||||||
|
`Canonical provenance LAB-сессии ${sessionId} не содержит replay capability.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return capability;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodePortableLabReplayCapabilityProvenance(
|
||||||
|
provenance: Readonly<Record<string, unknown>>,
|
||||||
|
sessionId: string,
|
||||||
|
resultId: string,
|
||||||
|
sourceSessionId: string,
|
||||||
|
definitionSha256: string,
|
||||||
|
): ObservationPortableLabReplayCapability {
|
||||||
|
assertExactKeys(
|
||||||
|
provenance,
|
||||||
|
PORTABLE_PROVENANCE_KEYS,
|
||||||
|
`Portable provenance LAB-сессии ${sessionId}`,
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
provenance.schema_version !== "missioncore.observatory-portable-result-publication/v1"
|
||||||
|
|| !isRecord(provenance.source)
|
||||||
|
|| provenance.source.session_id !== sourceSessionId
|
||||||
|
|| !isRecord(provenance.run_definition)
|
||||||
|
|| provenance.run_definition.definition_sha256 !== definitionSha256
|
||||||
|
|| !isRecord(provenance.result_package)
|
||||||
|
|| typeof provenance.result_package.manifest_sha256 !== "string"
|
||||||
|
|| !SHA256.test(provenance.result_package.manifest_sha256)
|
||||||
|
|| typeof provenance.result_package.artifact_manifest_id !== "string"
|
||||||
|
|| !SHA256.test(provenance.result_package.artifact_manifest_id)
|
||||||
|
|| sessionId !== resultId
|
||||||
|
) {
|
||||||
|
throw new ObservationLabReplayCapabilityContractError(
|
||||||
|
`Portable provenance LAB-сессии ${sessionId} потеряла immutable identity.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const capability = decodeLabReplayCapability(provenance.replay_capability, sessionId);
|
||||||
|
if (capability === null || capability.kind !== "portable-result-review") {
|
||||||
|
throw new ObservationLabReplayCapabilityContractError(
|
||||||
|
`Portable provenance LAB-сессии ${sessionId} не содержит viewer capability.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return capability;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Rolling bridge for a new frontend against the pre-v2 catalog endpoint. */
|
||||||
|
export function decodeRollingCanonicalLabReplayCapability(
|
||||||
|
provenance: Readonly<Record<string, unknown>>,
|
||||||
|
sessionId: string,
|
||||||
|
resultId: string,
|
||||||
|
): ObservationLabReplayCapability | null {
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(provenance, "replay_capability")) return null;
|
||||||
|
return decodeCanonicalLabReplayCapabilityProvenance(provenance, sessionId, resultId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import { isUsableRecordedPlaybackRange } from "./recordedRerunLifecycle";
|
||||||
|
|
||||||
|
export function createReentrantViewerDisposer(
|
||||||
|
cleanupOnce: () => void,
|
||||||
|
releaseNativeViewer: () => void,
|
||||||
|
): () => void {
|
||||||
|
let cleanupComplete = false;
|
||||||
|
return () => {
|
||||||
|
try {
|
||||||
|
if (!cleanupComplete) {
|
||||||
|
cleanupComplete = true;
|
||||||
|
cleanupOnce();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
// A deferred native start can resolve after a pre-ready stop. Reapply
|
||||||
|
// release at every boundary so it cannot reopen after React unmount.
|
||||||
|
releaseNativeViewer();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ActiveLiveViewerOwner {
|
||||||
|
release: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
let activeLiveViewerOwner: ActiveLiveViewerOwner | null = null;
|
||||||
|
|
||||||
|
/** Own exactly one native live receiver per application document. */
|
||||||
|
export function claimExclusiveLiveViewer(release: () => void): () => void {
|
||||||
|
const owner = { release };
|
||||||
|
const previous = activeLiveViewerOwner;
|
||||||
|
activeLiveViewerOwner = owner;
|
||||||
|
previous?.release();
|
||||||
|
return () => {
|
||||||
|
if (activeLiveViewerOwner === owner) activeLiveViewerOwner = null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isLiveRerunPresentationReady(
|
||||||
|
viewerStarted: boolean,
|
||||||
|
rangeNs: { min: number; max: number } | null,
|
||||||
|
backendActivitySequence: number | null,
|
||||||
|
): boolean {
|
||||||
|
return viewerStarted
|
||||||
|
&& Number.isSafeInteger(backendActivitySequence)
|
||||||
|
&& (backendActivitySequence ?? 0) > 0
|
||||||
|
&& isUsableRecordedPlaybackRange(rangeNs);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function liveTimelineNeedsSynchronization(
|
||||||
|
followLive: boolean,
|
||||||
|
activeTimeline: string | null | undefined,
|
||||||
|
rangeNs: { min: number; max: number } | null,
|
||||||
|
): boolean {
|
||||||
|
return followLive
|
||||||
|
&& isUsableRecordedPlaybackRange(rangeNs)
|
||||||
|
&& activeTimeline !== "stream_time";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function liveRerunReceiverBindingIdentity(
|
||||||
|
sourceUrl: string,
|
||||||
|
liveStreamId: string | null,
|
||||||
|
followLive: boolean,
|
||||||
|
): string {
|
||||||
|
return JSON.stringify([
|
||||||
|
followLive ? "live" : "recorded",
|
||||||
|
sourceUrl.trim(),
|
||||||
|
followLive ? liveStreamId?.trim() ?? "" : "",
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -327,6 +327,53 @@ export function verifyLiveViewerClientBuild(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recover a recorded viewer whose lazy module disappeared during a frontend
|
||||||
|
* deployment. Live acquisition deliberately never reloads automatically, but
|
||||||
|
* a saved recording has no device-side authority to preserve and can safely
|
||||||
|
* move to the current immutable application build.
|
||||||
|
*/
|
||||||
|
export async function reloadRecordedViewerAfterStaleModuleFailure({
|
||||||
|
loadedUiBuildId,
|
||||||
|
signal,
|
||||||
|
fetcher = globalThis.fetch,
|
||||||
|
reload = () => window.location.reload(),
|
||||||
|
}: {
|
||||||
|
loadedUiBuildId: string;
|
||||||
|
signal?: AbortSignal;
|
||||||
|
fetcher?: typeof globalThis.fetch;
|
||||||
|
reload?: () => void;
|
||||||
|
}): Promise<boolean> {
|
||||||
|
if (
|
||||||
|
signal?.aborted ||
|
||||||
|
loadedUiBuildId === DEVELOPMENT_UI_BUILD_ID ||
|
||||||
|
!HASHED_UI_BUILD_ID.test(loadedUiBuildId)
|
||||||
|
) return false;
|
||||||
|
try {
|
||||||
|
const response = await fetcher("/api/v1/viewer/client-contract", {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json",
|
||||||
|
"X-MissionCore-UI-Build": loadedUiBuildId,
|
||||||
|
},
|
||||||
|
cache: "no-store",
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
if (signal?.aborted || !response.ok) return false;
|
||||||
|
const expectedUiBuildId = response.headers.get(UI_BUILD_HEADER);
|
||||||
|
if (
|
||||||
|
!expectedUiBuildId ||
|
||||||
|
expectedUiBuildId === loadedUiBuildId ||
|
||||||
|
!HASHED_UI_BUILD_ID.test(expectedUiBuildId)
|
||||||
|
) return false;
|
||||||
|
browserUiBuildCoordinator().report({ loadedUiBuildId, expectedUiBuildId });
|
||||||
|
reload();
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function startBuildMonitor(): void {
|
function startBuildMonitor(): void {
|
||||||
if (buildMonitorAbort || typeof window === "undefined") return;
|
if (buildMonitorAbort || typeof window === "undefined") return;
|
||||||
const lineage = createLiveViewerLineage(createLiveViewerInstanceId(), 1);
|
const lineage = createLiveViewerLineage(createLiveViewerInstanceId(), 1);
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/** Small lease for the server's ephemeral blueprint, never for the recording. */
|
||||||
|
export function keepRecordedBlueprintSession({
|
||||||
|
endpointUrl,
|
||||||
|
origin,
|
||||||
|
applicationId,
|
||||||
|
recordingId,
|
||||||
|
ownerId,
|
||||||
|
fetcher = fetch,
|
||||||
|
schedule = (callback: () => void) => window.setInterval(callback, 30_000),
|
||||||
|
cancel = (handle: number) => window.clearInterval(handle),
|
||||||
|
}: {
|
||||||
|
endpointUrl: string;
|
||||||
|
origin: string;
|
||||||
|
applicationId: string;
|
||||||
|
recordingId: string;
|
||||||
|
ownerId: string;
|
||||||
|
fetcher?: typeof fetch;
|
||||||
|
schedule?: (callback: () => void) => number;
|
||||||
|
cancel?: (handle: number) => void;
|
||||||
|
}): () => void {
|
||||||
|
const endpoint = new URL(endpointUrl, origin);
|
||||||
|
if (endpoint.origin !== origin || endpoint.search || endpoint.hash ||
|
||||||
|
!/^\/api\/v1\/observation-sessions\/[A-Za-z0-9._:-]+\/blueprint\.rrd$/.test(endpoint.pathname)) {
|
||||||
|
throw new Error("Unsafe recorded blueprint lifecycle endpoint");
|
||||||
|
}
|
||||||
|
endpoint.pathname = endpoint.pathname.replace(/blueprint\.rrd$/, "blueprint-lifecycle");
|
||||||
|
const identity = { application_id: applicationId, recording_id: recordingId,
|
||||||
|
blueprint_session_id: ownerId };
|
||||||
|
let closed = false;
|
||||||
|
let pending: AbortController | null = null;
|
||||||
|
const renew = () => {
|
||||||
|
if (closed) return;
|
||||||
|
pending?.abort();
|
||||||
|
pending = new AbortController();
|
||||||
|
void fetcher(endpoint.href, {
|
||||||
|
method: "POST", headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ ...identity, action: "renew" }), signal: pending.signal,
|
||||||
|
}).catch(() => { /* The TTL cleans up after a lost browser/network. */ });
|
||||||
|
};
|
||||||
|
const timer = schedule(renew);
|
||||||
|
return () => {
|
||||||
|
if (closed) return;
|
||||||
|
closed = true;
|
||||||
|
cancel(timer);
|
||||||
|
pending?.abort();
|
||||||
|
pending = null;
|
||||||
|
void fetcher(endpoint.href, {
|
||||||
|
method: "POST", headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ ...identity, action: "release" }),
|
||||||
|
keepalive: true, signal: AbortSignal.timeout(5_000),
|
||||||
|
}).catch(() => { /* Terminal release is best-effort; the server also has TTL. */ });
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,303 @@
|
|||||||
|
import type { SceneSettings } from "../../sceneSettings";
|
||||||
|
import type { RecordedAdmissionPhase } from "./recordedSessionAdmission";
|
||||||
|
import type { RerunPlaybackState, RerunViewportStatus } from "./viewerProfile";
|
||||||
|
|
||||||
|
const BUFFER_END_TOLERANCE_NS = 1_000_000;
|
||||||
|
const RECORDED_OPEN_MIN_TIMEOUT_MS = 120_000;
|
||||||
|
const RECORDED_OPEN_MAX_TIMEOUT_MS = 1_800_000;
|
||||||
|
const RECORDED_OPEN_GRACE_MS = 30_000;
|
||||||
|
const RECORDED_OPEN_MIN_BYTES_PER_SECOND = 2 * 1024 * 1024;
|
||||||
|
|
||||||
|
export const RECORDED_BASE_POINT_COLOR_KEY = "intensity|turbo|-";
|
||||||
|
|
||||||
|
export interface RecordedPlaybackBufferState {
|
||||||
|
bufferedEndNs: number | null;
|
||||||
|
expectedStartNs: number | null;
|
||||||
|
expectedEndNs: number | null;
|
||||||
|
bufferProgress: number | null;
|
||||||
|
fullyBuffered: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function recordedPointColorKey(
|
||||||
|
settings: Pick<SceneSettings, "colorMode" | "palette" | "customColor">,
|
||||||
|
): string {
|
||||||
|
const custom = settings.palette === "custom" || settings.colorMode === "class"
|
||||||
|
? settings.customColor.toLowerCase()
|
||||||
|
: "-";
|
||||||
|
return `${settings.colorMode}|${settings.palette}|${custom}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function recordedOpenWatchdogTimeoutMs(byteLength: number): number {
|
||||||
|
if (!Number.isSafeInteger(byteLength) || byteLength < 4) {
|
||||||
|
throw new Error("Unsafe recorded RRD byte length");
|
||||||
|
}
|
||||||
|
const transferBudgetMs = Math.ceil(
|
||||||
|
(byteLength / RECORDED_OPEN_MIN_BYTES_PER_SECOND) * 1_000,
|
||||||
|
);
|
||||||
|
return Math.min(
|
||||||
|
RECORDED_OPEN_MAX_TIMEOUT_MS,
|
||||||
|
Math.max(RECORDED_OPEN_MIN_TIMEOUT_MS, transferBudgetMs + RECORDED_OPEN_GRACE_MS),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createRecordedOpenWatchdog<T>({
|
||||||
|
byteLength,
|
||||||
|
schedule,
|
||||||
|
cancel,
|
||||||
|
onTimeout,
|
||||||
|
}: {
|
||||||
|
byteLength: number;
|
||||||
|
schedule: (callback: () => void, timeoutMs: number) => T;
|
||||||
|
cancel: (handle: T) => void;
|
||||||
|
onTimeout: () => void;
|
||||||
|
}): { arm: () => void; clear: () => void; pending: () => boolean } {
|
||||||
|
const timeoutMs = recordedOpenWatchdogTimeoutMs(byteLength);
|
||||||
|
let handle: T | null = null;
|
||||||
|
return {
|
||||||
|
arm() {
|
||||||
|
if (handle !== null) return;
|
||||||
|
handle = schedule(() => {
|
||||||
|
handle = null;
|
||||||
|
onTimeout();
|
||||||
|
}, timeoutMs);
|
||||||
|
},
|
||||||
|
clear() {
|
||||||
|
if (handle === null) return;
|
||||||
|
cancel(handle);
|
||||||
|
handle = null;
|
||||||
|
},
|
||||||
|
pending: () => handle !== null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isUsableRecordedPlaybackRange(
|
||||||
|
rangeNs: { min: number; max: number } | null,
|
||||||
|
): rangeNs is { min: number; max: number } {
|
||||||
|
return Boolean(
|
||||||
|
rangeNs
|
||||||
|
&& Number.isFinite(rangeNs.min)
|
||||||
|
&& Number.isFinite(rangeNs.max)
|
||||||
|
&& rangeNs.max >= rangeNs.min,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function recordedPlaybackBufferState(
|
||||||
|
rangeNs: { min: number; max: number } | null,
|
||||||
|
expectedTimelineEndSeconds?: number,
|
||||||
|
expectedTimelineStartSeconds?: number,
|
||||||
|
): RecordedPlaybackBufferState {
|
||||||
|
const usableRange = isUsableRecordedPlaybackRange(rangeNs) ? rangeNs : null;
|
||||||
|
const bufferedEndNs = usableRange?.max ?? null;
|
||||||
|
if (expectedTimelineEndSeconds === undefined) {
|
||||||
|
return {
|
||||||
|
bufferedEndNs,
|
||||||
|
expectedStartNs: null,
|
||||||
|
expectedEndNs: null,
|
||||||
|
bufferProgress: null,
|
||||||
|
fullyBuffered: usableRange !== null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!Number.isFinite(expectedTimelineEndSeconds) || expectedTimelineEndSeconds < 0) {
|
||||||
|
return {
|
||||||
|
bufferedEndNs,
|
||||||
|
expectedStartNs: null,
|
||||||
|
expectedEndNs: null,
|
||||||
|
bufferProgress: null,
|
||||||
|
fullyBuffered: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const expectedEndNs = expectedTimelineEndSeconds * 1_000_000_000;
|
||||||
|
const expectedStartNs = expectedTimelineStartSeconds === undefined
|
||||||
|
? 0
|
||||||
|
: expectedTimelineStartSeconds * 1_000_000_000;
|
||||||
|
if (
|
||||||
|
!Number.isFinite(expectedEndNs)
|
||||||
|
|| !Number.isFinite(expectedStartNs)
|
||||||
|
|| expectedStartNs < 0
|
||||||
|
|| expectedEndNs < expectedStartNs
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
bufferedEndNs,
|
||||||
|
expectedStartNs: null,
|
||||||
|
expectedEndNs: null,
|
||||||
|
bufferProgress: null,
|
||||||
|
fullyBuffered: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const fullyBuffered = usableRange !== null
|
||||||
|
&& usableRange.min <= expectedStartNs + BUFFER_END_TOLERANCE_NS
|
||||||
|
&& usableRange.max >= expectedEndNs - BUFFER_END_TOLERANCE_NS;
|
||||||
|
const expectedDurationNs = expectedEndNs - expectedStartNs;
|
||||||
|
const bufferProgress = bufferedEndNs === null
|
||||||
|
? 0
|
||||||
|
: expectedDurationNs <= 0
|
||||||
|
? (fullyBuffered ? 1 : 0)
|
||||||
|
: Math.min(1, Math.max(0, (bufferedEndNs - expectedStartNs) / expectedDurationNs));
|
||||||
|
return {
|
||||||
|
bufferedEndNs,
|
||||||
|
expectedStartNs,
|
||||||
|
expectedEndNs,
|
||||||
|
bufferProgress,
|
||||||
|
fullyBuffered,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isRecordedPlaybackReady(
|
||||||
|
viewerStarted: boolean,
|
||||||
|
artifactVerified: boolean,
|
||||||
|
buffer: RecordedPlaybackBufferState,
|
||||||
|
): boolean {
|
||||||
|
return viewerStarted && artifactVerified && buffer.bufferedEndNs !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function recordedPlaybackRangeWhenReady(
|
||||||
|
rangeNs: { min: number; max: number } | null,
|
||||||
|
buffer: RecordedPlaybackBufferState,
|
||||||
|
artifactVerified: boolean,
|
||||||
|
): { min: number; max: number } | null {
|
||||||
|
if (
|
||||||
|
!artifactVerified
|
||||||
|
|| !buffer.fullyBuffered
|
||||||
|
|| !isUsableRecordedPlaybackRange(rangeNs)
|
||||||
|
) return null;
|
||||||
|
return {
|
||||||
|
min: buffer.expectedStartNs === null
|
||||||
|
? rangeNs.min
|
||||||
|
: Math.max(rangeNs.min, buffer.expectedStartNs),
|
||||||
|
max: buffer.expectedEndNs === null
|
||||||
|
? rangeNs.max
|
||||||
|
: Math.min(rangeNs.max, buffer.expectedEndNs),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isRecordedPlaybackPresentationReady(
|
||||||
|
status: RerunViewportStatus,
|
||||||
|
playback: RerunPlaybackState | null,
|
||||||
|
): boolean {
|
||||||
|
return status === "ready"
|
||||||
|
&& playback?.fullyBuffered === true
|
||||||
|
&& isUsableRecordedPlaybackRange(playback.rangeNs);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function rerunPresentationStatus(
|
||||||
|
status: RerunViewportStatus,
|
||||||
|
_gate: RecordedAdmissionPhase,
|
||||||
|
recorded: boolean,
|
||||||
|
): RerunViewportStatus {
|
||||||
|
if (!recorded) return status;
|
||||||
|
return status === "error" ? "error" : status;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isRecordedPlaybackFullyBuffered(
|
||||||
|
rangeNs: { min: number; max: number } | null,
|
||||||
|
expectedTimelineEndSeconds?: number,
|
||||||
|
): boolean {
|
||||||
|
return recordedPlaybackBufferState(rangeNs, expectedTimelineEndSeconds).fullyBuffered;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function attemptRecordedAutoplay(
|
||||||
|
seekToStart: () => void,
|
||||||
|
startPlaying: () => void,
|
||||||
|
): boolean {
|
||||||
|
try {
|
||||||
|
seekToStart();
|
||||||
|
startPlaying();
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createRecordedInitialSeekGate(): {
|
||||||
|
attempt: (
|
||||||
|
viewerStarted: boolean,
|
||||||
|
fullyBuffered: boolean,
|
||||||
|
presentationReady: boolean,
|
||||||
|
rangeNs: { min: number; max: number } | null,
|
||||||
|
seekToStart: (startNs: number) => void,
|
||||||
|
preferredStartNs?: number,
|
||||||
|
) => boolean;
|
||||||
|
attempted: () => boolean;
|
||||||
|
} {
|
||||||
|
let consumed = false;
|
||||||
|
return {
|
||||||
|
attempt(
|
||||||
|
viewerStarted,
|
||||||
|
fullyBuffered,
|
||||||
|
presentationReady,
|
||||||
|
rangeNs,
|
||||||
|
seekToStart,
|
||||||
|
preferredStartNs,
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
consumed
|
||||||
|
|| !viewerStarted
|
||||||
|
|| !fullyBuffered
|
||||||
|
|| !presentationReady
|
||||||
|
|| !isUsableRecordedPlaybackRange(rangeNs)
|
||||||
|
) return false;
|
||||||
|
consumed = true;
|
||||||
|
const startNs = Number.isFinite(preferredStartNs)
|
||||||
|
? Math.min(Math.max(preferredStartNs as number, rangeNs.min), rangeNs.max)
|
||||||
|
: rangeNs.min;
|
||||||
|
try {
|
||||||
|
seekToStart(startNs);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
attempted: () => consumed,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createRecordedAutoplayGate(): {
|
||||||
|
attempt: (
|
||||||
|
viewerStarted: boolean,
|
||||||
|
fullyBuffered: boolean,
|
||||||
|
presentationReady: boolean,
|
||||||
|
rangeNs: { min: number; max: number } | null,
|
||||||
|
seekToStart: (startNs: number) => void,
|
||||||
|
startPlaying: () => void,
|
||||||
|
preferredStartNs?: number,
|
||||||
|
) => boolean;
|
||||||
|
attempted: () => boolean;
|
||||||
|
} {
|
||||||
|
let consumed = false;
|
||||||
|
return {
|
||||||
|
attempt(
|
||||||
|
viewerStarted,
|
||||||
|
fullyBuffered,
|
||||||
|
presentationReady,
|
||||||
|
rangeNs,
|
||||||
|
seekToStart,
|
||||||
|
startPlaying,
|
||||||
|
preferredStartNs,
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
consumed
|
||||||
|
|| !viewerStarted
|
||||||
|
|| !fullyBuffered
|
||||||
|
|| !presentationReady
|
||||||
|
|| !isUsableRecordedPlaybackRange(rangeNs)
|
||||||
|
) return false;
|
||||||
|
consumed = true;
|
||||||
|
const startNs = Number.isFinite(preferredStartNs)
|
||||||
|
? Math.min(Math.max(preferredStartNs as number, rangeNs.min), rangeNs.max)
|
||||||
|
: rangeNs.min;
|
||||||
|
return attemptRecordedAutoplay(
|
||||||
|
() => seekToStart(startNs),
|
||||||
|
startPlaying,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
attempted: () => consumed,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function canPublishRecordedPlaybackController(
|
||||||
|
readyToRender: boolean,
|
||||||
|
presentationGate: RecordedAdmissionPhase,
|
||||||
|
): boolean {
|
||||||
|
return readyToRender && presentationGate === "ready";
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
export interface ObservationReplayAttempt {
|
||||||
|
readonly signal: AbortSignal;
|
||||||
|
isCurrent: () => boolean;
|
||||||
|
finish: () => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ObservationReplayCoordinator {
|
||||||
|
begin: () => ObservationReplayAttempt;
|
||||||
|
cancel: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Latest explicit admission owns the viewer, even if an older promise settles later. */
|
||||||
|
export function createObservationReplayCoordinator(): ObservationReplayCoordinator {
|
||||||
|
let sequence = 0;
|
||||||
|
let active: AbortController | null = null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
begin() {
|
||||||
|
active?.abort();
|
||||||
|
const controller = new AbortController();
|
||||||
|
const attemptSequence = ++sequence;
|
||||||
|
active = controller;
|
||||||
|
return {
|
||||||
|
signal: controller.signal,
|
||||||
|
isCurrent: () => (
|
||||||
|
!controller.signal.aborted
|
||||||
|
&& active === controller
|
||||||
|
&& sequence === attemptSequence
|
||||||
|
),
|
||||||
|
finish: () => {
|
||||||
|
if (active !== controller || sequence !== attemptSequence) return false;
|
||||||
|
active = null;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
cancel() {
|
||||||
|
active?.abort();
|
||||||
|
// Keep settlement ownership until finish(). isCurrent() already fails,
|
||||||
|
// while begin() can replace this attempt immediately.
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,6 +1,20 @@
|
|||||||
import {
|
import {
|
||||||
MAX_RECORDED_CAMERA_SOURCES,
|
MAX_RECORDED_CAMERA_SOURCES,
|
||||||
} from "./recordedSessionAdmission";
|
} from "./recordedSessionAdmission";
|
||||||
|
import {
|
||||||
|
decodeLabReplayCapability,
|
||||||
|
decodeRollingCanonicalLabReplayCapability,
|
||||||
|
ObservationLabReplayCapabilityContractError,
|
||||||
|
type ObservationLabReplayCapability,
|
||||||
|
} from "./labReplayCapability";
|
||||||
|
import {
|
||||||
|
decodeObservationLabCalculationProfile,
|
||||||
|
ObservationLabCalculationProfileContractError,
|
||||||
|
type ObservationLabCalculationProfile,
|
||||||
|
} from "./labCalculationProfile";
|
||||||
|
|
||||||
|
export type { ObservationLabReplayCapability } from "./labReplayCapability";
|
||||||
|
export type { ObservationLabCalculationProfile } from "./labCalculationProfile";
|
||||||
|
|
||||||
export type ObservationSessionStatus =
|
export type ObservationSessionStatus =
|
||||||
| "recording"
|
| "recording"
|
||||||
@@ -20,6 +34,8 @@ export interface ObservationLabInstance {
|
|||||||
configSha256: string | null;
|
configSha256: string | null;
|
||||||
runCreatedAtUtc: string;
|
runCreatedAtUtc: string;
|
||||||
publishedAtUtc: string;
|
publishedAtUtc: string;
|
||||||
|
replayCapability: ObservationLabReplayCapability | null;
|
||||||
|
calculationProfile: ObservationLabCalculationProfile | null;
|
||||||
provenance: Readonly<Record<string, unknown>>;
|
provenance: Readonly<Record<string, unknown>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,10 +48,21 @@ export interface ObservationSessionSummary {
|
|||||||
modalities: readonly string[];
|
modalities: readonly string[];
|
||||||
durationSeconds: number;
|
durationSeconds: number;
|
||||||
replayable: boolean;
|
replayable: boolean;
|
||||||
|
captureAttestation: ObservationCaptureAttestation | null;
|
||||||
preparation: ObservationSessionCatalogPreparation | null;
|
preparation: ObservationSessionCatalogPreparation | null;
|
||||||
lab: ObservationLabInstance | null;
|
lab: ObservationLabInstance | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ObservationCaptureAttestation {
|
||||||
|
readonly equipmentModelId: string;
|
||||||
|
readonly equipmentModelSha256: string;
|
||||||
|
readonly equipmentDisplayName: string;
|
||||||
|
readonly captureProfileId: string;
|
||||||
|
readonly captureProfileSha256: string;
|
||||||
|
readonly captureAttestationSha256: string;
|
||||||
|
readonly state: "attested" | "attested-with-legacy-evidence" | "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
export interface ObservationSessionCatalog {
|
export interface ObservationSessionCatalog {
|
||||||
items: readonly ObservationSessionSummary[];
|
items: readonly ObservationSessionSummary[];
|
||||||
}
|
}
|
||||||
@@ -152,10 +179,11 @@ const ITEM_KEYS = new Set([
|
|||||||
"modalities",
|
"modalities",
|
||||||
"duration_seconds",
|
"duration_seconds",
|
||||||
"replayable",
|
"replayable",
|
||||||
|
"capture_attestation",
|
||||||
"preparation",
|
"preparation",
|
||||||
"lab",
|
"lab",
|
||||||
]);
|
]);
|
||||||
const LAB_KEYS = new Set([
|
const LEGACY_LAB_KEYS = new Set([
|
||||||
"lab_id",
|
"lab_id",
|
||||||
"source_session_id",
|
"source_session_id",
|
||||||
"result_kind",
|
"result_kind",
|
||||||
@@ -166,6 +194,27 @@ const LAB_KEYS = new Set([
|
|||||||
"published_at_utc",
|
"published_at_utc",
|
||||||
"provenance",
|
"provenance",
|
||||||
]);
|
]);
|
||||||
|
const LAB_V2_KEYS = new Set([...LEGACY_LAB_KEYS, "replay_capability"]);
|
||||||
|
const LAB_V3_KEYS = new Set([
|
||||||
|
...LAB_V2_KEYS,
|
||||||
|
"calculation_profile",
|
||||||
|
]);
|
||||||
|
const CAPTURE_ATTESTATION_KEYS = new Set([
|
||||||
|
"schema_version",
|
||||||
|
"equipment_model",
|
||||||
|
"capture_profile",
|
||||||
|
"capture_attestation_sha256",
|
||||||
|
"state",
|
||||||
|
]);
|
||||||
|
const EQUIPMENT_MODEL_KEYS = new Set([
|
||||||
|
"equipment_model_id",
|
||||||
|
"equipment_model_sha256",
|
||||||
|
"display_name",
|
||||||
|
]);
|
||||||
|
const CAPTURE_PROFILE_KEYS = new Set([
|
||||||
|
"capture_profile_id",
|
||||||
|
"capture_profile_sha256",
|
||||||
|
]);
|
||||||
const CATALOG_PREPARATION_KEYS = new Set([
|
const CATALOG_PREPARATION_KEYS = new Set([
|
||||||
"preparation_id",
|
"preparation_id",
|
||||||
"state",
|
"state",
|
||||||
@@ -379,11 +428,103 @@ function decodeItem(value: unknown, index: number): ObservationSessionSummary {
|
|||||||
modalities: requireModalities(value.modalities),
|
modalities: requireModalities(value.modalities),
|
||||||
durationSeconds: value.duration_seconds,
|
durationSeconds: value.duration_seconds,
|
||||||
replayable: value.replayable,
|
replayable: value.replayable,
|
||||||
|
captureAttestation: decodeCaptureAttestation(value.capture_attestation, id),
|
||||||
preparation: decodeCatalogPreparation(value.preparation, id),
|
preparation: decodeCatalogPreparation(value.preparation, id),
|
||||||
lab: decodeLabInstance(value.lab, id),
|
lab: decodeLabInstance(value.lab, id),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function decodeCaptureAttestation(
|
||||||
|
value: unknown,
|
||||||
|
sessionId: string,
|
||||||
|
): ObservationCaptureAttestation | null {
|
||||||
|
if (value === null || value === undefined) return null;
|
||||||
|
if (!isRecord(value)) {
|
||||||
|
throw new ObservationSessionContractError(
|
||||||
|
`Аттестация записи ${sessionId} должна быть объектом или null.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assertExactKeys(value, CAPTURE_ATTESTATION_KEYS, `Аттестация записи ${sessionId}`);
|
||||||
|
if (value.schema_version !== "missioncore.recorded-capture-attestation/v1") {
|
||||||
|
throw new ObservationSessionContractError(
|
||||||
|
`Аттестация записи ${sessionId} имеет неизвестную схему.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!isRecord(value.equipment_model) || !isRecord(value.capture_profile)) {
|
||||||
|
throw new ObservationSessionContractError(
|
||||||
|
`Аттестация записи ${sessionId} не содержит оборудование или профиль записи.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assertExactKeys(
|
||||||
|
value.equipment_model,
|
||||||
|
EQUIPMENT_MODEL_KEYS,
|
||||||
|
`Оборудование записи ${sessionId}`,
|
||||||
|
);
|
||||||
|
assertExactKeys(
|
||||||
|
value.capture_profile,
|
||||||
|
CAPTURE_PROFILE_KEYS,
|
||||||
|
`Профиль записи ${sessionId}`,
|
||||||
|
);
|
||||||
|
const equipmentModelId = requireString(
|
||||||
|
value.equipment_model.equipment_model_id,
|
||||||
|
"equipment_model_id",
|
||||||
|
128,
|
||||||
|
);
|
||||||
|
const equipmentModelSha256 = requireString(
|
||||||
|
value.equipment_model.equipment_model_sha256,
|
||||||
|
"equipment_model_sha256",
|
||||||
|
64,
|
||||||
|
);
|
||||||
|
const captureProfileId = requireString(
|
||||||
|
value.capture_profile.capture_profile_id,
|
||||||
|
"capture_profile_id",
|
||||||
|
128,
|
||||||
|
);
|
||||||
|
const captureProfileSha256 = requireString(
|
||||||
|
value.capture_profile.capture_profile_sha256,
|
||||||
|
"capture_profile_sha256",
|
||||||
|
64,
|
||||||
|
);
|
||||||
|
const captureAttestationSha256 = requireString(
|
||||||
|
value.capture_attestation_sha256,
|
||||||
|
"capture_attestation_sha256",
|
||||||
|
64,
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
!SAFE_ID.test(equipmentModelId)
|
||||||
|
|| !SAFE_ID.test(captureProfileId)
|
||||||
|
|| !SHA256.test(equipmentModelSha256)
|
||||||
|
|| !SHA256.test(captureProfileSha256)
|
||||||
|
|| !SHA256.test(captureAttestationSha256)
|
||||||
|
) {
|
||||||
|
throw new ObservationSessionContractError(
|
||||||
|
`Аттестация записи ${sessionId} содержит некорректную immutable identity.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
value.state !== "attested"
|
||||||
|
&& value.state !== "attested-with-legacy-evidence"
|
||||||
|
&& value.state !== "unknown"
|
||||||
|
) {
|
||||||
|
throw new ObservationSessionContractError(
|
||||||
|
`Аттестация записи ${sessionId} содержит неизвестное состояние.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
equipmentModelId,
|
||||||
|
equipmentModelSha256,
|
||||||
|
equipmentDisplayName: requireString(
|
||||||
|
value.equipment_model.display_name,
|
||||||
|
"equipment display_name",
|
||||||
|
128,
|
||||||
|
),
|
||||||
|
captureProfileId,
|
||||||
|
captureProfileSha256,
|
||||||
|
captureAttestationSha256,
|
||||||
|
state: value.state,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function decodeLabInstance(
|
function decodeLabInstance(
|
||||||
value: unknown,
|
value: unknown,
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
@@ -394,7 +535,23 @@ function decodeLabInstance(
|
|||||||
`LAB-привязка сессии ${sessionId} должна быть объектом или null.`,
|
`LAB-привязка сессии ${sessionId} должна быть объектом или null.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
assertExactKeys(value, LAB_KEYS, `LAB-привязка сессии ${sessionId}`);
|
const hasTypedCapability = Object.prototype.hasOwnProperty.call(
|
||||||
|
value,
|
||||||
|
"replay_capability",
|
||||||
|
);
|
||||||
|
const hasCalculationProfile = Object.prototype.hasOwnProperty.call(
|
||||||
|
value,
|
||||||
|
"calculation_profile",
|
||||||
|
);
|
||||||
|
assertExactKeys(
|
||||||
|
value,
|
||||||
|
hasCalculationProfile
|
||||||
|
? LAB_V3_KEYS
|
||||||
|
: hasTypedCapability
|
||||||
|
? LAB_V2_KEYS
|
||||||
|
: LEGACY_LAB_KEYS,
|
||||||
|
`LAB-привязка сессии ${sessionId}`,
|
||||||
|
);
|
||||||
const labId = requireString(value.lab_id, `lab(${sessionId}).lab_id`, 36);
|
const labId = requireString(value.lab_id, `lab(${sessionId}).lab_id`, 36);
|
||||||
if (!/^LAB [A-Z][A-Z0-9._-]{0,31}$/.test(labId)) {
|
if (!/^LAB [A-Z][A-Z0-9._-]{0,31}$/.test(labId)) {
|
||||||
throw new ObservationSessionContractError("Каталог содержит некорректный LAB-маркер.");
|
throw new ObservationSessionContractError("Каталог содержит некорректный LAB-маркер.");
|
||||||
@@ -430,6 +587,28 @@ function decodeLabInstance(
|
|||||||
if (!isRecord(value.provenance)) {
|
if (!isRecord(value.provenance)) {
|
||||||
throw new ObservationSessionContractError("LAB provenance должен быть JSON-объектом.");
|
throw new ObservationSessionContractError("LAB provenance должен быть JSON-объектом.");
|
||||||
}
|
}
|
||||||
|
let replayCapability: ObservationLabReplayCapability | null;
|
||||||
|
try {
|
||||||
|
replayCapability = hasTypedCapability
|
||||||
|
? decodeLabReplayCapability(value.replay_capability, sessionId)
|
||||||
|
: decodeRollingCanonicalLabReplayCapability(value.provenance, sessionId, resultId);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ObservationLabReplayCapabilityContractError) {
|
||||||
|
throw new ObservationSessionContractError(error.message);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
let calculationProfile: ObservationLabCalculationProfile | null;
|
||||||
|
try {
|
||||||
|
calculationProfile = hasCalculationProfile
|
||||||
|
? decodeObservationLabCalculationProfile(value.calculation_profile, sessionId)
|
||||||
|
: null;
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ObservationLabCalculationProfileContractError) {
|
||||||
|
throw new ObservationSessionContractError(error.message);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
labId,
|
labId,
|
||||||
sourceSessionId,
|
sourceSessionId,
|
||||||
@@ -445,6 +624,8 @@ function decodeLabInstance(
|
|||||||
value.published_at_utc,
|
value.published_at_utc,
|
||||||
`lab(${sessionId}).published_at_utc`,
|
`lab(${sessionId}).published_at_utc`,
|
||||||
),
|
),
|
||||||
|
replayCapability,
|
||||||
|
calculationProfile,
|
||||||
provenance: value.provenance,
|
provenance: value.provenance,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1181,6 +1362,7 @@ export async function fetchObservationSessionCatalog({
|
|||||||
queryParameters.set("limit", String(Number(limit)));
|
queryParameters.set("limit", String(Number(limit)));
|
||||||
}
|
}
|
||||||
if (scope !== "all") queryParameters.set("scope", scope);
|
if (scope !== "all") queryParameters.set("scope", scope);
|
||||||
|
if (scope === "laboratory") queryParameters.set("lab_contract", "v3");
|
||||||
const serializedQuery = queryParameters.toString();
|
const serializedQuery = queryParameters.toString();
|
||||||
const query = serializedQuery ? `?${serializedQuery}` : "";
|
const query = serializedQuery ? `?${serializedQuery}` : "";
|
||||||
let response: Response;
|
let response: Response;
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import {
|
||||||
|
decodeObservationSessionCatalog,
|
||||||
|
ObservationSessionApiError,
|
||||||
|
ObservationSessionContractError,
|
||||||
|
type ObservationSessionCatalog,
|
||||||
|
type ObservationSessionFetch,
|
||||||
|
type ObservationSessionScope,
|
||||||
|
} from "./sessionArchive";
|
||||||
|
|
||||||
|
const PAGE_SCHEMA = "missioncore.observation-session-page/v1";
|
||||||
|
const CURSOR = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$/;
|
||||||
|
|
||||||
|
export interface ObservationSessionCatalogPage extends ObservationSessionCatalog {
|
||||||
|
readonly nextCursor: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Opt-in paging leaves the legacy catalog response and consumers unchanged. */
|
||||||
|
export async function fetchObservationSessionCatalogPage({
|
||||||
|
scope,
|
||||||
|
limit = 100,
|
||||||
|
cursor = null,
|
||||||
|
signal,
|
||||||
|
fetcher = globalThis.fetch,
|
||||||
|
}: {
|
||||||
|
scope: ObservationSessionScope;
|
||||||
|
limit?: number;
|
||||||
|
cursor?: string | null;
|
||||||
|
signal?: AbortSignal;
|
||||||
|
fetcher?: ObservationSessionFetch;
|
||||||
|
}): Promise<ObservationSessionCatalogPage> {
|
||||||
|
if (!Number.isInteger(limit) || limit < 1 || limit > 100
|
||||||
|
|| (cursor !== null && !CURSOR.test(cursor))) {
|
||||||
|
throw new ObservationSessionContractError("Некорректная страница каталога сессий.");
|
||||||
|
}
|
||||||
|
signal?.throwIfAborted();
|
||||||
|
const query = new URLSearchParams({ limit: String(limit), scope });
|
||||||
|
if (scope === "laboratory") query.set("lab_contract", "v3");
|
||||||
|
query.set("pagination", "cursor-v1");
|
||||||
|
if (cursor !== null) query.set("cursor", cursor);
|
||||||
|
const response = await fetcher(`/api/v1/observation-sessions?${query}`, {
|
||||||
|
method: "GET", headers: { Accept: "application/json" }, signal,
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new ObservationSessionApiError(
|
||||||
|
`Не удалось загрузить страницу каталога сессий (HTTP ${response.status}).`,
|
||||||
|
response.status,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let body: unknown;
|
||||||
|
try {
|
||||||
|
body = await response.json();
|
||||||
|
} catch {
|
||||||
|
throw new ObservationSessionContractError("Некорректный ответ каталога сессий.");
|
||||||
|
}
|
||||||
|
if (typeof body !== "object" || body === null || Array.isArray(body)) {
|
||||||
|
throw new ObservationSessionContractError("Страница каталога должна быть объектом.");
|
||||||
|
}
|
||||||
|
const page = body as Record<string, unknown>;
|
||||||
|
if (Object.keys(page).length !== 3
|
||||||
|
|| page.schema_version !== PAGE_SCHEMA
|
||||||
|
|| !Object.hasOwn(page, "items")
|
||||||
|
|| !(page.next_cursor === null
|
||||||
|
|| (typeof page.next_cursor === "string" && CURSOR.test(page.next_cursor)))) {
|
||||||
|
throw new ObservationSessionContractError("Нарушен контракт страницы каталога сессий.");
|
||||||
|
}
|
||||||
|
const catalog = decodeObservationSessionCatalog({ items: page.items });
|
||||||
|
if (catalog.items.length > limit) {
|
||||||
|
throw new ObservationSessionContractError("Страница каталога превысила запрошенный размер.");
|
||||||
|
}
|
||||||
|
signal?.throwIfAborted();
|
||||||
|
return { ...catalog, nextCursor: page.next_cursor };
|
||||||
|
}
|
||||||
@@ -12,6 +12,16 @@ import {
|
|||||||
type ObservationSessionScope,
|
type ObservationSessionScope,
|
||||||
type ObservationSessionSummary,
|
type ObservationSessionSummary,
|
||||||
} from "./sessionArchive";
|
} from "./sessionArchive";
|
||||||
|
import {
|
||||||
|
createObservationReplayCoordinator,
|
||||||
|
type ObservationReplayCoordinator,
|
||||||
|
} from "./replayCoordinator";
|
||||||
|
|
||||||
|
export {
|
||||||
|
createObservationReplayCoordinator,
|
||||||
|
type ObservationReplayAttempt,
|
||||||
|
type ObservationReplayCoordinator,
|
||||||
|
} from "./replayCoordinator";
|
||||||
|
|
||||||
export type ObservationSessionsLoadState = "idle" | "loading" | "ready" | "error";
|
export type ObservationSessionsLoadState = "idle" | "loading" | "ready" | "error";
|
||||||
export type ObservationReplayOutcome = "accepted" | "error" | "cancelled";
|
export type ObservationReplayOutcome = "accepted" | "error" | "cancelled";
|
||||||
@@ -41,17 +51,6 @@ export interface ObservationSessionsController {
|
|||||||
remove: (sessionId: string) => Promise<boolean>;
|
remove: (sessionId: string) => Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ObservationReplayAttempt {
|
|
||||||
readonly signal: AbortSignal;
|
|
||||||
isCurrent: () => boolean;
|
|
||||||
finish: () => boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ObservationReplayCoordinator {
|
|
||||||
begin: () => ObservationReplayAttempt;
|
|
||||||
cancel: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function deleteObservationSessionAfterTeardown(
|
export async function deleteObservationSessionAfterTeardown(
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
{
|
{
|
||||||
@@ -93,41 +92,6 @@ export class ObservationPreparationStalledError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Latest selection wins, even if an obsolete server job finishes later. */
|
|
||||||
export function createObservationReplayCoordinator(): ObservationReplayCoordinator {
|
|
||||||
let sequence = 0;
|
|
||||||
let active: AbortController | null = null;
|
|
||||||
|
|
||||||
return {
|
|
||||||
begin() {
|
|
||||||
active?.abort();
|
|
||||||
const controller = new AbortController();
|
|
||||||
const attemptSequence = ++sequence;
|
|
||||||
active = controller;
|
|
||||||
return {
|
|
||||||
signal: controller.signal,
|
|
||||||
isCurrent: () => (
|
|
||||||
!controller.signal.aborted &&
|
|
||||||
active === controller &&
|
|
||||||
sequence === attemptSequence
|
|
||||||
),
|
|
||||||
finish: () => {
|
|
||||||
if (active !== controller || sequence !== attemptSequence) return false;
|
|
||||||
active = null;
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
cancel() {
|
|
||||||
active?.abort();
|
|
||||||
// Keep ownership until the cancelled attempt reaches `finish()`. This
|
|
||||||
// lets its finally block settle a replacement that already passed
|
|
||||||
// onReplayBegin, while `isCurrent()` still fails immediately because the
|
|
||||||
// signal is aborted. A later `begin()` replaces and invalidates it.
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function errorMessage(error: unknown): string {
|
function errorMessage(error: unknown): string {
|
||||||
return error instanceof Error && error.message.trim()
|
return error instanceof Error && error.message.trim()
|
||||||
? error.message
|
? error.message
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
import type { RecordedAdmissionPhase } from "./recordedSessionAdmission";
|
||||||
|
|
||||||
|
export type RerunViewportStatus = "idle" | "loading" | "ready" | "error";
|
||||||
|
export type RecordedRerunView = "spatial" | "perception" | "perception3d" | "metrics";
|
||||||
|
|
||||||
|
export interface RerunPlaybackState {
|
||||||
|
recordingId: string;
|
||||||
|
timeline: string;
|
||||||
|
rangeNs: { min: number; max: number } | null;
|
||||||
|
currentNs: number;
|
||||||
|
playing: boolean;
|
||||||
|
bufferedEndNs: number | null;
|
||||||
|
expectedStartNs: number | null;
|
||||||
|
expectedEndNs: number | null;
|
||||||
|
bufferProgress: number | null;
|
||||||
|
fullyBuffered: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RerunPlaybackController {
|
||||||
|
seek: (timeNs: number) => void;
|
||||||
|
setPlaying: (playing: boolean) => void;
|
||||||
|
jumpToEnd: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RecordedPerceptionLayers {
|
||||||
|
enabled: boolean;
|
||||||
|
cameraImage?: boolean;
|
||||||
|
detections2d: boolean;
|
||||||
|
segmentation: boolean;
|
||||||
|
cuboids3d: boolean;
|
||||||
|
costmap?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RecordedRrdArtifactDescriptor {
|
||||||
|
sourceUrl: string;
|
||||||
|
viewerSourceUrl: string;
|
||||||
|
byteLength: number;
|
||||||
|
sha256: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The native live receiver owns one acquisition lineage and never exposes
|
||||||
|
* recorded transport or autoplay policy.
|
||||||
|
*/
|
||||||
|
export interface LiveAcquisitionRerunProfile {
|
||||||
|
kind: "live-acquisition";
|
||||||
|
clock: "stream_time";
|
||||||
|
sourceUrl: string;
|
||||||
|
liveActivitySequence: number | null;
|
||||||
|
liveStreamId: string | null;
|
||||||
|
liveRecoveryAuthorityIdentity: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A sealed session is progressively presentable, while its shared playback
|
||||||
|
* controls remain fenced by the aggregate session admission gate.
|
||||||
|
*/
|
||||||
|
export interface RecordedSessionRerunProfile {
|
||||||
|
kind: "recorded-session";
|
||||||
|
clock: "session_time";
|
||||||
|
sourceUrl: string;
|
||||||
|
artifact: RecordedRrdArtifactDescriptor | null;
|
||||||
|
autoplayWhenReady: boolean;
|
||||||
|
presentationGate: RecordedAdmissionPhase;
|
||||||
|
expectedTimelineStartSeconds?: number;
|
||||||
|
expectedTimelineEndSeconds?: number;
|
||||||
|
initialPlaybackStartSeconds?: number;
|
||||||
|
view: RecordedRerunView;
|
||||||
|
viewResetGeneration: 0 | 1;
|
||||||
|
followTrajectory: boolean;
|
||||||
|
perceptionLayers: RecordedPerceptionLayers;
|
||||||
|
/** Explicit small blueprint endpoint when the viewer source is a merged LAB RRD. */
|
||||||
|
blueprintSourceUrl?: string;
|
||||||
|
/** Optional immutable RRD sidecar for LAB/model evidence on the same recording clock. */
|
||||||
|
perceptionSourceUrl?: string;
|
||||||
|
/** Selects one semantic entity without changing the sealed sidecar. */
|
||||||
|
semanticLayer?: "city" | "vegetation";
|
||||||
|
/** Keeps one native Rerun store/viewer while presenting the accepted two-pane LAB layout. */
|
||||||
|
unifiedPerception?: boolean;
|
||||||
|
/** Controlled camera-column share for the native horizontal Rerun container. */
|
||||||
|
unifiedCameraShare?: number;
|
||||||
|
/** Requests the canonical top-down eye without changing the world coordinate frame. */
|
||||||
|
planView?: boolean;
|
||||||
|
perceptionRetryGeneration: number;
|
||||||
|
lockPerceptionCameraInteraction: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A LAB owns its result presentation; it does not inherit session-view settings. */
|
||||||
|
export interface LaboratoryResultRerunProfile
|
||||||
|
extends Omit<RecordedSessionRerunProfile, "kind"> {
|
||||||
|
kind: "laboratory-result";
|
||||||
|
resultId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deprecated comparison-only contract for LAB artifacts that have not yet
|
||||||
|
* been republished as a native Rerun sidecar. It must never be selected by a
|
||||||
|
* canonical LAB route or start work in the background.
|
||||||
|
*/
|
||||||
|
export interface LaboratoryRecordedEvidenceViewerProfile {
|
||||||
|
kind: "lab-recorded-evidence";
|
||||||
|
clock: "source-sequence";
|
||||||
|
cameraTransport: "generation-bound-fmp4";
|
||||||
|
spatialTransport: "bounded-sealed-artifacts";
|
||||||
|
loadPolicy: "explicit-legacy-comparison-only";
|
||||||
|
workerRequired: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RerunViewerProfile =
|
||||||
|
| LiveAcquisitionRerunProfile
|
||||||
|
| RecordedSessionRerunProfile
|
||||||
|
| LaboratoryResultRerunProfile;
|
||||||
|
|
||||||
|
export type ObservationViewerProfile =
|
||||||
|
| RerunViewerProfile
|
||||||
|
| LaboratoryRecordedEvidenceViewerProfile;
|
||||||
|
|
||||||
|
export const LABORATORY_RECORDED_EVIDENCE_VIEWER_PROFILE = Object.freeze({
|
||||||
|
kind: "lab-recorded-evidence",
|
||||||
|
clock: "source-sequence",
|
||||||
|
cameraTransport: "generation-bound-fmp4",
|
||||||
|
spatialTransport: "bounded-sealed-artifacts",
|
||||||
|
loadPolicy: "explicit-legacy-comparison-only",
|
||||||
|
workerRequired: false,
|
||||||
|
} satisfies LaboratoryRecordedEvidenceViewerProfile);
|
||||||
|
|
||||||
|
export function liveAcquisitionRerunProfile(
|
||||||
|
input: Omit<LiveAcquisitionRerunProfile, "kind" | "clock">,
|
||||||
|
): LiveAcquisitionRerunProfile {
|
||||||
|
return { ...input, kind: "live-acquisition", clock: "stream_time" };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function recordedSessionRerunProfile(
|
||||||
|
input: Omit<RecordedSessionRerunProfile, "kind" | "clock">,
|
||||||
|
): RecordedSessionRerunProfile {
|
||||||
|
return { ...input, kind: "recorded-session", clock: "session_time" };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function laboratoryResultRerunProfile(
|
||||||
|
input: Omit<LaboratoryResultRerunProfile, "kind" | "clock">,
|
||||||
|
): LaboratoryResultRerunProfile {
|
||||||
|
if (!input.resultId.trim()) throw new Error("LAB result identity is required");
|
||||||
|
return { ...input, kind: "laboratory-result", clock: "session_time" };
|
||||||
|
}
|
||||||
@@ -0,0 +1,347 @@
|
|||||||
|
import {
|
||||||
|
decodeObservatoryRecordedJob,
|
||||||
|
type ObservatoryRecordedJob,
|
||||||
|
} from "./recordedJobs";
|
||||||
|
|
||||||
|
const COMPOSITION_SCHEMA = "missioncore.observatory-ai-composition/v1";
|
||||||
|
const CATALOG_SCHEMA = "missioncore.observatory-ai-module-catalog/v1";
|
||||||
|
const RECEIPT_SCHEMA = "missioncore.observatory-ai-composition-receipt/v3";
|
||||||
|
const SHA256 = /^[a-f0-9]{64}$/;
|
||||||
|
|
||||||
|
export type AIGroupId = "segmentation" | "detection" | "geometry" | "range" | "motion" | "policy";
|
||||||
|
|
||||||
|
export interface AIModule {
|
||||||
|
readonly moduleId: string;
|
||||||
|
readonly moduleSha256: string;
|
||||||
|
readonly label: string;
|
||||||
|
readonly dockerName: string;
|
||||||
|
readonly requires: readonly string[];
|
||||||
|
readonly provides: readonly string[];
|
||||||
|
readonly defaults: Readonly<Record<string, unknown>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AIModuleGroup {
|
||||||
|
readonly group: AIGroupId;
|
||||||
|
readonly modules: readonly AIModule[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AIModuleCatalog {
|
||||||
|
readonly groups: readonly AIModuleGroup[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AICompositionReceipt {
|
||||||
|
readonly compositionSha256: string;
|
||||||
|
readonly created: boolean;
|
||||||
|
readonly dispatchReady: boolean;
|
||||||
|
readonly dispatchReason: string;
|
||||||
|
readonly setupIds: readonly string[];
|
||||||
|
readonly jobs: readonly ObservatoryRecordedJob[];
|
||||||
|
readonly run: AICompositionRun;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AIViewerLayer {
|
||||||
|
readonly layerId: string;
|
||||||
|
readonly paneId: "camera" | "spatial";
|
||||||
|
readonly label: string;
|
||||||
|
readonly control: "toggle" | "toggle-with-settings";
|
||||||
|
readonly order: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AICompositionPresentation {
|
||||||
|
readonly configurationLabel: string;
|
||||||
|
readonly modules: readonly { readonly moduleId: string; readonly label: string }[];
|
||||||
|
readonly viewerLayers: readonly AIViewerLayer[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AICompositionRun {
|
||||||
|
readonly runId: string;
|
||||||
|
readonly sourceSessionId: string;
|
||||||
|
readonly compositionSha256: string;
|
||||||
|
readonly moduleIds: readonly string[];
|
||||||
|
readonly setupIds: readonly string[];
|
||||||
|
readonly jobIds: readonly string[];
|
||||||
|
readonly resultIds: readonly string[];
|
||||||
|
readonly createdAtUtc: string;
|
||||||
|
readonly state: "running" | "ready" | "failed";
|
||||||
|
readonly configurationLabel: string;
|
||||||
|
readonly displayName: string | null;
|
||||||
|
readonly presentation: AICompositionPresentation;
|
||||||
|
readonly jobs: readonly ObservatoryRecordedJob[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AI_MODULE_SETUP_IDS: Readonly<Record<string, string>> = Object.freeze({
|
||||||
|
ddrnet: "ai-segmentation-ddrnet-v1",
|
||||||
|
eomt: "ai-segmentation-eomt-v1",
|
||||||
|
tgs: "m49-tgs-portable-v2",
|
||||||
|
"rf-detr": "ai-detection-rf-detr-v1",
|
||||||
|
"object-distance": "ai-range-object-distance-v1",
|
||||||
|
});
|
||||||
|
|
||||||
|
export function aiModuleIdForSetup(setupId: string): string | null {
|
||||||
|
return Object.entries(AI_MODULE_SETUP_IDS).find(([, value]) => value === setupId)?.[0] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchAIModuleCatalog(signal?: AbortSignal): Promise<AIModuleCatalog> {
|
||||||
|
const response = await fetch("/api/v1/observatory/ai-module-catalog", {
|
||||||
|
headers: { Accept: "application/json" }, signal,
|
||||||
|
});
|
||||||
|
const body = await bodyOf(response);
|
||||||
|
if (!response.ok) throw apiError(body, response.status);
|
||||||
|
const root = record(body, "каталог AI-модулей");
|
||||||
|
exactKeys(root, ["authority", "groups", "schema_version"]);
|
||||||
|
if (root.schema_version !== CATALOG_SCHEMA) throw new Error("Версия каталога AI-модулей изменилась.");
|
||||||
|
const known = new Set<AIGroupId>(["segmentation", "detection", "geometry", "range", "motion", "policy"]);
|
||||||
|
const groups = array(root.groups).map((value): AIModuleGroup => {
|
||||||
|
const group = record(value, "группа AI-модулей");
|
||||||
|
exactKeys(group, ["group", "modules"]);
|
||||||
|
if (typeof group.group !== "string" || !known.has(group.group as AIGroupId)) {
|
||||||
|
throw new Error("Каталог вернул неизвестную группу AI-модулей.");
|
||||||
|
}
|
||||||
|
return { group: group.group as AIGroupId, modules: array(group.modules).map(moduleOf) };
|
||||||
|
});
|
||||||
|
if (new Set(groups.map((group) => group.group)).size !== groups.length) {
|
||||||
|
throw new Error("Каталог AI-модулей содержит повторяющиеся группы.");
|
||||||
|
}
|
||||||
|
return { groups };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveAIComposition(
|
||||||
|
sourceSessionId: string,
|
||||||
|
selections: readonly { group: AIGroupId; module: AIModule }[],
|
||||||
|
): Promise<AICompositionReceipt> {
|
||||||
|
const response = await fetch("/api/v1/observatory/ai-compositions", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
schema_version: COMPOSITION_SCHEMA,
|
||||||
|
source_session_id: sourceSessionId,
|
||||||
|
idempotency_key: createIdempotencyKey(),
|
||||||
|
selections: selections.map(({ group, module }) => ({
|
||||||
|
group, module_id: module.moduleId, module_sha256: module.moduleSha256,
|
||||||
|
parameters: module.defaults,
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const body = await bodyOf(response);
|
||||||
|
if (!response.ok) throw apiError(body, response.status);
|
||||||
|
const root = record(body, "конфигурация AI-слоя");
|
||||||
|
exactKeys(root, ["composition", "composition_sha256", "created", "dispatch", "run", "schema_version", "source_session_id"]);
|
||||||
|
if (root.schema_version !== RECEIPT_SCHEMA || root.source_session_id !== sourceSessionId) {
|
||||||
|
throw new Error("Сервер вернул конфигурацию для другой записи.");
|
||||||
|
}
|
||||||
|
const digest = text(root.composition_sha256);
|
||||||
|
if (!SHA256.test(digest)) throw new Error("Сервер вернул некорректную идентичность композиции.");
|
||||||
|
const dispatch = record(root.dispatch, "готовность композиции");
|
||||||
|
exactKeys(dispatch, ["jobs", "ready", "reason", "setup_ids"]);
|
||||||
|
if (dispatch.ready !== true) {
|
||||||
|
throw new Error(text(dispatch.reason));
|
||||||
|
}
|
||||||
|
const setupIds = array(dispatch.setup_ids).map(text);
|
||||||
|
const jobs = array(dispatch.jobs).map(decodeObservatoryRecordedJob);
|
||||||
|
if (!jobs.length || jobs.length !== setupIds.length
|
||||||
|
|| jobs.some((job, index) => job.setupId !== setupIds[index])) {
|
||||||
|
throw new Error("Сервер вернул неполную очередь композиции.");
|
||||||
|
}
|
||||||
|
const run = compositionRunOf(root.run);
|
||||||
|
return {
|
||||||
|
compositionSha256: digest,
|
||||||
|
created: boolean(dispatch && root.created),
|
||||||
|
dispatchReady: boolean(dispatch.ready),
|
||||||
|
dispatchReason: text(dispatch.reason),
|
||||||
|
setupIds,
|
||||||
|
jobs,
|
||||||
|
run,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchAICompositionRuns(
|
||||||
|
sourceSessionId: string,
|
||||||
|
signal?: AbortSignal,
|
||||||
|
): Promise<readonly AICompositionRun[]> {
|
||||||
|
const query = new URLSearchParams({ source_session_id: sourceSessionId });
|
||||||
|
const response = await fetch(`/api/v1/observatory/ai-composition-runs?${query}`, {
|
||||||
|
headers: { Accept: "application/json" }, signal,
|
||||||
|
});
|
||||||
|
const body = await bodyOf(response);
|
||||||
|
if (!response.ok) throw apiError(body, response.status);
|
||||||
|
const root = record(body, "запуски композиций");
|
||||||
|
exactKeys(root, ["items", "schema_version"]);
|
||||||
|
if (root.schema_version !== "missioncore.observatory-ai-composition-run-list/v1") {
|
||||||
|
throw new Error("Версия запусков AI-композиций изменилась.");
|
||||||
|
}
|
||||||
|
return array(root.items).map(compositionRunOf);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function renameAICompositionRunProjection(
|
||||||
|
runId: string,
|
||||||
|
displayName: string,
|
||||||
|
): Promise<string> {
|
||||||
|
if (!/^ai-composition-[a-f0-9]{64}$/.test(runId)) {
|
||||||
|
throw new Error("Некорректная идентичность результата AI Inference.");
|
||||||
|
}
|
||||||
|
const normalized = displayName.trim();
|
||||||
|
if (!normalized || normalized.length > 160) {
|
||||||
|
throw new Error("Название результата должно содержать от 1 до 160 символов.");
|
||||||
|
}
|
||||||
|
const response = await fetch(`/api/v1/observatory/ai-composition-runs/${encodeURIComponent(runId)}`, {
|
||||||
|
method: "PATCH",
|
||||||
|
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
schema_version: "missioncore.observatory-ai-composition-run-rename/v1",
|
||||||
|
display_name: normalized,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const body = await bodyOf(response);
|
||||||
|
if (!response.ok) throw apiError(body, response.status);
|
||||||
|
const row = record(body, "результат AI Inference");
|
||||||
|
exactKeys(row, ["display_name", "run_id", "schema_version"]);
|
||||||
|
if (
|
||||||
|
row.schema_version !== "missioncore.observatory-ai-composition-run-projection/v1"
|
||||||
|
|| row.run_id !== runId
|
||||||
|
|| row.display_name !== normalized
|
||||||
|
) throw new Error("Сервер не подтвердил переименование результата AI Inference.");
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteAICompositionRunProjection(runId: string): Promise<void> {
|
||||||
|
if (!/^ai-composition-[a-f0-9]{64}$/.test(runId)) {
|
||||||
|
throw new Error("Некорректная идентичность результата AI Inference.");
|
||||||
|
}
|
||||||
|
const response = await fetch(`/api/v1/observatory/ai-composition-runs/${encodeURIComponent(runId)}`, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: { Accept: "application/json" },
|
||||||
|
});
|
||||||
|
if (!response.ok || response.status !== 204) {
|
||||||
|
throw apiError(await bodyOf(response), response.status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function compositionRunOf(value: unknown): AICompositionRun {
|
||||||
|
const row = record(value, "запуск AI-композиции");
|
||||||
|
const expected = [
|
||||||
|
"composition_sha256", "configuration_label", "created_at_utc", "display_name", "job_ids", "jobs",
|
||||||
|
"module_ids", "presentation", "result_ids", "run_id", "schema_version", "setup_ids",
|
||||||
|
"source_session_id", "state",
|
||||||
|
];
|
||||||
|
// The receipt embeds the immutable run before its live state projection.
|
||||||
|
const receiptShape = [
|
||||||
|
"composition_sha256", "created_at_utc", "job_ids", "module_ids", "presentation",
|
||||||
|
"run_id", "schema_version", "setup_ids", "source_session_id",
|
||||||
|
];
|
||||||
|
const keys = Object.keys(row);
|
||||||
|
const projected = keys.length === expected.length;
|
||||||
|
exactKeys(row, projected ? expected : receiptShape);
|
||||||
|
if (row.schema_version !== "missioncore.observatory-ai-composition-run/v1") {
|
||||||
|
throw new Error("Версия запуска AI-композиции изменилась.");
|
||||||
|
}
|
||||||
|
const sourceSessionId = text(row.source_session_id);
|
||||||
|
const compositionSha256 = text(row.composition_sha256);
|
||||||
|
const runId = text(row.run_id);
|
||||||
|
if (!/^ai-composition-[a-f0-9]{64}$/.test(runId) || !SHA256.test(compositionSha256)) {
|
||||||
|
throw new Error("Некорректная идентичность запуска AI-композиции.");
|
||||||
|
}
|
||||||
|
const presentation = presentationOf(row.presentation);
|
||||||
|
const state = projected ? text(row.state) : "running";
|
||||||
|
if (!new Set(["running", "ready", "failed"]).has(state)) {
|
||||||
|
throw new Error("Неизвестное состояние запуска AI-композиции.");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
runId,
|
||||||
|
sourceSessionId,
|
||||||
|
compositionSha256,
|
||||||
|
moduleIds: array(row.module_ids).map(text),
|
||||||
|
setupIds: array(row.setup_ids).map(text),
|
||||||
|
jobIds: array(row.job_ids).map(text),
|
||||||
|
resultIds: projected ? array(row.result_ids).map(text) : [],
|
||||||
|
createdAtUtc: text(row.created_at_utc),
|
||||||
|
state: state as AICompositionRun["state"],
|
||||||
|
configurationLabel: projected ? text(row.configuration_label) : presentation.configurationLabel,
|
||||||
|
displayName: projected
|
||||||
|
? row.display_name === null ? null : text(row.display_name)
|
||||||
|
: null,
|
||||||
|
presentation,
|
||||||
|
jobs: projected ? array(row.jobs).map(decodeObservatoryRecordedJob) : [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function presentationOf(value: unknown): AICompositionPresentation {
|
||||||
|
const row = record(value, "проекция AI-композиции");
|
||||||
|
exactKeys(row, ["configuration_label", "modules", "schema_version", "viewer_layers"]);
|
||||||
|
if (row.schema_version !== "missioncore.observatory-presentation-projection/v1") {
|
||||||
|
throw new Error("Версия проекции AI-композиции изменилась.");
|
||||||
|
}
|
||||||
|
const modules = array(row.modules).map((value) => {
|
||||||
|
const module = record(value, "модуль проекции");
|
||||||
|
exactKeys(module, ["label", "module_id"]);
|
||||||
|
return { moduleId: text(module.module_id), label: text(module.label) };
|
||||||
|
});
|
||||||
|
const viewerLayers = array(row.viewer_layers).map((value): AIViewerLayer => {
|
||||||
|
const layer = record(value, "слой viewer");
|
||||||
|
exactKeys(layer, ["control", "label", "layer_id", "order", "pane_id"]);
|
||||||
|
const paneId = text(layer.pane_id);
|
||||||
|
const control = text(layer.control);
|
||||||
|
if (!new Set(["camera", "spatial"]).has(paneId)
|
||||||
|
|| !new Set(["toggle", "toggle-with-settings"]).has(control)
|
||||||
|
|| typeof layer.order !== "number") {
|
||||||
|
throw new Error("Некорректная проекция слоя viewer.");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
layerId: text(layer.layer_id), paneId: paneId as AIViewerLayer["paneId"],
|
||||||
|
label: text(layer.label), control: control as AIViewerLayer["control"], order: layer.order,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return { configurationLabel: text(row.configuration_label), modules, viewerLayers };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchAICompositionJobs(
|
||||||
|
sourceSessionId: string,
|
||||||
|
signal?: AbortSignal,
|
||||||
|
): Promise<readonly ObservatoryRecordedJob[]> {
|
||||||
|
const query = new URLSearchParams({ source_session_id: sourceSessionId });
|
||||||
|
const response = await fetch(`/api/v1/observatory/ai-runs?${query}`, {
|
||||||
|
headers: { Accept: "application/json" }, signal,
|
||||||
|
});
|
||||||
|
const body = await bodyOf(response);
|
||||||
|
if (!response.ok) throw apiError(body, response.status);
|
||||||
|
const root = record(body, "очередь AI-слоёв");
|
||||||
|
exactKeys(root, ["authority", "items", "schema_version"]);
|
||||||
|
if (root.schema_version !== "missioncore.observatory-recorded-job-list/v1") {
|
||||||
|
throw new Error("Версия очереди AI-слоёв изменилась.");
|
||||||
|
}
|
||||||
|
return array(root.items).map(decodeObservatoryRecordedJob);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moduleOf(value: unknown): AIModule {
|
||||||
|
const row = record(value, "AI-модуль");
|
||||||
|
exactKeys(row, ["defaults", "docker_name", "label", "module_id", "module_sha256", "parameter_choices", "provides", "requires"]);
|
||||||
|
const digest = text(row.module_sha256);
|
||||||
|
if (!SHA256.test(digest)) throw new Error("Каталог вернул некорректную версию AI-модуля.");
|
||||||
|
return {
|
||||||
|
moduleId: text(row.module_id), moduleSha256: digest, label: text(row.label),
|
||||||
|
dockerName: text(row.docker_name), requires: array(row.requires).map(text),
|
||||||
|
provides: array(row.provides).map(text), defaults: record(row.defaults, "параметры AI-модуля"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function record(value: unknown, label: string): Record<string, unknown> {
|
||||||
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`Некорректный ${label}.`);
|
||||||
|
return value as Record<string, unknown>;
|
||||||
|
}
|
||||||
|
function array(value: unknown): unknown[] { if (!Array.isArray(value)) throw new Error("Ожидался список."); return value; }
|
||||||
|
function text(value: unknown): string { if (typeof value !== "string" || !value) throw new Error("Ожидался текст."); return value; }
|
||||||
|
function boolean(value: unknown): boolean { if (typeof value !== "boolean") throw new Error("Ожидалось логическое значение."); return value; }
|
||||||
|
function exactKeys(value: Record<string, unknown>, expected: string[]): void {
|
||||||
|
if (Object.keys(value).sort().join() !== [...expected].sort().join()) throw new Error("Контракт AI-слоя изменился.");
|
||||||
|
}
|
||||||
|
async function bodyOf(response: Response): Promise<unknown> { const value = await response.text(); try { return JSON.parse(value); } catch { return value; } }
|
||||||
|
function apiError(body: unknown, status: number): Error {
|
||||||
|
const detail = body && typeof body === "object" && !Array.isArray(body) ? (body as Record<string, unknown>).detail : null;
|
||||||
|
return new Error(typeof detail === "string" ? detail : `Observatory API вернул HTTP ${status}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createIdempotencyKey(): string {
|
||||||
|
const entropy = typeof globalThis.crypto?.randomUUID === "function"
|
||||||
|
? globalThis.crypto.randomUUID()
|
||||||
|
: `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
||||||
|
return `ai-layer:${entropy}`;
|
||||||
|
}
|
||||||
@@ -0,0 +1,321 @@
|
|||||||
|
import {
|
||||||
|
type ObservationLabInstance,
|
||||||
|
type ObservationSessionFetch,
|
||||||
|
type ObservationSessionSummary,
|
||||||
|
} from "../observation/sessionArchive";
|
||||||
|
import { fetchObservationSessionCatalogPage } from "../observation/sessionCatalogPage";
|
||||||
|
import {
|
||||||
|
observatoryRecordedRunBinding,
|
||||||
|
type ObservatoryRecordedRunBinding,
|
||||||
|
} from "./recordedRun";
|
||||||
|
|
||||||
|
export interface ObservatoryEvidence {
|
||||||
|
readonly sessionId: string;
|
||||||
|
readonly label: string;
|
||||||
|
readonly status: ObservationSessionSummary["status"];
|
||||||
|
readonly publishedAtUtc: string;
|
||||||
|
readonly lab: ObservationLabInstance;
|
||||||
|
readonly recordedRun: ObservatoryRecordedRunBinding | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ObservatorySession {
|
||||||
|
readonly source: ObservationSessionSummary;
|
||||||
|
readonly evidence: readonly ObservatoryEvidence[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ObservatoryCatalog {
|
||||||
|
readonly items: readonly ObservatorySession[];
|
||||||
|
readonly unresolvedEvidence: readonly ObservatoryEvidence[];
|
||||||
|
readonly window: {
|
||||||
|
readonly limit: number;
|
||||||
|
readonly sourceCount: number;
|
||||||
|
readonly laboratoryCount: number;
|
||||||
|
readonly sourceLimitReached: boolean;
|
||||||
|
readonly laboratoryLimitReached: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ObservatoryCatalogMutation =
|
||||||
|
| {
|
||||||
|
readonly kind: "rename";
|
||||||
|
readonly displayName: string;
|
||||||
|
readonly revision: number;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
readonly kind: "delete";
|
||||||
|
readonly revision: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ObservatoryCatalogMutationOverlay = ReadonlyMap<
|
||||||
|
string,
|
||||||
|
ObservatoryCatalogMutation
|
||||||
|
>;
|
||||||
|
|
||||||
|
export interface ObservatoryCatalogReconciliation {
|
||||||
|
readonly catalog: ObservatoryCatalog;
|
||||||
|
readonly overlay: ObservatoryCatalogMutationOverlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ObservatoryCatalogContractError extends Error {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
this.name = "ObservatoryCatalogContractError";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findObservatoryEvidence(
|
||||||
|
catalog: ObservatoryCatalog,
|
||||||
|
sessionId: string,
|
||||||
|
): ObservatoryEvidence | null {
|
||||||
|
for (const item of catalog.items) {
|
||||||
|
const evidence = item.evidence.find(
|
||||||
|
(candidate) => candidate.sessionId === sessionId,
|
||||||
|
);
|
||||||
|
if (evidence) return evidence;
|
||||||
|
}
|
||||||
|
return catalog.unresolvedEvidence.find(
|
||||||
|
(candidate) => candidate.sessionId === sessionId,
|
||||||
|
) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function observatoryCatalogConfirmsEvidenceDeletion(
|
||||||
|
catalog: ObservatoryCatalog,
|
||||||
|
sessionId: string,
|
||||||
|
): boolean {
|
||||||
|
return !catalog.window.laboratoryLimitReached
|
||||||
|
&& findObservatoryEvidence(catalog, sessionId) === null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applyObservatoryCatalogMutationOverlay(
|
||||||
|
catalog: ObservatoryCatalog,
|
||||||
|
overlay: ObservatoryCatalogMutationOverlay,
|
||||||
|
): ObservatoryCatalog {
|
||||||
|
if (overlay.size === 0) return catalog;
|
||||||
|
|
||||||
|
const removedSessionIds = new Set<string>();
|
||||||
|
const projectEvidence = (
|
||||||
|
evidence: ObservatoryEvidence,
|
||||||
|
): ObservatoryEvidence | null => {
|
||||||
|
const mutation = overlay.get(evidence.sessionId);
|
||||||
|
if (!mutation) return evidence;
|
||||||
|
if (mutation.kind === "delete") {
|
||||||
|
removedSessionIds.add(evidence.sessionId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return evidence.label === mutation.displayName
|
||||||
|
? evidence
|
||||||
|
: { ...evidence, label: mutation.displayName };
|
||||||
|
};
|
||||||
|
const projectEvidenceList = (
|
||||||
|
evidence: readonly ObservatoryEvidence[],
|
||||||
|
): ObservatoryEvidence[] => evidence.flatMap((candidate) => {
|
||||||
|
const projected = projectEvidence(candidate);
|
||||||
|
return projected ? [projected] : [];
|
||||||
|
});
|
||||||
|
|
||||||
|
const items = catalog.items.map((item) => ({
|
||||||
|
...item,
|
||||||
|
evidence: projectEvidenceList(item.evidence),
|
||||||
|
}));
|
||||||
|
const unresolvedEvidence = projectEvidenceList(catalog.unresolvedEvidence);
|
||||||
|
return {
|
||||||
|
...catalog,
|
||||||
|
items,
|
||||||
|
unresolvedEvidence,
|
||||||
|
window: {
|
||||||
|
...catalog.window,
|
||||||
|
laboratoryCount: Math.max(
|
||||||
|
0,
|
||||||
|
catalog.window.laboratoryCount - removedSessionIds.size,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function reconcileObservatoryCatalogMutationOverlay(
|
||||||
|
serverCatalog: ObservatoryCatalog,
|
||||||
|
overlay: ObservatoryCatalogMutationOverlay,
|
||||||
|
requestMutationRevision: number,
|
||||||
|
): ObservatoryCatalogReconciliation {
|
||||||
|
const remaining = new Map(overlay);
|
||||||
|
for (const [sessionId, mutation] of overlay) {
|
||||||
|
if (mutation.revision > requestMutationRevision) continue;
|
||||||
|
const evidence = findObservatoryEvidence(serverCatalog, sessionId);
|
||||||
|
const confirmed = mutation.kind === "rename"
|
||||||
|
? evidence?.label === mutation.displayName
|
||||||
|
: observatoryCatalogConfirmsEvidenceDeletion(serverCatalog, sessionId);
|
||||||
|
if (confirmed) remaining.delete(sessionId);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
catalog: applyObservatoryCatalogMutationOverlay(serverCatalog, remaining),
|
||||||
|
overlay: remaining,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function newestFirst(left: string, right: string): number {
|
||||||
|
return Date.parse(right) - Date.parse(left);
|
||||||
|
}
|
||||||
|
|
||||||
|
function boundedLimit(limit: number): number {
|
||||||
|
return Number.isFinite(limit)
|
||||||
|
? Math.min(100, Math.max(1, Math.floor(limit)))
|
||||||
|
: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
function evidenceFromSession(
|
||||||
|
session: ObservationSessionSummary,
|
||||||
|
): ObservatoryEvidence | null {
|
||||||
|
if (!session.lab) {
|
||||||
|
throw new ObservatoryCatalogContractError(
|
||||||
|
`LAB-каталог вернул сессию ${session.id} без типизированной связи с источником.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Historical LABs retain their archive and replay adapters. Observatory is
|
||||||
|
// the portable-profile product; a familiar LAB label is not admission.
|
||||||
|
if (session.lab.replayCapability?.kind !== "portable-result-review") return null;
|
||||||
|
const recordedRun = observatoryRecordedRunBinding(session.id, session.lab);
|
||||||
|
return {
|
||||||
|
sessionId: session.id,
|
||||||
|
label: session.label,
|
||||||
|
status: session.status,
|
||||||
|
publishedAtUtc: session.lab.publishedAtUtc,
|
||||||
|
lab: session.lab,
|
||||||
|
recordedRun,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortEvidence(
|
||||||
|
evidence: readonly ObservatoryEvidence[],
|
||||||
|
): ObservatoryEvidence[] {
|
||||||
|
return [...evidence].sort((left, right) =>
|
||||||
|
newestFirst(left.publishedAtUtc, right.publishedAtUtc)
|
||||||
|
|| left.sessionId.localeCompare(right.sessionId));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildObservatoryCatalog(
|
||||||
|
sourceSessions: readonly ObservationSessionSummary[],
|
||||||
|
laboratorySessions: readonly ObservationSessionSummary[],
|
||||||
|
limit = 100,
|
||||||
|
): ObservatoryCatalog {
|
||||||
|
const safeLimit = boundedLimit(limit);
|
||||||
|
const sources = new Map<string, ObservationSessionSummary>();
|
||||||
|
for (const source of sourceSessions) {
|
||||||
|
if (source.lab !== null) {
|
||||||
|
throw new ObservatoryCatalogContractError(
|
||||||
|
`Каталог источников вернул LAB-сессию ${source.id}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
sources.set(source.id, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
const linked = new Map<string, ObservatoryEvidence[]>();
|
||||||
|
const unresolvedEvidence: ObservatoryEvidence[] = [];
|
||||||
|
for (const laboratorySession of laboratorySessions) {
|
||||||
|
const evidence = evidenceFromSession(laboratorySession);
|
||||||
|
if (!evidence) continue;
|
||||||
|
const sourceId = evidence.lab.sourceSessionId;
|
||||||
|
if (!sources.has(sourceId)) {
|
||||||
|
// A bounded traversal may stop before this source. Absence alone is
|
||||||
|
// not proof of a broken relationship or permission to remove data.
|
||||||
|
unresolvedEvidence.push(evidence);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const current = linked.get(sourceId) ?? [];
|
||||||
|
current.push(evidence);
|
||||||
|
linked.set(sourceId, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = [...sources.values()]
|
||||||
|
.sort((left, right) =>
|
||||||
|
newestFirst(left.startedAtUtc, right.startedAtUtc)
|
||||||
|
|| left.id.localeCompare(right.id))
|
||||||
|
.map((source) => ({
|
||||||
|
source,
|
||||||
|
evidence: sortEvidence(linked.get(source.id) ?? []),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
items,
|
||||||
|
unresolvedEvidence: sortEvidence(unresolvedEvidence),
|
||||||
|
window: {
|
||||||
|
limit: safeLimit,
|
||||||
|
sourceCount: sourceSessions.length,
|
||||||
|
laboratoryCount: [...linked.values()].reduce((count, items) => count + items.length, 0)
|
||||||
|
+ unresolvedEvidence.length,
|
||||||
|
sourceLimitReached: sourceSessions.length >= safeLimit,
|
||||||
|
laboratoryLimitReached: laboratorySessions.length >= safeLimit,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchObservatoryCatalog({
|
||||||
|
signal,
|
||||||
|
limit = 100,
|
||||||
|
fetcher = globalThis.fetch,
|
||||||
|
}: {
|
||||||
|
signal?: AbortSignal;
|
||||||
|
limit?: number;
|
||||||
|
fetcher?: ObservationSessionFetch;
|
||||||
|
} = {}): Promise<ObservatoryCatalog> {
|
||||||
|
const safeLimit = boundedLimit(limit);
|
||||||
|
const request = new AbortController();
|
||||||
|
const abort = () => request.abort(signal?.reason);
|
||||||
|
if (signal?.aborted) abort();
|
||||||
|
else signal?.addEventListener("abort", abort, { once: true });
|
||||||
|
try {
|
||||||
|
const [sources, laboratories] = await Promise.all([
|
||||||
|
readCatalogPages("source", safeLimit, fetcher, request.signal),
|
||||||
|
readCatalogPages("laboratory", safeLimit, fetcher, request.signal),
|
||||||
|
]);
|
||||||
|
const catalog = buildObservatoryCatalog(sources.items, laboratories.items, safeLimit);
|
||||||
|
return {
|
||||||
|
...catalog,
|
||||||
|
window: {
|
||||||
|
...catalog.window,
|
||||||
|
sourceLimitReached: sources.hasMore,
|
||||||
|
laboratoryLimitReached: laboratories.hasMore,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} finally {
|
||||||
|
request.abort();
|
||||||
|
signal?.removeEventListener("abort", abort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata only, never recordings or result artifacts. The cap is an explicit
|
||||||
|
// partial window, not silent completeness or an unbounded background crawler.
|
||||||
|
const MAX_CATALOG_PAGES = 256;
|
||||||
|
|
||||||
|
async function readCatalogPages(
|
||||||
|
scope: "source" | "laboratory",
|
||||||
|
limit: number,
|
||||||
|
fetcher: ObservationSessionFetch,
|
||||||
|
signal: AbortSignal,
|
||||||
|
): Promise<{ items: ObservationSessionSummary[]; hasMore: boolean }> {
|
||||||
|
const items: ObservationSessionSummary[] = [];
|
||||||
|
const seenIds = new Set<string>();
|
||||||
|
const cursors = new Set<string>();
|
||||||
|
let cursor: string | null = null;
|
||||||
|
for (let index = 0; index < MAX_CATALOG_PAGES; index += 1) {
|
||||||
|
const page = await fetchObservationSessionCatalogPage({ scope, limit, cursor, signal, fetcher });
|
||||||
|
for (const item of page.items) {
|
||||||
|
if (seenIds.has(item.id)) {
|
||||||
|
throw new ObservatoryCatalogContractError("Каталог повторил сессию между страницами.");
|
||||||
|
}
|
||||||
|
seenIds.add(item.id);
|
||||||
|
// Do not accumulate legacy provenance while walking the shared catalog.
|
||||||
|
if (scope === "source" || item.lab?.replayCapability?.kind === "portable-result-review") {
|
||||||
|
items.push(item);
|
||||||
|
} else if (!item.lab) {
|
||||||
|
throw new ObservatoryCatalogContractError("LAB-каталог вернул исходную сессию.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cursor = page.nextCursor;
|
||||||
|
if (cursor === null) return { items, hasMore: false };
|
||||||
|
if (cursors.has(cursor)) {
|
||||||
|
throw new ObservatoryCatalogContractError("Каталог повторил курсор страницы.");
|
||||||
|
}
|
||||||
|
cursors.add(cursor);
|
||||||
|
}
|
||||||
|
return { items, hasMore: true };
|
||||||
|
}
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
import type { ObservatoryRecordedRunBinding } from "./recordedRun";
|
||||||
|
|
||||||
|
const SAFE_SESSION_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
||||||
|
const RENAME_SCHEMA = "missioncore.observatory-lab-projection-rename/v1";
|
||||||
|
const PROJECTION_SCHEMA = "missioncore.observatory-lab-projection/v1";
|
||||||
|
|
||||||
|
export type ObservatoryCatalogMutationFetch = (
|
||||||
|
input: RequestInfo | URL,
|
||||||
|
init?: RequestInit,
|
||||||
|
) => Promise<Response>;
|
||||||
|
|
||||||
|
export interface ObservatoryLabProjectionMutationResult {
|
||||||
|
readonly schemaVersion: typeof PROJECTION_SCHEMA;
|
||||||
|
readonly sessionId: string;
|
||||||
|
readonly displayName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ObservatoryCatalogMutationError extends Error {
|
||||||
|
readonly status: number | null;
|
||||||
|
|
||||||
|
constructor(message: string, status: number | null = null) {
|
||||||
|
super(message);
|
||||||
|
this.name = "ObservatoryCatalogMutationError";
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function renameObservatoryLabProjection(
|
||||||
|
binding: ObservatoryRecordedRunBinding,
|
||||||
|
displayName: string,
|
||||||
|
{
|
||||||
|
signal,
|
||||||
|
fetcher = globalThis.fetch,
|
||||||
|
}: {
|
||||||
|
signal?: AbortSignal;
|
||||||
|
fetcher?: ObservatoryCatalogMutationFetch;
|
||||||
|
} = {},
|
||||||
|
): Promise<ObservatoryLabProjectionMutationResult> {
|
||||||
|
const sessionId = admittedProjectionId(binding);
|
||||||
|
const normalizedName = displayName.trim();
|
||||||
|
if (normalizedName.length < 1 || normalizedName.length > 160) {
|
||||||
|
throw new ObservatoryCatalogMutationError(
|
||||||
|
"Название лабораторного результата должно содержать от 1 до 160 символов.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const response = await request(
|
||||||
|
fetcher,
|
||||||
|
`/api/v1/observatory/lab-projections/${encodeURIComponent(sessionId)}`,
|
||||||
|
{
|
||||||
|
method: "PATCH",
|
||||||
|
headers: {
|
||||||
|
Accept: "application/json",
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
schema_version: RENAME_SCHEMA,
|
||||||
|
display_name: normalizedName,
|
||||||
|
}),
|
||||||
|
signal,
|
||||||
|
},
|
||||||
|
"Не удалось переименовать лабораторный результат.",
|
||||||
|
);
|
||||||
|
const body = await responseBody(response);
|
||||||
|
if (!response.ok || response.status !== 200) {
|
||||||
|
throw apiError(
|
||||||
|
body,
|
||||||
|
`Переименование лабораторного результата вернуло HTTP ${response.status}.`,
|
||||||
|
response.status,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const decoded = decodeProjection(body);
|
||||||
|
if (decoded.sessionId !== sessionId || decoded.displayName !== normalizedName) {
|
||||||
|
throw new ObservatoryCatalogMutationError(
|
||||||
|
"Сервер не подтвердил точное переименование выбранного результата.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteObservatoryLabProjection(
|
||||||
|
binding: ObservatoryRecordedRunBinding,
|
||||||
|
{
|
||||||
|
signal,
|
||||||
|
fetcher = globalThis.fetch,
|
||||||
|
}: {
|
||||||
|
signal?: AbortSignal;
|
||||||
|
fetcher?: ObservatoryCatalogMutationFetch;
|
||||||
|
} = {},
|
||||||
|
): Promise<void> {
|
||||||
|
const sessionId = admittedProjectionId(binding);
|
||||||
|
const response = await request(
|
||||||
|
fetcher,
|
||||||
|
`/api/v1/observatory/lab-projections/${encodeURIComponent(sessionId)}`,
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
headers: { Accept: "application/json" },
|
||||||
|
signal,
|
||||||
|
},
|
||||||
|
"Не удалось удалить лабораторный результат из AI Inference.",
|
||||||
|
);
|
||||||
|
const body = await responseBody(response);
|
||||||
|
if (!response.ok || response.status !== 204) {
|
||||||
|
throw apiError(
|
||||||
|
body,
|
||||||
|
`Удаление лабораторного результата вернуло HTTP ${response.status}.`,
|
||||||
|
response.status,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (body !== undefined) {
|
||||||
|
throw new ObservatoryCatalogMutationError(
|
||||||
|
"Сервер вернул данные после подтверждённого удаления лабораторного результата.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function admittedProjectionId(binding: ObservatoryRecordedRunBinding): string {
|
||||||
|
const sessionId = binding.evidenceSessionId;
|
||||||
|
const admittedViewer = (
|
||||||
|
binding.kind === "canonical-recorded-rerun"
|
||||||
|
&& binding.viewerProfile === "recorded-session"
|
||||||
|
&& binding.timeline === "session_time"
|
||||||
|
) || (
|
||||||
|
binding.kind === "portable-result-review"
|
||||||
|
&& binding.viewerProfile === "portable-result"
|
||||||
|
&& binding.timeline === "result-defined"
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
!admittedViewer
|
||||||
|
|| binding.activation !== "explicit"
|
||||||
|
|| binding.resultId !== sessionId
|
||||||
|
|| binding.sourceSessionId === sessionId
|
||||||
|
|| !SAFE_SESSION_ID.test(sessionId)
|
||||||
|
) {
|
||||||
|
throw new ObservatoryCatalogMutationError(
|
||||||
|
"Выбранный результат не допущен к изменению каталога AI Inference.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function request(
|
||||||
|
fetcher: ObservatoryCatalogMutationFetch,
|
||||||
|
input: string,
|
||||||
|
init: RequestInit,
|
||||||
|
fallback: string,
|
||||||
|
): Promise<Response> {
|
||||||
|
try {
|
||||||
|
return await fetcher(input, init);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof DOMException && error.name === "AbortError") throw error;
|
||||||
|
throw new ObservatoryCatalogMutationError(fallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function responseBody(response: Response): Promise<unknown> {
|
||||||
|
const text = await response.text();
|
||||||
|
if (!text) return undefined;
|
||||||
|
try {
|
||||||
|
return JSON.parse(text) as unknown;
|
||||||
|
} catch {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function apiError(body: unknown, fallback: string, status: number): Error {
|
||||||
|
if (isRecord(body) && typeof body.detail === "string" && body.detail.trim()) {
|
||||||
|
return new ObservatoryCatalogMutationError(body.detail.trim(), status);
|
||||||
|
}
|
||||||
|
if (typeof body === "string" && body.trim()) {
|
||||||
|
return new ObservatoryCatalogMutationError(body.trim(), status);
|
||||||
|
}
|
||||||
|
return new ObservatoryCatalogMutationError(fallback, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeProjection(value: unknown): ObservatoryLabProjectionMutationResult {
|
||||||
|
if (!isRecord(value)) {
|
||||||
|
throw new ObservatoryCatalogMutationError(
|
||||||
|
"Сервер вернул некорректное подтверждение лабораторного результата.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const keys = Object.keys(value).sort();
|
||||||
|
const expected = ["display_name", "schema_version", "session_id"];
|
||||||
|
if (keys.length !== expected.length || keys.some((key, index) => key !== expected[index])) {
|
||||||
|
throw new ObservatoryCatalogMutationError(
|
||||||
|
"Подтверждение лабораторного результата содержит неизвестные поля.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
value.schema_version !== PROJECTION_SCHEMA
|
||||||
|
|| typeof value.session_id !== "string"
|
||||||
|
|| !SAFE_SESSION_ID.test(value.session_id)
|
||||||
|
|| typeof value.display_name !== "string"
|
||||||
|
|| value.display_name.trim() !== value.display_name
|
||||||
|
|| value.display_name.length < 1
|
||||||
|
|| value.display_name.length > 160
|
||||||
|
) {
|
||||||
|
throw new ObservatoryCatalogMutationError(
|
||||||
|
"Сервер вернул неподдерживаемый контракт лабораторного результата.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
schemaVersion: PROJECTION_SCHEMA,
|
||||||
|
sessionId: value.session_id,
|
||||||
|
displayName: value.display_name,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import type { SceneSettings } from "../../sceneSettings";
|
||||||
|
|
||||||
|
const PROFILE_SCHEMA = "missioncore.observatory-lab-view-profile/v1";
|
||||||
|
|
||||||
|
export async function fetchLabViewProfile(
|
||||||
|
resultId: string,
|
||||||
|
signal?: AbortSignal,
|
||||||
|
): Promise<SceneSettings | null> {
|
||||||
|
const response = await fetch(`/api/v1/observatory/lab-view-profiles/${encodeURIComponent(resultId)}`, {
|
||||||
|
headers: { Accept: "application/json" },
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
if (response.status === 404) return null;
|
||||||
|
const body: unknown = await response.json();
|
||||||
|
if (!response.ok) throw new Error("Профиль отображения LAB не загрузился.");
|
||||||
|
return decodeProfile(body, resultId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveLabViewProfile(
|
||||||
|
resultId: string,
|
||||||
|
settings: SceneSettings,
|
||||||
|
signal?: AbortSignal,
|
||||||
|
): Promise<SceneSettings> {
|
||||||
|
const response = await fetch(`/api/v1/observatory/lab-view-profiles/${encodeURIComponent(resultId)}`, {
|
||||||
|
method: "PUT",
|
||||||
|
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
schema_version: PROFILE_SCHEMA,
|
||||||
|
result_id: resultId,
|
||||||
|
scene_settings: {
|
||||||
|
point_size: settings.pointSize,
|
||||||
|
accumulation_seconds: settings.accumulationSeconds,
|
||||||
|
color_mode: settings.colorMode,
|
||||||
|
palette: settings.palette,
|
||||||
|
show_grid: settings.showGrid,
|
||||||
|
show_labels: settings.showLabels,
|
||||||
|
show_camera_frustums: settings.showCameraFrustums,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
const body: unknown = await response.json();
|
||||||
|
if (!response.ok) throw new Error("Настройки LAB не сохранились. Повторите закрытие окна.");
|
||||||
|
return decodeProfile(body, resultId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeProfile(value: unknown, expectedResultId: string): SceneSettings {
|
||||||
|
const row = record(value);
|
||||||
|
exactKeys(row, ["result_id", "scene_settings", "schema_version", "updated_at_utc"]);
|
||||||
|
if (row.schema_version !== PROFILE_SCHEMA || row.result_id !== expectedResultId) {
|
||||||
|
throw new Error("Сервер вернул профиль другой LAB.");
|
||||||
|
}
|
||||||
|
const settings = record(row.scene_settings);
|
||||||
|
exactKeys(settings, [
|
||||||
|
"accumulation_seconds", "color_mode", "palette", "point_size",
|
||||||
|
"show_camera_frustums", "show_grid", "show_labels",
|
||||||
|
]);
|
||||||
|
const pointSize = finite(settings.point_size);
|
||||||
|
const accumulationSeconds = finite(settings.accumulation_seconds);
|
||||||
|
const colorMode = settings.color_mode;
|
||||||
|
const palette = settings.palette;
|
||||||
|
if (
|
||||||
|
pointSize < 0.1
|
||||||
|
|| accumulationSeconds < 0
|
||||||
|
|| !new Set(["intensity", "height", "distance", "rgb", "class"]).has(String(colorMode))
|
||||||
|
|| !new Set(["turbo", "viridis", "plasma", "grayscale"]).has(String(palette))
|
||||||
|
|| typeof settings.show_grid !== "boolean"
|
||||||
|
|| typeof settings.show_labels !== "boolean"
|
||||||
|
|| typeof settings.show_camera_frustums !== "boolean"
|
||||||
|
) throw new Error("Сервер вернул повреждённый профиль LAB.");
|
||||||
|
return {
|
||||||
|
projection: "3d",
|
||||||
|
pointSize,
|
||||||
|
colorMode: colorMode as SceneSettings["colorMode"],
|
||||||
|
palette: palette as SceneSettings["palette"],
|
||||||
|
customColor: "#35d7c1",
|
||||||
|
accumulationSeconds,
|
||||||
|
showPoints: true,
|
||||||
|
showTrajectory: true,
|
||||||
|
showGrid: settings.show_grid,
|
||||||
|
showLabels: settings.show_labels,
|
||||||
|
showCameraFrustums: settings.show_camera_frustums,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function record(value: unknown): Record<string, unknown> {
|
||||||
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||||
|
throw new Error("Некорректный профиль отображения LAB.");
|
||||||
|
}
|
||||||
|
return value as Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function finite(value: unknown): number {
|
||||||
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
||||||
|
throw new Error("Некорректное числовое значение профиля LAB.");
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function exactKeys(value: Record<string, unknown>, expected: string[]): void {
|
||||||
|
if (Object.keys(value).sort().join() !== [...expected].sort().join()) {
|
||||||
|
throw new Error("Контракт профиля отображения LAB изменился.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,449 @@
|
|||||||
|
export { fetchObservatoryPortableLaboratorySetups } from "./portableLaboratorySetups";
|
||||||
|
|
||||||
|
const CATALOG_SCHEMA = "missioncore.observatory-laboratory-setup-catalog/v1";
|
||||||
|
const PREFLIGHT_REQUEST_SCHEMA = "missioncore.observatory-run-preflight-request/v1";
|
||||||
|
const PREFLIGHT_SCHEMA = "missioncore.observatory-run-preflight/v1";
|
||||||
|
const SHA256 = /^[a-f0-9]{64}$/;
|
||||||
|
|
||||||
|
export type ObservatoryLaboratorySetupOrigin =
|
||||||
|
| "archived-definition"
|
||||||
|
| "existing-result"
|
||||||
|
| "portable-definition";
|
||||||
|
export type ObservatoryLaboratorySetupAction =
|
||||||
|
| "open-existing"
|
||||||
|
| "open-legacy"
|
||||||
|
| "check"
|
||||||
|
| "blocked";
|
||||||
|
|
||||||
|
export interface ObservatoryLaboratoryRunDefinition {
|
||||||
|
readonly definitionId: string;
|
||||||
|
readonly version: number;
|
||||||
|
readonly workId: string | null;
|
||||||
|
readonly definitionSha256: string;
|
||||||
|
readonly resultSchema: string | null;
|
||||||
|
readonly resultKind: string | null;
|
||||||
|
readonly models: readonly {
|
||||||
|
readonly name: string;
|
||||||
|
readonly releaseId: string;
|
||||||
|
readonly modelId: string;
|
||||||
|
readonly architecture: string;
|
||||||
|
}[];
|
||||||
|
readonly configuration: readonly {
|
||||||
|
readonly role: string;
|
||||||
|
readonly sha256: string;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ObservatoryLaboratoryPreservedResult {
|
||||||
|
readonly resultId: string;
|
||||||
|
readonly resultKind: string;
|
||||||
|
readonly relation: string;
|
||||||
|
readonly access: "legacy-lab" | "observatory" | "evidence-only";
|
||||||
|
readonly createdAtUtc: string;
|
||||||
|
readonly observatoryProjectionAvailable: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ObservatoryLaboratorySetup {
|
||||||
|
readonly setupId: string;
|
||||||
|
readonly displayName: string;
|
||||||
|
readonly description: string;
|
||||||
|
readonly origin: ObservatoryLaboratorySetupOrigin;
|
||||||
|
readonly runDefinition: ObservatoryLaboratoryRunDefinition | null;
|
||||||
|
readonly compatibility: {
|
||||||
|
readonly compatible: boolean;
|
||||||
|
readonly reasons: readonly { readonly code: string; readonly message: string }[];
|
||||||
|
};
|
||||||
|
readonly executor: {
|
||||||
|
readonly contourId: string;
|
||||||
|
readonly state: "not-installed" | "ready";
|
||||||
|
readonly reasonCode: string;
|
||||||
|
readonly reason: string;
|
||||||
|
};
|
||||||
|
readonly preservedResults: readonly ObservatoryLaboratoryPreservedResult[];
|
||||||
|
readonly preflight: {
|
||||||
|
readonly outcome: "existing" | "ready" | "blocked";
|
||||||
|
readonly action: ObservatoryLaboratorySetupAction;
|
||||||
|
readonly reason: string;
|
||||||
|
readonly submissionAllowed: boolean;
|
||||||
|
readonly existingResultIds: readonly string[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ObservatoryLaboratorySetupCatalog {
|
||||||
|
readonly sourceSessionId: string;
|
||||||
|
readonly setups: readonly ObservatoryLaboratorySetup[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache identity is verified by the portable catalog decoder/server, never by a LAB label.
|
||||||
|
export function selectableObservatorySetups(catalog: ObservatoryLaboratorySetupCatalog | null) {
|
||||||
|
return catalog?.setups.filter((setup) => (
|
||||||
|
setup.origin === "portable-definition"
|
||||||
|
&& setup.runDefinition !== null
|
||||||
|
&& setup.compatibility.compatible
|
||||||
|
&& setup.preflight.outcome !== "existing"
|
||||||
|
)) ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ObservatoryLaboratoryRunPreflight {
|
||||||
|
readonly sourceSessionId: string;
|
||||||
|
readonly setupId: string;
|
||||||
|
readonly definitionSha256: string | null;
|
||||||
|
readonly checkSha256: string | null;
|
||||||
|
readonly outcome: "existing" | "queueable" | "blocked";
|
||||||
|
readonly submissionAllowed: boolean;
|
||||||
|
readonly checks: readonly {
|
||||||
|
readonly checkId: string;
|
||||||
|
readonly outcome: "pass" | "fail" | "not-applicable";
|
||||||
|
readonly reasonCode: string;
|
||||||
|
readonly message: string;
|
||||||
|
}[];
|
||||||
|
readonly existingResultIds: readonly string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ObservatoryLaboratorySetupFetch = (
|
||||||
|
input: RequestInfo | URL,
|
||||||
|
init?: RequestInit,
|
||||||
|
) => Promise<Response>;
|
||||||
|
|
||||||
|
export class ObservatoryLaboratorySetupContractError extends Error {
|
||||||
|
readonly status: number | null;
|
||||||
|
|
||||||
|
constructor(message: string, status: number | null = null) {
|
||||||
|
super(message);
|
||||||
|
this.name = "ObservatoryLaboratorySetupContractError";
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchObservatoryLaboratorySetups(
|
||||||
|
sourceSessionId: string,
|
||||||
|
{
|
||||||
|
signal,
|
||||||
|
fetcher = globalThis.fetch,
|
||||||
|
}: {
|
||||||
|
signal?: AbortSignal;
|
||||||
|
fetcher?: ObservatoryLaboratorySetupFetch;
|
||||||
|
} = {},
|
||||||
|
): Promise<ObservatoryLaboratorySetupCatalog> {
|
||||||
|
const response = await request(
|
||||||
|
fetcher,
|
||||||
|
`/api/v1/observatory/laboratory-setups?source_session_id=${encodeURIComponent(sourceSessionId)}`,
|
||||||
|
{ method: "GET", headers: { Accept: "application/json" }, signal },
|
||||||
|
);
|
||||||
|
const body = await responseBody(response);
|
||||||
|
if (!response.ok) throw apiError(body, response.status);
|
||||||
|
const catalog = decodeCatalog(body);
|
||||||
|
if (catalog.sourceSessionId !== sourceSessionId) {
|
||||||
|
throw new ObservatoryLaboratorySetupContractError(
|
||||||
|
"Каталог сетапов относится к другой исходной сессии.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return catalog;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function preflightObservatoryLaboratorySetup(
|
||||||
|
sourceSessionId: string,
|
||||||
|
setup: ObservatoryLaboratorySetup,
|
||||||
|
{
|
||||||
|
signal,
|
||||||
|
fetcher = globalThis.fetch,
|
||||||
|
}: {
|
||||||
|
signal?: AbortSignal;
|
||||||
|
fetcher?: ObservatoryLaboratorySetupFetch;
|
||||||
|
} = {},
|
||||||
|
): Promise<ObservatoryLaboratoryRunPreflight> {
|
||||||
|
const response = await request(
|
||||||
|
fetcher,
|
||||||
|
"/api/v1/observatory/run-preflights",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
schema_version: PREFLIGHT_REQUEST_SCHEMA,
|
||||||
|
source_session_id: sourceSessionId,
|
||||||
|
setup_id: setup.setupId,
|
||||||
|
definition_sha256: setup.runDefinition?.definitionSha256 ?? null,
|
||||||
|
}),
|
||||||
|
signal,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const body = await responseBody(response);
|
||||||
|
if (!response.ok) throw apiError(body, response.status);
|
||||||
|
const preflight = decodePreflight(body);
|
||||||
|
if (preflight.sourceSessionId !== sourceSessionId || preflight.setupId !== setup.setupId) {
|
||||||
|
throw new ObservatoryLaboratorySetupContractError(
|
||||||
|
"Preflight относится к другому источнику или сетапу.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (preflight.definitionSha256 !== (setup.runDefinition?.definitionSha256 ?? null)) {
|
||||||
|
throw new ObservatoryLaboratorySetupContractError(
|
||||||
|
"Preflight относится к другой версии сетапа.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return preflight;
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeCatalog(value: unknown): ObservatoryLaboratorySetupCatalog {
|
||||||
|
const row = record(value, "каталог сетапов");
|
||||||
|
exactKeys(row, ["authority", "schema_version", "setups", "source_session_id"], "каталог сетапов");
|
||||||
|
exact(row.schema_version, CATALOG_SCHEMA, "schema_version каталога сетапов");
|
||||||
|
observationAuthority(row.authority);
|
||||||
|
return {
|
||||||
|
sourceSessionId: text(row.source_session_id, "source_session_id"),
|
||||||
|
setups: array(row.setups, "setups").map(decodeSetup),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeSetup(value: unknown): ObservatoryLaboratorySetup {
|
||||||
|
const row = record(value, "сетап");
|
||||||
|
exactKeys(row, [
|
||||||
|
"authority", "compatibility", "description", "display_name", "executor", "origin",
|
||||||
|
"preflight", "preserved_results", "run_definition", "setup_id", "source",
|
||||||
|
], "сетап");
|
||||||
|
observationAuthority(row.authority);
|
||||||
|
const origin = oneOf(row.origin, ["archived-definition", "existing-result"] as const, "origin");
|
||||||
|
const compatibility = record(row.compatibility, "compatibility");
|
||||||
|
const executor = record(row.executor, "executor");
|
||||||
|
const preflight = record(row.preflight, "preflight");
|
||||||
|
const source = record(row.source, "source");
|
||||||
|
const runDefinition = row.run_definition === null
|
||||||
|
? null
|
||||||
|
: decodeRunDefinition(row.run_definition);
|
||||||
|
if ((origin === "archived-definition") !== (runDefinition !== null)) {
|
||||||
|
throw new ObservatoryLaboratorySetupContractError(
|
||||||
|
"Происхождение сетапа не совпадает с RunDefinition.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
exactKeys(source, ["label", "required_modalities", "session_id"], "source");
|
||||||
|
text(source.session_id, "source.session_id");
|
||||||
|
text(source.label, "source.label");
|
||||||
|
array(source.required_modalities, "source.required_modalities").forEach((item) => text(item, "source modality"));
|
||||||
|
exactKeys(compatibility, ["compatible", "reasons"], "compatibility");
|
||||||
|
exactKeys(executor, ["contour_id", "reason", "reason_code", "state"], "executor");
|
||||||
|
exactKeys(preflight, ["action", "existing_result_ids", "outcome", "reason", "submission_allowed"], "preflight");
|
||||||
|
return {
|
||||||
|
setupId: text(row.setup_id, "setup_id"),
|
||||||
|
displayName: text(row.display_name, "display_name"),
|
||||||
|
description: text(row.description, "description"),
|
||||||
|
origin,
|
||||||
|
runDefinition,
|
||||||
|
compatibility: {
|
||||||
|
compatible: boolean(row.compatibility && compatibility.compatible, "compatible"),
|
||||||
|
reasons: array(compatibility.reasons, "compatibility.reasons").map((item) => {
|
||||||
|
const reason = record(item, "compatibility reason");
|
||||||
|
exactKeys(reason, ["code", "message"], "compatibility reason");
|
||||||
|
return { code: text(reason.code, "reason.code"), message: text(reason.message, "reason.message") };
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
executor: {
|
||||||
|
contourId: text(executor.contour_id, "executor.contour_id"),
|
||||||
|
state: exact(executor.state, "not-installed", "executor.state"),
|
||||||
|
reasonCode: text(executor.reason_code, "executor.reason_code"),
|
||||||
|
reason: text(executor.reason, "executor.reason"),
|
||||||
|
},
|
||||||
|
preservedResults: array(row.preserved_results, "preserved_results").map(decodePreservedResult),
|
||||||
|
preflight: {
|
||||||
|
outcome: oneOf(preflight.outcome, ["existing", "blocked"] as const, "preflight.outcome"),
|
||||||
|
action: oneOf(preflight.action, ["open-existing", "open-legacy", "blocked"] as const, "preflight.action"),
|
||||||
|
reason: text(preflight.reason, "preflight.reason"),
|
||||||
|
submissionAllowed: exact(preflight.submission_allowed, false, "preflight.submission_allowed"),
|
||||||
|
existingResultIds: array(preflight.existing_result_ids, "preflight.existing_result_ids").map((item) => text(item, "existing result id")),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeRunDefinition(value: unknown): ObservatoryLaboratoryRunDefinition {
|
||||||
|
const row = record(value, "RunDefinition");
|
||||||
|
exactKeys(row, [
|
||||||
|
"authority", "configuration", "definition_id", "definition_sha256", "schema_version",
|
||||||
|
"source", "version", "work_id",
|
||||||
|
], "RunDefinition");
|
||||||
|
exact(row.schema_version, "missioncore.observatory-run-definition/v1", "RunDefinition schema");
|
||||||
|
observationAuthority(row.authority);
|
||||||
|
const source = record(row.source, "RunDefinition source");
|
||||||
|
exactKeys(source, ["label", "required_modalities", "session_id"], "RunDefinition source");
|
||||||
|
text(source.session_id, "RunDefinition source.session_id");
|
||||||
|
text(source.label, "RunDefinition source.label");
|
||||||
|
array(source.required_modalities, "RunDefinition source.required_modalities")
|
||||||
|
.forEach((item) => text(item, "RunDefinition source modality"));
|
||||||
|
const digest = text(row.definition_sha256, "definition_sha256");
|
||||||
|
if (!SHA256.test(digest)) throw new ObservatoryLaboratorySetupContractError("Некорректный digest RunDefinition.");
|
||||||
|
return {
|
||||||
|
definitionId: text(row.definition_id, "definition_id"),
|
||||||
|
version: positiveInteger(row.version, "definition version"),
|
||||||
|
workId: text(row.work_id, "work_id"),
|
||||||
|
definitionSha256: digest,
|
||||||
|
resultSchema: null,
|
||||||
|
resultKind: null,
|
||||||
|
models: [],
|
||||||
|
configuration: array(row.configuration, "configuration").map((item) => {
|
||||||
|
const reference = record(item, "configuration reference");
|
||||||
|
exactKeys(reference, ["role", "sha256"], "configuration reference");
|
||||||
|
const sha256 = text(reference.sha256, "configuration sha256");
|
||||||
|
if (!SHA256.test(sha256)) throw new ObservatoryLaboratorySetupContractError("Некорректный digest конфигурации.");
|
||||||
|
return { role: text(reference.role, "configuration role"), sha256 };
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodePreservedResult(value: unknown): ObservatoryLaboratoryPreservedResult {
|
||||||
|
const row = record(value, "preserved result");
|
||||||
|
exactKeys(row, [
|
||||||
|
"access", "created_at_utc", "observatory_projection_available", "relation",
|
||||||
|
"result_id", "result_kind",
|
||||||
|
], "preserved result");
|
||||||
|
return {
|
||||||
|
resultId: text(row.result_id, "result_id"),
|
||||||
|
resultKind: text(row.result_kind, "result_kind"),
|
||||||
|
relation: text(row.relation, "relation"),
|
||||||
|
access: oneOf(row.access, ["legacy-lab", "observatory", "evidence-only"] as const, "result access"),
|
||||||
|
createdAtUtc: text(row.created_at_utc, "created_at_utc"),
|
||||||
|
observatoryProjectionAvailable: boolean(row.observatory_projection_available, "observatory projection availability"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodePreflight(value: unknown): ObservatoryLaboratoryRunPreflight {
|
||||||
|
const row = record(value, "preflight");
|
||||||
|
const baseKeys = [
|
||||||
|
"authority", "checks", "definition_sha256", "executor", "existing_result_ids",
|
||||||
|
"outcome", "schema_version", "setup_id", "source_session_id", "submission_allowed",
|
||||||
|
] as const;
|
||||||
|
const hasPortableCheck = Object.hasOwn(row, "check_sha256");
|
||||||
|
exactKeys(
|
||||||
|
row,
|
||||||
|
hasPortableCheck ? [...baseKeys, "check_sha256"] : baseKeys,
|
||||||
|
"preflight",
|
||||||
|
);
|
||||||
|
exact(row.schema_version, PREFLIGHT_SCHEMA, "preflight schema");
|
||||||
|
observationAuthority(row.authority);
|
||||||
|
const digest = row.definition_sha256;
|
||||||
|
if (digest !== null && (typeof digest !== "string" || !SHA256.test(digest))) {
|
||||||
|
throw new ObservatoryLaboratorySetupContractError("Некорректный digest preflight.");
|
||||||
|
}
|
||||||
|
const checkDigest = hasPortableCheck ? row.check_sha256 : null;
|
||||||
|
if (
|
||||||
|
checkDigest !== null
|
||||||
|
&& (typeof checkDigest !== "string" || !SHA256.test(checkDigest))
|
||||||
|
) {
|
||||||
|
throw new ObservatoryLaboratorySetupContractError(
|
||||||
|
"Некорректный check digest preflight.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const outcome = oneOf(
|
||||||
|
row.outcome,
|
||||||
|
["existing", "queueable", "blocked"] as const,
|
||||||
|
"preflight outcome",
|
||||||
|
);
|
||||||
|
const submissionAllowed = boolean(
|
||||||
|
row.submission_allowed,
|
||||||
|
"preflight submission_allowed",
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
submissionAllowed !== (outcome === "queueable")
|
||||||
|
|| (hasPortableCheck && outcome === "queueable" && checkDigest === null)
|
||||||
|
) {
|
||||||
|
throw new ObservatoryLaboratorySetupContractError(
|
||||||
|
"Preflight содержит противоречивое разрешение постановки в очередь.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
sourceSessionId: text(row.source_session_id, "preflight source_session_id"),
|
||||||
|
setupId: text(row.setup_id, "preflight setup_id"),
|
||||||
|
definitionSha256: digest,
|
||||||
|
checkSha256: checkDigest,
|
||||||
|
outcome,
|
||||||
|
submissionAllowed,
|
||||||
|
checks: array(row.checks, "preflight checks").map((item) => {
|
||||||
|
const check = record(item, "preflight check");
|
||||||
|
exactKeys(check, ["check_id", "message", "outcome", "reason_code"], "preflight check");
|
||||||
|
return {
|
||||||
|
checkId: text(check.check_id, "check_id"),
|
||||||
|
outcome: oneOf(check.outcome, ["pass", "fail", "not-applicable"] as const, "check outcome"),
|
||||||
|
reasonCode: text(check.reason_code, "check reason_code"),
|
||||||
|
message: text(check.message, "check message"),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
existingResultIds: array(row.existing_result_ids, "existing_result_ids").map((item) => text(item, "existing result id")),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function observationAuthority(value: unknown): void {
|
||||||
|
const row = record(value, "authority");
|
||||||
|
const keys = ["commands_enabled", "actuation_allowed", "navigation_or_safety_accepted", "production_accepted"];
|
||||||
|
exactKeys(row, keys, "authority");
|
||||||
|
for (const key of keys) {
|
||||||
|
exact(row[key], false, `authority.${key}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function request(fetcher: ObservatoryLaboratorySetupFetch, input: string, init: RequestInit): Promise<Response> {
|
||||||
|
try {
|
||||||
|
return await fetcher(input, init);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof DOMException && error.name === "AbortError") throw error;
|
||||||
|
throw new ObservatoryLaboratorySetupContractError("Каталог сетапов недоступен.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function responseBody(response: Response): Promise<unknown> {
|
||||||
|
const textBody = await response.text();
|
||||||
|
if (!textBody) return undefined;
|
||||||
|
try { return JSON.parse(textBody) as unknown; } catch { return textBody; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function apiError(body: unknown, status: number): ObservatoryLaboratorySetupContractError {
|
||||||
|
const detail = body && typeof body === "object" && !Array.isArray(body)
|
||||||
|
? (body as Record<string, unknown>).detail
|
||||||
|
: null;
|
||||||
|
return new ObservatoryLaboratorySetupContractError(
|
||||||
|
typeof detail === "string" && detail.trim() ? detail : `Observatory API вернул HTTP ${status}.`,
|
||||||
|
status,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function record(value: unknown, label: string): Record<string, unknown> {
|
||||||
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||||
|
throw new ObservatoryLaboratorySetupContractError(`${label}: ожидался объект.`);
|
||||||
|
}
|
||||||
|
return value as Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function array(value: unknown, label: string): unknown[] {
|
||||||
|
if (!Array.isArray(value)) throw new ObservatoryLaboratorySetupContractError(`${label}: ожидался массив.`);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function text(value: unknown, label: string): string {
|
||||||
|
if (typeof value !== "string" || !value.trim()) throw new ObservatoryLaboratorySetupContractError(`${label}: ожидался текст.`);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function boolean(value: unknown, label: string): boolean {
|
||||||
|
if (typeof value !== "boolean") throw new ObservatoryLaboratorySetupContractError(`${label}: ожидался boolean.`);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function positiveInteger(value: unknown, label: string): number {
|
||||||
|
if (!Number.isInteger(value) || Number(value) < 1) throw new ObservatoryLaboratorySetupContractError(`${label}: ожидалось положительное целое.`);
|
||||||
|
return Number(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exact<T>(value: unknown, expected: T, label: string): T {
|
||||||
|
if (value !== expected) throw new ObservatoryLaboratorySetupContractError(`${label}: значение изменилось.`);
|
||||||
|
return expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
function oneOf<const T extends readonly string[]>(value: unknown, allowed: T, label: string): T[number] {
|
||||||
|
if (typeof value !== "string" || !allowed.includes(value)) {
|
||||||
|
throw new ObservatoryLaboratorySetupContractError(`${label}: значение не поддерживается.`);
|
||||||
|
}
|
||||||
|
return value as T[number];
|
||||||
|
}
|
||||||
|
|
||||||
|
function exactKeys(value: Record<string, unknown>, expected: readonly string[], label: string): void {
|
||||||
|
const actual = Object.keys(value).sort();
|
||||||
|
const sortedExpected = [...expected].sort();
|
||||||
|
if (actual.length !== sortedExpected.length || actual.some((key, index) => key !== sortedExpected[index])) {
|
||||||
|
throw new ObservatoryLaboratorySetupContractError(`${label}: обнаружены неизвестные поля.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,373 @@
|
|||||||
|
import type {
|
||||||
|
ObservatoryLaboratoryPreservedResult,
|
||||||
|
ObservatoryLaboratoryRunDefinition,
|
||||||
|
ObservatoryLaboratorySetup,
|
||||||
|
ObservatoryLaboratorySetupCatalog,
|
||||||
|
} from "./laboratorySetups";
|
||||||
|
|
||||||
|
const PORTABLE_CATALOG_SCHEMA = "missioncore.observatory-portable-setup-catalog/v2";
|
||||||
|
const SHA256 = /^[a-f0-9]{64}$/;
|
||||||
|
|
||||||
|
export class ObservatoryPortableSetupDecodeError extends Error {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
this.name = "ObservatoryPortableSetupDecodeError";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodePortableCatalog(value: unknown): ObservatoryLaboratorySetupCatalog {
|
||||||
|
const row = record(value, "portable-каталог сетапов");
|
||||||
|
exactKeys(
|
||||||
|
row,
|
||||||
|
["authority", "schema_version", "setups", "source_session_id"],
|
||||||
|
"portable-каталог сетапов",
|
||||||
|
);
|
||||||
|
exact(row.schema_version, PORTABLE_CATALOG_SCHEMA, "schema_version portable-каталога");
|
||||||
|
observationAuthority(row.authority);
|
||||||
|
const sourceSessionId = text(row.source_session_id, "portable source_session_id");
|
||||||
|
return {
|
||||||
|
sourceSessionId,
|
||||||
|
setups: array(row.setups, "portable setups").map((item) => decodePortableSetup(item, sourceSessionId)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodePortableSetup(value: unknown, sourceSessionId: string): ObservatoryLaboratorySetup {
|
||||||
|
const row = record(value, "portable-сетап");
|
||||||
|
exactKeys(row, [
|
||||||
|
"authority", "description", "display_name", "executor", "existing_results",
|
||||||
|
"origin", "preflight", "run_definition", "setup_id", "source_compatibility",
|
||||||
|
"source_requirements",
|
||||||
|
], "portable-сетап");
|
||||||
|
exact(row.origin, "portable-definition", "portable origin");
|
||||||
|
observationAuthority(row.authority);
|
||||||
|
decodePortableSourceRequirements(row.source_requirements);
|
||||||
|
const setupId = text(row.setup_id, "portable setup_id");
|
||||||
|
const runDefinition = decodePortableRunDefinition(row.run_definition);
|
||||||
|
|
||||||
|
const compatibility = record(row.source_compatibility, "portable source_compatibility");
|
||||||
|
exactKeys(
|
||||||
|
compatibility,
|
||||||
|
["compatible", "outcome", "reason", "reason_code"],
|
||||||
|
"portable source_compatibility",
|
||||||
|
);
|
||||||
|
const compatible = boolean(compatibility.compatible, "portable compatible");
|
||||||
|
exact(
|
||||||
|
compatibility.outcome,
|
||||||
|
compatible ? "pass" : "blocked",
|
||||||
|
"portable compatibility outcome",
|
||||||
|
);
|
||||||
|
const compatibilityReason = text(compatibility.reason, "portable compatibility reason");
|
||||||
|
const compatibilityReasonCode = text(
|
||||||
|
compatibility.reason_code,
|
||||||
|
"portable compatibility reason_code",
|
||||||
|
);
|
||||||
|
|
||||||
|
const executor = record(row.executor, "portable executor");
|
||||||
|
exactKeys(
|
||||||
|
executor,
|
||||||
|
["contour_id", "ready", "reason", "reason_code", "state"],
|
||||||
|
"portable executor",
|
||||||
|
);
|
||||||
|
const executorState = oneOf(
|
||||||
|
executor.state,
|
||||||
|
["not-installed", "ready"] as const,
|
||||||
|
"portable executor state",
|
||||||
|
);
|
||||||
|
const executorReady = boolean(executor.ready, "portable executor ready");
|
||||||
|
if (executorReady !== (executorState === "ready")) {
|
||||||
|
throw new ObservatoryPortableSetupDecodeError(
|
||||||
|
"Portable executor: состояние готовности противоречиво.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const executorReason = executor.reason === null
|
||||||
|
? "Исполнитель установлен."
|
||||||
|
: text(executor.reason, "portable executor reason");
|
||||||
|
const executorReasonCode = executor.reason_code === null
|
||||||
|
? "portable-executor-ready"
|
||||||
|
: text(executor.reason_code, "portable executor reason_code");
|
||||||
|
if (
|
||||||
|
(executorReady && (executor.reason !== null || executor.reason_code !== null))
|
||||||
|
|| (!executorReady && (executor.reason === null || executor.reason_code === null))
|
||||||
|
) {
|
||||||
|
throw new ObservatoryPortableSetupDecodeError(
|
||||||
|
"Portable executor: причина недоступности противоречит состоянию.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const preflight = record(row.preflight, "portable preflight");
|
||||||
|
exactKeys(preflight, [
|
||||||
|
"action", "existing_result_ids", "outcome", "reason", "submission_allowed",
|
||||||
|
], "portable preflight");
|
||||||
|
const preflightOutcome = oneOf(
|
||||||
|
preflight.outcome,
|
||||||
|
["existing", "ready", "blocked"] as const,
|
||||||
|
"portable preflight outcome",
|
||||||
|
);
|
||||||
|
const preflightAction = oneOf(
|
||||||
|
preflight.action,
|
||||||
|
["open-existing", "check", "blocked"] as const,
|
||||||
|
"portable preflight action",
|
||||||
|
);
|
||||||
|
const submissionAllowed = boolean(
|
||||||
|
preflight.submission_allowed,
|
||||||
|
"portable preflight submission_allowed",
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
submissionAllowed !== (preflightOutcome === "ready")
|
||||||
|
|| (preflightOutcome === "ready" && preflightAction !== "check")
|
||||||
|
|| (preflightOutcome === "blocked" && preflightAction !== "blocked")
|
||||||
|
|| (preflightOutcome === "existing" && preflightAction !== "open-existing")
|
||||||
|
|| (submissionAllowed && (!compatible || !executorReady))
|
||||||
|
) {
|
||||||
|
throw new ObservatoryPortableSetupDecodeError(
|
||||||
|
"Portable preflight: состояние запуска противоречиво.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const existingResults = array(row.existing_results, "portable existing_results").map(
|
||||||
|
(item) => decodePortableResult(item, sourceSessionId, setupId, runDefinition),
|
||||||
|
);
|
||||||
|
const existingResultIds = array(
|
||||||
|
preflight.existing_result_ids,
|
||||||
|
"portable existing_result_ids",
|
||||||
|
).map((item) => text(item, "portable existing result id"));
|
||||||
|
if (
|
||||||
|
(existingResults.length > 0) !== (preflightOutcome === "existing")
|
||||||
|
|| existingResultIds.length !== existingResults.length
|
||||||
|
|| new Set(existingResultIds).size !== existingResultIds.length
|
||||||
|
|| existingResults.some((result, index) => result.resultId !== existingResultIds[index])
|
||||||
|
) {
|
||||||
|
throw new ObservatoryPortableSetupDecodeError(
|
||||||
|
"Portable result: готовность не соответствует проверенным результатам.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
setupId,
|
||||||
|
displayName: text(row.display_name, "portable display_name"),
|
||||||
|
description: text(row.description, "portable description"),
|
||||||
|
origin: "portable-definition",
|
||||||
|
runDefinition,
|
||||||
|
compatibility: {
|
||||||
|
compatible,
|
||||||
|
reasons: compatible
|
||||||
|
? []
|
||||||
|
: [{ code: compatibilityReasonCode, message: compatibilityReason }],
|
||||||
|
},
|
||||||
|
executor: {
|
||||||
|
contourId: text(executor.contour_id, "portable executor contour_id"),
|
||||||
|
state: executorState,
|
||||||
|
reasonCode: executorReasonCode,
|
||||||
|
reason: executorReason,
|
||||||
|
},
|
||||||
|
preservedResults: existingResults,
|
||||||
|
preflight: {
|
||||||
|
outcome: preflightOutcome,
|
||||||
|
action: preflightAction,
|
||||||
|
reason: text(preflight.reason, "portable preflight reason"),
|
||||||
|
submissionAllowed,
|
||||||
|
existingResultIds,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodePortableResult(
|
||||||
|
value: unknown, sourceSessionId: string, setupId: string,
|
||||||
|
definition: ObservatoryLaboratoryRunDefinition,
|
||||||
|
): ObservatoryLaboratoryPreservedResult {
|
||||||
|
const row = record(value, "portable result");
|
||||||
|
exactKeys(row, [
|
||||||
|
"result_id", "result_kind", "relation", "access", "created_at_utc",
|
||||||
|
"observatory_projection_available", "identity",
|
||||||
|
], "portable result");
|
||||||
|
exact(row.result_kind, definition.resultKind, "portable result kind");
|
||||||
|
exact(row.relation, "exact-recorded-computation", "portable result relation");
|
||||||
|
exact(row.access, "observatory", "portable result access");
|
||||||
|
exact(row.observatory_projection_available, true, "portable result availability");
|
||||||
|
const identity = record(row.identity, "portable result identity");
|
||||||
|
exactKeys(identity, [
|
||||||
|
"job_id", "source_session_id", "source_catalog_sha256", "source_bundle_sha256",
|
||||||
|
"source_capability_manifest_sha256", "setup_id", "definition_sha256",
|
||||||
|
"package_sha256", "artifact_manifest_id",
|
||||||
|
], "portable result identity");
|
||||||
|
exact(identity.source_session_id, sourceSessionId, "portable result source");
|
||||||
|
exact(identity.setup_id, setupId, "portable result setup");
|
||||||
|
exact(identity.definition_sha256, definition.definitionSha256, "portable result definition");
|
||||||
|
text(identity.job_id, "portable result job");
|
||||||
|
for (const key of [
|
||||||
|
"source_catalog_sha256", "source_bundle_sha256", "source_capability_manifest_sha256",
|
||||||
|
"definition_sha256", "package_sha256", "artifact_manifest_id",
|
||||||
|
]) {
|
||||||
|
if (!SHA256.test(text(identity[key], `portable result ${key}`))) {
|
||||||
|
throw new ObservatoryPortableSetupDecodeError(`Portable result: некорректный ${key}.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
resultId: text(row.result_id, "portable result id"),
|
||||||
|
resultKind: text(row.result_kind, "portable result kind"),
|
||||||
|
relation: "exact-recorded-computation",
|
||||||
|
access: "observatory",
|
||||||
|
createdAtUtc: text(row.created_at_utc, "portable result created_at_utc"),
|
||||||
|
observatoryProjectionAvailable: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodePortableSourceRequirements(value: unknown): void {
|
||||||
|
const row = record(value, "portable source_requirements");
|
||||||
|
exactKeys(row, [
|
||||||
|
"archive_id", "calibration_identity_sha256", "calibration_slot", "camera_height",
|
||||||
|
"camera_semantic_channel_id", "camera_source_id", "camera_width",
|
||||||
|
"exactly_one_media_epoch", "plugin_id", "recorded_media_init_sha256",
|
||||||
|
"recorded_media_type", "required_modalities", "seekable",
|
||||||
|
], "portable source_requirements");
|
||||||
|
for (const key of [
|
||||||
|
"archive_id", "calibration_slot", "camera_semantic_channel_id", "camera_source_id",
|
||||||
|
"plugin_id", "recorded_media_type",
|
||||||
|
]) text(row[key], `portable source_requirements.${key}`);
|
||||||
|
for (const key of ["calibration_identity_sha256", "recorded_media_init_sha256"]) {
|
||||||
|
const digest = text(row[key], `portable source_requirements.${key}`);
|
||||||
|
if (!SHA256.test(digest)) {
|
||||||
|
throw new ObservatoryPortableSetupDecodeError(
|
||||||
|
`portable source_requirements.${key}: некорректный digest.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
positiveInteger(row.camera_width, "portable camera_width");
|
||||||
|
positiveInteger(row.camera_height, "portable camera_height");
|
||||||
|
exact(row.exactly_one_media_epoch, true, "portable exactly_one_media_epoch");
|
||||||
|
exact(row.seekable, true, "portable seekable");
|
||||||
|
array(row.required_modalities, "portable required_modalities")
|
||||||
|
.forEach((item) => text(item, "portable required modality"));
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodePortableRunDefinition(value: unknown): ObservatoryLaboratoryRunDefinition {
|
||||||
|
const row = record(value, "portable RunDefinition");
|
||||||
|
exactKeys(row, [
|
||||||
|
"definition_id", "definition_sha256", "models", "result_kind", "result_schema",
|
||||||
|
"version",
|
||||||
|
], "portable RunDefinition");
|
||||||
|
const digest = text(row.definition_sha256, "portable definition_sha256");
|
||||||
|
if (!SHA256.test(digest)) {
|
||||||
|
throw new ObservatoryPortableSetupDecodeError(
|
||||||
|
"Portable RunDefinition: некорректный digest.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
definitionId: text(row.definition_id, "portable definition_id"),
|
||||||
|
version: positiveInteger(row.version, "portable definition version"),
|
||||||
|
workId: null,
|
||||||
|
definitionSha256: digest,
|
||||||
|
resultSchema: text(row.result_schema, "portable result_schema"),
|
||||||
|
resultKind: text(row.result_kind, "portable result_kind"),
|
||||||
|
models: array(row.models, "portable models").map((item) => {
|
||||||
|
const model = record(item, "portable model");
|
||||||
|
exactKeys(
|
||||||
|
model,
|
||||||
|
["architecture", "model_id", "name", "release_id"],
|
||||||
|
"portable model",
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
name: text(model.name, "portable model name"),
|
||||||
|
releaseId: text(model.release_id, "portable model release_id"),
|
||||||
|
modelId: text(model.model_id, "portable model model_id"),
|
||||||
|
architecture: text(model.architecture, "portable model architecture"),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
configuration: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function observationAuthority(value: unknown): void {
|
||||||
|
const row = record(value, "authority");
|
||||||
|
const keys = [
|
||||||
|
"commands_enabled",
|
||||||
|
"actuation_allowed",
|
||||||
|
"navigation_or_safety_accepted",
|
||||||
|
"production_accepted",
|
||||||
|
];
|
||||||
|
exactKeys(row, keys, "authority");
|
||||||
|
for (const key of keys) exact(row[key], false, `authority.${key}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function record(value: unknown, label: string): Record<string, unknown> {
|
||||||
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||||
|
throw new ObservatoryPortableSetupDecodeError(
|
||||||
|
`${label}: ожидался объект.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return value as Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function array(value: unknown, label: string): unknown[] {
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
throw new ObservatoryPortableSetupDecodeError(
|
||||||
|
`${label}: ожидался массив.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function text(value: unknown, label: string): string {
|
||||||
|
if (typeof value !== "string" || !value.trim()) {
|
||||||
|
throw new ObservatoryPortableSetupDecodeError(
|
||||||
|
`${label}: ожидался текст.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function boolean(value: unknown, label: string): boolean {
|
||||||
|
if (typeof value !== "boolean") {
|
||||||
|
throw new ObservatoryPortableSetupDecodeError(
|
||||||
|
`${label}: ожидался boolean.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function positiveInteger(value: unknown, label: string): number {
|
||||||
|
if (!Number.isInteger(value) || Number(value) < 1) {
|
||||||
|
throw new ObservatoryPortableSetupDecodeError(
|
||||||
|
`${label}: ожидалось положительное целое.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return Number(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exact<T>(value: unknown, expected: T, label: string): T {
|
||||||
|
if (value !== expected) {
|
||||||
|
throw new ObservatoryPortableSetupDecodeError(
|
||||||
|
`${label}: значение изменилось.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
function oneOf<const T extends readonly string[]>(
|
||||||
|
value: unknown,
|
||||||
|
allowed: T,
|
||||||
|
label: string,
|
||||||
|
): T[number] {
|
||||||
|
if (typeof value !== "string" || !allowed.includes(value)) {
|
||||||
|
throw new ObservatoryPortableSetupDecodeError(
|
||||||
|
`${label}: значение не поддерживается.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return value as T[number];
|
||||||
|
}
|
||||||
|
|
||||||
|
function exactKeys(
|
||||||
|
value: Record<string, unknown>,
|
||||||
|
expected: readonly string[],
|
||||||
|
label: string,
|
||||||
|
): void {
|
||||||
|
const actual = Object.keys(value).sort();
|
||||||
|
const sortedExpected = [...expected].sort();
|
||||||
|
if (
|
||||||
|
actual.length !== sortedExpected.length
|
||||||
|
|| actual.some((key, index) => key !== sortedExpected[index])
|
||||||
|
) {
|
||||||
|
throw new ObservatoryPortableSetupDecodeError(
|
||||||
|
`${label}: обнаружены неизвестные поля.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { decodePortableCatalog } from "./portableLaboratorySetupDecoder";
|
||||||
|
import type {
|
||||||
|
ObservatoryLaboratorySetupCatalog,
|
||||||
|
} from "./laboratorySetups";
|
||||||
|
|
||||||
|
export type ObservatoryPortableLaboratorySetupFetch = (
|
||||||
|
input: RequestInfo | URL,
|
||||||
|
init?: RequestInit,
|
||||||
|
) => Promise<Response>;
|
||||||
|
|
||||||
|
class ObservatoryPortableLaboratorySetupContractError extends Error {
|
||||||
|
readonly status: number | null;
|
||||||
|
|
||||||
|
constructor(message: string, status: number | null = null) {
|
||||||
|
super(message);
|
||||||
|
this.name = "ObservatoryPortableLaboratorySetupContractError";
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchObservatoryPortableLaboratorySetups(
|
||||||
|
sourceSessionId: string,
|
||||||
|
{
|
||||||
|
signal,
|
||||||
|
fetcher = globalThis.fetch,
|
||||||
|
}: {
|
||||||
|
signal?: AbortSignal;
|
||||||
|
fetcher?: ObservatoryPortableLaboratorySetupFetch;
|
||||||
|
} = {},
|
||||||
|
): Promise<ObservatoryLaboratorySetupCatalog> {
|
||||||
|
const response = await request(
|
||||||
|
fetcher,
|
||||||
|
`/api/v1/observatory/portable-laboratory-setups?source_session_id=${encodeURIComponent(sourceSessionId)}`,
|
||||||
|
{ method: "GET", headers: { Accept: "application/json" }, signal },
|
||||||
|
);
|
||||||
|
const body = await responseBody(response);
|
||||||
|
if (!response.ok) throw apiError(body, response.status);
|
||||||
|
const catalog = decodePortableCatalog(body);
|
||||||
|
if (catalog.sourceSessionId !== sourceSessionId) {
|
||||||
|
throw new ObservatoryPortableLaboratorySetupContractError(
|
||||||
|
"Portable-каталог сетапов относится к другой исходной сессии.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return catalog;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function request(
|
||||||
|
fetcher: ObservatoryPortableLaboratorySetupFetch,
|
||||||
|
input: string,
|
||||||
|
init: RequestInit,
|
||||||
|
): Promise<Response> {
|
||||||
|
try {
|
||||||
|
return await fetcher(input, init);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof DOMException && error.name === "AbortError") throw error;
|
||||||
|
throw new ObservatoryPortableLaboratorySetupContractError(
|
||||||
|
"Portable-каталог сетапов недоступен.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function responseBody(response: Response): Promise<unknown> {
|
||||||
|
const textBody = await response.text();
|
||||||
|
if (!textBody) return undefined;
|
||||||
|
try {
|
||||||
|
return JSON.parse(textBody) as unknown;
|
||||||
|
} catch {
|
||||||
|
return textBody;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function apiError(
|
||||||
|
body: unknown,
|
||||||
|
status: number,
|
||||||
|
): ObservatoryPortableLaboratorySetupContractError {
|
||||||
|
const detail = body && typeof body === "object" && !Array.isArray(body)
|
||||||
|
? (body as Record<string, unknown>).detail
|
||||||
|
: null;
|
||||||
|
return new ObservatoryPortableLaboratorySetupContractError(
|
||||||
|
typeof detail === "string" && detail.trim()
|
||||||
|
? detail
|
||||||
|
: `Observatory API вернул HTTP ${status}.`,
|
||||||
|
status,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,338 @@
|
|||||||
|
const SUBMIT_SCHEMA = "missioncore.observatory-recorded-run-submit/v1";
|
||||||
|
const JOB_SCHEMA = "missioncore.observatory-recorded-job/v1";
|
||||||
|
const JOB_LIST_SCHEMA = "missioncore.observatory-recorded-job-list/v1";
|
||||||
|
const SHA256 = /^[a-f0-9]{64}$/;
|
||||||
|
|
||||||
|
export type ObservatoryRecordedJobState =
|
||||||
|
| "accepted"
|
||||||
|
| "queued"
|
||||||
|
| "claimed"
|
||||||
|
| "running"
|
||||||
|
| "paused"
|
||||||
|
| "preemption-pending"
|
||||||
|
| "succeeded"
|
||||||
|
| "failed"
|
||||||
|
| "reconciliation-required";
|
||||||
|
|
||||||
|
export type ObservatoryRecordedJobPublicationState =
|
||||||
|
| "not-required"
|
||||||
|
| "pending"
|
||||||
|
| "failed"
|
||||||
|
| "published";
|
||||||
|
|
||||||
|
export interface ObservatoryRecordedJob {
|
||||||
|
readonly jobId: string;
|
||||||
|
readonly idempotencyKey: string;
|
||||||
|
readonly sourceSessionId: string;
|
||||||
|
readonly setupId: string;
|
||||||
|
readonly definitionSha256: string;
|
||||||
|
readonly claimGeneration: number;
|
||||||
|
readonly state: ObservatoryRecordedJobState;
|
||||||
|
readonly restartFromZero: boolean;
|
||||||
|
readonly resultId: string | null;
|
||||||
|
readonly terminalMessage: string | null;
|
||||||
|
readonly publication: {
|
||||||
|
readonly state: ObservatoryRecordedJobPublicationState;
|
||||||
|
readonly attempts: number;
|
||||||
|
readonly error: string | null;
|
||||||
|
readonly publishedAtUtc: string | null;
|
||||||
|
};
|
||||||
|
readonly createdAtUtc: string;
|
||||||
|
readonly updatedAtUtc: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ObservatoryRecordedJobFetch = (
|
||||||
|
input: RequestInfo | URL,
|
||||||
|
init?: RequestInit,
|
||||||
|
) => Promise<Response>;
|
||||||
|
|
||||||
|
export class ObservatoryRecordedJobContractError extends Error {
|
||||||
|
readonly status: number | null;
|
||||||
|
|
||||||
|
constructor(message: string, status: number | null = null) {
|
||||||
|
super(message);
|
||||||
|
this.name = "ObservatoryRecordedJobContractError";
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchObservatoryRecordedJobs(
|
||||||
|
sourceSessionId: string,
|
||||||
|
setupId: string,
|
||||||
|
{
|
||||||
|
signal,
|
||||||
|
definitionSha256,
|
||||||
|
fetcher = globalThis.fetch,
|
||||||
|
}: {
|
||||||
|
signal?: AbortSignal;
|
||||||
|
definitionSha256?: string;
|
||||||
|
fetcher?: ObservatoryRecordedJobFetch;
|
||||||
|
} = {},
|
||||||
|
): Promise<readonly ObservatoryRecordedJob[]> {
|
||||||
|
const query = new URLSearchParams({
|
||||||
|
source_session_id: sourceSessionId,
|
||||||
|
setup_id: setupId,
|
||||||
|
limit: "20",
|
||||||
|
});
|
||||||
|
if (definitionSha256 !== undefined) query.set("definition_sha256", definitionSha256);
|
||||||
|
const response = await request(fetcher, `/api/v1/observatory/runs?${query}`, {
|
||||||
|
method: "GET",
|
||||||
|
headers: { Accept: "application/json" },
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
const body = await responseBody(response);
|
||||||
|
if (!response.ok) throw apiError(body, response.status);
|
||||||
|
const row = record(body, "список расчётов");
|
||||||
|
exactKeys(row, ["authority", "items", "schema_version"], "список расчётов");
|
||||||
|
exact(row.schema_version, JOB_LIST_SCHEMA, "schema_version списка расчётов");
|
||||||
|
observationAuthority(row.authority);
|
||||||
|
return array(row.items, "items").map(decodeObservatoryRecordedJob).filter((job) => (
|
||||||
|
job.sourceSessionId === sourceSessionId && job.setupId === setupId
|
||||||
|
&& (definitionSha256 === undefined || job.definitionSha256 === definitionSha256)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function submitObservatoryRecordedJob(
|
||||||
|
sourceSessionId: string,
|
||||||
|
setupId: string,
|
||||||
|
idempotencyKey: string,
|
||||||
|
portableBinding: {
|
||||||
|
readonly definitionSha256: string;
|
||||||
|
readonly checkSha256: string;
|
||||||
|
} | null,
|
||||||
|
{
|
||||||
|
signal,
|
||||||
|
fetcher = globalThis.fetch,
|
||||||
|
}: {
|
||||||
|
signal?: AbortSignal;
|
||||||
|
fetcher?: ObservatoryRecordedJobFetch;
|
||||||
|
} = {},
|
||||||
|
): Promise<ObservatoryRecordedJob> {
|
||||||
|
const response = await request(fetcher, "/api/v1/observatory/runs", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
schema_version: SUBMIT_SCHEMA,
|
||||||
|
idempotency_key: idempotencyKey,
|
||||||
|
source_session_id: sourceSessionId,
|
||||||
|
setup_id: setupId,
|
||||||
|
...(portableBinding === null ? {} : {
|
||||||
|
definition_sha256: portableBinding.definitionSha256,
|
||||||
|
check_sha256: portableBinding.checkSha256,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
const body = await responseBody(response);
|
||||||
|
if (!response.ok) throw apiError(body, response.status);
|
||||||
|
const job = decodeObservatoryRecordedJob(body);
|
||||||
|
if (job.sourceSessionId !== sourceSessionId || job.setupId !== setupId
|
||||||
|
|| (portableBinding !== null && job.definitionSha256 !== portableBinding.definitionSha256)) {
|
||||||
|
throw new ObservatoryRecordedJobContractError(
|
||||||
|
"Расчёт относится к другой сессии, сетапу или версии профиля.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function retryObservatoryRecordedJobPublication(
|
||||||
|
jobId: string,
|
||||||
|
{
|
||||||
|
signal,
|
||||||
|
fetcher = globalThis.fetch,
|
||||||
|
}: {
|
||||||
|
signal?: AbortSignal;
|
||||||
|
fetcher?: ObservatoryRecordedJobFetch;
|
||||||
|
} = {},
|
||||||
|
): Promise<ObservatoryRecordedJob> {
|
||||||
|
const response = await request(
|
||||||
|
fetcher,
|
||||||
|
`/api/v1/observatory/runs/${encodeURIComponent(jobId)}/publication/retry`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: { Accept: "application/json" },
|
||||||
|
signal,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const body = await responseBody(response);
|
||||||
|
if (!response.ok) throw apiError(body, response.status);
|
||||||
|
const job = decodeObservatoryRecordedJob(body);
|
||||||
|
if (job.jobId !== jobId) {
|
||||||
|
throw new ObservatoryRecordedJobContractError(
|
||||||
|
"Повтор публикации вернул другой расчёт.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeObservatoryRecordedJob(value: unknown): ObservatoryRecordedJob {
|
||||||
|
const row = record(value, "расчёт");
|
||||||
|
exactKeys(row, [
|
||||||
|
"authority", "checkpoint_policy", "claim_generation", "claim_lease", "created_at_utc", "executor",
|
||||||
|
"idempotency_key", "identity_sha256", "job_id", "preemption_receipt_sha256",
|
||||||
|
"preemption_requested", "priority", "publication", "request_sha256", "restart_from_zero", "result",
|
||||||
|
"schema_version", "setup", "source", "state", "submission_receipt_sha256", "terminal",
|
||||||
|
"updated_at_utc",
|
||||||
|
], "расчёт");
|
||||||
|
exact(row.schema_version, JOB_SCHEMA, "schema_version расчёта");
|
||||||
|
observationAuthority(row.authority);
|
||||||
|
digest(row.request_sha256, "request_sha256");
|
||||||
|
digest(row.identity_sha256, "identity_sha256");
|
||||||
|
digest(row.submission_receipt_sha256, "submission_receipt_sha256");
|
||||||
|
const source = record(row.source, "source");
|
||||||
|
exactKeys(source, [
|
||||||
|
"adapter", "bundle_sha256", "capability_manifest_sha256", "catalog_sha256", "session_id",
|
||||||
|
], "source");
|
||||||
|
digest(source.catalog_sha256, "source.catalog_sha256");
|
||||||
|
digest(source.bundle_sha256, "source.bundle_sha256");
|
||||||
|
digest(source.capability_manifest_sha256, "source.capability_manifest_sha256");
|
||||||
|
const setup = record(row.setup, "setup");
|
||||||
|
exactKeys(setup, ["definition_id", "definition_sha256", "definition_version", "setup_id"], "setup");
|
||||||
|
digest(setup.definition_sha256, "setup.definition_sha256");
|
||||||
|
const state = oneOf(row.state, [
|
||||||
|
"accepted", "queued", "claimed", "running", "paused", "preemption-pending",
|
||||||
|
"succeeded", "failed", "reconciliation-required",
|
||||||
|
] as const, "state");
|
||||||
|
if (row.claim_lease !== null) {
|
||||||
|
const lease = record(row.claim_lease, "claim_lease");
|
||||||
|
exactKeys(
|
||||||
|
lease,
|
||||||
|
["claimed_at_utc", "expires_at_utc", "heartbeat_at_utc", "renewal_count"],
|
||||||
|
"claim_lease",
|
||||||
|
);
|
||||||
|
text(lease.claimed_at_utc, "claim_lease.claimed_at_utc");
|
||||||
|
text(lease.expires_at_utc, "claim_lease.expires_at_utc");
|
||||||
|
text(lease.heartbeat_at_utc, "claim_lease.heartbeat_at_utc");
|
||||||
|
nonNegativeInteger(lease.renewal_count, "claim_lease.renewal_count");
|
||||||
|
}
|
||||||
|
const result = row.result === null ? null : record(row.result, "result");
|
||||||
|
if (result !== null) exactKeys(result, ["result_id", "sha256"], "result");
|
||||||
|
const terminal = row.terminal === null ? null : record(row.terminal, "terminal");
|
||||||
|
if (terminal !== null) exactKeys(terminal, ["code", "message"], "terminal");
|
||||||
|
const publication = record(row.publication, "publication");
|
||||||
|
exactKeys(
|
||||||
|
publication,
|
||||||
|
["attempts", "error", "published_at_utc", "state"],
|
||||||
|
"publication",
|
||||||
|
);
|
||||||
|
const publicationState = oneOf(publication.state, [
|
||||||
|
"not-required", "pending", "failed", "published",
|
||||||
|
] as const, "publication.state");
|
||||||
|
const publicationError = publication.error === null
|
||||||
|
? null
|
||||||
|
: text(publication.error, "publication.error");
|
||||||
|
const publishedAtUtc = publication.published_at_utc === null
|
||||||
|
? null
|
||||||
|
: text(publication.published_at_utc, "publication.published_at_utc");
|
||||||
|
return {
|
||||||
|
jobId: text(row.job_id, "job_id"),
|
||||||
|
idempotencyKey: text(row.idempotency_key, "idempotency_key"),
|
||||||
|
sourceSessionId: text(source.session_id, "source.session_id"),
|
||||||
|
setupId: text(setup.setup_id, "setup.setup_id"),
|
||||||
|
definitionSha256: String(setup.definition_sha256),
|
||||||
|
claimGeneration: nonNegativeInteger(row.claim_generation, "claim_generation"),
|
||||||
|
state,
|
||||||
|
restartFromZero: boolean(row.restart_from_zero, "restart_from_zero"),
|
||||||
|
resultId: result === null ? null : text(result.result_id, "result.result_id"),
|
||||||
|
terminalMessage: terminal === null || terminal.message === null
|
||||||
|
? null
|
||||||
|
: text(terminal.message, "terminal.message"),
|
||||||
|
publication: {
|
||||||
|
state: publicationState,
|
||||||
|
attempts: nonNegativeInteger(publication.attempts, "publication.attempts"),
|
||||||
|
error: publicationError,
|
||||||
|
publishedAtUtc,
|
||||||
|
},
|
||||||
|
createdAtUtc: text(row.created_at_utc, "created_at_utc"),
|
||||||
|
updatedAtUtc: text(row.updated_at_utc, "updated_at_utc"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function observationAuthority(value: unknown): void {
|
||||||
|
const row = record(value, "authority");
|
||||||
|
const keys = ["commands_enabled", "actuation_allowed", "navigation_or_safety_accepted", "production_accepted"];
|
||||||
|
exactKeys(row, keys, "authority");
|
||||||
|
keys.forEach((key) => exact(row[key], false, `authority.${key}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function request(fetcher: ObservatoryRecordedJobFetch, input: string, init: RequestInit): Promise<Response> {
|
||||||
|
try {
|
||||||
|
return await fetcher(input, init);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof DOMException && error.name === "AbortError") throw error;
|
||||||
|
throw new ObservatoryRecordedJobContractError("Очередь расчётов недоступна.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function responseBody(response: Response): Promise<unknown> {
|
||||||
|
const body = await response.text();
|
||||||
|
if (!body) return undefined;
|
||||||
|
try { return JSON.parse(body) as unknown; } catch { return body; }
|
||||||
|
}
|
||||||
|
|
||||||
|
function apiError(body: unknown, status: number): ObservatoryRecordedJobContractError {
|
||||||
|
const detail = body && typeof body === "object" && !Array.isArray(body)
|
||||||
|
? (body as Record<string, unknown>).detail
|
||||||
|
: null;
|
||||||
|
return new ObservatoryRecordedJobContractError(
|
||||||
|
typeof detail === "string" && detail.trim() ? detail : `Observatory API вернул HTTP ${status}.`,
|
||||||
|
status,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function record(value: unknown, label: string): Record<string, unknown> {
|
||||||
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||||
|
throw new ObservatoryRecordedJobContractError(`${label}: ожидался объект.`);
|
||||||
|
}
|
||||||
|
return value as Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function array(value: unknown, label: string): unknown[] {
|
||||||
|
if (!Array.isArray(value)) throw new ObservatoryRecordedJobContractError(`${label}: ожидался массив.`);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function text(value: unknown, label: string): string {
|
||||||
|
if (typeof value !== "string" || !value.trim()) {
|
||||||
|
throw new ObservatoryRecordedJobContractError(`${label}: ожидался текст.`);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function digest(value: unknown, label: string): string {
|
||||||
|
const valueText = text(value, label);
|
||||||
|
if (!SHA256.test(valueText)) throw new ObservatoryRecordedJobContractError(`${label}: некорректный digest.`);
|
||||||
|
return valueText;
|
||||||
|
}
|
||||||
|
|
||||||
|
function boolean(value: unknown, label: string): boolean {
|
||||||
|
if (typeof value !== "boolean") throw new ObservatoryRecordedJobContractError(`${label}: ожидался boolean.`);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function nonNegativeInteger(value: unknown, label: string): number {
|
||||||
|
if (!Number.isInteger(value) || Number(value) < 0) {
|
||||||
|
throw new ObservatoryRecordedJobContractError(`${label}: ожидалось целое число.`);
|
||||||
|
}
|
||||||
|
return Number(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function exact<T>(value: unknown, expected: T, label: string): T {
|
||||||
|
if (value !== expected) throw new ObservatoryRecordedJobContractError(`${label}: значение изменилось.`);
|
||||||
|
return expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
function oneOf<const T extends readonly string[]>(value: unknown, allowed: T, label: string): T[number] {
|
||||||
|
if (typeof value !== "string" || !allowed.includes(value)) {
|
||||||
|
throw new ObservatoryRecordedJobContractError(`${label}: значение не поддерживается.`);
|
||||||
|
}
|
||||||
|
return value as T[number];
|
||||||
|
}
|
||||||
|
|
||||||
|
function exactKeys(value: Record<string, unknown>, expected: readonly string[], label: string): void {
|
||||||
|
const actual = Object.keys(value).sort();
|
||||||
|
const sortedExpected = [...expected].sort();
|
||||||
|
if (actual.length !== sortedExpected.length || actual.some((key, index) => key !== sortedExpected[index])) {
|
||||||
|
throw new ObservatoryRecordedJobContractError(`${label}: обнаружены неизвестные поля.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
import type { ObservatoryRecordedJob } from "./recordedJobs";
|
||||||
|
|
||||||
|
const PHASES = {
|
||||||
|
"source-transfer": "Передаём входные данные",
|
||||||
|
"source-preparation": "Подготавливаем вход",
|
||||||
|
computing: "Обрабатываем запись",
|
||||||
|
"result-assembly": "Собираем результат",
|
||||||
|
"result-transfer": "Передаём результат",
|
||||||
|
} as const;
|
||||||
|
const UNITS = { frames: "кадров", members: "файлов", steps: "операций" } as const;
|
||||||
|
type Phase = keyof typeof PHASES;
|
||||||
|
type Unit = keyof typeof UNITS;
|
||||||
|
|
||||||
|
export interface RecordedProgressView {
|
||||||
|
readonly jobId: string;
|
||||||
|
readonly claimGeneration: number;
|
||||||
|
readonly sequence: number;
|
||||||
|
readonly phase: Phase;
|
||||||
|
readonly unit: Unit;
|
||||||
|
readonly completed: number;
|
||||||
|
readonly total: number | null;
|
||||||
|
readonly ageSeconds: number;
|
||||||
|
readonly elapsedSeconds: number;
|
||||||
|
readonly phaseElapsedSeconds: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchRecordedProgress(
|
||||||
|
job: ObservatoryRecordedJob,
|
||||||
|
{ signal, fetcher = globalThis.fetch }: {
|
||||||
|
signal?: AbortSignal;
|
||||||
|
fetcher?: typeof globalThis.fetch;
|
||||||
|
} = {},
|
||||||
|
): Promise<RecordedProgressView | null> {
|
||||||
|
const request = new AbortController();
|
||||||
|
const abort = () => request.abort();
|
||||||
|
signal?.addEventListener("abort", abort, { once: true });
|
||||||
|
if (signal?.aborted) request.abort();
|
||||||
|
const timer = globalThis.setTimeout(abort, 2_500);
|
||||||
|
try {
|
||||||
|
const response = await fetcher(
|
||||||
|
`/api/v1/observatory/runs/${encodeURIComponent(job.jobId)}/progress`,
|
||||||
|
{ signal: request.signal, headers: { Accept: "application/json" } },
|
||||||
|
);
|
||||||
|
if (!response.ok) throw new Error("Прогресс расчёта недоступен.");
|
||||||
|
return decodeRecordedProgress(await response.json(), job);
|
||||||
|
} finally {
|
||||||
|
globalThis.clearTimeout(timer);
|
||||||
|
signal?.removeEventListener("abort", abort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function decodeRecordedProgress(
|
||||||
|
value: unknown, job: ObservatoryRecordedJob,
|
||||||
|
): RecordedProgressView | null {
|
||||||
|
const row = object(value);
|
||||||
|
keys(row, ["schema_version", "job_id", "source_session_id", "setup_id",
|
||||||
|
"definition_sha256", "claim_generation", "state", "received_at_utc", "age_seconds", "progress"]);
|
||||||
|
if (row.schema_version !== "missioncore.observatory-recorded-progress-view/v1"
|
||||||
|
|| row.job_id !== job.jobId || row.source_session_id !== job.sourceSessionId
|
||||||
|
|| row.setup_id !== job.setupId || row.definition_sha256 !== job.definitionSha256
|
||||||
|
|| row.claim_generation !== job.claimGeneration) throw new Error("Прогресс другой попытки.");
|
||||||
|
if (row.progress === null || row.state !== job.state) return null;
|
||||||
|
const progress = object(row.progress);
|
||||||
|
keys(progress, ["schema_version", "claim_generation", "sequence", "phase_index",
|
||||||
|
"phase", "unit", "completed", "total", "elapsed_seconds", "phase_elapsed_seconds"]);
|
||||||
|
if (progress.schema_version !== "missioncore.observatory-recorded-progress/v1"
|
||||||
|
|| progress.claim_generation !== row.claim_generation
|
||||||
|
|| !(typeof progress.phase === "string" && Object.hasOwn(PHASES, progress.phase))
|
||||||
|
|| !(typeof progress.unit === "string" && Object.hasOwn(UNITS, progress.unit))
|
||||||
|
|| typeof row.received_at_utc !== "string" || !Number.isFinite(Date.parse(row.received_at_utc))) {
|
||||||
|
throw new Error("Некорректный прогресс.");
|
||||||
|
}
|
||||||
|
const completed = integer(progress.completed);
|
||||||
|
const total = progress.total === null ? null : integer(progress.total, 1);
|
||||||
|
const elapsed = finite(progress.elapsed_seconds);
|
||||||
|
const phaseElapsed = finite(progress.phase_elapsed_seconds);
|
||||||
|
if ((total !== null && completed > total) || phaseElapsed > elapsed) {
|
||||||
|
throw new Error("Некорректные счётчики прогресса.");
|
||||||
|
}
|
||||||
|
integer(progress.phase_index);
|
||||||
|
return {
|
||||||
|
jobId: job.jobId, claimGeneration: integer(progress.claim_generation, 1),
|
||||||
|
sequence: integer(progress.sequence, 1), phase: progress.phase as Phase,
|
||||||
|
unit: progress.unit as Unit, completed, total, ageSeconds: finite(row.age_seconds),
|
||||||
|
elapsedSeconds: elapsed, phaseElapsedSeconds: phaseElapsed,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function recordedProgressLabel(
|
||||||
|
job: ObservatoryRecordedJob | null, progress: RecordedProgressView | null,
|
||||||
|
): string {
|
||||||
|
if (job?.publication.state === "pending") return "Сохраняем результат";
|
||||||
|
if (!job || job.state === "accepted" || job.state === "queued") return "Ожидаем расчёт";
|
||||||
|
if (job.state === "failed") return "Ошибка расчёта";
|
||||||
|
if (job.state === "succeeded") return "Расчёт завершён";
|
||||||
|
if (job.state === "paused" || job.state === "preemption-pending") return "Расчёт приостановлен";
|
||||||
|
if (job.state === "reconciliation-required") return "Проверяем состояние расчёта";
|
||||||
|
if (!progress || progress.jobId !== job.jobId
|
||||||
|
|| progress.claimGeneration !== job.claimGeneration) return "Ожидаем данные расчёта";
|
||||||
|
if (progress.ageSeconds > 15) return "Ожидаем обновление прогресса";
|
||||||
|
const count = progress.total === null
|
||||||
|
? (progress.completed > 0 ? ` · ${progress.completed.toLocaleString("ru-RU")} ${UNITS[progress.unit]}` : "")
|
||||||
|
: ` · ${progress.completed.toLocaleString("ru-RU")} / ${progress.total.toLocaleString("ru-RU")} ${UNITS[progress.unit]}`;
|
||||||
|
return PHASES[progress.phase] + count;
|
||||||
|
}
|
||||||
|
|
||||||
|
function object(value: unknown): Record<string, unknown> {
|
||||||
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Прогресс: ожидался объект.");
|
||||||
|
return value as Record<string, unknown>;
|
||||||
|
}
|
||||||
|
function keys(row: Record<string, unknown>, expected: string[]): void {
|
||||||
|
if (Object.keys(row).length !== expected.length || expected.some((key) => !Object.hasOwn(row, key))) {
|
||||||
|
throw new Error("Прогресс: неизвестные поля.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function finite(value: unknown): number {
|
||||||
|
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) throw new Error("Прогресс: некорректное число.");
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
function integer(value: unknown, minimum = 0): number {
|
||||||
|
const number = finite(value);
|
||||||
|
if (!Number.isSafeInteger(number) || number < minimum) throw new Error("Прогресс: некорректный счётчик.");
|
||||||
|
return number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,335 @@
|
|||||||
|
import type { LaboratoryFetch } from "../laboratory/advancedResults";
|
||||||
|
import {
|
||||||
|
fetchVegetationShadowResultMetadata,
|
||||||
|
type VegetationFullRouteReview,
|
||||||
|
type VegetationShadowResult,
|
||||||
|
} from "../laboratory/vegetationShadow";
|
||||||
|
import type { ObservationLabInstance } from "../observation/sessionArchive";
|
||||||
|
import {
|
||||||
|
decodeLabReplayCapability,
|
||||||
|
decodeCanonicalLabReplayCapabilityProvenance,
|
||||||
|
decodePortableLabReplayCapabilityProvenance,
|
||||||
|
ObservationLabReplayCapabilityContractError,
|
||||||
|
} from "../observation/labReplayCapability";
|
||||||
|
|
||||||
|
const CANONICAL_RESULT_ID = /^lab-v1-vegetation-shadow-[a-f0-9]{64}$/;
|
||||||
|
const CANONICAL_SOURCE_SESSION_ID = "20260828T130511Z_viewer_live";
|
||||||
|
|
||||||
|
interface ObservatoryRecordedRunBindingBase {
|
||||||
|
readonly evidenceSessionId: string;
|
||||||
|
readonly sourceSessionId: string;
|
||||||
|
readonly resultId: string;
|
||||||
|
readonly activation: "explicit";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ObservatoryCanonicalRecordedRunBinding
|
||||||
|
extends ObservatoryRecordedRunBindingBase {
|
||||||
|
readonly kind: "canonical-recorded-rerun";
|
||||||
|
readonly viewerProfile: "recorded-session";
|
||||||
|
readonly timeline: "session_time";
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ObservatoryPortableResultBinding
|
||||||
|
extends ObservatoryRecordedRunBindingBase {
|
||||||
|
readonly kind: "portable-result-review";
|
||||||
|
readonly viewerProfile: "portable-result";
|
||||||
|
readonly timeline: "result-defined";
|
||||||
|
readonly definitionSha256: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ObservatoryRecordedRunBinding =
|
||||||
|
| ObservatoryCanonicalRecordedRunBinding
|
||||||
|
| ObservatoryPortableResultBinding;
|
||||||
|
|
||||||
|
export interface ObservatoryPortableResultReview {
|
||||||
|
readonly kind: "portable-result";
|
||||||
|
readonly resultId: string;
|
||||||
|
readonly sourceSessionId: string;
|
||||||
|
readonly resultKind: string;
|
||||||
|
readonly definitionSha256: string;
|
||||||
|
readonly calculationProfile: Readonly<Record<string, unknown>> | null;
|
||||||
|
readonly artifactManifestId: string;
|
||||||
|
readonly artifacts: readonly {
|
||||||
|
readonly role: string;
|
||||||
|
readonly mediaType: string;
|
||||||
|
readonly sha256: string;
|
||||||
|
readonly byteLength: number;
|
||||||
|
}[];
|
||||||
|
readonly resultDocument: Readonly<Record<string, unknown>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ObservatoryRecordedRunReview =
|
||||||
|
| { readonly kind: "canonical-recorded-rerun"; readonly review: VegetationFullRouteReview }
|
||||||
|
| ObservatoryPortableResultReview;
|
||||||
|
|
||||||
|
export class ObservatoryRecordedRunContractError extends Error {
|
||||||
|
constructor(message: string) {
|
||||||
|
super(message);
|
||||||
|
this.name = "ObservatoryRecordedRunContractError";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function observatoryRecordedRunBinding(
|
||||||
|
evidenceSessionId: string,
|
||||||
|
lab: ObservationLabInstance,
|
||||||
|
): ObservatoryRecordedRunBinding | null {
|
||||||
|
const capability = lab.replayCapability;
|
||||||
|
if (!capability) return null;
|
||||||
|
if (capability.kind === "portable-result-review") {
|
||||||
|
if (
|
||||||
|
evidenceSessionId !== lab.resultId
|
||||||
|
|| lab.sourceSessionId === evidenceSessionId
|
||||||
|
|| lab.configSha256 === null
|
||||||
|
|| !/^[a-f0-9]{64}$/.test(lab.configSha256)
|
||||||
|
|| capability.viewerProfile !== "portable-result"
|
||||||
|
|| capability.timeline !== "result-defined"
|
||||||
|
|| capability.activation !== "explicit"
|
||||||
|
|| capability.commandsEnabled !== false
|
||||||
|
) {
|
||||||
|
throw new ObservatoryRecordedRunContractError(
|
||||||
|
`Portable-результат ${lab.resultId} не допущен к универсальному viewer.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
decodePortableLabReplayCapabilityProvenance(
|
||||||
|
lab.provenance,
|
||||||
|
evidenceSessionId,
|
||||||
|
lab.resultId,
|
||||||
|
lab.sourceSessionId,
|
||||||
|
lab.configSha256,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
if (!(error instanceof ObservationLabReplayCapabilityContractError)) throw error;
|
||||||
|
throw new ObservatoryRecordedRunContractError(error.message);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
kind: "portable-result-review",
|
||||||
|
evidenceSessionId,
|
||||||
|
sourceSessionId: lab.sourceSessionId,
|
||||||
|
resultId: lab.resultId,
|
||||||
|
viewerProfile: "portable-result",
|
||||||
|
timeline: "result-defined",
|
||||||
|
activation: "explicit",
|
||||||
|
definitionSha256: lab.configSha256,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
decodeCanonicalLabReplayCapabilityProvenance(
|
||||||
|
lab.provenance,
|
||||||
|
evidenceSessionId,
|
||||||
|
lab.resultId,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
if (!(error instanceof ObservationLabReplayCapabilityContractError)) throw error;
|
||||||
|
throw new ObservatoryRecordedRunContractError(error.message);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
evidenceSessionId !== lab.resultId
|
||||||
|
|| lab.sourceSessionId !== CANONICAL_SOURCE_SESSION_ID
|
||||||
|
|| lab.labId !== "LAB V1"
|
||||||
|
|| lab.resultKind !== "recorded-perception-qualification"
|
||||||
|
|| !CANONICAL_RESULT_ID.test(lab.resultId)
|
||||||
|
|| lab.configSha256 !== null
|
||||||
|
|| lab.sourceResultId === null
|
||||||
|
|| !CANONICAL_RESULT_ID.test(lab.sourceResultId)
|
||||||
|
|| capability.kind !== "canonical-recorded-rerun"
|
||||||
|
|| capability.viewerProfile !== "recorded-session"
|
||||||
|
|| capability.timeline !== "session_time"
|
||||||
|
|| capability.activation !== "explicit"
|
||||||
|
|| capability.commandsEnabled !== false
|
||||||
|
) {
|
||||||
|
throw new ObservatoryRecordedRunContractError(
|
||||||
|
`LAB-результат ${lab.resultId} не допущен к каноническому recorded replay.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
kind: "canonical-recorded-rerun",
|
||||||
|
evidenceSessionId,
|
||||||
|
sourceSessionId: lab.sourceSessionId,
|
||||||
|
resultId: lab.resultId,
|
||||||
|
viewerProfile: "recorded-session",
|
||||||
|
timeline: "session_time",
|
||||||
|
activation: "explicit",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchObservatoryRecordedRunReview(
|
||||||
|
binding: ObservatoryRecordedRunBinding,
|
||||||
|
{
|
||||||
|
selectedSourceSessionId,
|
||||||
|
fetcher = fetch,
|
||||||
|
signal,
|
||||||
|
}: {
|
||||||
|
selectedSourceSessionId: string;
|
||||||
|
fetcher?: LaboratoryFetch;
|
||||||
|
signal?: AbortSignal;
|
||||||
|
},
|
||||||
|
): Promise<ObservatoryRecordedRunReview> {
|
||||||
|
if (binding.sourceSessionId !== selectedSourceSessionId) {
|
||||||
|
throw new ObservatoryRecordedRunContractError(
|
||||||
|
"Запуск не связан с выбранной исходной сессией.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (binding.kind === "portable-result-review") {
|
||||||
|
return fetchPortableResultReview(binding, { fetcher, signal });
|
||||||
|
}
|
||||||
|
const result = await fetchVegetationShadowResultMetadata(binding.resultId, {
|
||||||
|
fetcher,
|
||||||
|
signal,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
kind: "canonical-recorded-rerun",
|
||||||
|
review: admitObservatoryRecordedRunReview(
|
||||||
|
binding,
|
||||||
|
selectedSourceSessionId,
|
||||||
|
result,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function admitObservatoryRecordedRunReview(
|
||||||
|
binding: ObservatoryCanonicalRecordedRunBinding,
|
||||||
|
selectedSourceSessionId: string,
|
||||||
|
result: VegetationShadowResult,
|
||||||
|
): VegetationFullRouteReview {
|
||||||
|
if (binding.sourceSessionId !== selectedSourceSessionId) {
|
||||||
|
throw new ObservatoryRecordedRunContractError(
|
||||||
|
"Запуск не связан с выбранной исходной сессией.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (result.resultId !== binding.resultId) {
|
||||||
|
throw new ObservatoryRecordedRunContractError(
|
||||||
|
"Запечатанный результат не совпадает с выбранным запуском.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const review = result.routeFullReview;
|
||||||
|
if (!review || review.sessionId !== binding.sourceSessionId) {
|
||||||
|
throw new ObservatoryRecordedRunContractError(
|
||||||
|
"Запечатанный результат потерял точную связь с исходной сессией.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return review;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchPortableResultReview(
|
||||||
|
binding: ObservatoryPortableResultBinding,
|
||||||
|
{
|
||||||
|
fetcher,
|
||||||
|
signal,
|
||||||
|
}: {
|
||||||
|
fetcher: LaboratoryFetch;
|
||||||
|
signal?: AbortSignal;
|
||||||
|
},
|
||||||
|
): Promise<ObservatoryPortableResultReview> {
|
||||||
|
const response = await fetcher(
|
||||||
|
`/api/v1/observatory/portable-results/${encodeURIComponent(binding.resultId)}`,
|
||||||
|
{ headers: { Accept: "application/json" }, signal },
|
||||||
|
);
|
||||||
|
let body: unknown;
|
||||||
|
try {
|
||||||
|
body = await response.json();
|
||||||
|
} catch {
|
||||||
|
throw new ObservatoryRecordedRunContractError(
|
||||||
|
"Portable viewer получил некорректный ответ сервера.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new ObservatoryRecordedRunContractError(
|
||||||
|
isRecord(body) && typeof body.detail === "string"
|
||||||
|
? body.detail
|
||||||
|
: `Portable viewer вернул HTTP ${response.status}.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return decodePortableResultReview(body, binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodePortableResultReview(
|
||||||
|
value: unknown,
|
||||||
|
binding: ObservatoryPortableResultBinding,
|
||||||
|
): ObservatoryPortableResultReview {
|
||||||
|
if (!isRecord(value)) {
|
||||||
|
throw new ObservatoryRecordedRunContractError("Portable viewer вернул не объект.");
|
||||||
|
}
|
||||||
|
const expected = new Set([
|
||||||
|
"schema_version",
|
||||||
|
"result_id",
|
||||||
|
"source_session_id",
|
||||||
|
"result_kind",
|
||||||
|
"definition_sha256",
|
||||||
|
"viewer_capability",
|
||||||
|
"calculation_profile",
|
||||||
|
"artifact_manifest_id",
|
||||||
|
"artifacts",
|
||||||
|
"result_document",
|
||||||
|
]);
|
||||||
|
if (Object.keys(value).some((key) => !expected.has(key)) || Object.keys(value).length !== expected.size) {
|
||||||
|
throw new ObservatoryRecordedRunContractError("Portable viewer изменил контракт ответа.");
|
||||||
|
}
|
||||||
|
let viewerCapability;
|
||||||
|
try {
|
||||||
|
viewerCapability = decodeLabReplayCapability(
|
||||||
|
value.viewer_capability,
|
||||||
|
binding.evidenceSessionId,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
if (!(error instanceof ObservationLabReplayCapabilityContractError)) throw error;
|
||||||
|
throw new ObservatoryRecordedRunContractError(error.message);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
value.schema_version !== "missioncore.observatory-portable-result-view/v1"
|
||||||
|
|| value.result_id !== binding.resultId
|
||||||
|
|| value.source_session_id !== binding.sourceSessionId
|
||||||
|
|| value.definition_sha256 !== binding.definitionSha256
|
||||||
|
|| typeof value.result_kind !== "string"
|
||||||
|
|| typeof value.artifact_manifest_id !== "string"
|
||||||
|
|| !/^[a-f0-9]{64}$/.test(value.artifact_manifest_id)
|
||||||
|
|| !Array.isArray(value.artifacts)
|
||||||
|
|| !isRecord(value.result_document)
|
||||||
|
|| !isRecord(value.calculation_profile)
|
||||||
|
|| viewerCapability === null
|
||||||
|
|| viewerCapability.kind !== "portable-result-review"
|
||||||
|
|| viewerCapability.viewerProfile !== binding.viewerProfile
|
||||||
|
|| viewerCapability.timeline !== binding.timeline
|
||||||
|
) {
|
||||||
|
throw new ObservatoryRecordedRunContractError("Portable viewer потерял identity результата.");
|
||||||
|
}
|
||||||
|
const artifacts = value.artifacts.map((item) => {
|
||||||
|
if (
|
||||||
|
!isRecord(item)
|
||||||
|
|| Object.keys(item).length !== 4
|
||||||
|
|| !["role", "media_type", "sha256", "byte_length"].every(
|
||||||
|
(key) => Object.prototype.hasOwnProperty.call(item, key),
|
||||||
|
)
|
||||||
|
|| typeof item.role !== "string"
|
||||||
|
|| typeof item.media_type !== "string"
|
||||||
|
|| typeof item.sha256 !== "string"
|
||||||
|
|| !/^[a-f0-9]{64}$/.test(item.sha256)
|
||||||
|
|| typeof item.byte_length !== "number"
|
||||||
|
|| !Number.isSafeInteger(item.byte_length)
|
||||||
|
|| item.byte_length < 0
|
||||||
|
) {
|
||||||
|
throw new ObservatoryRecordedRunContractError("Portable viewer получил неверный artifact.");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
role: item.role,
|
||||||
|
mediaType: item.media_type,
|
||||||
|
sha256: item.sha256,
|
||||||
|
byteLength: item.byte_length,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
kind: "portable-result",
|
||||||
|
resultId: binding.resultId,
|
||||||
|
sourceSessionId: binding.sourceSessionId,
|
||||||
|
resultKind: value.result_kind,
|
||||||
|
definitionSha256: binding.definitionSha256,
|
||||||
|
calculationProfile: value.calculation_profile,
|
||||||
|
artifactManifestId: value.artifact_manifest_id,
|
||||||
|
artifacts,
|
||||||
|
resultDocument: value.result_document,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
AI_MODULE_SETUP_IDS,
|
||||||
|
fetchAIModuleCatalog,
|
||||||
|
fetchAICompositionJobs,
|
||||||
|
fetchAICompositionRuns,
|
||||||
|
type AICompositionRun,
|
||||||
|
} from "./aiComposition";
|
||||||
|
import { fetchRecordedProgress, type RecordedProgressView } from "./recordedProgress";
|
||||||
|
import type { ObservatoryRecordedJob } from "./recordedJobs";
|
||||||
|
|
||||||
|
const OPEN = new Set(["accepted", "queued", "claimed", "running", "paused",
|
||||||
|
"preemption-pending", "reconciliation-required"]);
|
||||||
|
|
||||||
|
interface Snapshot {
|
||||||
|
readonly sourceSessionId: string;
|
||||||
|
readonly jobs: readonly ObservatoryRecordedJob[];
|
||||||
|
readonly runs: readonly AICompositionRun[];
|
||||||
|
readonly moduleLabelsBySetup: Readonly<Record<string, string>>;
|
||||||
|
readonly progress: Readonly<Record<string, RecordedProgressView>>;
|
||||||
|
readonly error: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAICompositionJobs(sourceSessionId: string) {
|
||||||
|
const [revision, setRevision] = useState(0);
|
||||||
|
const [snapshot, setSnapshot] = useState<Snapshot>({
|
||||||
|
sourceSessionId: "", jobs: [], runs: [], moduleLabelsBySetup: {}, progress: {}, error: null,
|
||||||
|
});
|
||||||
|
const current = snapshot.sourceSessionId === sourceSessionId ? snapshot : {
|
||||||
|
sourceSessionId, jobs: [], runs: [], moduleLabelsBySetup: {}, progress: {}, error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!sourceSessionId) {
|
||||||
|
setSnapshot({ sourceSessionId, jobs: [], runs: [], moduleLabelsBySetup: {}, progress: {}, error: null });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const request = new AbortController();
|
||||||
|
void Promise.all([
|
||||||
|
fetchAICompositionJobs(sourceSessionId, request.signal),
|
||||||
|
fetchAICompositionRuns(sourceSessionId, request.signal),
|
||||||
|
fetchAIModuleCatalog(request.signal),
|
||||||
|
]).then(async ([jobs, runs, catalog]) => {
|
||||||
|
const active = jobs.filter((job) => OPEN.has(job.state));
|
||||||
|
const samples = await Promise.all(active.map(async (job) => [
|
||||||
|
job.jobId, await fetchRecordedProgress(job, { signal: request.signal }).catch(() => null),
|
||||||
|
] as const));
|
||||||
|
if (request.signal.aborted) return;
|
||||||
|
const catalogLabels = catalog.groups.flatMap(({ modules }) => (
|
||||||
|
modules.flatMap((module) => {
|
||||||
|
const setupId = AI_MODULE_SETUP_IDS[module.moduleId];
|
||||||
|
return setupId ? [[setupId, module.label] as const] : [];
|
||||||
|
})
|
||||||
|
));
|
||||||
|
const projectedLabels = runs.flatMap((run) => run.presentation.modules.flatMap((module) => {
|
||||||
|
const setupId = AI_MODULE_SETUP_IDS[module.moduleId];
|
||||||
|
return setupId ? [[setupId, module.label] as const] : [];
|
||||||
|
}));
|
||||||
|
setSnapshot({
|
||||||
|
sourceSessionId, jobs, runs,
|
||||||
|
moduleLabelsBySetup: Object.fromEntries([...catalogLabels, ...projectedLabels]),
|
||||||
|
progress: Object.fromEntries(samples.filter(
|
||||||
|
(sample): sample is readonly [string, RecordedProgressView] => sample[1] !== null,
|
||||||
|
)),
|
||||||
|
error: null,
|
||||||
|
});
|
||||||
|
}).catch((error: unknown) => {
|
||||||
|
if (request.signal.aborted) return;
|
||||||
|
setSnapshot((value) => ({
|
||||||
|
sourceSessionId, jobs: value.sourceSessionId === sourceSessionId ? value.jobs : [],
|
||||||
|
runs: value.sourceSessionId === sourceSessionId ? value.runs : [],
|
||||||
|
moduleLabelsBySetup: value.sourceSessionId === sourceSessionId
|
||||||
|
? value.moduleLabelsBySetup : {},
|
||||||
|
progress: value.sourceSessionId === sourceSessionId ? value.progress : {},
|
||||||
|
error: error instanceof Error ? error.message : "Очередь AI-слоёв недоступна.",
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
return () => request.abort();
|
||||||
|
}, [revision, sourceSessionId]);
|
||||||
|
|
||||||
|
const active = current.runs.some((run) => run.state === "running")
|
||||||
|
|| current.jobs.some((job) => OPEN.has(job.state) || job.publication.state === "pending");
|
||||||
|
useEffect(() => {
|
||||||
|
if (!active) return;
|
||||||
|
const timer = globalThis.setTimeout(() => setRevision((value) => value + 1), 1_500);
|
||||||
|
return () => globalThis.clearTimeout(timer);
|
||||||
|
}, [active, revision]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
jobs: current.jobs,
|
||||||
|
runs: current.runs,
|
||||||
|
moduleLabelsBySetup: current.moduleLabelsBySetup,
|
||||||
|
progress: current.progress,
|
||||||
|
error: current.error,
|
||||||
|
refresh: useCallback(() => setRevision((value) => value + 1), []),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
applyObservatoryCatalogMutationOverlay,
|
||||||
|
fetchObservatoryCatalog,
|
||||||
|
reconcileObservatoryCatalogMutationOverlay,
|
||||||
|
type ObservatoryCatalog,
|
||||||
|
type ObservatoryCatalogMutationOverlay,
|
||||||
|
} from "./catalog";
|
||||||
|
|
||||||
|
export type ObservatoryCatalogState =
|
||||||
|
| "idle"
|
||||||
|
| "loading"
|
||||||
|
| "ready"
|
||||||
|
| "refreshing"
|
||||||
|
| "error";
|
||||||
|
|
||||||
|
export interface ObservatoryCatalogController {
|
||||||
|
readonly catalog: ObservatoryCatalog | null;
|
||||||
|
readonly state: ObservatoryCatalogState;
|
||||||
|
readonly error: string | null;
|
||||||
|
readonly refresh: () => Promise<ObservatoryCatalog | null>;
|
||||||
|
readonly applyEvidenceRename: (
|
||||||
|
sessionId: string,
|
||||||
|
displayName: string,
|
||||||
|
) => void;
|
||||||
|
readonly applyEvidenceDeletion: (sessionId: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ActiveCatalogRequest {
|
||||||
|
readonly id: number;
|
||||||
|
readonly controller: AbortController;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ObservatoryCatalogMutationDraft =
|
||||||
|
| { readonly kind: "rename"; readonly displayName: string }
|
||||||
|
| { readonly kind: "delete" };
|
||||||
|
|
||||||
|
function errorMessage(error: unknown): string {
|
||||||
|
return error instanceof Error && error.message.trim()
|
||||||
|
? error.message
|
||||||
|
: "Каталог AI Inference недоступен.";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAbortError(error: unknown): boolean {
|
||||||
|
return error instanceof DOMException && error.name === "AbortError";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useObservatoryCatalog(): ObservatoryCatalogController {
|
||||||
|
const [catalog, setCatalog] = useState<ObservatoryCatalog | null>(null);
|
||||||
|
const [state, setState] = useState<ObservatoryCatalogState>("idle");
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const catalogRef = useRef<ObservatoryCatalog | null>(null);
|
||||||
|
const overlayRef = useRef<ObservatoryCatalogMutationOverlay>(new Map());
|
||||||
|
const mutationRevisionRef = useRef(0);
|
||||||
|
const requestSequenceRef = useRef(0);
|
||||||
|
const activeRequestRef = useRef<ActiveCatalogRequest | null>(null);
|
||||||
|
|
||||||
|
const loadCatalog = useCallback(async (): Promise<ObservatoryCatalog | null> => {
|
||||||
|
const id = requestSequenceRef.current + 1;
|
||||||
|
requestSequenceRef.current = id;
|
||||||
|
activeRequestRef.current?.controller.abort();
|
||||||
|
const controller = new AbortController();
|
||||||
|
activeRequestRef.current = { id, controller };
|
||||||
|
const requestMutationRevision = mutationRevisionRef.current;
|
||||||
|
|
||||||
|
setState(catalogRef.current ? "refreshing" : "loading");
|
||||||
|
setError(null);
|
||||||
|
try {
|
||||||
|
const serverCatalog = await fetchObservatoryCatalog({
|
||||||
|
signal: controller.signal,
|
||||||
|
});
|
||||||
|
if (controller.signal.aborted || requestSequenceRef.current !== id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const reconciled = reconcileObservatoryCatalogMutationOverlay(
|
||||||
|
serverCatalog,
|
||||||
|
overlayRef.current,
|
||||||
|
requestMutationRevision,
|
||||||
|
);
|
||||||
|
overlayRef.current = reconciled.overlay;
|
||||||
|
catalogRef.current = reconciled.catalog;
|
||||||
|
activeRequestRef.current = null;
|
||||||
|
setCatalog(reconciled.catalog);
|
||||||
|
setState("ready");
|
||||||
|
return reconciled.catalog;
|
||||||
|
} catch (loadError: unknown) {
|
||||||
|
if (
|
||||||
|
controller.signal.aborted
|
||||||
|
|| requestSequenceRef.current !== id
|
||||||
|
|| isAbortError(loadError)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
activeRequestRef.current = null;
|
||||||
|
setError(errorMessage(loadError));
|
||||||
|
setState("error");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
void loadCatalog();
|
||||||
|
return () => {
|
||||||
|
requestSequenceRef.current += 1;
|
||||||
|
activeRequestRef.current?.controller.abort();
|
||||||
|
activeRequestRef.current = null;
|
||||||
|
};
|
||||||
|
}, [loadCatalog]);
|
||||||
|
|
||||||
|
const applyMutation = useCallback((
|
||||||
|
sessionId: string,
|
||||||
|
mutation: ObservatoryCatalogMutationDraft,
|
||||||
|
) => {
|
||||||
|
const revision = mutationRevisionRef.current + 1;
|
||||||
|
mutationRevisionRef.current = revision;
|
||||||
|
const overlay = new Map(overlayRef.current);
|
||||||
|
overlay.set(sessionId, { ...mutation, revision });
|
||||||
|
overlayRef.current = overlay;
|
||||||
|
if (!catalogRef.current) return;
|
||||||
|
const projected = applyObservatoryCatalogMutationOverlay(
|
||||||
|
catalogRef.current,
|
||||||
|
overlay,
|
||||||
|
);
|
||||||
|
catalogRef.current = projected;
|
||||||
|
setCatalog(projected);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const applyEvidenceRename = useCallback((
|
||||||
|
sessionId: string,
|
||||||
|
displayName: string,
|
||||||
|
) => {
|
||||||
|
applyMutation(sessionId, { kind: "rename", displayName });
|
||||||
|
}, [applyMutation]);
|
||||||
|
|
||||||
|
const applyEvidenceDeletion = useCallback((sessionId: string) => {
|
||||||
|
applyMutation(sessionId, { kind: "delete" });
|
||||||
|
}, [applyMutation]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
catalog,
|
||||||
|
state,
|
||||||
|
error,
|
||||||
|
refresh: loadCatalog,
|
||||||
|
applyEvidenceRename,
|
||||||
|
applyEvidenceDeletion,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
fetchObservatoryPortableLaboratorySetups,
|
||||||
|
preflightObservatoryLaboratorySetup,
|
||||||
|
selectableObservatorySetups,
|
||||||
|
type ObservatoryLaboratoryRunPreflight,
|
||||||
|
type ObservatoryLaboratorySetupCatalog,
|
||||||
|
} from "./laboratorySetups";
|
||||||
|
|
||||||
|
type SetupCatalogState = "idle" | "loading" | "ready" | "refreshing" | "error";
|
||||||
|
export type SetupPreflightState =
|
||||||
|
| { readonly kind: "idle" }
|
||||||
|
| { readonly kind: "checking" }
|
||||||
|
| { readonly kind: "ready"; readonly value: ObservatoryLaboratoryRunPreflight }
|
||||||
|
| { readonly kind: "error"; readonly message: string };
|
||||||
|
|
||||||
|
export function useObservatoryLaboratorySetups(sourceSessionId: string) {
|
||||||
|
const [catalog, setCatalog] = useState<ObservatoryLaboratorySetupCatalog | null>(null);
|
||||||
|
const [state, setState] = useState<SetupCatalogState>("idle");
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const [selectedSetupId, setSelectedSetupId] = useState("");
|
||||||
|
const [revision, setRevision] = useState(0);
|
||||||
|
const [preflight, setPreflight] = useState<SetupPreflightState>({ kind: "idle" });
|
||||||
|
const requestSequence = useRef(0);
|
||||||
|
const preflightRequest = useRef<AbortController | null>(null);
|
||||||
|
const activeCatalog = catalog?.sourceSessionId === sourceSessionId ? catalog : null;
|
||||||
|
const selectableSetups = useMemo(() => selectableObservatorySetups(activeCatalog), [activeCatalog]);
|
||||||
|
const selectedSetup = selectableSetups.find((setup) => setup.setupId === selectedSetupId) ?? null;
|
||||||
|
const selectedDefinitionSha256 = selectedSetup?.runDefinition?.definitionSha256 ?? null;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
preflightRequest.current?.abort();
|
||||||
|
preflightRequest.current = null;
|
||||||
|
if (!sourceSessionId) {
|
||||||
|
setCatalog(null);
|
||||||
|
setState("idle");
|
||||||
|
setError(null);
|
||||||
|
setSelectedSetupId("");
|
||||||
|
setPreflight({ kind: "idle" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const sequence = ++requestSequence.current;
|
||||||
|
const request = new AbortController();
|
||||||
|
setState((current) => activeCatalog && current !== "idle" ? "refreshing" : "loading");
|
||||||
|
setError(null);
|
||||||
|
setPreflight({ kind: "idle" });
|
||||||
|
// Archived definitions belong to evidence, not to the calculation selector.
|
||||||
|
void fetchObservatoryPortableLaboratorySetups(sourceSessionId, { signal: request.signal })
|
||||||
|
.then((next) => {
|
||||||
|
if (request.signal.aborted || requestSequence.current !== sequence) return;
|
||||||
|
setCatalog(next);
|
||||||
|
const selectable = selectableObservatorySetups(next);
|
||||||
|
setSelectedSetupId((current) => (
|
||||||
|
selectable.some((setup) => setup.setupId === current)
|
||||||
|
? current
|
||||||
|
: selectable[0]?.setupId ?? ""
|
||||||
|
));
|
||||||
|
setState("ready");
|
||||||
|
})
|
||||||
|
.catch((caught: unknown) => {
|
||||||
|
if (request.signal.aborted || requestSequence.current !== sequence) return;
|
||||||
|
setState("error");
|
||||||
|
setError(caught instanceof Error && caught.message.trim()
|
||||||
|
? caught.message
|
||||||
|
: "Каталог профилей недоступен.");
|
||||||
|
});
|
||||||
|
return () => request.abort();
|
||||||
|
}, [revision, sourceSessionId]);
|
||||||
|
|
||||||
|
useEffect(() => () => preflightRequest.current?.abort(), []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
preflightRequest.current?.abort();
|
||||||
|
preflightRequest.current = null;
|
||||||
|
setPreflight((current) => current.kind === "idle" ? current : { kind: "idle" });
|
||||||
|
}, [selectedSetupId, sourceSessionId, selectedDefinitionSha256]);
|
||||||
|
|
||||||
|
const selectSetup = useCallback((setupId: string) => {
|
||||||
|
preflightRequest.current?.abort();
|
||||||
|
preflightRequest.current = null;
|
||||||
|
setSelectedSetupId(setupId);
|
||||||
|
setPreflight({ kind: "idle" });
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const refresh = useCallback(() => setRevision((value) => value + 1), []);
|
||||||
|
|
||||||
|
const check = useCallback(async () => {
|
||||||
|
if (!sourceSessionId || !selectedSetup || preflight.kind === "checking") return;
|
||||||
|
preflightRequest.current?.abort();
|
||||||
|
const request = new AbortController();
|
||||||
|
preflightRequest.current = request;
|
||||||
|
setPreflight({ kind: "checking" });
|
||||||
|
try {
|
||||||
|
const value = await preflightObservatoryLaboratorySetup(
|
||||||
|
sourceSessionId,
|
||||||
|
selectedSetup,
|
||||||
|
{ signal: request.signal },
|
||||||
|
);
|
||||||
|
if (request.signal.aborted || preflightRequest.current !== request) return;
|
||||||
|
setPreflight({ kind: "ready", value });
|
||||||
|
} catch (caught) {
|
||||||
|
if (request.signal.aborted || preflightRequest.current !== request) return;
|
||||||
|
setPreflight({
|
||||||
|
kind: "error",
|
||||||
|
message: caught instanceof Error && caught.message.trim()
|
||||||
|
? caught.message
|
||||||
|
: "Не удалось проверить возможность запуска.",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
if (preflightRequest.current === request) preflightRequest.current = null;
|
||||||
|
}
|
||||||
|
}, [preflight.kind, selectedSetup, sourceSessionId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (state !== "ready" || !selectedSetup || preflight.kind !== "idle") return;
|
||||||
|
void check();
|
||||||
|
}, [check, preflight.kind, selectedSetup, state]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
catalog: activeCatalog,
|
||||||
|
selectableSetups,
|
||||||
|
state,
|
||||||
|
error,
|
||||||
|
selectedSetupId: selectedSetup?.setupId ?? "",
|
||||||
|
selectedSetup,
|
||||||
|
selectSetup,
|
||||||
|
refresh,
|
||||||
|
preflight,
|
||||||
|
check,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
import { fetchRecordedProgress, type RecordedProgressView } from "./recordedProgress";
|
||||||
|
|
||||||
|
import {
|
||||||
|
fetchObservatoryRecordedJobs,
|
||||||
|
retryObservatoryRecordedJobPublication,
|
||||||
|
submitObservatoryRecordedJob,
|
||||||
|
type ObservatoryRecordedJob,
|
||||||
|
} from "./recordedJobs";
|
||||||
|
|
||||||
|
type RecordedJobsState =
|
||||||
|
| "idle"
|
||||||
|
| "loading"
|
||||||
|
| "ready"
|
||||||
|
| "refreshing"
|
||||||
|
| "submitting"
|
||||||
|
| "retrying-publication"
|
||||||
|
| "error";
|
||||||
|
|
||||||
|
interface JobSnapshot {
|
||||||
|
readonly selectionKey: string;
|
||||||
|
readonly jobs: readonly ObservatoryRecordedJob[];
|
||||||
|
readonly state: RecordedJobsState;
|
||||||
|
readonly error: string | null;
|
||||||
|
readonly progress?: RecordedProgressView | null;
|
||||||
|
}
|
||||||
|
const EMPTY_JOBS = [] as const;
|
||||||
|
const OPEN_STATES = new Set([
|
||||||
|
"accepted", "queued", "claimed", "running", "paused", "preemption-pending",
|
||||||
|
"reconciliation-required",
|
||||||
|
]);
|
||||||
|
const POLL_INTERVAL_MS = 1_500;
|
||||||
|
|
||||||
|
export function useObservatoryRecordedJobs(
|
||||||
|
sourceSessionId: string,
|
||||||
|
setupId: string,
|
||||||
|
definitionSha256: string,
|
||||||
|
) {
|
||||||
|
const selectionKey = JSON.stringify([sourceSessionId, setupId, definitionSha256]);
|
||||||
|
const [snapshot, setSnapshot] = useState<JobSnapshot>({
|
||||||
|
selectionKey: "", jobs: EMPTY_JOBS, state: "idle", error: null,
|
||||||
|
});
|
||||||
|
const [revision, setRevision] = useState(0);
|
||||||
|
const requestRef = useRef<AbortController | null>(null);
|
||||||
|
const requestSequence = useRef(0);
|
||||||
|
const idempotencyKeys = useRef(new Map<string, string>());
|
||||||
|
const observedJobId = useRef<string | null>(null);
|
||||||
|
const hasSelection = Boolean(sourceSessionId && setupId && definitionSha256);
|
||||||
|
// A source/profile change must not display even one render of the previous queue.
|
||||||
|
const current = snapshot.selectionKey === selectionKey ? snapshot : null;
|
||||||
|
const jobs = current?.jobs ?? EMPTY_JOBS;
|
||||||
|
const state = current?.state ?? (hasSelection ? "loading" : "idle");
|
||||||
|
const error = current?.error ?? null;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
requestRef.current?.abort();
|
||||||
|
requestRef.current = null;
|
||||||
|
if (!hasSelection) {
|
||||||
|
setSnapshot({ selectionKey, jobs: EMPTY_JOBS, state: "idle", error: null });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const sequence = ++requestSequence.current;
|
||||||
|
const request = new AbortController();
|
||||||
|
requestRef.current = request;
|
||||||
|
setSnapshot({
|
||||||
|
selectionKey, jobs,
|
||||||
|
state: jobs.length > 0 ? "refreshing" : "loading", error: null,
|
||||||
|
progress: current?.progress ?? null,
|
||||||
|
});
|
||||||
|
void fetchObservatoryRecordedJobs(sourceSessionId, setupId, {
|
||||||
|
definitionSha256, signal: request.signal,
|
||||||
|
}).then(async (next) => {
|
||||||
|
if (request.signal.aborted || requestSequence.current !== sequence) return;
|
||||||
|
const pending = next.find((job) => OPEN_STATES.has(job.state));
|
||||||
|
const progress = pending
|
||||||
|
? await fetchRecordedProgress(pending, { signal: request.signal }).catch(() => null)
|
||||||
|
: null;
|
||||||
|
if (request.signal.aborted || requestSequence.current !== sequence) return;
|
||||||
|
if (pending) observedJobId.current = pending.jobId;
|
||||||
|
setSnapshot({ selectionKey, jobs: next, state: "ready", error: null, progress });
|
||||||
|
}).catch((caught: unknown) => {
|
||||||
|
if (request.signal.aborted || requestSequence.current !== sequence) return;
|
||||||
|
setSnapshot({
|
||||||
|
selectionKey, jobs, state: "error",
|
||||||
|
error: caught instanceof Error && caught.message.trim()
|
||||||
|
? caught.message
|
||||||
|
: "Очередь расчётов недоступна.",
|
||||||
|
});
|
||||||
|
}).finally(() => {
|
||||||
|
if (requestRef.current === request) requestRef.current = null;
|
||||||
|
});
|
||||||
|
return () => request.abort();
|
||||||
|
}, [revision, selectionKey]);
|
||||||
|
|
||||||
|
useEffect(() => () => requestRef.current?.abort(), []);
|
||||||
|
|
||||||
|
const latestJob = jobs[0] ?? null;
|
||||||
|
const activeJob = jobs.find((job) => OPEN_STATES.has(job.state)) ?? null;
|
||||||
|
const publicationPending = jobs.some((job) => job.publication.state === "pending");
|
||||||
|
// Do not promote historical terminal logs to a new operator action's error.
|
||||||
|
const computationFailed = latestJob?.state === "failed"
|
||||||
|
&& latestJob.jobId === observedJobId.current;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if ((state !== "ready" && state !== "error") || (!activeJob && !publicationPending)) return;
|
||||||
|
const timer = globalThis.setTimeout(
|
||||||
|
() => setRevision((value) => value + 1),
|
||||||
|
POLL_INTERVAL_MS,
|
||||||
|
);
|
||||||
|
return () => globalThis.clearTimeout(timer);
|
||||||
|
}, [activeJob, publicationPending, revision, state]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!latestJob || activeJob) return;
|
||||||
|
idempotencyKeys.current.delete(selectionKey);
|
||||||
|
}, [activeJob, latestJob, selectionKey]);
|
||||||
|
|
||||||
|
const refresh = useCallback(() => setRevision((value) => value + 1), []);
|
||||||
|
|
||||||
|
const submit = useCallback(async (
|
||||||
|
portableBinding: {
|
||||||
|
readonly definitionSha256: string;
|
||||||
|
readonly checkSha256: string;
|
||||||
|
} | null = null,
|
||||||
|
): Promise<ObservatoryRecordedJob | null> => {
|
||||||
|
if (!hasSelection || state !== "ready"
|
||||||
|
|| portableBinding?.definitionSha256 !== definitionSha256
|
||||||
|
|| publicationPending || latestJob?.publication.state === "failed") return null;
|
||||||
|
if (activeJob) return activeJob;
|
||||||
|
requestRef.current?.abort();
|
||||||
|
const request = new AbortController();
|
||||||
|
requestRef.current = request;
|
||||||
|
const key = idempotencyKeys.current.get(selectionKey) ?? createIdempotencyKey();
|
||||||
|
idempotencyKeys.current.set(selectionKey, key);
|
||||||
|
setSnapshot({ selectionKey, jobs, state: "submitting", error: null });
|
||||||
|
try {
|
||||||
|
const job = await submitObservatoryRecordedJob(
|
||||||
|
sourceSessionId, setupId, key, portableBinding, { signal: request.signal },
|
||||||
|
);
|
||||||
|
if (request.signal.aborted || requestRef.current !== request) return null;
|
||||||
|
observedJobId.current = job.jobId;
|
||||||
|
setSnapshot({
|
||||||
|
selectionKey, jobs: [job, ...jobs.filter((candidate) => candidate.jobId !== job.jobId)],
|
||||||
|
state: "ready", error: null,
|
||||||
|
});
|
||||||
|
return job;
|
||||||
|
} catch (caught) {
|
||||||
|
if (request.signal.aborted || requestRef.current !== request) return null;
|
||||||
|
setSnapshot({
|
||||||
|
selectionKey, jobs, state: "error",
|
||||||
|
error: caught instanceof Error && caught.message.trim()
|
||||||
|
? caught.message
|
||||||
|
: "Не удалось поставить расчёт в очередь.",
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
if (requestRef.current === request) requestRef.current = null;
|
||||||
|
}
|
||||||
|
}, [activeJob, definitionSha256, hasSelection, jobs, latestJob,
|
||||||
|
publicationPending, selectionKey, setupId, sourceSessionId, state]);
|
||||||
|
|
||||||
|
const retryPublication = useCallback(async (): Promise<ObservatoryRecordedJob | null> => {
|
||||||
|
if (!latestJob || latestJob.publication.state !== "failed"
|
||||||
|
|| state === "retrying-publication") return null;
|
||||||
|
requestRef.current?.abort();
|
||||||
|
const request = new AbortController();
|
||||||
|
requestRef.current = request;
|
||||||
|
setSnapshot({ selectionKey, jobs, state: "retrying-publication", error: null });
|
||||||
|
try {
|
||||||
|
const job = await retryObservatoryRecordedJobPublication(latestJob.jobId, {
|
||||||
|
signal: request.signal,
|
||||||
|
});
|
||||||
|
if (request.signal.aborted || requestRef.current !== request) return null;
|
||||||
|
setSnapshot({
|
||||||
|
selectionKey, jobs: [job, ...jobs.filter((candidate) => candidate.jobId !== job.jobId)],
|
||||||
|
state: "ready", error: null,
|
||||||
|
});
|
||||||
|
return job;
|
||||||
|
} catch (caught) {
|
||||||
|
if (request.signal.aborted || requestRef.current !== request) return null;
|
||||||
|
setSnapshot({
|
||||||
|
selectionKey, jobs, state: "error",
|
||||||
|
error: caught instanceof Error && caught.message.trim()
|
||||||
|
? caught.message
|
||||||
|
: "Не удалось повторить публикацию результата.",
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
if (requestRef.current === request) requestRef.current = null;
|
||||||
|
}
|
||||||
|
}, [jobs, latestJob, selectionKey, state]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
jobs, latestJob, activeJob, publicationPending, computationFailed,
|
||||||
|
progress: current?.progress ?? null,
|
||||||
|
state, error, refresh, submit, retryPublication,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createIdempotencyKey(): string {
|
||||||
|
const entropy = typeof globalThis.crypto?.randomUUID === "function"
|
||||||
|
? globalThis.crypto.randomUUID()
|
||||||
|
: `${Date.now().toString(36)}-${Math.random().toString(16).slice(2)}`;
|
||||||
|
return `observatory-ui:${entropy}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ObservatoryRecordedJobsController = ReturnType<typeof useObservatoryRecordedJobs>;
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
export type SimulationSceneType = "interior" | "outdoor" | "object";
|
export type SimulationSceneType = "interior" | "outdoor" | "object";
|
||||||
export type SimulationTransformAxis = "x" | "y" | "z";
|
|
||||||
export type SimulationViewerQuality = "low" | "medium" | "high" | "ultra" | "maximum";
|
export type SimulationViewerQuality = "low" | "medium" | "high" | "ultra" | "maximum";
|
||||||
|
export interface SimulationEulerRotation {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
z: number;
|
||||||
|
}
|
||||||
export type SimulationProjectStatus =
|
export type SimulationProjectStatus =
|
||||||
| "uploading"
|
| "uploading"
|
||||||
| "queued"
|
| "queued"
|
||||||
@@ -35,13 +39,38 @@ export interface SimulationWorldManifest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface SimulationViewerSettings {
|
export interface SimulationViewerSettings {
|
||||||
schemaVersion: "missioncore.simulation-viewer-settings/v1";
|
schemaVersion: "missioncore.simulation-viewer-settings/v3";
|
||||||
quality: SimulationViewerQuality;
|
quality: SimulationViewerQuality;
|
||||||
visual: { inverted: boolean; axis: SimulationTransformAxis };
|
visual: { rotationDegrees: SimulationEulerRotation };
|
||||||
collision: { inverted: boolean; axis: SimulationTransformAxis };
|
collision: { rotationDegrees: SimulationEulerRotation };
|
||||||
camera: { invertHorizontal: boolean; invertVertical: boolean };
|
camera: { invertHorizontal: boolean; invertVertical: boolean };
|
||||||
|
ugv: SimulationUgvPreset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SimulationUgvPreset {
|
||||||
|
presetName: string;
|
||||||
|
massKg: number;
|
||||||
|
dimensionsMeters: {
|
||||||
|
length: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
groundClearance: number;
|
||||||
|
};
|
||||||
|
maxSpeedMetersPerSecond: number;
|
||||||
|
maxTurnRateDegrees: number;
|
||||||
|
invertSteering: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SIMULATION_UGV_EXACT_VALUE_LIMITS = {
|
||||||
|
massKg: { min: 0.01, max: 1_000_000_000 },
|
||||||
|
length: { min: 0.01, max: 1_000_000_000 },
|
||||||
|
width: { min: 0.01, max: 1_000_000_000 },
|
||||||
|
height: { min: 0.07, max: 1_000_000_000 },
|
||||||
|
groundClearance: { min: 0.01, max: 1_000_000_000 },
|
||||||
|
maxSpeedMetersPerSecond: { min: 0, max: 1_000_000_000 },
|
||||||
|
maxTurnRateDegrees: { min: 0, max: 1_000_000_000 },
|
||||||
|
} as const;
|
||||||
|
|
||||||
export interface SimulationProject {
|
export interface SimulationProject {
|
||||||
schemaVersion: "missioncore.simulation-project/v1";
|
schemaVersion: "missioncore.simulation-project/v1";
|
||||||
projectId: string;
|
projectId: string;
|
||||||
@@ -58,7 +87,9 @@ export interface SimulationProject {
|
|||||||
provider: {
|
provider: {
|
||||||
providerId: "gaussian-pipeline";
|
providerId: "gaussian-pipeline";
|
||||||
jobId: string | null;
|
jobId: string | null;
|
||||||
|
jobCreatedAtUtc: string | null;
|
||||||
state: string | null;
|
state: string | null;
|
||||||
|
stateStartedAtUtc: string | null;
|
||||||
progress: { completed_steps?: number; total_steps?: number; stage?: string } | null;
|
progress: { completed_steps?: number; total_steps?: number; stage?: string } | null;
|
||||||
runtime: { source_revision?: string; image_digest?: string } | null;
|
runtime: { source_revision?: string; image_digest?: string } | null;
|
||||||
};
|
};
|
||||||
@@ -85,11 +116,60 @@ export interface SimulationUploadProgress {
|
|||||||
uploadedBytes: number;
|
uploadedBytes: number;
|
||||||
totalBytes: number;
|
totalBytes: number;
|
||||||
currentPath: string;
|
currentPath: string;
|
||||||
|
bytesPerSecond: number | null;
|
||||||
|
estimatedSecondsRemaining: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const API_ROOT = "/api/v1/simulation-worlds/projects";
|
const API_ROOT = "/api/v1/simulation-worlds/projects";
|
||||||
const CHUNK_BYTES = 8 * 1024 * 1024;
|
const CHUNK_BYTES = 8 * 1024 * 1024;
|
||||||
const MAX_UPLOAD_ATTEMPTS = 3;
|
const MAX_UPLOAD_ATTEMPTS = 3;
|
||||||
|
const REFERENCE_SOURCE_BYTES = 4_100_808_854;
|
||||||
|
const REFERENCE_PREPARATION_SECONDS = 187;
|
||||||
|
const REFERENCE_STREAM_SECONDS = 2_317;
|
||||||
|
|
||||||
|
export interface SimulationProcessingEstimate {
|
||||||
|
percent: number;
|
||||||
|
estimatedSecondsRemaining: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function estimateSimulationProcessing(
|
||||||
|
project: SimulationProject,
|
||||||
|
nowMilliseconds = Date.now(),
|
||||||
|
): SimulationProcessingEstimate | null {
|
||||||
|
if (project.status !== "processing") return null;
|
||||||
|
const jobStarted = Date.parse(project.provider.jobCreatedAtUtc ?? "");
|
||||||
|
if (!Number.isFinite(jobStarted)) return null;
|
||||||
|
const elapsedSeconds = Math.max(0, (nowMilliseconds - jobStarted) / 1_000);
|
||||||
|
const sourceScale = clamp(
|
||||||
|
project.source.totalByteLength / REFERENCE_SOURCE_BYTES,
|
||||||
|
0.1,
|
||||||
|
4,
|
||||||
|
);
|
||||||
|
let expectedSeconds = (
|
||||||
|
REFERENCE_PREPARATION_SECONDS + REFERENCE_STREAM_SECONDS
|
||||||
|
) * sourceScale;
|
||||||
|
if (project.provider.state === "building_streamed_sog") {
|
||||||
|
const streamedStarted = Date.parse(project.provider.stateStartedAtUtc ?? "");
|
||||||
|
if (Number.isFinite(streamedStarted) && streamedStarted >= jobStarted) {
|
||||||
|
const preparationSeconds = Math.max(1, (streamedStarted - jobStarted) / 1_000);
|
||||||
|
const observedLoad = clamp(
|
||||||
|
preparationSeconds / (REFERENCE_PREPARATION_SECONDS * sourceScale),
|
||||||
|
0.5,
|
||||||
|
2,
|
||||||
|
);
|
||||||
|
expectedSeconds = preparationSeconds
|
||||||
|
+ REFERENCE_STREAM_SECONDS * sourceScale * observedLoad;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const rawPercent = expectedSeconds > 0 ? elapsedSeconds / expectedSeconds * 100 : 0;
|
||||||
|
if (rawPercent >= 100) {
|
||||||
|
return { percent: 99, estimatedSecondsRemaining: null };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
percent: Math.max(1, Math.floor(rawPercent)),
|
||||||
|
estimatedSecondsRemaining: Math.max(0, Math.ceil(expectedSeconds - elapsedSeconds)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchSimulationProjects(signal?: AbortSignal): Promise<SimulationProject[]> {
|
export async function fetchSimulationProjects(signal?: AbortSignal): Promise<SimulationProject[]> {
|
||||||
const response = await fetch(API_ROOT, { signal, cache: "no-store" });
|
const response = await fetch(API_ROOT, { signal, cache: "no-store" });
|
||||||
@@ -107,6 +187,15 @@ export async function createAndUploadSimulationProject(
|
|||||||
sceneType: SimulationSceneType,
|
sceneType: SimulationSceneType,
|
||||||
candidates: SimulationUploadCandidate[],
|
candidates: SimulationUploadCandidate[],
|
||||||
onProgress: (progress: SimulationUploadProgress) => void,
|
onProgress: (progress: SimulationUploadProgress) => void,
|
||||||
|
): Promise<SimulationProject> {
|
||||||
|
const project = await createSimulationProject(name, sceneType, candidates);
|
||||||
|
return uploadSimulationProjectSource(project, candidates, onProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createSimulationProject(
|
||||||
|
name: string,
|
||||||
|
sceneType: SimulationSceneType,
|
||||||
|
candidates: SimulationUploadCandidate[],
|
||||||
): Promise<SimulationProject> {
|
): Promise<SimulationProject> {
|
||||||
const sourceKind = sourceKindFor(candidates);
|
const sourceKind = sourceKindFor(candidates);
|
||||||
const createdResponse = await fetch(API_ROOT, {
|
const createdResponse = await fetch(API_ROOT, {
|
||||||
@@ -123,36 +212,85 @@ export async function createAndUploadSimulationProject(
|
|||||||
})),
|
})),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
let project = parseProject(await jsonResponse(createdResponse));
|
return parseProject(await jsonResponse(createdResponse));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function uploadSimulationProjectSource(
|
||||||
|
project: SimulationProject,
|
||||||
|
candidates: SimulationUploadCandidate[],
|
||||||
|
onProgress: (progress: SimulationUploadProgress) => void,
|
||||||
|
signal?: AbortSignal,
|
||||||
|
): Promise<SimulationProject> {
|
||||||
const byPath = new Map(candidates.map((candidate) => [candidate.logicalPath, candidate.file]));
|
const byPath = new Map(candidates.map((candidate) => [candidate.logicalPath, candidate.file]));
|
||||||
const totalBytes = candidates.reduce((total, candidate) => total + candidate.file.size, 0);
|
const totalBytes = candidates.reduce((total, candidate) => total + candidate.file.size, 0);
|
||||||
let confirmedBytes = 0;
|
let confirmedBytes = 0;
|
||||||
|
let sampleBytes = 0;
|
||||||
|
let sampleAt = performance.now();
|
||||||
|
let smoothedBytesPerSecond: number | null = null;
|
||||||
|
const reportProgress = (currentPath: string) => {
|
||||||
|
const remainingBytes = Math.max(0, totalBytes - confirmedBytes);
|
||||||
|
onProgress({
|
||||||
|
uploadedBytes: confirmedBytes,
|
||||||
|
totalBytes,
|
||||||
|
currentPath,
|
||||||
|
bytesPerSecond: smoothedBytesPerSecond,
|
||||||
|
estimatedSecondsRemaining: smoothedBytesPerSecond && remainingBytes > 0
|
||||||
|
? Math.ceil(remainingBytes / smoothedBytesPerSecond)
|
||||||
|
: remainingBytes === 0 ? 0 : null,
|
||||||
|
});
|
||||||
|
};
|
||||||
for (const sourceFile of project.source.files) {
|
for (const sourceFile of project.source.files) {
|
||||||
|
signal?.throwIfAborted();
|
||||||
const file = byPath.get(sourceFile.logicalPath);
|
const file = byPath.get(sourceFile.logicalPath);
|
||||||
if (!file) throw new Error(`Сервер изменил состав источника: ${sourceFile.logicalPath}`);
|
if (!file) throw new Error(`Сервер изменил состав источника: ${sourceFile.logicalPath}`);
|
||||||
const uploadUrl = `${API_ROOT}/${encodeURIComponent(project.projectId)}/source/${encodeURIComponent(sourceFile.fileId)}`;
|
const uploadUrl = `${API_ROOT}/${encodeURIComponent(project.projectId)}/source/${encodeURIComponent(sourceFile.fileId)}`;
|
||||||
let offset = await readUploadOffset(uploadUrl, file.size);
|
let offset = await readUploadOffset(uploadUrl, file.size, signal);
|
||||||
confirmedBytes += offset;
|
confirmedBytes += offset;
|
||||||
onProgress({ uploadedBytes: confirmedBytes, totalBytes, currentPath: sourceFile.logicalPath });
|
sampleBytes = confirmedBytes;
|
||||||
|
sampleAt = performance.now();
|
||||||
|
reportProgress(sourceFile.logicalPath);
|
||||||
while (offset < file.size) {
|
while (offset < file.size) {
|
||||||
|
signal?.throwIfAborted();
|
||||||
const nextOffset = Math.min(file.size, offset + CHUNK_BYTES);
|
const nextOffset = Math.min(file.size, offset + CHUNK_BYTES);
|
||||||
const confirmedOffset = await uploadChunk(uploadUrl, file, offset, nextOffset);
|
const confirmedOffset = await uploadChunk(uploadUrl, file, offset, nextOffset, signal);
|
||||||
confirmedBytes += confirmedOffset - offset;
|
confirmedBytes += confirmedOffset - offset;
|
||||||
offset = confirmedOffset;
|
offset = confirmedOffset;
|
||||||
onProgress({ uploadedBytes: confirmedBytes, totalBytes, currentPath: sourceFile.logicalPath });
|
const now = performance.now();
|
||||||
|
const elapsedSeconds = (now - sampleAt) / 1_000;
|
||||||
|
const transferredBytes = confirmedBytes - sampleBytes;
|
||||||
|
if (elapsedSeconds > 0 && transferredBytes > 0) {
|
||||||
|
const currentBytesPerSecond = transferredBytes / elapsedSeconds;
|
||||||
|
smoothedBytesPerSecond = smoothedBytesPerSecond === null
|
||||||
|
? currentBytesPerSecond
|
||||||
|
: smoothedBytesPerSecond * 0.7 + currentBytesPerSecond * 0.3;
|
||||||
|
}
|
||||||
|
sampleBytes = confirmedBytes;
|
||||||
|
sampleAt = now;
|
||||||
|
reportProgress(sourceFile.logicalPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const buildResponse = await fetch(`${API_ROOT}/${encodeURIComponent(project.projectId)}/build`, {
|
return startSimulationProjectBuild(project.projectId, signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function startSimulationProjectBuild(
|
||||||
|
projectId: string,
|
||||||
|
signal?: AbortSignal,
|
||||||
|
): Promise<SimulationProject> {
|
||||||
|
const buildResponse = await fetch(`${API_ROOT}/${encodeURIComponent(projectId)}/build`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: "{}",
|
body: "{}",
|
||||||
|
signal,
|
||||||
});
|
});
|
||||||
project = parseProject(await jsonResponse(buildResponse));
|
return parseProject(await jsonResponse(buildResponse));
|
||||||
return project;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function readUploadOffset(uploadUrl: string, fileSize: number): Promise<number> {
|
async function readUploadOffset(
|
||||||
const head = await fetch(uploadUrl, { method: "HEAD", cache: "no-store" });
|
uploadUrl: string,
|
||||||
|
fileSize: number,
|
||||||
|
signal?: AbortSignal,
|
||||||
|
): Promise<number> {
|
||||||
|
const head = await fetch(uploadUrl, { method: "HEAD", cache: "no-store", signal });
|
||||||
if (!head.ok) await jsonResponse(head);
|
if (!head.ok) await jsonResponse(head);
|
||||||
const value = head.headers.get("Upload-Offset");
|
const value = head.headers.get("Upload-Offset");
|
||||||
if (!value || !/^\d+$/.test(value)) {
|
if (!value || !/^\d+$/.test(value)) {
|
||||||
@@ -170,11 +308,13 @@ async function uploadChunk(
|
|||||||
file: File,
|
file: File,
|
||||||
offset: number,
|
offset: number,
|
||||||
nextOffset: number,
|
nextOffset: number,
|
||||||
|
signal?: AbortSignal,
|
||||||
): Promise<number> {
|
): Promise<number> {
|
||||||
let lastFailure: unknown = null;
|
let lastFailure: unknown = null;
|
||||||
for (let attempt = 1; attempt <= MAX_UPLOAD_ATTEMPTS; attempt += 1) {
|
for (let attempt = 1; attempt <= MAX_UPLOAD_ATTEMPTS; attempt += 1) {
|
||||||
let response: Response | null = null;
|
let response: Response | null = null;
|
||||||
try {
|
try {
|
||||||
|
signal?.throwIfAborted();
|
||||||
response = await fetch(uploadUrl, {
|
response = await fetch(uploadUrl, {
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -182,8 +322,10 @@ async function uploadChunk(
|
|||||||
"Upload-Offset": String(offset),
|
"Upload-Offset": String(offset),
|
||||||
},
|
},
|
||||||
body: file.slice(offset, nextOffset),
|
body: file.slice(offset, nextOffset),
|
||||||
|
signal,
|
||||||
});
|
});
|
||||||
} catch (caught) {
|
} catch (caught) {
|
||||||
|
if (signal?.aborted) throw caught;
|
||||||
lastFailure = caught;
|
lastFailure = caught;
|
||||||
}
|
}
|
||||||
if (response?.ok) {
|
if (response?.ok) {
|
||||||
@@ -203,7 +345,7 @@ async function uploadChunk(
|
|||||||
lastFailure = new Error(`Mission Core временно вернул HTTP ${response.status}.`);
|
lastFailure = new Error(`Mission Core временно вернул HTTP ${response.status}.`);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const confirmed = await readUploadOffset(uploadUrl, file.size);
|
const confirmed = await readUploadOffset(uploadUrl, file.size, signal);
|
||||||
if (confirmed === nextOffset) return confirmed;
|
if (confirmed === nextOffset) return confirmed;
|
||||||
if (confirmed !== offset) {
|
if (confirmed !== offset) {
|
||||||
throw new Error("Сервер подтвердил неожиданное смещение загрузки.");
|
throw new Error("Сервер подтвердил неожиданное смещение загрузки.");
|
||||||
@@ -246,26 +388,32 @@ export async function saveSimulationViewerSettings(
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
schema_version: settings.schemaVersion,
|
schema_version: settings.schemaVersion,
|
||||||
quality: settings.quality,
|
quality: settings.quality,
|
||||||
visual: settings.visual,
|
visual: { rotation_degrees: settings.visual.rotationDegrees },
|
||||||
collision: settings.collision,
|
collision: { rotation_degrees: settings.collision.rotationDegrees },
|
||||||
camera: {
|
camera: {
|
||||||
invert_horizontal: settings.camera.invertHorizontal,
|
invert_horizontal: settings.camera.invertHorizontal,
|
||||||
invert_vertical: settings.camera.invertVertical,
|
invert_vertical: settings.camera.invertVertical,
|
||||||
},
|
},
|
||||||
|
ugv: {
|
||||||
|
preset_name: settings.ugv.presetName,
|
||||||
|
mass_kg: settings.ugv.massKg,
|
||||||
|
dimensions_m: {
|
||||||
|
length: settings.ugv.dimensionsMeters.length,
|
||||||
|
width: settings.ugv.dimensionsMeters.width,
|
||||||
|
height: settings.ugv.dimensionsMeters.height,
|
||||||
|
ground_clearance: settings.ugv.dimensionsMeters.groundClearance,
|
||||||
|
},
|
||||||
|
max_speed_mps: settings.ugv.maxSpeedMetersPerSecond,
|
||||||
|
max_turn_rate_degrees: settings.ugv.maxTurnRateDegrees,
|
||||||
|
invert_steering: settings.ugv.invertSteering,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return parseProject(await jsonResponse(response));
|
return parseProject(await jsonResponse(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function retrySimulationProject(projectId: string): Promise<SimulationProject> {
|
export const retrySimulationProject = startSimulationProjectBuild;
|
||||||
const response = await fetch(`${API_ROOT}/${encodeURIComponent(projectId)}/build`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: "{}",
|
|
||||||
});
|
|
||||||
return parseProject(await jsonResponse(response));
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function deleteSimulationProject(projectId: string): Promise<void> {
|
export async function deleteSimulationProject(projectId: string): Promise<void> {
|
||||||
const response = await fetch(`${API_ROOT}/${encodeURIComponent(projectId)}`, { method: "DELETE" });
|
const response = await fetch(`${API_ROOT}/${encodeURIComponent(projectId)}`, { method: "DELETE" });
|
||||||
@@ -321,7 +469,9 @@ function parseProject(value: unknown): SimulationProject {
|
|||||||
provider: {
|
provider: {
|
||||||
providerId: provider.provider_id === "gaussian-pipeline" ? "gaussian-pipeline" : invalid("provider id"),
|
providerId: provider.provider_id === "gaussian-pipeline" ? "gaussian-pipeline" : invalid("provider id"),
|
||||||
jobId: nullableString(provider.job_id, "provider job id"),
|
jobId: nullableString(provider.job_id, "provider job id"),
|
||||||
|
jobCreatedAtUtc: nullableString(provider.job_created_at_utc ?? null, "provider job creation"),
|
||||||
state: nullableString(provider.state, "provider state"),
|
state: nullableString(provider.state, "provider state"),
|
||||||
|
stateStartedAtUtc: nullableString(provider.state_started_at_utc ?? null, "provider state start"),
|
||||||
progress: provider.progress === null ? null : objectValue(provider.progress, "provider progress"),
|
progress: provider.progress === null ? null : objectValue(provider.progress, "provider progress"),
|
||||||
runtime: provider.runtime === null ? null : objectValue(provider.runtime, "provider runtime"),
|
runtime: provider.runtime === null ? null : objectValue(provider.runtime, "provider runtime"),
|
||||||
},
|
},
|
||||||
@@ -345,16 +495,17 @@ function parseProject(value: unknown): SimulationProject {
|
|||||||
|
|
||||||
function parseViewerSettings(value: unknown): SimulationViewerSettings {
|
function parseViewerSettings(value: unknown): SimulationViewerSettings {
|
||||||
const record = objectValue(value, "viewer settings");
|
const record = objectValue(value, "viewer settings");
|
||||||
exactKeys(record, ["schema_version", "quality", "visual", "collision", "camera"], "viewer settings");
|
exactKeys(record, ["schema_version", "quality", "visual", "collision", "camera", "ugv"], "viewer settings");
|
||||||
if (record.schema_version !== "missioncore.simulation-viewer-settings/v1") {
|
if (record.schema_version !== "missioncore.simulation-viewer-settings/v3") {
|
||||||
throw new Error("Настройки сцены вернули неподдерживаемую версию.");
|
throw new Error("Настройки сцены вернули неподдерживаемую версию.");
|
||||||
}
|
}
|
||||||
const visual = parseLayerViewerSettings(record.visual, "visual settings");
|
const visual = parseLayerViewerSettings(record.visual, "visual settings");
|
||||||
const collision = parseLayerViewerSettings(record.collision, "collision settings");
|
const collision = parseLayerViewerSettings(record.collision, "collision settings");
|
||||||
const camera = objectValue(record.camera, "camera settings");
|
const camera = objectValue(record.camera, "camera settings");
|
||||||
exactKeys(camera, ["invert_horizontal", "invert_vertical"], "camera settings");
|
exactKeys(camera, ["invert_horizontal", "invert_vertical"], "camera settings");
|
||||||
|
const ugv = parseUgvPreset(record.ugv);
|
||||||
return {
|
return {
|
||||||
schemaVersion: "missioncore.simulation-viewer-settings/v1",
|
schemaVersion: "missioncore.simulation-viewer-settings/v3",
|
||||||
quality: qualityValue(record.quality),
|
quality: qualityValue(record.quality),
|
||||||
visual,
|
visual,
|
||||||
collision,
|
collision,
|
||||||
@@ -362,18 +513,88 @@ function parseViewerSettings(value: unknown): SimulationViewerSettings {
|
|||||||
invertHorizontal: booleanValue(camera.invert_horizontal, "horizontal camera inversion"),
|
invertHorizontal: booleanValue(camera.invert_horizontal, "horizontal camera inversion"),
|
||||||
invertVertical: booleanValue(camera.invert_vertical, "vertical camera inversion"),
|
invertVertical: booleanValue(camera.invert_vertical, "vertical camera inversion"),
|
||||||
},
|
},
|
||||||
|
ugv,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseUgvPreset(value: unknown): SimulationUgvPreset {
|
||||||
|
const record = objectValue(value, "UGV settings");
|
||||||
|
exactKeys(
|
||||||
|
record,
|
||||||
|
["preset_name", "mass_kg", "dimensions_m", "max_speed_mps", "max_turn_rate_degrees", "invert_steering"],
|
||||||
|
"UGV settings",
|
||||||
|
);
|
||||||
|
const dimensions = objectValue(record.dimensions_m, "UGV dimensions");
|
||||||
|
exactKeys(dimensions, ["length", "width", "height", "ground_clearance"], "UGV dimensions");
|
||||||
|
const presetName = stringValue(record.preset_name, "UGV preset name");
|
||||||
|
if (presetName.length > 80) return invalid("UGV preset name");
|
||||||
|
const height = finiteNumberValue(
|
||||||
|
dimensions.height,
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS.height.min,
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS.height.max,
|
||||||
|
"UGV height",
|
||||||
|
);
|
||||||
|
const groundClearance = finiteNumberValue(
|
||||||
|
dimensions.ground_clearance,
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS.groundClearance.min,
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS.groundClearance.max,
|
||||||
|
"UGV ground clearance",
|
||||||
|
);
|
||||||
|
if (groundClearance >= height - 0.05) return invalid("UGV ground clearance");
|
||||||
|
return {
|
||||||
|
presetName,
|
||||||
|
massKg: finiteNumberValue(
|
||||||
|
record.mass_kg,
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS.massKg.min,
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS.massKg.max,
|
||||||
|
"UGV mass",
|
||||||
|
),
|
||||||
|
dimensionsMeters: {
|
||||||
|
length: finiteNumberValue(
|
||||||
|
dimensions.length,
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS.length.min,
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS.length.max,
|
||||||
|
"UGV length",
|
||||||
|
),
|
||||||
|
width: finiteNumberValue(
|
||||||
|
dimensions.width,
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS.width.min,
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS.width.max,
|
||||||
|
"UGV width",
|
||||||
|
),
|
||||||
|
height,
|
||||||
|
groundClearance,
|
||||||
|
},
|
||||||
|
maxSpeedMetersPerSecond: finiteNumberValue(
|
||||||
|
record.max_speed_mps,
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS.maxSpeedMetersPerSecond.min,
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS.maxSpeedMetersPerSecond.max,
|
||||||
|
"UGV maximum speed",
|
||||||
|
),
|
||||||
|
maxTurnRateDegrees: finiteNumberValue(
|
||||||
|
record.max_turn_rate_degrees,
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS.maxTurnRateDegrees.min,
|
||||||
|
SIMULATION_UGV_EXACT_VALUE_LIMITS.maxTurnRateDegrees.max,
|
||||||
|
"UGV maximum turn rate",
|
||||||
|
),
|
||||||
|
invertSteering: booleanValue(record.invert_steering, "UGV steering inversion"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseLayerViewerSettings(
|
function parseLayerViewerSettings(
|
||||||
value: unknown,
|
value: unknown,
|
||||||
label: string,
|
label: string,
|
||||||
): { inverted: boolean; axis: SimulationTransformAxis } {
|
): { rotationDegrees: SimulationEulerRotation } {
|
||||||
const record = objectValue(value, label);
|
const record = objectValue(value, label);
|
||||||
exactKeys(record, ["inverted", "axis"], label);
|
exactKeys(record, ["rotation_degrees"], label);
|
||||||
|
const rotation = objectValue(record.rotation_degrees, `${label} rotation`);
|
||||||
|
exactKeys(rotation, ["x", "y", "z"], `${label} rotation`);
|
||||||
return {
|
return {
|
||||||
inverted: booleanValue(record.inverted, `${label} inversion`),
|
rotationDegrees: {
|
||||||
axis: axisValue(record.axis),
|
x: rotationValue(rotation.x, `${label} X rotation`),
|
||||||
|
y: rotationValue(rotation.y, `${label} Y rotation`),
|
||||||
|
z: rotationValue(rotation.z, `${label} Z rotation`),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,9 +687,18 @@ function sceneTypeValue(value: unknown): SimulationSceneType {
|
|||||||
return invalid("scene type");
|
return invalid("scene type");
|
||||||
}
|
}
|
||||||
|
|
||||||
function axisValue(value: unknown): SimulationTransformAxis {
|
function rotationValue(value: unknown, label: string): number {
|
||||||
if (value === "x" || value === "y" || value === "z") return value;
|
if (typeof value === "number" && Number.isFinite(value) && value >= -360 && value <= 360) {
|
||||||
return invalid("transform axis");
|
return value;
|
||||||
|
}
|
||||||
|
return invalid(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
function finiteNumberValue(value: unknown, minimum: number, maximum: number, label: string): number {
|
||||||
|
if (typeof value === "number" && Number.isFinite(value) && value >= minimum && value <= maximum) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return invalid(label);
|
||||||
}
|
}
|
||||||
|
|
||||||
function qualityValue(value: unknown): SimulationViewerQuality {
|
function qualityValue(value: unknown): SimulationViewerQuality {
|
||||||
@@ -488,3 +718,7 @@ function statusValue(value: unknown): SimulationProjectStatus {
|
|||||||
function invalid(label: string): never {
|
function invalid(label: string): never {
|
||||||
throw new Error(`Некорректный ${label}.`);
|
throw new Error(`Некорректный ${label}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clamp(value: number, minimum: number, maximum: number): number {
|
||||||
|
return Math.min(maximum, Math.max(minimum, value));
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
import type { SimulationUploadCandidate } from "./projects";
|
||||||
|
|
||||||
|
const ARCHIVE_PATTERN = /\.(zip|rar|7z)$/i;
|
||||||
|
export const MAX_SIMULATION_SOURCE_BYTES = 16 * 1024 ** 3;
|
||||||
|
|
||||||
|
interface LegacyFileEntry {
|
||||||
|
isFile: boolean;
|
||||||
|
isDirectory: boolean;
|
||||||
|
name: string;
|
||||||
|
fullPath: string;
|
||||||
|
file?: (callback: (file: File) => void, error?: (error: DOMException) => void) => void;
|
||||||
|
createReader?: () => LegacyDirectoryReader;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LegacyDirectoryReader {
|
||||||
|
readEntries: (
|
||||||
|
callback: (entries: LegacyFileEntry[]) => void,
|
||||||
|
error?: (error: DOMException) => void,
|
||||||
|
) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function candidatesFromDrop(
|
||||||
|
transfer: DataTransfer,
|
||||||
|
): Promise<SimulationUploadCandidate[]> {
|
||||||
|
const candidates: SimulationUploadCandidate[] = [];
|
||||||
|
for (const item of Array.from(transfer.items)) {
|
||||||
|
const entry = (item as DataTransferItem & {
|
||||||
|
webkitGetAsEntry?: () => LegacyFileEntry | null;
|
||||||
|
}).webkitGetAsEntry?.();
|
||||||
|
if (entry?.isDirectory) {
|
||||||
|
await collectEntry(entry, candidates);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const file = item.getAsFile();
|
||||||
|
if (file) {
|
||||||
|
candidates.push({ file, logicalPath: file.name });
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (entry) await collectEntry(entry, candidates);
|
||||||
|
}
|
||||||
|
if (!candidates.length) {
|
||||||
|
for (const file of Array.from(transfer.files)) {
|
||||||
|
candidates.push({ file, logicalPath: file.name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function archivesFromDrop(transfer: DataTransfer): Promise<File[]> {
|
||||||
|
const candidates = await candidatesFromDrop(transfer);
|
||||||
|
const invalid = candidates.filter((candidate) => !ARCHIVE_PATTERN.test(candidate.logicalPath));
|
||||||
|
if (invalid.length) {
|
||||||
|
throw new Error("В каталог можно перетащить только отдельные архивы ZIP, RAR или 7z.");
|
||||||
|
}
|
||||||
|
if (!candidates.length) {
|
||||||
|
throw new Error("В перетаскивании не найдено архивов ZIP, RAR или 7z.");
|
||||||
|
}
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
if (candidate.logicalPath.includes("/")) {
|
||||||
|
throw new Error("Перетащите сами архивы, а не содержащую их папку.");
|
||||||
|
}
|
||||||
|
if (candidate.file.size <= 0) {
|
||||||
|
throw new Error(`Пустой архив: ${candidate.file.name}`);
|
||||||
|
}
|
||||||
|
if (candidate.file.size > MAX_SIMULATION_SOURCE_BYTES) {
|
||||||
|
throw new Error(`Архив ${candidate.file.name} превышает лимит 16 ГБ.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return candidates.map((candidate) => candidate.file);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function validateSimulationCandidates(candidates: SimulationUploadCandidate[]): void {
|
||||||
|
if (!candidates.length) throw new Error("Выберите архив или папку с результатом LCC/LCC2.");
|
||||||
|
const archives = candidates.filter((candidate) => ARCHIVE_PATTERN.test(candidate.logicalPath));
|
||||||
|
if (archives.length) {
|
||||||
|
if (candidates.length !== 1) throw new Error("Архив нужно загружать одним файлом.");
|
||||||
|
if (archives[0]!.file.size <= 0) throw new Error(`Пустой файл: ${archives[0]!.logicalPath}`);
|
||||||
|
if (archives[0]!.file.size > MAX_SIMULATION_SOURCE_BYTES) {
|
||||||
|
throw new Error(`Архив ${archives[0]!.logicalPath} превышает лимит 16 ГБ.`);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const descriptors = candidates.filter((candidate) => /\.lcc2?$/i.test(candidate.logicalPath));
|
||||||
|
if (descriptors.length !== 1) {
|
||||||
|
throw new Error("В папке должна быть ровно одна сцена .lcc или .lcc2.");
|
||||||
|
}
|
||||||
|
const paths = new Set<string>();
|
||||||
|
let totalBytes = 0;
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
const parts = candidate.logicalPath.split("/");
|
||||||
|
if (
|
||||||
|
!candidate.logicalPath
|
||||||
|
|| candidate.logicalPath.startsWith("/")
|
||||||
|
|| candidate.logicalPath.includes("\\")
|
||||||
|
|| candidate.logicalPath.includes("\0")
|
||||||
|
|| parts.some((part) => part === "" || part === "." || part === "..")
|
||||||
|
) {
|
||||||
|
throw new Error("Папка содержит небезопасный путь.");
|
||||||
|
}
|
||||||
|
if (candidate.file.size <= 0) throw new Error(`Пустой файл: ${candidate.logicalPath}`);
|
||||||
|
totalBytes += candidate.file.size;
|
||||||
|
if (paths.has(candidate.logicalPath)) throw new Error(`Повторяющийся путь: ${candidate.logicalPath}`);
|
||||||
|
paths.add(candidate.logicalPath);
|
||||||
|
}
|
||||||
|
if (totalBytes > MAX_SIMULATION_SOURCE_BYTES) {
|
||||||
|
throw new Error("Папка с исходниками превышает лимит 16 ГБ.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasDroppedFiles(transfer: DataTransfer): boolean {
|
||||||
|
return transfer.files.length > 0
|
||||||
|
|| Array.from(transfer.items).some((item) => item.kind === "file")
|
||||||
|
|| Array.from(transfer.types).some((type) => type.toLowerCase() === "files");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function archiveProjectName(fileName: string): string {
|
||||||
|
const name = fileName.replace(ARCHIVE_PATTERN, "").trim();
|
||||||
|
return name || "Новая сцена";
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectEntry(
|
||||||
|
entry: LegacyFileEntry,
|
||||||
|
target: SimulationUploadCandidate[],
|
||||||
|
): Promise<void> {
|
||||||
|
if (entry.isFile && entry.file) {
|
||||||
|
return new Promise<File>((resolve, reject) => entry.file?.(resolve, reject))
|
||||||
|
.then((file) => {
|
||||||
|
target.push({ file, logicalPath: entry.fullPath.replace(/^\//, "") || file.name });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!entry.isDirectory || !entry.createReader) return Promise.resolve();
|
||||||
|
return collectDirectory(entry.createReader(), target);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function collectDirectory(
|
||||||
|
reader: LegacyDirectoryReader,
|
||||||
|
target: SimulationUploadCandidate[],
|
||||||
|
): Promise<void> {
|
||||||
|
while (true) {
|
||||||
|
const batch = await new Promise<LegacyFileEntry[]>((resolve, reject) => {
|
||||||
|
reader.readEntries(resolve, reject);
|
||||||
|
});
|
||||||
|
if (!batch.length) return;
|
||||||
|
for (const child of batch) await collectEntry(child, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,6 +38,8 @@ export function useWorkerTelemetry(
|
|||||||
setError(null);
|
setError(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setTelemetry(null);
|
||||||
|
setError(null);
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const stopPolling = startSequentialPolling(async () => {
|
const stopPolling = startSequentialPolling(async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -48,6 +50,7 @@ export function useWorkerTelemetry(
|
|||||||
setError(null);
|
setError(null);
|
||||||
} catch (reason: unknown) {
|
} catch (reason: unknown) {
|
||||||
if (controller.signal.aborted) return;
|
if (controller.signal.aborted) return;
|
||||||
|
setTelemetry(null);
|
||||||
setError(
|
setError(
|
||||||
reason instanceof Error
|
reason instanceof Error
|
||||||
? reason.message
|
? reason.message
|
||||||
|
|||||||
@@ -114,6 +114,9 @@ export interface WorkerTelemetry {
|
|||||||
power_watts?: number;
|
power_watts?: number;
|
||||||
temperature_celsius?: number;
|
temperature_celsius?: number;
|
||||||
memory_used_percent?: number;
|
memory_used_percent?: number;
|
||||||
|
sm_clock_mhz?: number | null;
|
||||||
|
memory_clock_mhz?: number | null;
|
||||||
|
driver_version?: string | null;
|
||||||
} | null;
|
} | null;
|
||||||
triton: {
|
triton: {
|
||||||
ready: boolean;
|
ready: boolean;
|
||||||
@@ -131,6 +134,13 @@ export interface WorkerTelemetry {
|
|||||||
completed_runs: number | null;
|
completed_runs: number | null;
|
||||||
failed_runs: number | null;
|
failed_runs: number | null;
|
||||||
model_load_seconds: number | null;
|
model_load_seconds: number | null;
|
||||||
|
profile_name?: string | null;
|
||||||
|
input_state?: string | null;
|
||||||
|
wait_reason?: string | null;
|
||||||
|
live_children?: number | null;
|
||||||
|
input_pauses?: number | null;
|
||||||
|
pending_bundles?: number | null;
|
||||||
|
buffer_bytes?: number | null;
|
||||||
stages: WorkerPipelineStage[];
|
stages: WorkerPipelineStage[];
|
||||||
};
|
};
|
||||||
network: {
|
network: {
|
||||||
@@ -189,7 +199,9 @@ export async function fetchWorkerTelemetry(
|
|||||||
`/api/v1/system/contours/${encodeURIComponent(contourId)}/telemetry?history=90`,
|
`/api/v1/system/contours/${encodeURIComponent(contourId)}/telemetry?history=90`,
|
||||||
{
|
{
|
||||||
method: "GET",
|
method: "GET",
|
||||||
signal,
|
signal: signal
|
||||||
|
? AbortSignal.any([signal, AbortSignal.timeout(5_000)])
|
||||||
|
: AbortSignal.timeout(5_000),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
"missioncore.worker-telemetry/v1",
|
"missioncore.worker-telemetry/v1",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import "@nodedc/tokens/themes.css";
|
|||||||
import "@nodedc/ui-core/styles.css";
|
import "@nodedc/ui-core/styles.css";
|
||||||
|
|
||||||
import App from "./App";
|
import App from "./App";
|
||||||
import { installedDevicePlugins } from "./composition/devicePlugins";
|
import { installedDevicePlugins, installedNodeSensorContributions } from "./composition/devicePlugins";
|
||||||
import { DevicePluginHostProvider } from "./core/device-plugins/DevicePluginHost";
|
import { DevicePluginHostProvider } from "./core/device-plugins/DevicePluginHost";
|
||||||
import { ComputeContourProvider } from "./core/system/ComputeContourContext";
|
import { ComputeContourProvider } from "./core/system/ComputeContourContext";
|
||||||
import "./styles.css";
|
import "./styles.css";
|
||||||
@@ -23,7 +23,7 @@ applyNodedcTheme(rootElement, { theme: "dark" });
|
|||||||
|
|
||||||
createRoot(rootElement).render(
|
createRoot(rootElement).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<DevicePluginHostProvider plugins={installedDevicePlugins}>
|
<DevicePluginHostProvider plugins={installedDevicePlugins} nodeSensorContributions={installedNodeSensorContributions}>
|
||||||
<ComputeContourProvider>
|
<ComputeContourProvider>
|
||||||
<App />
|
<App />
|
||||||
</ComputeContourProvider>
|
</ComputeContourProvider>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export type WorkspaceKind =
|
|||||||
| "map"
|
| "map"
|
||||||
| "timeline"
|
| "timeline"
|
||||||
| "missions"
|
| "missions"
|
||||||
|
| "vehicles"
|
||||||
| "catalog"
|
| "catalog"
|
||||||
| "contour-health"
|
| "contour-health"
|
||||||
| "compute-modules"
|
| "compute-modules"
|
||||||
@@ -23,7 +24,8 @@ export type WorkspaceKind =
|
|||||||
| "datasets"
|
| "datasets"
|
||||||
| "artifact-health"
|
| "artifact-health"
|
||||||
| "lab-archive"
|
| "lab-archive"
|
||||||
| "simulations";
|
| "simulations"
|
||||||
|
| "observatory";
|
||||||
|
|
||||||
export type CapabilityStatus = "active" | "ready" | "contract" | "later";
|
export type CapabilityStatus = "active" | "ready" | "contract" | "later";
|
||||||
|
|
||||||
@@ -92,7 +94,7 @@ export const roots: RootDefinition[] = [
|
|||||||
label: "Парк",
|
label: "Парк",
|
||||||
title: "Аппараты и устройства",
|
title: "Аппараты и устройства",
|
||||||
eyebrow: "ПАРК / УСТРОЙСТВА",
|
eyebrow: "ПАРК / УСТРОЙСТВА",
|
||||||
description: "Реестр аппаратов, локальное подключение, сенсоры и конфигурации борта.",
|
description: "Реестр аппаратов, бортовые компьютеры, сенсоры и конфигурации борта.",
|
||||||
statement: "Одинаково подключать одиночный стенд, наземную платформу и будущий рой.",
|
statement: "Одинаково подключать одиночный стенд, наземную платформу и будущий рой.",
|
||||||
accent: "АППАРАТЫ И ПОЛЕЗНАЯ НАГРУЗКА",
|
accent: "АППАРАТЫ И ПОЛЕЗНАЯ НАГРУЗКА",
|
||||||
},
|
},
|
||||||
@@ -161,11 +163,22 @@ export const workspaces: WorkspaceDefinition[] = [
|
|||||||
label: "Симуляции",
|
label: "Симуляции",
|
||||||
title: "Симуляции",
|
title: "Симуляции",
|
||||||
eyebrow: "ТЕСТОВЫЙ КОНТУР / СИМУЛЯЦИИ",
|
eyebrow: "ТЕСТОВЫЙ КОНТУР / СИМУЛЯЦИИ",
|
||||||
description: "Каталог Gaussian-миров, переносимые сборки и интерактивные PlayCanvas-сцены.",
|
description: "Каталог Gaussian-миров, переносимые сборки и интерактивные сцены.",
|
||||||
icon: "globe",
|
icon: "globe",
|
||||||
kind: "simulations",
|
kind: "simulations",
|
||||||
groups: [],
|
groups: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: "observatory",
|
||||||
|
root: "polygon",
|
||||||
|
label: "AI Inference",
|
||||||
|
title: "AI Inference",
|
||||||
|
eyebrow: "ТЕСТОВЫЙ КОНТУР / AI INFERENCE",
|
||||||
|
description: "Сессии и квалификация без доступа к управлению.",
|
||||||
|
icon: "eye",
|
||||||
|
kind: "observatory",
|
||||||
|
groups: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "contour-health",
|
id: "contour-health",
|
||||||
root: "fleet",
|
root: "fleet",
|
||||||
@@ -181,31 +194,20 @@ export const workspaces: WorkspaceDefinition[] = [
|
|||||||
id: "vehicles",
|
id: "vehicles",
|
||||||
root: "fleet",
|
root: "fleet",
|
||||||
label: "Аппараты",
|
label: "Аппараты",
|
||||||
title: "Реестр аппаратов",
|
title: "Аппараты",
|
||||||
eyebrow: "ПАРК / РЕЕСТР",
|
eyebrow: "ПАРК / РЕЕСТР",
|
||||||
description: "Нейтральный реестр наземных, воздушных и стационарных платформ.",
|
description: "Аппараты и их бортовые компьютеры в частном контуре.",
|
||||||
icon: "apps",
|
icon: "apps",
|
||||||
kind: "catalog",
|
kind: "vehicles",
|
||||||
groups: [
|
groups: [],
|
||||||
{
|
|
||||||
title: "Идентичность аппарата",
|
|
||||||
description: "Никакой привязки продуктовой модели к конкретному производителю.",
|
|
||||||
capabilities: [
|
|
||||||
ready("Локальный стенд", "Первый аппарат представлен текущим устройством и его адаптером."),
|
|
||||||
contract("Паспорт борта", "Тип, серийный профиль, вычислитель, питание и транспорт."),
|
|
||||||
contract("Состояние доступности", "Онлайн, занят, обслуживание, потеря связи."),
|
|
||||||
later("Группы и рои", "Логические группы, роли и совместное назначение миссий."),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "local-device",
|
id: "local-device",
|
||||||
root: "fleet",
|
root: "polygon",
|
||||||
label: "Подключение",
|
label: "Тестовые устройства",
|
||||||
title: "Подключение",
|
title: "Тестовые устройства",
|
||||||
eyebrow: "ПАРК / ТЕКУЩИЙ АДАПТЕР",
|
eyebrow: "LAB / ТЕСТОВЫЕ УСТРОЙСТВА",
|
||||||
description: "Выбор модели, сценарий установленного плагина и запуск доступного потока.",
|
description: "Подключение устройств к компьютеру оператора для испытаний и записи.",
|
||||||
icon: "network",
|
icon: "network",
|
||||||
kind: "device",
|
kind: "device",
|
||||||
groups: [],
|
groups: [],
|
||||||
@@ -256,11 +258,11 @@ export const workspaces: WorkspaceDefinition[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "spatial-scene",
|
id: "spatial-scene",
|
||||||
root: "observation",
|
root: "polygon",
|
||||||
label: "Пространственная сцена",
|
label: "Пространственная сцена",
|
||||||
title: "Пространственная сцена",
|
title: "Пространственная сцена",
|
||||||
eyebrow: "НАБЛЮДЕНИЕ / ЭФИР",
|
eyebrow: "LAB / ПРОСТРАНСТВЕННАЯ СЦЕНА",
|
||||||
description: "Облако точек, траектория, преобразования и пространственные слои в единой 3D-сцене.",
|
description: "Облако точек, камеры и траектория тестового устройства в реальном времени.",
|
||||||
icon: "globe",
|
icon: "globe",
|
||||||
kind: "spatial",
|
kind: "spatial",
|
||||||
groups: [
|
groups: [
|
||||||
|
|||||||
@@ -1,31 +1 @@
|
|||||||
export type SceneProjection = "3d" | "2d" | "map";
|
export * from "../../../packages/spatial-ui/src/sceneSettings";
|
||||||
export type PointColorMode = "intensity" | "height" | "distance" | "rgb" | "class";
|
|
||||||
export type PointPalette = "turbo" | "viridis" | "plasma" | "grayscale" | "custom";
|
|
||||||
|
|
||||||
export interface SceneSettings {
|
|
||||||
projection: SceneProjection;
|
|
||||||
pointSize: number;
|
|
||||||
colorMode: PointColorMode;
|
|
||||||
palette: PointPalette;
|
|
||||||
customColor: string;
|
|
||||||
accumulationSeconds: number;
|
|
||||||
showPoints: boolean;
|
|
||||||
showTrajectory: boolean;
|
|
||||||
showGrid: boolean;
|
|
||||||
showLabels: boolean;
|
|
||||||
showCameraFrustums: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const defaultSceneSettings: SceneSettings = {
|
|
||||||
projection: "3d",
|
|
||||||
pointSize: 2.5,
|
|
||||||
colorMode: "intensity",
|
|
||||||
palette: "turbo",
|
|
||||||
customColor: "#35d7c1",
|
|
||||||
accumulationSeconds: 12,
|
|
||||||
showPoints: true,
|
|
||||||
showTrajectory: true,
|
|
||||||
showGrid: true,
|
|
||||||
showLabels: false,
|
|
||||||
showCameraFrustums: true,
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
@import "./styles/device.css";
|
@import "./styles/device.css";
|
||||||
@import "./styles/responsive.css";
|
@import "./styles/responsive.css";
|
||||||
@import "./styles/observation.css";
|
@import "./styles/observation.css";
|
||||||
@import "./styles/environment-settings.css";
|
@import "./styles/observatory.css";
|
||||||
@import "./styles/system-telemetry.css";
|
@import "./styles/system-telemetry.css";
|
||||||
@import "./styles/artifact-health.css";
|
@import "./styles/artifact-health.css";
|
||||||
@import "./styles/map.css";
|
@import "./styles/map.css";
|
||||||
|
|||||||
@@ -1,141 +0,0 @@
|
|||||||
.environment-settings {
|
|
||||||
display: grid;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-settings__copy {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
||||||
gap: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-settings__editor {
|
|
||||||
display: grid;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-settings__surface {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: minmax(0, 1fr) minmax(18rem, 24rem);
|
|
||||||
align-items: center;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-settings__surface > span {
|
|
||||||
color: var(--nodedc-text-secondary);
|
|
||||||
font-size: 0.74rem;
|
|
||||||
font-weight: 650;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-settings__surface .nodedc-select-anchor,
|
|
||||||
.environment-settings__surface .nodedc-select {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-settings__quick-actions {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
||||||
gap: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-settings__quick-actions > div {
|
|
||||||
display: grid;
|
|
||||||
gap: 0.4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-settings__quick-actions span {
|
|
||||||
color: var(--nodedc-text-secondary);
|
|
||||||
font-size: 0.68rem;
|
|
||||||
font-weight: 650;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-settings__quick-actions .nodedc-select-anchor,
|
|
||||||
.environment-settings__quick-actions .nodedc-select {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-media-playlist {
|
|
||||||
display: grid;
|
|
||||||
min-width: 0;
|
|
||||||
gap: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-media-playlist__head {
|
|
||||||
display: flex;
|
|
||||||
min-width: 0;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-media-playlist__head > div {
|
|
||||||
display: grid;
|
|
||||||
min-width: 0;
|
|
||||||
gap: 0.22rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-media-playlist__head span {
|
|
||||||
color: var(--nodedc-text-secondary);
|
|
||||||
font-size: var(--nodedc-font-size-sm);
|
|
||||||
font-weight: var(--nodedc-font-weight-medium);
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-media-playlist__head p,
|
|
||||||
.environment-media-playlist__empty,
|
|
||||||
.environment-media-playlist__timing > span,
|
|
||||||
.environment-media-playlist__error {
|
|
||||||
margin: 0;
|
|
||||||
color: var(--nodedc-text-muted);
|
|
||||||
font-size: var(--nodedc-font-size-xs);
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-media-playlist__error {
|
|
||||||
color: rgb(var(--nodedc-danger-rgb));
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-media-playlist__items {
|
|
||||||
display: grid;
|
|
||||||
min-width: 0;
|
|
||||||
gap: 0.7rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-media-playlist__item {
|
|
||||||
display: grid;
|
|
||||||
min-width: 0;
|
|
||||||
grid-template-columns: minmax(0, 1fr) auto;
|
|
||||||
align-items: start;
|
|
||||||
gap: 0.4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-media-playlist__item-actions {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.1rem;
|
|
||||||
padding-top: 1.65rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-media-playlist__timing {
|
|
||||||
display: grid;
|
|
||||||
gap: 0.42rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 760px) {
|
|
||||||
.environment-settings__copy,
|
|
||||||
.environment-settings__quick-actions {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-settings__surface {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-media-playlist__item {
|
|
||||||
grid-template-columns: minmax(0, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
.environment-media-playlist__item-actions {
|
|
||||||
justify-self: end;
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -27,6 +27,78 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.m4-replay-threat-visual__unified-content {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
inset: 0;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.m4-replay-threat-visual__unified-content > *,
|
||||||
|
.m4-replay-threat-visual__unified-content .rerun-viewport {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.m4-replay-threat-visual__deck[data-native-rerun="true"] > .nodedc-split-pane {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.m4-replay-threat-visual__deck[data-native-rerun="true"]
|
||||||
|
.m4-replay-threat-visual__pane {
|
||||||
|
background: transparent;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.m4-replay-threat-visual__deck[data-native-rerun="true"]
|
||||||
|
.nodedc-split-pane__separator,
|
||||||
|
.m4-replay-threat-visual__deck[data-native-rerun="true"]
|
||||||
|
.m4-replay-threat-visual__pane-toolbar,
|
||||||
|
.m4-replay-threat-visual__deck[data-native-rerun="true"]
|
||||||
|
.m4-replay-threat-visual__pane-toolbar * {
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canonical-vegetation-rerun-replay {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canonical-vegetation-rerun-replay:not([data-presentation-state="ready"])
|
||||||
|
.m4-replay-threat-visual__pane-toolbar,
|
||||||
|
.canonical-vegetation-rerun-replay:not([data-presentation-state="ready"])
|
||||||
|
.laboratory-evidence-viewer__controls,
|
||||||
|
.canonical-vegetation-rerun-replay:not([data-presentation-state="ready"])
|
||||||
|
.laboratory-evidence-viewer__transport {
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canonical-vegetation-rerun-replay[data-presentation-state="loading"]
|
||||||
|
.m4-replay-threat-visual__unified-content {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canonical-vegetation-rerun-replay__viewport-lock {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.canonical-vegetation-rerun-replay__loading {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 6;
|
||||||
|
inset: 0;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
background: var(--nodedc-canvas);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.m4-replay-threat-visual__deck[data-empty="true"] > .l3-visual-audit__state {
|
.m4-replay-threat-visual__deck[data-empty="true"] > .l3-visual-audit__state {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
@@ -73,12 +145,14 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
align-content: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
gap: 0.35rem;
|
gap: 0.35rem;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.m4-replay-threat-visual__pane-toolbar[data-pane-toolbar="media"] {
|
.m4-replay-threat-visual__pane-toolbar[data-pane-toolbar="media"] {
|
||||||
justify-content: flex-end;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.m4-replay-threat-evidence-viewer[data-mode-controls="content"]:has(
|
.m4-replay-threat-evidence-viewer[data-mode-controls="content"]:has(
|
||||||
@@ -88,12 +162,17 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.m4-replay-threat-visual__pane-toolbar[data-pane-toolbar="spatial"] {
|
.m4-replay-threat-visual__pane-toolbar[data-pane-toolbar="spatial"] {
|
||||||
justify-content: space-between;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.m4-replay-threat-evidence-viewer[data-mode-controls="content"]
|
.m4-replay-threat-evidence-viewer[data-mode-controls="content"]
|
||||||
.m4-replay-threat-visual__pane-toolbar[data-pane-toolbar="spatial"] {
|
.m4-replay-threat-visual__pane-toolbar[data-pane-toolbar="spatial"],
|
||||||
right: 3.8rem;
|
.m4-replay-threat-evidence-viewer[data-mode-controls="content"]
|
||||||
|
.m4-replay-threat-visual__deck:not([data-split="true"])
|
||||||
|
.m4-replay-threat-visual__pane-toolbar[data-pane-toolbar="media"] {
|
||||||
|
/* The two viewer-wide actions stay at the deck's right edge. Keep their
|
||||||
|
complete hit area out of this viewport-owned toolbar at every split. */
|
||||||
|
right: 7rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.m4-replay-threat-visual__pane-toolbar > *,
|
.m4-replay-threat-visual__pane-toolbar > *,
|
||||||
@@ -102,18 +181,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.m4-replay-threat-visual__spatial-toolbar-end {
|
.m4-replay-threat-visual__spatial-toolbar-end {
|
||||||
display: flex;
|
display: contents;
|
||||||
min-width: 0;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 0.35rem;
|
|
||||||
overflow-x: auto;
|
|
||||||
overscroll-behavior-inline: contain;
|
|
||||||
scrollbar-width: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.m4-replay-threat-visual__spatial-toolbar-end::-webkit-scrollbar {
|
.m4-replay-threat-visual__pane-toolbar > .m4-replay-threat-visual__pane-layer-controls,
|
||||||
display: none;
|
.m4-replay-threat-visual__spatial-toolbar-end > .m4-replay-threat-visual__pane-layer-controls {
|
||||||
|
/* A viewport toolbar is one adaptive flex sequence. `display: contents`
|
||||||
|
keeps the semantic layer group while allowing each control to move to the
|
||||||
|
next row only when the viewport itself runs out of width. */
|
||||||
|
display: contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
.m4-replay-threat-visual__pane-mode-controls {
|
.m4-replay-threat-visual__pane-mode-controls {
|
||||||
@@ -162,7 +238,8 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.4rem;
|
gap: 0.4rem;
|
||||||
border: 1px solid var(--nodedc-glass-outline);
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
border-radius: var(--nodedc-radius-control-compact);
|
border-radius: var(--nodedc-radius-control-compact);
|
||||||
background: var(--nodedc-floating-surface);
|
background: var(--nodedc-floating-surface);
|
||||||
padding: 0.42rem 0.58rem;
|
padding: 0.42rem 0.58rem;
|
||||||
@@ -183,6 +260,15 @@
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.canonical-vegetation-rerun-replay
|
||||||
|
.m4-replay-threat-evidence-viewer[data-mode-controls="content"]
|
||||||
|
.laboratory-evidence-viewer__controls {
|
||||||
|
right: 0.6rem;
|
||||||
|
left: auto;
|
||||||
|
width: auto;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
.m4-replay-threat-evidence-viewer[data-mode-controls="content"] .laboratory-evidence-viewer__controls > .nodedc-icon-button {
|
.m4-replay-threat-evidence-viewer[data-mode-controls="content"] .laboratory-evidence-viewer__controls > .nodedc-icon-button {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
@@ -207,6 +293,30 @@
|
|||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.m4-replay-threat-visual__layer-with-settings {
|
||||||
|
display: inline-flex;
|
||||||
|
flex: none;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.18rem;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--nodedc-glass-control-bg);
|
||||||
|
padding: 0.12rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.m4-replay-threat-visual__layer-with-settings > .nodedc-icon-button {
|
||||||
|
width: 1.75rem;
|
||||||
|
height: 1.75rem;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.m4-replay-threat-visual__layer-with-settings > .nodedc-icon-button:focus-visible {
|
||||||
|
background: var(--nodedc-focus-surface);
|
||||||
|
box-shadow: inset 0 0 0 1px rgb(var(--nodedc-accent-rgb) / 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
.m4-replay-threat-visual__review-controls,
|
.m4-replay-threat-visual__review-controls,
|
||||||
.m4-replay-threat-visual__review-controls > *,
|
.m4-replay-threat-visual__review-controls > *,
|
||||||
.m4-replay-threat-visual__single-pane-controls,
|
.m4-replay-threat-visual__single-pane-controls,
|
||||||
@@ -232,6 +342,10 @@
|
|||||||
grid-template-columns: auto auto auto minmax(0, 1fr) auto;
|
grid-template-columns: auto auto auto minmax(0, 1fr) auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.canonical-vegetation-rerun-replay__timeline .observation-timeline__playback {
|
||||||
|
grid-template-columns: auto auto minmax(0, 1fr) auto;
|
||||||
|
}
|
||||||
|
|
||||||
.m4-replay-threat-visual__overlay {
|
.m4-replay-threat-visual__overlay {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|||||||
@@ -1,659 +1 @@
|
|||||||
.scene-source-controls {
|
@import "../../../../packages/spatial-ui/src/observation.css";
|
||||||
position: absolute;
|
|
||||||
z-index: 12;
|
|
||||||
top: 0.85rem;
|
|
||||||
left: 0.85rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.45rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scene-device-controls {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 30;
|
|
||||||
top: 0.85rem;
|
|
||||||
left: 50%;
|
|
||||||
max-width: calc(100% - 9rem);
|
|
||||||
transform: translateX(-50%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.scene-source-picker__trigger,
|
|
||||||
.scene-source-control,
|
|
||||||
.scene-focus-exit {
|
|
||||||
display: inline-grid;
|
|
||||||
width: 2.75rem;
|
|
||||||
height: 2.75rem;
|
|
||||||
place-items: center;
|
|
||||||
border: 1px solid rgb(255 255 255 / 0.09);
|
|
||||||
border-radius: 50%;
|
|
||||||
background: rgb(12 13 16 / 0.82);
|
|
||||||
color: var(--nodedc-text-secondary);
|
|
||||||
cursor: pointer;
|
|
||||||
backdrop-filter: blur(18px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.scene-navigation-hint {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 11;
|
|
||||||
right: 0.85rem;
|
|
||||||
bottom: 4.9rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.75rem;
|
|
||||||
border: 1px solid rgb(255 255 255 / 0.07);
|
|
||||||
border-radius: 999px;
|
|
||||||
background: rgb(9 10 13 / 0.7);
|
|
||||||
color: var(--nodedc-text-muted);
|
|
||||||
padding: 0.42rem 0.65rem;
|
|
||||||
font-size: 0.52rem;
|
|
||||||
font-weight: 680;
|
|
||||||
pointer-events: none;
|
|
||||||
backdrop-filter: blur(14px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.scene-source-picker__trigger {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scene-source-picker__trigger:hover,
|
|
||||||
.scene-source-control:hover,
|
|
||||||
.scene-focus-exit:hover,
|
|
||||||
.scene-source-picker__trigger[data-active="true"] {
|
|
||||||
border-color: rgb(255 255 255 / 0.18);
|
|
||||||
color: var(--nodedc-text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.scene-source-picker__trigger > span {
|
|
||||||
position: absolute;
|
|
||||||
top: -0.2rem;
|
|
||||||
right: -0.2rem;
|
|
||||||
display: grid;
|
|
||||||
min-width: 1.05rem;
|
|
||||||
height: 1.05rem;
|
|
||||||
place-items: center;
|
|
||||||
border-radius: 999px;
|
|
||||||
background: var(--nodedc-text-primary);
|
|
||||||
color: #090a0c;
|
|
||||||
padding: 0 0.25rem;
|
|
||||||
font-size: 0.5rem;
|
|
||||||
font-weight: 800;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scene-focus-exit {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 20;
|
|
||||||
top: 0.85rem;
|
|
||||||
right: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scene-status--top-left {
|
|
||||||
left: 7.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scene-status[aria-hidden="true"],
|
|
||||||
.scene-metrics[aria-hidden="true"] {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-source-menu {
|
|
||||||
overflow: hidden;
|
|
||||||
font-family: var(--nodedc-font-family);
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-source-menu__head,
|
|
||||||
.observation-source-menu__foot {
|
|
||||||
padding: 0.85rem 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-source-menu__head {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-end;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-source-menu__head strong,
|
|
||||||
.observation-source-menu__head span {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-source-menu__head strong {
|
|
||||||
margin-top: 0.34rem;
|
|
||||||
color: var(--nodedc-text-primary);
|
|
||||||
font-size: var(--nodedc-font-size-md);
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-source-menu__head small,
|
|
||||||
.observation-source-menu__foot {
|
|
||||||
color: var(--nodedc-text-muted);
|
|
||||||
font-size: var(--nodedc-font-size-xs);
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-source-menu__list {
|
|
||||||
display: grid;
|
|
||||||
max-height: min(28rem, 60vh);
|
|
||||||
overflow: auto;
|
|
||||||
padding: 0.35rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-source-option .nodedc-dropdown-option__description {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.35rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-source-option i,
|
|
||||||
.floating-observation-window i,
|
|
||||||
.camera-slot__status i {
|
|
||||||
width: 0.42rem;
|
|
||||||
height: 0.42rem;
|
|
||||||
flex: 0 0 0.42rem;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: var(--nodedc-text-muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
i[data-availability="available"],
|
|
||||||
i[data-availability="streaming"] {
|
|
||||||
background: rgb(var(--nodedc-success-rgb));
|
|
||||||
}
|
|
||||||
|
|
||||||
i[data-availability="connecting"],
|
|
||||||
i[data-availability="declared"] {
|
|
||||||
background: rgb(var(--nodedc-warning-rgb));
|
|
||||||
}
|
|
||||||
|
|
||||||
i[data-availability="unverified"] {
|
|
||||||
background: var(--nodedc-text-muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
i[data-availability="degraded"],
|
|
||||||
i[data-availability="error"] {
|
|
||||||
background: rgb(var(--nodedc-danger-rgb));
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-source-menu__empty,
|
|
||||||
.camera-grid__empty,
|
|
||||||
.observation-media__empty {
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
align-content: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
color: var(--nodedc-text-muted);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-source-menu__empty {
|
|
||||||
min-height: 10rem;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-source-menu__empty strong,
|
|
||||||
.observation-media__empty strong,
|
|
||||||
.camera-grid__empty strong {
|
|
||||||
color: var(--nodedc-text-secondary);
|
|
||||||
font-size: 0.7rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-source-menu__empty span,
|
|
||||||
.observation-media__empty span,
|
|
||||||
.camera-grid__empty span {
|
|
||||||
max-width: 24rem;
|
|
||||||
font-size: 0.59rem;
|
|
||||||
line-height: 1.45;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-source-menu__foot {
|
|
||||||
border-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-media__asset,
|
|
||||||
.observation-media__empty {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-media__asset {
|
|
||||||
display: block;
|
|
||||||
object-fit: contain;
|
|
||||||
background: #050608;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recorded-media-player {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
background: #070809;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recorded-media-player:not([data-state="ready"]) .observation-media__asset {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recorded-media-player__notice {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
padding: 18px;
|
|
||||||
color: rgba(247, 248, 244, 0.72);
|
|
||||||
background: #070809;
|
|
||||||
font: inherit;
|
|
||||||
font-size: 11px;
|
|
||||||
text-align: center;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mse-fmp4-player {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
min-height: 9rem;
|
|
||||||
overflow: hidden;
|
|
||||||
background: #050608;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mse-fmp4-player__status {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
align-content: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
padding: 1rem;
|
|
||||||
background: rgb(5 6 8 / 0.88);
|
|
||||||
color: var(--nodedc-text-muted);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mse-fmp4-player__status strong {
|
|
||||||
color: var(--nodedc-text-secondary);
|
|
||||||
font-size: 0.7rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mse-fmp4-player__status span {
|
|
||||||
max-width: 24rem;
|
|
||||||
font-size: 0.59rem;
|
|
||||||
line-height: 1.45;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mse-fmp4-player__status button {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.4rem;
|
|
||||||
margin-top: 0.25rem;
|
|
||||||
border: 1px solid rgb(255 255 255 / 0.12);
|
|
||||||
border-radius: 999px;
|
|
||||||
background: rgb(255 255 255 / 0.06);
|
|
||||||
color: var(--nodedc-text-primary);
|
|
||||||
padding: 0.48rem 0.72rem;
|
|
||||||
font: inherit;
|
|
||||||
font-size: 0.58rem;
|
|
||||||
font-weight: 760;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mse-fmp4-player__status button:hover {
|
|
||||||
background: rgb(255 255 255 / 0.11);
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-media__empty {
|
|
||||||
min-height: 9rem;
|
|
||||||
background: #07080a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.floating-observation-window__status,
|
|
||||||
.floating-observation-window__footer {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.45rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-slot__head-actions button {
|
|
||||||
display: grid;
|
|
||||||
width: 1.9rem;
|
|
||||||
height: 1.9rem;
|
|
||||||
place-items: center;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: rgb(255 255 255 / 0.05);
|
|
||||||
color: var(--nodedc-text-secondary);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-slot__head-actions button:hover {
|
|
||||||
background: rgb(255 255 255 / 0.1);
|
|
||||||
color: var(--nodedc-text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-slot__head-actions .camera-slot__activation {
|
|
||||||
width: auto;
|
|
||||||
min-width: 5.6rem;
|
|
||||||
height: 1.9rem;
|
|
||||||
border: 1px solid rgb(255 255 255 / 0.09);
|
|
||||||
border-radius: 999px;
|
|
||||||
padding: 0 0.65rem;
|
|
||||||
font: inherit;
|
|
||||||
font-size: 0.53rem;
|
|
||||||
font-weight: 760;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-slot__head-actions .camera-slot__activation:disabled {
|
|
||||||
cursor: wait;
|
|
||||||
opacity: 0.48;
|
|
||||||
}
|
|
||||||
|
|
||||||
.floating-observation-window__status {
|
|
||||||
color: var(--nodedc-text-secondary);
|
|
||||||
font-size: 0.54rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.floating-observation-window__footer {
|
|
||||||
width: 100%;
|
|
||||||
justify-content: space-between;
|
|
||||||
color: var(--nodedc-text-muted);
|
|
||||||
font-size: 0.53rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.floating-observation-window .nodedc-workspace-window__body {
|
|
||||||
background: #06070a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.floating-observation-window.nodedc-material-rim::after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.floating-observation-window[data-active="true"] {
|
|
||||||
box-shadow: 0 1.5rem 4rem rgb(0 0 0 / 0.34);
|
|
||||||
}
|
|
||||||
|
|
||||||
.floating-observation-window--hidden {
|
|
||||||
visibility: hidden;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline {
|
|
||||||
display: block;
|
|
||||||
min-width: 0;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 0.9rem;
|
|
||||||
background: rgb(9 10 13 / 0.78);
|
|
||||||
padding: 0.4rem;
|
|
||||||
backdrop-filter: blur(16px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline[data-accumulation="true"] {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__playback {
|
|
||||||
display: grid;
|
|
||||||
min-width: 0;
|
|
||||||
grid-template-columns: auto auto minmax(0, 1fr) auto auto;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__playback > .nodedc-select-anchor {
|
|
||||||
display: inline-flex;
|
|
||||||
width: auto;
|
|
||||||
flex: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__transport {
|
|
||||||
width: var(--nodedc-control-height-compact);
|
|
||||||
padding: 0;
|
|
||||||
border-radius: var(--nodedc-radius-circle);
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__accumulation {
|
|
||||||
display: grid;
|
|
||||||
min-width: 0;
|
|
||||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.7rem;
|
|
||||||
padding: 0 0.45rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__accumulation span,
|
|
||||||
.observation-timeline__accumulation code {
|
|
||||||
color: var(--nodedc-text-muted);
|
|
||||||
font-size: 0.55rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__accumulation code {
|
|
||||||
min-width: 2.65rem;
|
|
||||||
color: var(--nodedc-text-primary);
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__track {
|
|
||||||
width: 100%;
|
|
||||||
height: 0.3rem;
|
|
||||||
appearance: none;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 999px;
|
|
||||||
background: rgb(255 255 255 / 0.08);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__track::-webkit-slider-thumb {
|
|
||||||
width: 0.72rem;
|
|
||||||
height: 0.72rem;
|
|
||||||
appearance: none;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: var(--nodedc-text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__track::-moz-range-thumb {
|
|
||||||
width: 0.72rem;
|
|
||||||
height: 0.72rem;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: var(--nodedc-text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__track:disabled {
|
|
||||||
opacity: 0.46;
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__meta {
|
|
||||||
display: grid;
|
|
||||||
justify-items: end;
|
|
||||||
gap: 0.12rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__meta code,
|
|
||||||
.observation-timeline__meta small {
|
|
||||||
color: var(--nodedc-text-muted);
|
|
||||||
font-size: 0.52rem;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__follow {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.38rem;
|
|
||||||
color: var(--nodedc-text-muted);
|
|
||||||
border: 0;
|
|
||||||
background: transparent;
|
|
||||||
padding: 0.3rem 0.45rem;
|
|
||||||
font-size: 0.52rem;
|
|
||||||
font-weight: 820;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__follow:not(:disabled) {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__follow::before {
|
|
||||||
width: 0.4rem;
|
|
||||||
height: 0.4rem;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: var(--nodedc-text-muted);
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__follow[data-active="true"] {
|
|
||||||
color: var(--nodedc-text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__follow[data-active="true"]::before {
|
|
||||||
background: rgb(var(--nodedc-success-rgb));
|
|
||||||
}
|
|
||||||
|
|
||||||
.scene-timeline {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 10;
|
|
||||||
right: 0.75rem;
|
|
||||||
bottom: 0.75rem;
|
|
||||||
left: 0.75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spatial-workspace[data-focused="true"] {
|
|
||||||
min-height: 0;
|
|
||||||
grid-template-rows: minmax(0, 1fr);
|
|
||||||
gap: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.spatial-workspace[data-focused="true"] > .spatial-toolbar,
|
|
||||||
.spatial-workspace[data-focused="true"] > .spatial-contract-strip {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cameras-workspace {
|
|
||||||
height: 100%;
|
|
||||||
min-height: 0;
|
|
||||||
grid-template-rows: auto auto minmax(0, 1fr) auto;
|
|
||||||
overflow: hidden;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-workspace__catalog {
|
|
||||||
display: flex;
|
|
||||||
min-width: 0;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
gap: 0.8rem;
|
|
||||||
color: var(--nodedc-text-muted);
|
|
||||||
font-size: 0.6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-grid {
|
|
||||||
display: grid;
|
|
||||||
min-height: 0;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(min(24rem, 100%), 1fr));
|
|
||||||
grid-template-rows: none;
|
|
||||||
grid-auto-rows: minmax(16rem, 1fr);
|
|
||||||
gap: 0.75rem;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-grid[data-count="1"] {
|
|
||||||
grid-template-columns: minmax(0, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-grid__empty {
|
|
||||||
min-height: 18rem;
|
|
||||||
border-radius: 1rem;
|
|
||||||
background: #07080a;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-slot {
|
|
||||||
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-slot header,
|
|
||||||
.camera-slot footer {
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-slot__head-actions,
|
|
||||||
.camera-slot__status {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.45rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-slot__status {
|
|
||||||
color: var(--nodedc-text-muted);
|
|
||||||
font-size: 0.56rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-slot__body {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
min-width: 0;
|
|
||||||
min-height: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-slot footer {
|
|
||||||
align-items: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-slot footer span {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-slot footer small {
|
|
||||||
flex: 0 0 auto;
|
|
||||||
color: var(--nodedc-text-secondary);
|
|
||||||
font-size: 0.53rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-timeline {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cameras-workspace[data-focused="true"] {
|
|
||||||
height: 100%;
|
|
||||||
grid-template-rows: minmax(0, 1fr) auto;
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cameras-workspace[data-focused="true"] > .workspace-lead,
|
|
||||||
.cameras-workspace[data-focused="true"] > .camera-workspace__catalog {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cameras-workspace[data-focused="true"] .camera-grid {
|
|
||||||
min-height: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 760px) {
|
|
||||||
.scene-status--top-left {
|
|
||||||
top: 4.2rem;
|
|
||||||
left: 0.6rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline[data-accumulation="true"] {
|
|
||||||
grid-template-columns: minmax(0, 1fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__playback {
|
|
||||||
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.observation-timeline__playback > .nodedc-button:first-child,
|
|
||||||
.observation-timeline__meta {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.camera-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
grid-auto-rows: minmax(14rem, 1fr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,640 @@
|
|||||||
|
.observatory-workspace {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-rows: max-content;
|
||||||
|
align-content: start;
|
||||||
|
gap: 1rem;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 100%;
|
||||||
|
padding: 1rem;
|
||||||
|
container-name: observatory-workspace;
|
||||||
|
container-type: inline-size;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-catalog-bar,
|
||||||
|
.observatory-catalog-bar__controls,
|
||||||
|
.observatory-notice,
|
||||||
|
.observatory-evidence > header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-notice {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-catalog-bar__copy,
|
||||||
|
.observatory-evidence header > div {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-catalog-bar h3,
|
||||||
|
.observatory-session-summary h3,
|
||||||
|
.observatory-evidence h3 {
|
||||||
|
margin: 0.3rem 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-catalog-bar h3,
|
||||||
|
.observatory-session-summary h3,
|
||||||
|
.observatory-evidence h3 {
|
||||||
|
font-size: var(--nodedc-font-size-lg);
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
line-height: 1.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-state p,
|
||||||
|
.observatory-evidence-empty p {
|
||||||
|
max-width: 52rem;
|
||||||
|
margin: 0.35rem 0 0;
|
||||||
|
color: var(--nodedc-text-muted);
|
||||||
|
font-size: var(--nodedc-font-size-sm);
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-catalog-bar,
|
||||||
|
.observatory-session-summary {
|
||||||
|
align-self: start;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-catalog-bar__copy {
|
||||||
|
display: grid;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
align-content: center;
|
||||||
|
gap: 0.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-catalog-bar h3 {
|
||||||
|
margin-top: 0.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-catalog-bar__controls {
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-catalog-bar__controls .nodedc-select-anchor {
|
||||||
|
flex: 1 1 18rem;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-catalog-bar__controls > .nodedc-button {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-catalog-bar__run {
|
||||||
|
display: flex;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.55rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-state {
|
||||||
|
display: grid;
|
||||||
|
min-height: 19rem;
|
||||||
|
place-items: center;
|
||||||
|
align-content: center;
|
||||||
|
gap: 0.7rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-state h3 {
|
||||||
|
max-width: 42rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-session-stack {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-rows: max-content;
|
||||||
|
align-content: start;
|
||||||
|
gap: 0.9rem;
|
||||||
|
container-name: observatory-session;
|
||||||
|
container-type: inline-size;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-session-summary {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(11rem, 1.05fr) minmax(20rem, 1.6fr) minmax(11rem, auto);
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-session-summary__identity {
|
||||||
|
display: grid;
|
||||||
|
min-width: 0;
|
||||||
|
gap: 0.22rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-session-summary__identity h3 {
|
||||||
|
margin: 0.1rem 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-session-summary__identity code {
|
||||||
|
overflow: hidden;
|
||||||
|
color: var(--nodedc-text-muted);
|
||||||
|
font-size: var(--nodedc-font-size-xs);
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-session-summary__facts {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(6.5rem, 1fr));
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-session-summary__facts > div {
|
||||||
|
display: grid;
|
||||||
|
min-width: 0;
|
||||||
|
gap: 0.24rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-session-summary__facts dt {
|
||||||
|
color: var(--nodedc-text-muted);
|
||||||
|
font-size: var(--nodedc-font-size-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-session-summary__facts dd {
|
||||||
|
min-width: 0;
|
||||||
|
margin: 0;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
color: var(--nodedc-text-secondary);
|
||||||
|
font-size: var(--nodedc-font-size-sm);
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-session-summary__end {
|
||||||
|
display: grid;
|
||||||
|
min-width: 0;
|
||||||
|
justify-items: end;
|
||||||
|
gap: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-modalities {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 0.45rem;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--nodedc-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.65rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence > header {
|
||||||
|
padding: 0 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence__ordering {
|
||||||
|
display: flex;
|
||||||
|
flex: none;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.55rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence__sort {
|
||||||
|
display: grid;
|
||||||
|
width: 1.7rem;
|
||||||
|
height: 1.7rem;
|
||||||
|
place-items: center;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
padding: 0;
|
||||||
|
color: var(--nodedc-text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence__sort:hover,
|
||||||
|
.observatory-evidence__sort:focus-visible {
|
||||||
|
background: var(--nodedc-glass-control-bg);
|
||||||
|
color: var(--nodedc-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence__sort svg {
|
||||||
|
width: 1.1rem;
|
||||||
|
height: 1.1rem;
|
||||||
|
transition: transform 160ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence__sort path {
|
||||||
|
fill: none;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-width: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence__sort[data-direction="oldest"] svg {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.4rem;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence-card {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto minmax(0, 1fr) auto auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.8rem;
|
||||||
|
min-height: 4.25rem;
|
||||||
|
padding: 0.7rem 0.85rem;
|
||||||
|
border-radius: var(--nodedc-radius-control-compact);
|
||||||
|
background: var(--nodedc-glass-control-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence-card__icon {
|
||||||
|
display: grid;
|
||||||
|
width: 2.15rem;
|
||||||
|
height: 2.15rem;
|
||||||
|
place-items: center;
|
||||||
|
border-radius: var(--nodedc-radius-circle);
|
||||||
|
background: var(--nodedc-panel-icon-bg);
|
||||||
|
color: var(--nodedc-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence-card__copy {
|
||||||
|
display: grid;
|
||||||
|
min-width: 0;
|
||||||
|
gap: 0.16rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence-card__copy strong,
|
||||||
|
.observatory-evidence-card__copy span,
|
||||||
|
.observatory-evidence-card__copy small {
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence-card__copy strong {
|
||||||
|
overflow: visible;
|
||||||
|
text-overflow: clip;
|
||||||
|
white-space: normal;
|
||||||
|
font-size: var(--nodedc-font-size-sm);
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence-card__copy span {
|
||||||
|
color: var(--nodedc-text-secondary);
|
||||||
|
font-size: var(--nodedc-font-size-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence-card__copy small {
|
||||||
|
color: var(--nodedc-text-muted);
|
||||||
|
font-size: var(--nodedc-font-size-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence-card--progress {
|
||||||
|
grid-template-columns: auto minmax(0, 1fr) auto auto;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence-card__progress {
|
||||||
|
height: 0.28rem;
|
||||||
|
margin: 0.18rem 0 0.08rem;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgb(var(--nodedc-accent-rgb) / 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence-card__progress > span {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: inherit;
|
||||||
|
background: rgb(var(--nodedc-accent-rgb));
|
||||||
|
transition: width 180ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence-card__actions,
|
||||||
|
.observatory-replay__header,
|
||||||
|
.observatory-replay-state,
|
||||||
|
.observatory-replay-state__actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-replay-state {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-replay__header > div,
|
||||||
|
.observatory-replay-state > div {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence-card__actions:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-replay,
|
||||||
|
.observatory-replay-state {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-replay {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.85rem;
|
||||||
|
font-family: var(--nodedc-font-family);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-replay__header {
|
||||||
|
padding: 0.25rem 0.25rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-replay__header h3,
|
||||||
|
.observatory-replay-state h3 {
|
||||||
|
margin: 0.3rem 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-replay__header h3 {
|
||||||
|
font-size: var(--nodedc-font-size-sm);
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-replay__header p,
|
||||||
|
.observatory-replay-state p,
|
||||||
|
.observatory-replay__description {
|
||||||
|
margin: 0.35rem 0 0;
|
||||||
|
color: var(--nodedc-text-muted);
|
||||||
|
font-family: var(--nodedc-font-family);
|
||||||
|
font-size: var(--nodedc-font-size-sm);
|
||||||
|
font-weight: var(--nodedc-font-weight-regular);
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-replay__description {
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-replay .m4-replay-threat-visual__buffering {
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-replay .laboratory-evidence-viewer {
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence-empty {
|
||||||
|
display: grid;
|
||||||
|
min-height: 13rem;
|
||||||
|
place-items: center;
|
||||||
|
align-content: center;
|
||||||
|
gap: 0.55rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-notice {
|
||||||
|
justify-content: flex-start;
|
||||||
|
color: var(--nodedc-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-notice__copy {
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-rename-form,
|
||||||
|
.observatory-delete-confirmation {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-delete-confirmation p,
|
||||||
|
.observatory-mutation-error {
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-mutation-error {
|
||||||
|
color: rgb(var(--nodedc-danger-rgb));
|
||||||
|
font-size: var(--nodedc-font-size-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@container observatory-session (max-width: 46rem) {
|
||||||
|
.observatory-session-summary {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-session-summary__end,
|
||||||
|
.observatory-modalities {
|
||||||
|
justify-items: start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container observatory-session (max-width: 48rem) {
|
||||||
|
.observatory-evidence-card {
|
||||||
|
grid-template-columns: auto minmax(0, 1fr) auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence-card > .nodedc-status {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container observatory-session (max-width: 38rem) {
|
||||||
|
.observatory-session-summary__facts {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-evidence-card__copy small {
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container observatory-workspace (max-width: 920px) {
|
||||||
|
.observatory-catalog-bar,
|
||||||
|
.observatory-catalog-bar__controls,
|
||||||
|
.observatory-notice {
|
||||||
|
align-items: stretch;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-catalog-bar__controls {
|
||||||
|
flex-basis: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-catalog-bar__controls .nodedc-select-anchor {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-catalog-bar__run {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 920px) {
|
||||||
|
.observatory-replay__header,
|
||||||
|
.observatory-replay-state {
|
||||||
|
align-items: stretch;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.observatory-ai-config {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--nodedc-space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-config__lead,
|
||||||
|
.observatory-ai-config__error {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--nodedc-text-secondary);
|
||||||
|
font-size: var(--nodedc-font-size-sm);
|
||||||
|
line-height: 1.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-config__error {
|
||||||
|
color: rgb(var(--nodedc-danger-rgb));
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-config__duplicate {
|
||||||
|
margin-right: auto;
|
||||||
|
color: var(--nodedc-text-secondary);
|
||||||
|
font-size: var(--nodedc-font-size-sm);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes observatory-ai-calculate-pulse {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
background-color: var(--nodedc-primary-action-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
background-color: color-mix(
|
||||||
|
in srgb,
|
||||||
|
var(--nodedc-primary-action-bg) 88%,
|
||||||
|
var(--nodedc-canvas-soft)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-config__calculate--saving:disabled {
|
||||||
|
animation: observatory-ai-calculate-pulse 1.8s ease-in-out infinite;
|
||||||
|
cursor: progress;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-config-window {
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: var(--nodedc-shadow-window);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-config-window::before,
|
||||||
|
.observatory-ai-config-window::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-module-group {
|
||||||
|
overflow: hidden;
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
border-radius: var(--nodedc-radius-control-compact);
|
||||||
|
background: var(--nodedc-glass-control-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-module-group > header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--nodedc-space-3);
|
||||||
|
padding: var(--nodedc-space-3) var(--nodedc-space-4);
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-module-group > header > div {
|
||||||
|
display: grid;
|
||||||
|
min-width: 0;
|
||||||
|
gap: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-module-group > header .nodedc-status {
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 55%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-module-group > header span {
|
||||||
|
color: var(--nodedc-text-muted);
|
||||||
|
font-size: var(--nodedc-font-size-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-module-group__body {
|
||||||
|
padding: var(--nodedc-space-4);
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-module-group__body .nodedc-select-anchor {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-config-window .nodedc-select-trigger,
|
||||||
|
.observatory-ai-config-window .nodedc-window__close {
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-config-window .nodedc-select-trigger:focus-visible,
|
||||||
|
.observatory-ai-config-window .nodedc-window__close:focus-visible {
|
||||||
|
background-color: var(--nodedc-focus-surface);
|
||||||
|
box-shadow:
|
||||||
|
var(--nodedc-glass-control-shadow),
|
||||||
|
inset 0 0 0 1px rgb(var(--nodedc-accent-rgb) / 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 560px) {
|
||||||
|
.observatory-ai-config-window .nodedc-window__title {
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-module-group > header {
|
||||||
|
align-items: flex-start;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.observatory-ai-module-group > header .nodedc-status {
|
||||||
|
max-width: 100%;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.observatory-ai-config__calculate--saving:disabled {
|
||||||
|
animation: none;
|
||||||
|
background-color: color-mix(
|
||||||
|
in srgb,
|
||||||
|
var(--nodedc-primary-action-bg) 94%,
|
||||||
|
var(--nodedc-canvas-soft)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,9 +3,6 @@
|
|||||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
|
|
||||||
.landing-stage__copy {
|
|
||||||
width: min(35rem, 54%);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1280px) {
|
@media (max-width: 1280px) {
|
||||||
@@ -190,15 +187,6 @@
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.landing-stage__status {
|
|
||||||
width: 20rem;
|
|
||||||
max-width: 36vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.landing-stage__copy h1 {
|
|
||||||
font-size: clamp(2.7rem, 7vw, 5.6rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
.scene-metrics {
|
.scene-metrics {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -424,36 +412,6 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.landing-stage__copy {
|
|
||||||
top: 42%;
|
|
||||||
right: 1.2rem;
|
|
||||||
left: 1.2rem;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.landing-stage__copy h1 {
|
|
||||||
font-size: clamp(2.8rem, 14vw, 5rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
.landing-stage__status {
|
|
||||||
top: auto;
|
|
||||||
right: 1rem;
|
|
||||||
bottom: 3.3rem;
|
|
||||||
left: 1rem;
|
|
||||||
width: auto;
|
|
||||||
max-width: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.landing-stage__footer {
|
|
||||||
right: 1rem;
|
|
||||||
bottom: 1.1rem;
|
|
||||||
left: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.landing-stage__footer span:first-child {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.metrics-grid,
|
.metrics-grid,
|
||||||
.capability-summary,
|
.capability-summary,
|
||||||
.camera-grid,
|
.camera-grid,
|
||||||
@@ -645,20 +603,5 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-height: 720px) and (min-width: 761px) {
|
@media (max-height: 720px) and (min-width: 761px) {
|
||||||
.landing-stage__copy h1 {
|
|
||||||
font-size: clamp(2.7rem, 6vw, 5rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
.landing-stage__copy p {
|
|
||||||
font-size: 0.76rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.landing-stage__actions {
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.landing-stage__status {
|
|
||||||
top: 1.2rem;
|
|
||||||
right: 1.2rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user