Author SHA1 Message Date
DCCONSTRUCTIONS 2e44e27967 feat(simulation): auto-build source mesh collision 2026-08-28 13:48:44 +03:00
DCCONSTRUCTIONS 2ccf172319 feat(simulation): add Gaussian UGV runtime pipeline 2026-08-28 11:51:48 +03:00
DCCONSTRUCTIONS 30080c51aa feat(perception): add DDRNet full-video vegetation replay 2026-08-28 01:13:18 +03:00
DCCONSTRUCTIONS 67e6ae98e2 fix(perception): reuse M4.8 for vegetation evidence 2026-08-28 00:13:58 +03:00
DCCONSTRUCTIONS 57204b3b0b feat(perception): add autonomous vegetation shadow lab 2026-08-27 23:26:35 +03:00
DCCONSTRUCTIONS 7594c71dd1 feat(perception): map vegetation provider labels 2026-08-27 21:53:25 +03:00
DCCONSTRUCTIONS 92aa796c9d chore(perception): freeze vegetation candidates 2026-08-27 21:46:02 +03:00
DCCONSTRUCTIONS 7c0fd81b2f feat(perception): add vegetation mission policy lab 2026-08-27 21:36:25 +03:00
DCCONSTRUCTIONS 95a1ef5057 feat(lab): stabilize autonomous TGS playback 2026-08-27 20:34:20 +03:00
DCCONSTRUCTIONS c8593207d3 docs(lab): record sealed playback data plane 2026-08-27 15:25:54 +03:00
DCCONSTRUCTIONS 7aeea4ed81 perf(lab): retain spatial scenes during replay 2026-08-27 15:19:00 +03:00
DCCONSTRUCTIONS bf76304e00 perf(lab): stream sealed spatial playback tracks 2026-08-27 15:18:45 +03:00
DCCONSTRUCTIONS b70dc4298a docs(perception): accept integrated TGS graph load gate 2026-08-27 12:01:27 +03:00
DCCONSTRUCTIONS f153d671d3 fix(perception): close integrated shadow packaging 2026-08-27 11:39:48 +03:00
DCCONSTRUCTIONS 7720594790 feat(perception): add integrated TGS graph shadow gate 2026-08-27 11:27:28 +03:00
DCCONSTRUCTIONS 296cf610cd fix(lab): preserve replay view and restore SLAM semantics 2026-08-26 23:37:05 +03:00
DCCONSTRUCTIONS 46cef8df17 docs(perception): record full TGS shadow acceptance 2026-08-26 23:07:16 +03:00
DCCONSTRUCTIONS d722b0f82b feat(lab): publish full TGS shadow evidence 2026-08-26 23:07:06 +03:00
DCCONSTRUCTIONS 40c850b167 feat(perception): add full TGS shadow runner 2026-08-26 21:59:39 +03:00
DCCONSTRUCTIONS 6544d9e918 feat(lab): publish fail-closed TGS evidence 2026-08-26 21:32:35 +03:00
DCCONSTRUCTIONS 67d5d6fa05 docs(perception): record gravity-aligned TGS evidence 2026-08-26 19:51:50 +03:00
DCCONSTRUCTIONS 941fb89616 feat(perception): add gravity-aligned TGS evidence gate 2026-08-26 19:47:23 +03:00
DCCONSTRUCTIONS 989a3ce28c docs(perception): reject direct TRAVEL AOS compatibility 2026-08-26 19:34:10 +03:00
DCCONSTRUCTIONS 28d5297237 feat(perception): add TRAVEL RAVNOVES compatibility probe 2026-08-26 19:25:19 +03:00
DCCONSTRUCTIONS b0738438d4 docs(perception): record TRAVEL upstream qualification 2026-08-26 19:06:56 +03:00
DCCONSTRUCTIONS 67578f4ce1 feat(perception): stage TRAVEL qualification 2026-08-26 18:53:26 +03:00
DCCONSTRUCTIONS 83ef98e9e5 docs(perception): record GSeg3D qualification rejection 2026-08-26 18:53:25 +03:00
DCCONSTRUCTIONS 316f0d7f85 fix(perception): match merged ROS test layout 2026-08-26 18:44:41 +03:00
DCCONSTRUCTIONS 2e7abbc56b fix(perception): source ROS setup outside nounset 2026-08-26 18:42:02 +03:00
DCCONSTRUCTIONS b445745d27 fix(perception): run Worker build interactively 2026-08-26 18:32:22 +03:00
DCCONSTRUCTIONS 1cc87e1836 fix(perception): isolate Worker Docker credentials 2026-08-26 18:29:33 +03:00
DCCONSTRUCTIONS aed59030b4 feat(perception): stage GSeg3D qualification 2026-08-26 18:27:39 +03:00
154 changed files with 24885 additions and 900 deletions
+1 -1
View File
@@ -16,6 +16,7 @@
"@nodedc/ui-core": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/ui-core",
"@nodedc/ui-react": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/ui-react",
"@rerun-io/web-viewer": "0.34.1",
"meshoptimizer": "1.1.1",
"playcanvas": "2.21.4",
"react": "^19.1.0",
"react-dom": "^19.1.0",
@@ -1661,7 +1662,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.1.1.tgz",
"integrity": "sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==",
"dev": true,
"license": "MIT"
},
"node_modules/ms": {
+1
View File
@@ -19,6 +19,7 @@
"@nodedc/ui-core": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/ui-core",
"@nodedc/ui-react": "file:../../../NODEDC_DESIGN_GUIDELINE/packages/ui-react",
"@rerun-io/web-viewer": "0.34.1",
"meshoptimizer": "1.1.1",
"playcanvas": "2.21.4",
"react": "^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;
@@ -352,6 +352,7 @@ interface RecordedSegmentStreamRuntime {
target: RecordedSegmentTarget | null;
fetchAbort: AbortController | null;
notifiedRevision: number;
hasPresentedFrame: boolean;
pumping: boolean;
disposed: boolean;
onTargetBuffered: ((target: RecordedSegmentTarget) => void) | null;
@@ -944,6 +945,7 @@ export function RecordedFmp4Player({
target: null,
fetchAbort: null,
notifiedRevision: 0,
hasPresentedFrame: false,
pumping: false,
disposed: false,
onTargetBuffered: null,
@@ -1090,8 +1092,13 @@ export function RecordedFmp4Player({
runtime.fetchAbort?.abort();
runtime.target = target;
const alreadyBuffered = recordedSegmentTargetBuffered(runtime, target);
const retainForwardFrame = Boolean(
runtime.hasPresentedFrame
&& previousTarget
&& candidateTarget.sequence >= previousTarget.sequence,
);
video.pause();
if (!alreadyBuffered) {
if (!alreadyBuffered && !retainForwardFrame) {
setReadyGeneration(null);
setState("loading");
}
@@ -1119,6 +1126,7 @@ export function RecordedFmp4Player({
|| runtime.target?.revision !== bufferedTarget.revision
) return;
setBufferRevision((revision) => revision + 1);
runtime.hasPresentedFrame = true;
setReadyGeneration(runtime.generation);
setState("ready");
reportAdmission({
@@ -1,6 +1,7 @@
import {
forwardRef,
type CSSProperties,
useCallback,
useEffect,
useImperativeHandle,
useRef,
@@ -41,11 +42,41 @@ export interface LaboratoryMetricCorridorVisual {
halfWidthM: number;
}
export type LaboratoryMetricCellState =
| "unobserved"
| "ground-support"
| "nonground-occupied"
| "unknown-rejected";
export interface LaboratoryMetricCellEvidence {
centerBodyXyM: readonly [number, number];
zBoundsM: readonly [number | null, number | null];
state: LaboratoryMetricCellState;
}
export interface LaboratoryMetricPackedCellEvidence {
centersBodyXyM: Float32Array;
zBoundsM: Float32Array;
stateCodes: Uint8Array;
}
export interface LaboratoryMetricLegendEntry {
id: LaboratoryMetricDecision | "context" | "local-surface" | "rolling" | "low-step";
id: LaboratoryMetricDecision
| LaboratoryMetricCellState
| "context"
| "local-surface"
| "rolling"
| "low-step";
label: string;
}
interface LaboratoryMetricRenderStats {
frameMs: number;
drawCalls: number;
triangles: number;
pixelRatio: number;
}
export function laboratoryMetricLegendEntries({
pointCloudCount,
localSurfaceCount,
@@ -152,6 +183,47 @@ function positions(points: readonly LaboratoryMetricPoint3[]): Float32Array {
return result;
}
function updatePointPositions(
geometry: THREE.BufferGeometry,
points: readonly LaboratoryMetricPoint3[],
): void {
const requiredValues = points.length * 3;
let attribute = geometry.getAttribute("position") as THREE.BufferAttribute | undefined;
if (!attribute || attribute.array.length < requiredValues) {
let capacity = 3;
while (capacity < requiredValues) capacity *= 2;
attribute = new THREE.BufferAttribute(new Float32Array(capacity), 3);
attribute.setUsage(THREE.DynamicDrawUsage);
geometry.setAttribute("position", attribute);
}
const values = attribute.array as Float32Array;
points.forEach((point, index) => {
const [x, y, z] = scenePoint(point);
const offset = index * 3;
values[offset] = x;
values[offset + 1] = y;
values[offset + 2] = z;
});
attribute.needsUpdate = true;
geometry.setDrawRange(0, points.length);
}
function ensurePointColors(
geometry: THREE.BufferGeometry,
pointCount: number,
): THREE.BufferAttribute {
const requiredValues = pointCount * 3;
let attribute = geometry.getAttribute("color") as THREE.BufferAttribute | undefined;
if (!attribute || attribute.array.length < requiredValues) {
let capacity = 3;
while (capacity < requiredValues) capacity *= 2;
attribute = new THREE.BufferAttribute(new Float32Array(capacity), 3);
attribute.setUsage(THREE.DynamicDrawUsage);
geometry.setAttribute("color", attribute);
}
return attribute;
}
function decisionColor(
host: HTMLElement,
decision: LaboratoryMetricDecision,
@@ -197,6 +269,10 @@ LaboratoryMetricEvidenceSceneHandle,
pointSemanticClassIds?: readonly (number | null)[];
semanticClasses?: readonly RecordedEvidenceSemanticClass[];
semanticPalette?: readonly RecordedEvidenceSemanticPaletteEntry[];
classifiedCells?: readonly LaboratoryMetricCellEvidence[];
classifiedPackedCells?: LaboratoryMetricPackedCellEvidence;
classifiedCellSizeM?: number;
showClassifiedCells?: boolean;
}
>(function LaboratoryMetricEvidenceScene({
pointCloudBodyXyzM,
@@ -214,6 +290,10 @@ LaboratoryMetricEvidenceSceneHandle,
pointSemanticClassIds,
semanticClasses,
semanticPalette,
classifiedCells = [],
classifiedPackedCells,
classifiedCellSizeM = 0.45,
showClassifiedCells = true,
}, ref) {
const hostRef = useRef<HTMLDivElement | null>(null);
const sceneRef = useRef<THREE.Scene | null>(null);
@@ -221,7 +301,19 @@ LaboratoryMetricEvidenceSceneHandle,
const controlsRef = useRef<OrbitControls | null>(null);
const staticContentRef = useRef<THREE.Group | null>(null);
const dynamicContentRef = useRef<THREE.Group | null>(null);
const classifiedContentRef = useRef<THREE.Group | null>(null);
const localSurfacePointsRef = useRef<THREE.Points | null>(null);
const currentIncrementPointsRef = useRef<THREE.Points | null>(null);
const requestRenderRef = useRef<() => void>(() => undefined);
const corridorForwardLengthRef = useRef(corridor.forwardLengthM);
const modeRef = useRef(mode);
const classifiedMeshesRef = useRef<ReadonlyMap<LaboratoryMetricCellState, THREE.InstancedMesh>>(
new Map(),
);
const [renderError, setRenderError] = useState<string | null>(null);
const [renderStats, setRenderStats] = useState<LaboratoryMetricRenderStats | null>(null);
corridorForwardLengthRef.current = corridor.forwardLengthM;
modeRef.current = mode;
useEffect(() => {
const host = hostRef.current;
@@ -255,12 +347,76 @@ LaboratoryMetricEvidenceSceneHandle,
controls.maxDistance = 80;
const staticContent = new THREE.Group();
const dynamicContent = new THREE.Group();
scene.add(staticContent, dynamicContent);
const classifiedContent = new THREE.Group();
const localSurfacePoints = new THREE.Points(
new THREE.BufferGeometry(),
new THREE.PointsMaterial({
color: tokenColor(host, "--nodedc-accent-rgb", [247, 248, 244]),
size: 1.3,
sizeAttenuation: false,
transparent: true,
opacity: 0.42,
depthWrite: false,
}),
);
const currentIncrementPoints = new THREE.Points(
new THREE.BufferGeometry(),
new THREE.PointsMaterial({
color: tokenColor(host, "--nodedc-text-muted", [147, 151, 159]),
size: 1.55,
sizeAttenuation: false,
transparent: true,
opacity: 0.58,
depthWrite: false,
}),
);
localSurfacePoints.visible = false;
localSurfacePoints.frustumCulled = false;
currentIncrementPoints.visible = false;
currentIncrementPoints.frustumCulled = false;
scene.add(
staticContent,
localSurfacePoints,
currentIncrementPoints,
dynamicContent,
classifiedContent,
);
sceneRef.current = scene;
cameraRef.current = camera;
controlsRef.current = controls;
staticContentRef.current = staticContent;
dynamicContentRef.current = dynamicContent;
classifiedContentRef.current = classifiedContent;
localSurfacePointsRef.current = localSurfacePoints;
currentIncrementPointsRef.current = currentIncrementPoints;
let animationFrame: number | null = null;
let lastStatsAt = Number.NEGATIVE_INFINITY;
const render = () => {
animationFrame = null;
// OrbitControls emits another change while damping is still settling, so
// this remains smooth during interaction without rendering forever while
// the evidence scene is idle.
controls.update();
const startedAt = performance.now();
renderer.render(scene, camera);
const completedAt = performance.now();
if (completedAt - lastStatsAt >= 1_000) {
lastStatsAt = completedAt;
setRenderStats({
frameMs: completedAt - startedAt,
drawCalls: renderer.info.render.calls,
triangles: renderer.info.render.triangles,
pixelRatio: renderer.getPixelRatio(),
});
}
};
const requestRender = () => {
if (animationFrame !== null) return;
animationFrame = window.requestAnimationFrame(render);
};
requestRenderRef.current = requestRender;
controls.addEventListener("change", requestRender);
const resize = () => {
const width = Math.max(host.clientWidth, 1);
@@ -268,21 +424,17 @@ LaboratoryMetricEvidenceSceneHandle,
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize(width, height, false);
requestRender();
};
const observer = new ResizeObserver(resize);
observer.observe(host);
resize();
let animationFrame = 0;
const render = () => {
animationFrame = window.requestAnimationFrame(render);
controls.update();
renderer.render(scene, camera);
};
render();
requestRender();
return () => {
window.cancelAnimationFrame(animationFrame);
if (animationFrame !== null) window.cancelAnimationFrame(animationFrame);
observer.disconnect();
controls.removeEventListener("change", requestRender);
controls.dispose();
scene.traverse(disposeRenderable);
renderer.dispose();
@@ -292,6 +444,11 @@ LaboratoryMetricEvidenceSceneHandle,
controlsRef.current = null;
staticContentRef.current = null;
dynamicContentRef.current = null;
classifiedContentRef.current = null;
localSurfacePointsRef.current = null;
currentIncrementPointsRef.current = null;
requestRenderRef.current = () => undefined;
classifiedMeshesRef.current = new Map();
};
}, []);
@@ -300,80 +457,77 @@ LaboratoryMetricEvidenceSceneHandle,
if (canvas) canvas.setAttribute("aria-label", label);
}, [label]);
useEffect(() => {
const points = localSurfacePointsRef.current;
if (!points) return;
points.visible = showLocalSurface && localSurfaceBodyXyzM.length > 0;
if (points.visible) updatePointPositions(points.geometry, localSurfaceBodyXyzM);
requestRenderRef.current();
}, [localSurfaceBodyXyzM, showLocalSurface]);
useEffect(() => {
const host = hostRef.current;
const points = currentIncrementPointsRef.current;
if (!host || !points) return;
points.visible = showCurrentIncrement && pointCloudBodyXyzM.length > 0;
if (!points.visible) {
requestRenderRef.current();
return;
}
updatePointPositions(points.geometry, pointCloudBodyXyzM);
const material = points.material as THREE.PointsMaterial;
const hasAlignedSemanticClasses =
pointSemanticClassIds !== undefined
&& pointSemanticClassIds.length === pointCloudBodyXyzM.length
&& semanticClasses !== undefined
&& semanticPalette !== undefined;
if (!hasAlignedSemanticClasses) {
if (material.vertexColors) {
material.vertexColors = false;
material.needsUpdate = true;
}
material.color.copy(tokenColor(host, "--nodedc-text-muted", [147, 151, 159]));
requestRenderRef.current();
return;
}
const declaredIds = new Set(semanticClasses.map((item) => item.id));
const colorsByClassId = new Map<number, readonly [number, number, number]>();
for (const entry of semanticPalette) {
if (!declaredIds.has(entry.classId)) continue;
const rgb = resolveRecordedEvidenceSemanticRgb(host, entry.color);
if (rgb) colorsByClassId.set(entry.classId, rgb);
}
const context = tokenColor(host, "--nodedc-text-muted", [147, 151, 159]);
const colorAttribute = ensurePointColors(points.geometry, pointCloudBodyXyzM.length);
const pointColors = colorAttribute.array as Float32Array;
pointSemanticClassIds.forEach((classId, index) => {
const rgb = classId === null ? undefined : colorsByClassId.get(classId);
const offset = index * 3;
pointColors[offset] = rgb ? rgb[0] / 255 : context.r;
pointColors[offset + 1] = rgb ? rgb[1] / 255 : context.g;
pointColors[offset + 2] = rgb ? rgb[2] / 255 : context.b;
});
colorAttribute.needsUpdate = true;
if (!material.vertexColors) {
material.vertexColors = true;
material.needsUpdate = true;
}
material.color.setRGB(1, 1, 1);
requestRenderRef.current();
}, [
pointCloudBodyXyzM,
pointSemanticClassIds,
semanticClasses,
semanticPalette,
showCurrentIncrement,
]);
useEffect(() => {
const host = hostRef.current;
const content = dynamicContentRef.current;
if (!host || !content) return;
clearGroup(content);
if (showLocalSurface) {
const localSurfaceGeometry = new THREE.BufferGeometry();
localSurfaceGeometry.setAttribute(
"position",
new THREE.BufferAttribute(positions(localSurfaceBodyXyzM), 3),
);
content.add(new THREE.Points(
localSurfaceGeometry,
new THREE.PointsMaterial({
color: tokenColor(host, "--nodedc-accent-rgb", [247, 248, 244]),
size: 1.3,
sizeAttenuation: false,
transparent: true,
opacity: 0.42,
depthWrite: false,
}),
));
}
if (showCurrentIncrement) {
const contextGeometry = new THREE.BufferGeometry();
contextGeometry.setAttribute(
"position",
new THREE.BufferAttribute(positions(pointCloudBodyXyzM), 3),
);
const hasAlignedSemanticClasses =
pointSemanticClassIds !== undefined
&& pointSemanticClassIds.length === pointCloudBodyXyzM.length
&& semanticClasses !== undefined
&& semanticPalette !== undefined;
if (hasAlignedSemanticClasses) {
const declaredIds = new Set(semanticClasses.map((item) => item.id));
const colorsByClassId = new Map<number, readonly [number, number, number]>();
for (const entry of semanticPalette) {
if (!declaredIds.has(entry.classId)) continue;
const rgb = resolveRecordedEvidenceSemanticRgb(host, entry.color);
if (rgb) colorsByClassId.set(entry.classId, rgb);
}
const context = tokenColor(host, "--nodedc-text-muted", [147, 151, 159]);
const pointColors = new Float32Array(pointCloudBodyXyzM.length * 3);
pointSemanticClassIds.forEach((classId, index) => {
const rgb = classId === null ? undefined : colorsByClassId.get(classId);
const offset = index * 3;
pointColors[offset] = rgb ? rgb[0] / 255 : context.r;
pointColors[offset + 1] = rgb ? rgb[1] / 255 : context.g;
pointColors[offset + 2] = rgb ? rgb[2] / 255 : context.b;
});
contextGeometry.setAttribute(
"color",
new THREE.BufferAttribute(pointColors, 3),
);
}
content.add(new THREE.Points(
contextGeometry,
new THREE.PointsMaterial({
color: hasAlignedSemanticClasses
? new THREE.Color(1, 1, 1)
: tokenColor(host, "--nodedc-text-muted", [147, 151, 159]),
vertexColors: hasAlignedSemanticClasses,
size: 1.55,
sizeAttenuation: false,
transparent: true,
opacity: 0.58,
depthWrite: false,
}),
));
}
for (const obstacle of obstacles) {
if (
(
@@ -442,21 +596,150 @@ LaboratoryMetricEvidenceSceneHandle,
content.add(centroid);
}
}
requestRenderRef.current();
}, [
obstacles,
occupiedVoxelSizeM,
localSurfaceBodyXyzM,
pointCloudBodyXyzM,
pointSemanticClassIds,
semanticClasses,
semanticPalette,
showCurrentIncrement,
showLocalSurface,
showRollingMap,
showLowStep,
]);
useEffect(() => {
const host = hostRef.current;
const content = classifiedContentRef.current;
if (!host || !content) return;
const packedCellCount = classifiedPackedCells?.stateCodes.length ?? 0;
const packedCellsValid = !classifiedPackedCells || (
classifiedPackedCells.centersBodyXyM.length === packedCellCount * 2
&& classifiedPackedCells.zBoundsM.length === packedCellCount * 2
);
const cellCount = packedCellsValid && classifiedPackedCells
? packedCellCount
: classifiedCells.length;
content.visible = showClassifiedCells && cellCount > 0;
if (!content.visible) {
requestRenderRef.current();
return;
}
const requiredCapacity = cellCount;
const currentMeshes = classifiedMeshesRef.current;
const firstMesh = currentMeshes.values().next().value as THREE.InstancedMesh | undefined;
const needsAllocation = !firstMesh
|| Number(firstMesh.userData.capacity ?? 0) < requiredCapacity
|| Number(firstMesh.userData.cellSizeM ?? 0) !== classifiedCellSizeM;
if (needsAllocation) {
clearGroup(content);
const meshes = new Map<LaboratoryMetricCellState, THREE.InstancedMesh>();
const states: readonly LaboratoryMetricCellState[] = [
"unobserved",
"ground-support",
"nonground-occupied",
"unknown-rejected",
];
for (const state of states) {
const color = state === "ground-support"
? tokenColor(host, "--nodedc-success-rgb", [181, 255, 90])
: state === "nonground-occupied"
? tokenColor(host, "--nodedc-danger-rgb", [255, 104, 112])
: state === "unknown-rejected"
? tokenColor(host, "--nodedc-warning-rgb", [255, 197, 92])
: tokenColor(host, "--nodedc-text-muted", [96, 99, 106]);
const mesh = new THREE.InstancedMesh(
new THREE.BoxGeometry(classifiedCellSizeM * 0.92, 1, classifiedCellSizeM * 0.92),
new THREE.MeshBasicMaterial({
color,
transparent: true,
opacity: state === "unobserved" ? 0.035 : state === "ground-support" ? 0.12 : 0.24,
depthWrite: false,
}),
requiredCapacity,
);
mesh.count = 0;
mesh.instanceMatrix.setUsage(THREE.DynamicDrawUsage);
mesh.frustumCulled = false;
mesh.userData.capacity = requiredCapacity;
mesh.userData.cellSizeM = classifiedCellSizeM;
meshes.set(state, mesh);
content.add(mesh);
}
classifiedMeshesRef.current = meshes;
}
const meshes = classifiedMeshesRef.current;
const counts = new Map<LaboratoryMetricCellState, number>();
const matrix = new THREE.Matrix4();
const position = new THREE.Vector3();
const scale = new THREE.Vector3(1, 1, 1);
const rotation = new THREE.Quaternion();
const setCell = (
state: LaboratoryMetricCellState,
centerX: number,
centerY: number,
minimum: number | null,
maximum: number | null,
) => {
const mesh = meshes.get(state);
if (!mesh) return;
const index = counts.get(state) ?? 0;
const height = minimum === null || maximum === null
? 0.018
: Math.max(0.018, maximum - minimum);
const centerZ = minimum === null || maximum === null
? -0.012
: (minimum + maximum) / 2;
const [sceneX, sceneY, sceneZ] = scenePoint([
centerX,
centerY,
centerZ,
]);
position.set(sceneX, sceneY, sceneZ);
scale.set(1, height, 1);
matrix.compose(position, rotation, scale);
mesh.setMatrixAt(index, matrix);
counts.set(state, index + 1);
};
if (packedCellsValid && classifiedPackedCells) {
for (let cellIndex = 0; cellIndex < packedCellCount; cellIndex += 1) {
const code = classifiedPackedCells.stateCodes[cellIndex];
const state: LaboratoryMetricCellState = code === 1
? "ground-support"
: code === 2
? "nonground-occupied"
: code === 3
? "unknown-rejected"
: code === 0
? "unobserved"
: "unknown-rejected";
const minimum = classifiedPackedCells.zBoundsM[cellIndex * 2]!;
const maximum = classifiedPackedCells.zBoundsM[cellIndex * 2 + 1]!;
setCell(
state,
classifiedPackedCells.centersBodyXyM[cellIndex * 2]!,
classifiedPackedCells.centersBodyXyM[cellIndex * 2 + 1]!,
Number.isFinite(minimum) ? minimum : null,
Number.isFinite(maximum) ? maximum : null,
);
}
} else {
for (const cell of classifiedCells) {
setCell(
cell.state,
cell.centerBodyXyM[0],
cell.centerBodyXyM[1],
cell.zBoundsM[0],
cell.zBoundsM[1],
);
}
}
for (const [state, mesh] of meshes) {
mesh.count = counts.get(state) ?? 0;
mesh.instanceMatrix.needsUpdate = true;
}
requestRenderRef.current();
}, [classifiedCellSizeM, classifiedCells, classifiedPackedCells, showClassifiedCells]);
useEffect(() => {
const host = hostRef.current;
const content = staticContentRef.current;
@@ -521,15 +804,24 @@ LaboratoryMetricEvidenceSceneHandle,
material.depthWrite = false;
});
content.add(grid);
}, [corridor, rig]);
requestRenderRef.current();
}, [
corridor.forwardLengthM,
corridor.halfWidthM,
corridor.rearMarginM,
rig.lengthM,
rig.nominalSensorHeightM,
rig.widthM,
]);
const resetView = () => {
const resetView = useCallback(() => {
const camera = cameraRef.current;
const controls = controlsRef.current;
if (!camera || !controls) return;
controls.target.set(corridor.forwardLengthM * 0.35, 0.6, 0);
if (mode === "plan") {
camera.position.set(corridor.forwardLengthM * 0.35, 15, 0.001);
const forwardLengthM = corridorForwardLengthRef.current;
controls.target.set(forwardLengthM * 0.35, 0.6, 0);
if (modeRef.current === "plan") {
camera.position.set(forwardLengthM * 0.35, 15, 0.001);
camera.up.set(0, 0, -1);
} else {
camera.position.set(-4.5, 4.8, 8.5);
@@ -537,10 +829,13 @@ LaboratoryMetricEvidenceSceneHandle,
}
camera.updateProjectionMatrix();
controls.update();
};
requestRenderRef.current();
}, []);
useEffect(resetView, [corridor.forwardLengthM, mode]);
useImperativeHandle(ref, () => ({ resetView }));
// Playback data and rig object identities must never reset an operator's
// orbit. Only a deliberate 3D/PLAN mode transition chooses a default view.
useEffect(() => resetView(), [mode, resetView]);
useImperativeHandle(ref, () => ({ resetView }), [resetView]);
const semanticLegendEntries = (() => {
if (
@@ -563,7 +858,9 @@ LaboratoryMetricEvidenceSceneHandle,
});
})();
const metricLegendEntries = laboratoryMetricLegendEntries({
pointCloudCount: pointCloudBodyXyzM.length,
pointCloudCount: pointSemanticClassIds?.every((item) => item !== null)
? 0
: pointCloudBodyXyzM.length,
localSurfaceCount: localSurfaceBodyXyzM.length,
obstacles,
showCurrentIncrement,
@@ -571,6 +868,43 @@ LaboratoryMetricEvidenceSceneHandle,
showRollingMap,
showLowStep,
});
const classifiedLegendEntries = (() => {
const states = new Set<LaboratoryMetricCellState>();
if (classifiedPackedCells) {
for (const code of classifiedPackedCells.stateCodes) {
states.add(code === 1
? "ground-support"
: code === 2
? "nonground-occupied"
: code === 3
? "unknown-rejected"
: code === 0
? "unobserved"
: "unknown-rejected");
}
} else {
classifiedCells.forEach((cell) => states.add(cell.state));
}
if (!showClassifiedCells || !states.size) return [];
return [
states.has("ground-support")
? { id: "ground-support" as const, label: "Ground support" }
: null,
states.has("nonground-occupied")
? { id: "nonground-occupied" as const, label: "Non-ground occupied" }
: null,
states.has("unknown-rejected")
? { id: "unknown-rejected" as const, label: "Unknown / rejected" }
: null,
states.has("unobserved")
? { id: "unobserved" as const, label: "Unobserved" }
: null,
]
.filter((entry): entry is NonNullable<typeof entry> => entry !== null)
.filter((entry) => !semanticLegendEntries.some(
(semanticEntry) => semanticEntry.label === entry.label,
));
})();
return (
<div className="laboratory-metric-evidence-scene">
@@ -578,9 +912,22 @@ LaboratoryMetricEvidenceSceneHandle,
{renderError ? <p>{renderError}</p> : null}
</div>
<div className="laboratory-metric-evidence-scene__legend">
{renderStats ? (
<span
data-decision="performance"
title="CPU-время отправки последнего WebGL render pass; это не GPU timer. В покое сцена не перерисовывается."
>
3D {renderStats.frameMs.toFixed(1)} ms · {renderStats.drawCalls} draw · {renderStats.triangles.toLocaleString("ru-RU")} tri · {(
classifiedPackedCells?.stateCodes.length ?? classifiedCells.length
).toLocaleString("ru-RU")} cells · {renderStats.pixelRatio.toFixed(1)}×
</span>
) : null}
{metricLegendEntries.map((entry) => (
<span key={entry.id} data-decision={entry.id}>{entry.label}</span>
))}
{classifiedLegendEntries.map((entry) => (
<span key={entry.id} data-decision={entry.id}>{entry.label}</span>
))}
{semanticLegendEntries.map((entry) => (
<span
key={entry.id}
@@ -510,16 +510,21 @@ export function RecordedEvidenceSemanticMaskOverlay({
}) {
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const rendererRef = useRef<SemanticMaskRenderer | null | undefined>(undefined);
const lastReadyMaskRef = useRef<{ series: string; mask: DecodedSemanticMask } | null>(null);
const [rendererMode, setRendererMode] = useState<"webgl" | "2d">("webgl");
const [mask, setMask] = useState<DecodedSemanticMask | null>(null);
const [failure, setFailure] = useState<string | null>(null);
const expectedKey = semanticMaskKey(src, imageWidth, imageHeight);
const prefetchSignature = prefetchSrcs.join("\n");
const renderMask = failure
const series = src.slice(0, Math.max(src.lastIndexOf("/"), 0));
if (lastReadyMaskRef.current?.series !== series) lastReadyMaskRef.current = null;
const exactMask = failure
? null
: mask?.key === expectedKey
? mask
: decodedMaskCache.get(expectedKey) ?? null;
if (exactMask) lastReadyMaskRef.current = { series, mask: exactMask };
const renderMask = exactMask ?? lastReadyMaskRef.current?.mask ?? null;
useEffect(() => {
setFailure(null);
@@ -627,8 +632,8 @@ export function RecordedEvidenceSemanticMaskOverlay({
className="recorded-evidence-semantic-mask-overlay"
role="img"
aria-label={ariaLabel}
aria-busy={!failure && !renderMask}
data-state={failure ? "error" : renderMask ? "ready" : "loading"}
aria-busy={!failure && !exactMask}
data-state={failure ? "error" : exactMask ? "ready" : renderMask ? "stale" : "loading"}
data-renderer={rendererMode}
style={{ zIndex: 1 }}
/>
@@ -1,25 +1,34 @@
import {
Application,
Asset,
BLEND_NONE,
BLEND_NORMAL,
Color,
CULLFACE_NONE,
Entity,
FILLMODE_NONE,
Mat4,
Quat,
RESOLUTION_AUTO,
StandardMaterial,
Vec2,
Vec3,
WasmModule,
type ContainerResource,
type RenderComponent,
type ModelComponent,
type ScriptType,
} from "playcanvas";
import { CameraControls } from "playcanvas/scripts/esm/camera-controls.mjs";
import {
prepareSimulationPhysics,
SimulationUgvController,
type AmmoApi,
} from "./SimulationUgvController";
import type {
SimulationTransformAxis,
SimulationEulerRotation,
SimulationUgvPreset,
SimulationViewerQuality,
SimulationWorldManifest,
} from "../../core/simulation/projects";
@@ -28,46 +37,18 @@ export type SimulationViewMode = "visual" | "collision" | "combined";
export type SimulationQuality = SimulationViewerQuality;
export type SimulationLayer = "visual" | "collision";
export type SimulationCameraAxis = "horizontal" | "vertical";
export type SimulationControlMode = "free" | "ugv";
export const PLAYCANVAS_IDENTITY_TRANSFORM = [
1, 0, 0, 0,
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 function playCanvasEulerTransform(rotation: SimulationEulerRotation): number[] {
return Array.from(new Mat4().setFromEulerAngles(rotation.x, rotation.y, rotation.z).data);
}
export interface SimulationRuntime {
mount(canvas: HTMLCanvasElement): Promise<void>;
loadWorld(manifest: SimulationWorldManifest): Promise<void>;
setViewMode(mode: SimulationViewMode): Promise<void>;
setControlMode(mode: SimulationControlMode): Promise<void>;
setUgvPreset(preset: SimulationUgvPreset): void;
setQuality(quality: SimulationQuality): void;
setLayerWorldTransform(layer: SimulationLayer, transform: number[]): void;
setCameraInversion(axis: SimulationCameraAxis, inverted: boolean): void;
@@ -78,10 +59,6 @@ export interface SimulationRuntime {
type DesktopCameraInput = {
read(): { mouse: number[] } & Record<string, number[]>;
_pointerId?: number;
_button?: number[];
_keyNow?: number[];
_keyPrev?: number[];
};
type CameraController = ScriptType & Pick<CameraControls, "reset" | "focus"> & {
@@ -123,6 +100,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
private visualEntity: Entity | null = null;
private visualAsset: Asset | null = null;
private collisionEntity: Entity | null = null;
private collisionVisuals: ModelComponent[] = [];
private collisionAsset: Asset | null = null;
private collisionMaterial: StandardMaterial | null = null;
private collisionSource: { meshUrl: string; projectId: string; worldTransform: number[] } | null = null;
@@ -131,23 +109,41 @@ export class PlayCanvasRuntime implements SimulationRuntime {
private viewMode: SimulationViewMode = "visual";
private quality: SimulationQuality = "high";
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 renderAccumulatorMs = 0;
private disposed = false;
private scheduleFrame = (deltaMs: number): void => {
if (!this.app || this.disposed) return;
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;
this.renderAccumulatorMs %= this.renderIntervalMs;
this.app.renderNextFrame = true;
};
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.renderAccumulatorMs = 0;
this.canvas = canvas;
configureDracoDecoder();
const ammo: AmmoApi = await prepareSimulationPhysics();
if (this.disposed) return;
canvas.tabIndex = 0;
canvas.addEventListener("contextmenu", preventContextMenu);
canvas.addEventListener("pointerdown", focusCanvas, true);
@@ -161,7 +157,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
});
this.app = app;
app.autoRender = false;
app.on("frameupdate", this.scheduleFrame);
app.on("update", this.updateFrame);
app.setCanvasFillMode(FILLMODE_NONE, 1, 1);
app.setCanvasResolution(RESOLUTION_AUTO);
app.scene.ambientLight = new Color(0.35, 0.37, 0.42);
@@ -188,6 +184,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
app.root.addChild(camera);
this.camera = camera;
this.cameraController = controller;
this.ugvController = new SimulationUgvController(app, canvas, camera, ammo);
const light = new Entity("SimulationSun");
light.addComponent("light", {
@@ -210,7 +207,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
const app = this.requiredApp();
this.unloadWorld();
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 });
app.assets.add(asset);
this.visualAsset = asset;
@@ -233,10 +230,11 @@ export class PlayCanvasRuntime implements SimulationRuntime {
this.visualEntity = visual;
this.applyQuality();
this.applyViewMode();
this.requestRender();
resolve();
};
const failed = (error: unknown) => {
reject(new Error(error instanceof Error ? error.message : "PlayCanvas не загрузил SOG."));
const failed = (_error: unknown) => {
reject(new Error("Визуальный слой не загрузился."));
};
asset.ready(ready);
asset.once("error", failed);
@@ -254,7 +252,6 @@ export class PlayCanvasRuntime implements SimulationRuntime {
}
async setViewMode(mode: SimulationViewMode): Promise<void> {
this.resetCameraInteraction();
this.viewMode = mode;
this.applyViewMode();
try {
@@ -263,13 +260,58 @@ export class PlayCanvasRuntime implements SimulationRuntime {
this.applyViewMode();
}
} finally {
this.requestRender();
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 {
this.quality = quality;
this.applyQuality();
this.requestRender();
}
setUgvPreset(preset: SimulationUgvPreset): void {
this.ugvController?.configure(preset);
this.cameraSnapshotReady = false;
this.requestRender();
}
setLayerWorldTransform(layer: SimulationLayer, transform: number[]): void {
@@ -278,10 +320,13 @@ export class PlayCanvasRuntime implements SimulationRuntime {
}
if (layer === "visual") {
if (this.visualEntity) applyWorldTransform(this.visualEntity, transform);
this.requestRender();
return;
}
if (this.collisionSource) this.collisionSource.worldTransform = [...transform];
if (this.collisionEntity) applyWorldTransform(this.collisionEntity, transform);
this.ugvController?.syncCollisionBodies();
this.requestRender();
}
setCameraInversion(axis: SimulationCameraAxis, inverted: boolean): void {
@@ -289,6 +334,12 @@ export class PlayCanvasRuntime implements SimulationRuntime {
}
home(): void {
if (this.controlMode === "ugv" && this.ugvController?.enabled) {
this.ugvController.reset();
this.cameraSnapshotReady = false;
this.requestRender();
return;
}
this.resetCameraInteraction();
if (this.cameraController) {
this.cameraController.reset(HOME_FOCUS, HOME_POSITION);
@@ -296,6 +347,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
this.camera.setPosition(HOME_POSITION);
this.camera.lookAt(HOME_FOCUS);
}
this.requestRender();
this.canvas?.focus({ preventScroll: true });
}
@@ -312,6 +364,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
this.camera.setPosition(position);
this.camera.lookAt(focus);
}
this.requestRender();
}
dispose(): void {
@@ -321,10 +374,12 @@ export class PlayCanvasRuntime implements SimulationRuntime {
this.resizeObserver = null;
const webgl = this.canvas?.getContext("webgl2") ?? this.canvas?.getContext("webgl");
this.unloadWorld();
this.ugvController?.dispose();
this.ugvController = null;
if (this.app) {
this.app.autoRender = false;
this.app.renderNextFrame = false;
this.app.off("frameupdate", this.scheduleFrame);
this.app.off("update", this.updateFrame);
this.app.destroy();
}
webgl?.getExtension("WEBGL_lose_context")?.loseContext();
@@ -340,19 +395,26 @@ export class PlayCanvasRuntime implements SimulationRuntime {
this.canvas = null;
this.camera = null;
this.cameraController = null;
this.controlMode = "free";
this.cameraSnapshotReady = false;
}
private requiredApp(): Application {
if (!this.app) throw new Error("PlayCanvas runtime не смонтирован.");
if (!this.app) throw new Error("Runtime сцены не запущен.");
return this.app;
}
private requestRender(): void {
if (this.app && !this.disposed) this.app.renderNextFrame = true;
}
private resize(): void {
if (!this.app || !this.canvas) return;
const bounds = this.canvas.parentElement?.getBoundingClientRect();
const width = Math.max(1, Math.round(bounds?.width ?? this.canvas.clientWidth));
const height = Math.max(1, Math.round(bounds?.height ?? this.canvas.clientHeight));
this.app.resizeCanvas(width, height);
this.requestRender();
}
private applyViewMode(): void {
@@ -360,36 +422,27 @@ export class PlayCanvasRuntime implements SimulationRuntime {
this.visualEntity.enabled = this.viewMode !== "collision" || !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) {
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.update();
}
this.requestRender();
}
private resetCameraInteraction(): void {
const controller = this.cameraController;
const canvas = this.canvas;
if (!controller || !canvas) return;
const input = controller._desktopInput;
const pointerId = input?._pointerId ?? -1;
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();
}
if (!controller) return;
// CameraControls owns pointer capture and its input-source lifecycle. Mutating
// those private fields here left the source unable to claim the next drag
// after a layer switch. The documented state event drains pending deltas.
controller.fire("state");
const state = controller._state;
if (state) {
@@ -430,45 +483,52 @@ export class PlayCanvasRuntime implements SimulationRuntime {
this.collisionAsset = asset;
await new Promise<void>((resolve, reject) => {
asset.ready((loaded) => {
if (this.disposed || !this.app) {
loaded.unload();
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;
try {
if (this.disposed || !this.app) {
loaded.unload();
resolve();
return;
}
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) => {
reject(new Error(error instanceof Error ? error.message : "PlayCanvas не загрузил collision GLB."));
reject(new Error(`Слой коллизий не загрузился: ${String(error)}`));
});
app.assets.load(asset);
});
@@ -477,7 +537,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
private async ensureCollision(): Promise<void> {
if (this.collisionEntity) return;
if (!this.collisionSource) {
throw new Error("Collision GLB отсутствует в world manifest.");
throw new Error("Слой коллизий отсутствует в манифесте локации.");
}
if (!this.collisionLoadPromise) {
this.collisionLoadPromise = this.loadCollision(
@@ -492,6 +552,10 @@ export class PlayCanvasRuntime implements SimulationRuntime {
}
private unloadWorld(): void {
this.controlRequestVersion += 1;
this.ugvController?.disable();
this.controlMode = "free";
if (this.cameraController) this.cameraController.enabled = true;
if (this.visualEntity) {
this.visualEntity.destroy();
this.visualEntity = null;
@@ -505,6 +569,7 @@ export class PlayCanvasRuntime implements SimulationRuntime {
this.collisionEntity.destroy();
this.collisionEntity = null;
}
this.collisionVisuals = [];
if (this.collisionAsset && this.app) {
this.app.assets.remove(this.collisionAsset);
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 SimulationUploadProgress,
} from "../../core/simulation/projects";
import {
candidatesFromDrop,
validateSimulationCandidates,
} from "../../core/simulation/sourceFiles";
export function SimulationProjectWindow({
open,
@@ -55,7 +59,7 @@ export function SimulationProjectWindow({
const acceptCandidates = (next: SimulationUploadCandidate[]) => {
try {
validateCandidates(next);
validateSimulationCandidates(next);
setCandidates(next);
setError(null);
if (!name.trim() && next.length) {
@@ -213,7 +217,10 @@ export function SimulationProjectWindow({
<div className="simulation-upload-progress" role="status">
<span><i style={{ width: `${Math.min(100, progress.totalBytes ? progress.uploadedBytes / progress.totalBytes * 100 : 0)}%` }} /></span>
<div>
<strong>{formatBytes(progress.uploadedBytes)} / {formatBytes(progress.totalBytes)}</strong>
<strong>
{formatBytes(progress.uploadedBytes)} / {formatBytes(progress.totalBytes)}
{" · "}{formatUploadEta(progress.estimatedSecondsRemaining)}
</strong>
<small>{progress.currentPath}</small>
</div>
</div>
@@ -224,83 +231,15 @@ export function SimulationProjectWindow({
);
}
interface LegacyFileEntry {
isFile: boolean;
isDirectory: boolean;
name: string;
fullPath: string;
file?: (callback: (file: File) => void, error?: (error: DOMException) => void) => void;
createReader?: () => {
readEntries: (
callback: (entries: LegacyFileEntry[]) => void,
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 formatUploadEta(seconds: number | null): string {
if (seconds === 0) return "загружено";
if (seconds === null) return "считаем время";
if (seconds < 60) return `осталось ≈ ${Math.max(1, seconds)} сек`;
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 summarizeCandidates(candidates: SimulationUploadCandidate[]): { title: string; detail: string } {
@@ -0,0 +1,999 @@
import {
Application,
Color,
Entity,
Mat4,
Mesh,
PRIMITIVE_TRIANGLES,
Quat,
StandardMaterial,
Vec3,
WasmModule,
type ModelComponent,
} from "playcanvas";
import { MeshoptSimplifier } from "meshoptimizer/simplifier";
import type { SimulationUgvPreset } from "../../core/simulation/projects";
const AMMO_MODULE_ROOT = "/vendor/playcanvas/ammo/2026-08-25";
const UGV_SPAWN_POSITION = new Vec3(0, 1.15, 0);
const UGV_SPAWN_ROTATION = new Vec3(0, 0, 0);
const ZERO = new Vec3(0, 0, 0);
const DISABLE_DEACTIVATION = 4;
const PHYSICS_PROXY_RATIO = 0.04;
const PHYSICS_PROXY_ERROR = 0.0003;
const PHYSICS_PROXY_MAX_TRIANGLES = 240_000;
const SERVICE_BRAKE_DECELERATION_MPS2 = 1.8;
const COAST_DECELERATION_MPS2 = 0.18;
const BRAKE_ATTITUDE_DAMPING = 6;
const DEFAULT_ORBIT_PITCH = 0.48;
const CAMERA_RETURN_DELAY_SECONDS = 1.2;
const CAMERA_RETURN_DURATION_SECONDS = 2;
const UGV_SPAWN_OFFSETS: ReadonlyArray<readonly [number, number]> = [
[0, 0],
[1.5, 0],
[-1.5, 0],
[0, 1.5],
[0, -1.5],
[1.5, 1.5],
[-1.5, 1.5],
[1.5, -1.5],
[-1.5, -1.5],
[3, 0],
[-3, 0],
[0, 3],
[0, -3],
];
export const DEFAULT_SIMULATION_UGV_PRESET: SimulationUgvPreset = {
presetName: "UGV 100 кг",
massKg: 100,
dimensionsMeters: {
length: 1,
width: 0.8,
height: 0.4,
groundClearance: 0.15,
},
maxSpeedMetersPerSecond: 1.2,
maxTurnRateDegrees: 45,
invertSteering: false,
};
type NativeObject = object;
interface NativeVector3 extends NativeObject {
x(): number;
y(): number;
z(): number;
setValue(x: number, y: number, z: number): void;
}
interface NativeQuaternion extends NativeObject {
x(): number;
y(): number;
z(): number;
w(): number;
}
interface NativeTransform extends NativeObject {
getOrigin(): NativeVector3;
getRotation(): NativeQuaternion;
}
interface NativeWheelInfo extends NativeObject {
set_m_suspensionStiffness(value: number): void;
set_m_wheelsDampingRelaxation(value: number): void;
set_m_wheelsDampingCompression(value: number): void;
set_m_frictionSlip(value: number): void;
set_m_rollInfluence(value: number): void;
}
interface NativeRaycastVehicle extends NativeObject {
setCoordinateSystem(right: number, up: number, forward: number): void;
addWheel(
connectionPoint: NativeVector3,
direction: NativeVector3,
axle: NativeVector3,
suspensionRestLength: number,
radius: number,
tuning: NativeObject,
isFront: boolean,
): NativeWheelInfo;
applyEngineForce(force: number, wheel: number): void;
setBrake(force: number, wheel: number): void;
setSteeringValue(value: number, wheel: number): void;
getNumWheels(): number;
updateWheelTransform(wheel: number, interpolated: boolean): void;
getWheelTransformWS(wheel: number): NativeTransform;
getForwardVector(): NativeVector3;
getCurrentSpeedKmHour(): number;
resetSuspension(): void;
}
export interface AmmoApi {
btVehicleTuning: new () => NativeObject;
btDefaultVehicleRaycaster: new (world: NativeObject) => NativeObject;
btRaycastVehicle: new (
tuning: NativeObject,
body: NativeObject,
raycaster: NativeObject,
) => NativeRaycastVehicle;
btVector3: new (x: number, y: number, z: number) => NativeVector3;
destroy(object: NativeObject): void;
}
interface NativeDynamicsWorld extends NativeObject {
addAction(action: NativeObject): void;
removeAction(action: NativeObject): void;
}
interface PhysicsSystemAccess {
systems: {
rigidbody: {
dynamicsWorld: NativeDynamicsWorld | null;
raycastFirst(start: Vec3, end: Vec3): { point: Vec3; normal: Vec3 } | null;
};
};
}
interface NativeRigidBodyAccess {
rigidbody?: {
body: NativeObject | null;
linearVelocity: Vec3;
angularVelocity: Vec3;
teleport(position: Vec3, rotation?: Vec3 | Quat): void;
};
}
interface WheelDefinition {
connection: Vec3;
left: boolean;
front: boolean;
anchor: Entity;
}
let ammoInstancePromise: Promise<AmmoApi> | null = null;
export function prepareSimulationPhysics(): Promise<AmmoApi> {
if (ammoInstancePromise) return ammoInstancePromise;
ammoInstancePromise = new Promise<AmmoApi>((resolve, reject) => {
if (!WasmModule.getConfig("Ammo")) {
WasmModule.setConfig("Ammo", {
glueUrl: `${AMMO_MODULE_ROOT}/ammo.wasm.js`,
wasmUrl: `${AMMO_MODULE_ROOT}/ammo.wasm.wasm`,
errorHandler: (error) => {
ammoInstancePromise = null;
reject(new Error(`Физический модуль UGV не загрузился: ${String(error)}`));
},
});
}
WasmModule.getInstance("Ammo", (instance) => {
if (!instance) {
ammoInstancePromise = null;
reject(new Error("Физический модуль UGV вернул пустой экземпляр."));
return;
}
resolve(instance as AmmoApi);
});
});
return ammoInstancePromise;
}
export class SimulationUgvController {
private readonly pressed = new Set<string>();
private readonly collisionBodies: Entity[] = [];
private readonly collisionMeshes: Mesh[] = [];
private readonly wheelDefinitions: WheelDefinition[] = [];
private readonly cameraTarget = new Vec3();
private readonly cameraDesired = new Vec3();
private readonly cameraLookAt = new Vec3();
private readonly forward = new Vec3(0, 0, 1);
private readonly smoothedCamera = new Vec3();
private readonly limitedLinearVelocity = new Vec3();
private readonly limitedAngularVelocity = new Vec3();
private readonly spawnPosition = UGV_SPAWN_POSITION.clone();
private orbitPointerId: number | null = null;
private orbitPointerX = 0;
private orbitPointerY = 0;
private orbitYaw = 0;
private orbitPitch = DEFAULT_ORBIT_PITCH;
private orbitDistance = 3.8;
private orbitIdleSeconds = 0;
private cameraOrbitInitialized = false;
private active = false;
private vehicleEntity: Entity | null = null;
private vehicle: NativeRaycastVehicle | null = null;
private vehicleTuning: NativeObject | null = null;
private vehicleRaycaster: NativeObject | null = null;
private dynamicsWorld: NativeDynamicsWorld | null = null;
private chassisMaterial: StandardMaterial | null = null;
private wheelMaterial: StandardMaterial | null = null;
private cameraInitialized = false;
private settings: SimulationUgvPreset;
constructor(
private readonly app: Application,
private readonly canvas: HTMLCanvasElement,
private readonly camera: Entity,
private readonly ammo: AmmoApi,
initialSettings: SimulationUgvPreset = DEFAULT_SIMULATION_UGV_PRESET,
) {
this.settings = cloneUgvPreset(initialSettings);
}
get enabled(): boolean {
return this.active;
}
configure(settings: SimulationUgvPreset): void {
const next = cloneUgvPreset(settings);
const rebuildRequired = this.active && vehicleGeometryChanged(this.settings, next);
this.settings = next;
if (!rebuildRequired) return;
this.destroyVehicle();
this.spawnPosition.copy(this.findSafeSpawnPosition());
this.createVehicle();
this.reset(false, false);
}
async enable(collisionWorld: Entity): Promise<void> {
if (this.active) return;
await nextAnimationFrame();
try {
await this.createStaticCollisionBodies(collisionWorld);
await nextAnimationFrame();
this.spawnPosition.copy(this.findSafeSpawnPosition());
this.createVehicle();
this.attachInput();
this.active = true;
this.reset();
} catch (error) {
this.disable();
throw error;
}
}
disable(): void {
this.active = false;
this.detachInput();
this.pressed.clear();
this.destroyVehicle();
for (const entity of this.collisionBodies.splice(0)) {
runCleanup("destroy static physics proxy", () => entity.destroy());
}
for (const mesh of this.collisionMeshes.splice(0)) {
runCleanup("destroy static physics mesh", () => mesh.destroy());
}
this.cameraInitialized = false;
this.cameraOrbitInitialized = false;
this.orbitIdleSeconds = 0;
}
dispose(): void {
this.disable();
}
reset(focusCanvas = true, resetCameraOrbit = true): void {
const vehicleEntity = this.vehicleEntity as (Entity & NativeRigidBodyAccess) | null;
const rigidbody = vehicleEntity?.rigidbody;
if (!vehicleEntity || !rigidbody) return;
rigidbody.teleport(this.spawnPosition, UGV_SPAWN_ROTATION);
rigidbody.linearVelocity = ZERO;
rigidbody.angularVelocity = ZERO;
this.vehicle?.resetSuspension();
this.cameraInitialized = false;
if (resetCameraOrbit) {
this.cameraOrbitInitialized = false;
this.orbitIdleSeconds = 0;
}
this.updateCamera(1);
if (focusCanvas) this.canvas.focus({ preventScroll: true });
}
syncCollisionBodies(): void {
for (const entity of this.collisionBodies) {
const rigidbody = (entity as Entity & NativeRigidBodyAccess).rigidbody;
rigidbody?.teleport(entity.getPosition(), entity.getRotation());
}
}
update(deltaSeconds: number): void {
if (!this.active || !this.vehicle || !this.vehicleEntity) return;
const forwardInput = Number(this.isPressed("KeyW", "ArrowUp")) - Number(this.isPressed("KeyS", "ArrowDown"));
const requestedTurn = Number(this.isPressed("KeyA", "ArrowLeft")) - Number(this.isPressed("KeyD", "ArrowRight"));
const turnInput = this.settings.invertSteering ? -requestedTurn : requestedTurn;
const braking = this.pressed.has("Space");
const rigidbody = (this.vehicleEntity as Entity & NativeRigidBodyAccess).rigidbody;
const speedMetersPerSecond = this.vehicle.getCurrentSpeedKmHour() / 3.6;
const maxSpeed = this.settings.maxSpeedMetersPerSecond;
const maxTurnRate = this.settings.maxTurnRateDegrees * Math.PI / 180;
const pureTurn = !braking && forwardInput === 0 && turnInput !== 0;
const desiredSpeed = forwardInput * maxSpeed;
const speedError = desiredSpeed - speedMetersPerSecond;
const speedResponseRange = Math.max(0.35, maxSpeed * 0.2);
const throttle = forwardInput === 0
? 0
: clamp(Math.abs(speedError) / speedResponseRange, 0, 1);
const maximumAcceleration = clamp(1.4 + maxSpeed * 0.35, 1.8, 4.2);
const driveForcePerWheel = this.settings.massKg * maximumAcceleration * throttle / 4;
const pivotForcePerWheel = this.settings.massKg * 0.65;
const forwardCommand = braking ? 0 : forwardInput;
const turnCommand = braking ? 0 : turnInput;
const leftCommand = clamp(forwardCommand - turnCommand, -1, 1);
const rightCommand = clamp(forwardCommand + turnCommand, -1, 1);
const engineForce = pureTurn ? pivotForcePerWheel : driveForcePerWheel;
for (let index = 0; index < this.wheelDefinitions.length; index += 1) {
const definition = this.wheelDefinitions[index];
const command = definition.left ? leftCommand : rightCommand;
this.vehicle.setSteeringValue(0, index);
this.vehicle.applyEngineForce(command * engineForce, index);
this.vehicle.setBrake(0, index);
this.vehicle.updateWheelTransform(index, true);
const transform = this.vehicle.getWheelTransformWS(index);
const position = transform.getOrigin();
const rotation = transform.getRotation();
definition.anchor.setPosition(position.x(), position.y(), position.z());
definition.anchor.setRotation(rotation.x(), rotation.y(), rotation.z(), rotation.w());
}
if (rigidbody) {
const linearVelocity = rigidbody.linearVelocity;
let nextLinearX = linearVelocity.x;
let nextLinearZ = linearVelocity.z;
let horizontalSpeed = Math.hypot(nextLinearX, nextLinearZ);
const coasting = !braking && forwardInput === 0 && turnInput === 0;
if ((braking || coasting) && horizontalSpeed > 0.001) {
const deceleration = braking
? SERVICE_BRAKE_DECELERATION_MPS2
: COAST_DECELERATION_MPS2;
const nextSpeed = Math.max(0, horizontalSpeed - deceleration * Math.max(0, deltaSeconds));
const scale = nextSpeed / horizontalSpeed;
nextLinearX *= scale;
nextLinearZ *= scale;
horizontalSpeed = nextSpeed;
}
if (pureTurn && horizontalSpeed > 0.001) {
const pivotDamping = Math.exp(-Math.max(0, deltaSeconds) * 8);
nextLinearX *= pivotDamping;
nextLinearZ *= pivotDamping;
horizontalSpeed *= pivotDamping;
}
if (horizontalSpeed > maxSpeed * 1.05) {
const scale = maxSpeed / horizontalSpeed;
nextLinearX *= scale;
nextLinearZ *= scale;
}
if (nextLinearX !== linearVelocity.x || nextLinearZ !== linearVelocity.z) {
this.limitedLinearVelocity.set(nextLinearX, linearVelocity.y, nextLinearZ);
rigidbody.linearVelocity = this.limitedLinearVelocity;
}
const angularVelocity = rigidbody.angularVelocity;
const attitudeDamping = braking
? Math.exp(-Math.max(0, deltaSeconds) * BRAKE_ATTITUDE_DAMPING)
: 1;
let nextAngularY = angularVelocity.y;
if (pureTurn) {
const desiredYawRate = -turnInput * maxTurnRate;
const turnAcceleration = Math.max(2, maxTurnRate * 4);
nextAngularY = approach(
angularVelocity.y,
desiredYawRate,
turnAcceleration * Math.max(0, deltaSeconds),
);
} else if (Math.abs(angularVelocity.y) > maxTurnRate) {
nextAngularY = Math.sign(angularVelocity.y) * maxTurnRate;
}
if (attitudeDamping !== 1 || nextAngularY !== angularVelocity.y) {
this.limitedAngularVelocity.set(
angularVelocity.x * attitudeDamping,
nextAngularY,
angularVelocity.z * attitudeDamping,
);
rigidbody.angularVelocity = this.limitedAngularVelocity;
}
}
const body = (this.vehicleEntity as Entity & NativeRigidBodyAccess).rigidbody?.body as {
setActivationState?: (state: number) => void;
} | null;
body?.setActivationState?.(DISABLE_DEACTIVATION);
if (this.vehicleEntity.getPosition().y < -8) this.reset();
this.updateCamera(deltaSeconds);
}
private async createStaticCollisionBodies(collisionWorld: Entity): Promise<void> {
const models = collisionWorld.findComponents("model") as ModelComponent[];
if (models.length === 0) throw new Error("В слое коллизий нет геометрии для физики UGV.");
const meshInstances = models.flatMap((model) => Array.from(model.meshInstances ?? []));
const totalTriangles = meshInstances.reduce(
(total, meshInstance) => total + Math.floor((meshInstance.mesh.primitive[0]?.count ?? 0) / 3),
0,
);
if (totalTriangles === 0) throw new Error("В слое коллизий нет треугольников для физики UGV.");
const targetRatio = totalTriangles <= PHYSICS_PROXY_MAX_TRIANGLES
? 1
: Math.min(PHYSICS_PROXY_RATIO, PHYSICS_PROXY_MAX_TRIANGLES / totalTriangles);
await MeshoptSimplifier.ready;
await nextAnimationFrame();
const rootInverse = collisionWorld.getWorldTransform().clone().invert();
for (let index = 0; index < meshInstances.length; index += 1) {
const meshInstance = meshInstances[index];
const physicsMesh = createPhysicsProxyMesh(
this.app,
meshInstance.mesh,
new Mat4().mul2(rootInverse, meshInstance.node.getWorldTransform()),
targetRatio,
);
if (!physicsMesh) continue;
const entity = new Entity(`UGV physics proxy ${index + 1}`);
collisionWorld.addChild(entity);
entity.addComponent("collision", {
type: "mesh",
render: { meshes: [physicsMesh] },
checkVertexDuplicates: false,
});
entity.addComponent("rigidbody", {
type: "static",
friction: 0.95,
restitution: 0,
});
this.collisionBodies.push(entity);
this.collisionMeshes.push(physicsMesh);
if ((index + 1) % 4 === 0) await nextAnimationFrame();
}
if (this.collisionBodies.length === 0) {
throw new Error("Не удалось создать статический физический слой UGV.");
}
}
private createVehicle(): void {
const access = this.app as unknown as PhysicsSystemAccess;
const dynamicsWorld = access.systems.rigidbody.dynamicsWorld;
if (!dynamicsWorld) throw new Error("Физический мир PlayCanvas не готов.");
const vehicle = new Entity("SimulationUGV");
vehicle.addComponent("collision", { type: "compound" });
vehicle.addComponent("rigidbody", {
type: "dynamic",
mass: this.settings.massKg,
friction: 0.85,
linearDamping: 0.08,
angularDamping: 0.45,
});
this.chassisMaterial = createMaterial(readThemeAccent(), new Color(0.03, 0.04, 0.05));
this.wheelMaterial = createMaterial(new Color(0.055, 0.06, 0.07), new Color(0.008, 0.009, 0.01));
const dimensions = vehicleDimensions(this.settings);
const chassis = createBox(
"UGV chassis",
new Vec3(dimensions.bodyWidth, dimensions.mainHeight, dimensions.length),
this.chassisMaterial,
);
chassis.setLocalPosition(0, dimensions.mainCenterY, 0);
chassis.addComponent("collision", {
type: "box",
halfExtents: new Vec3(
dimensions.bodyWidth / 2,
dimensions.mainHeight / 2,
dimensions.length / 2,
),
});
vehicle.addChild(chassis);
const equipment = createBox(
"UGV equipment",
new Vec3(dimensions.bodyWidth * 0.68, dimensions.equipmentHeight, dimensions.length * 0.56),
this.chassisMaterial,
);
equipment.setLocalPosition(0, dimensions.equipmentCenterY, -dimensions.length * 0.04);
equipment.addComponent("collision", {
type: "box",
halfExtents: new Vec3(
dimensions.bodyWidth * 0.34,
dimensions.equipmentHeight / 2,
dimensions.length * 0.28,
),
});
vehicle.addChild(equipment);
const wheelConnections = [
{ connection: new Vec3(dimensions.wheelX, dimensions.wheelConnectionY, dimensions.wheelZ), left: false, front: true },
{ connection: new Vec3(-dimensions.wheelX, dimensions.wheelConnectionY, dimensions.wheelZ), left: true, front: true },
{ connection: new Vec3(dimensions.wheelX, dimensions.wheelConnectionY, -dimensions.wheelZ), left: false, front: false },
{ connection: new Vec3(-dimensions.wheelX, dimensions.wheelConnectionY, -dimensions.wheelZ), left: true, front: false },
];
for (const definition of wheelConnections) {
const anchor = new Entity(definition.left ? "UGV wheel left" : "UGV wheel right");
anchor.setLocalPosition(definition.connection);
const wheelMesh = new Entity("UGV wheel mesh");
wheelMesh.addComponent("render", { type: "cylinder", castShadows: false, receiveShadows: false });
wheelMesh.setLocalEulerAngles(0, 0, 90);
wheelMesh.setLocalScale(
dimensions.wheelRadius * 2,
dimensions.wheelThickness,
dimensions.wheelRadius * 2,
);
applyMaterial(wheelMesh, this.wheelMaterial);
anchor.addChild(wheelMesh);
vehicle.addChild(anchor);
this.wheelDefinitions.push({ ...definition, anchor });
}
vehicle.setLocalPosition(this.spawnPosition);
this.app.root.addChild(vehicle);
const body = (vehicle as Entity & NativeRigidBodyAccess).rigidbody?.body;
if (!body) {
vehicle.destroy();
throw new Error("Динамическое тело UGV не создалось.");
}
const tuning = new this.ammo.btVehicleTuning();
const raycaster = new this.ammo.btDefaultVehicleRaycaster(dynamicsWorld);
const nativeVehicle = new this.ammo.btRaycastVehicle(tuning, body, raycaster);
nativeVehicle.setCoordinateSystem(0, 1, 2);
const axle = new this.ammo.btVector3(-1, 0, 0);
const direction = new this.ammo.btVector3(0, -1, 0);
const connection = new this.ammo.btVector3(0, 0, 0);
for (const definition of this.wheelDefinitions) {
connection.setValue(definition.connection.x, definition.connection.y, definition.connection.z);
const wheel = nativeVehicle.addWheel(
connection,
direction,
axle,
dimensions.suspensionRestLength,
dimensions.wheelRadius,
tuning,
definition.front,
);
wheel.set_m_suspensionStiffness(24);
wheel.set_m_wheelsDampingRelaxation(3.2);
wheel.set_m_wheelsDampingCompression(4.8);
wheel.set_m_frictionSlip(5.5);
wheel.set_m_rollInfluence(0.08);
}
this.ammo.destroy(axle);
this.ammo.destroy(direction);
this.ammo.destroy(connection);
dynamicsWorld.addAction(nativeVehicle);
this.vehicleEntity = vehicle;
this.vehicleTuning = tuning;
this.vehicleRaycaster = raycaster;
this.vehicle = nativeVehicle;
this.dynamicsWorld = dynamicsWorld;
}
private updateCamera(deltaSeconds: number): void {
if (!this.vehicleEntity || !this.vehicle) return;
this.cameraTarget.copy(this.vehicleEntity.getPosition());
this.cameraLookAt.copy(this.cameraTarget);
this.cameraLookAt.y += Math.max(0.15, this.settings.dimensionsMeters.height * 0.35);
const nativeForward = this.vehicle.getForwardVector();
this.forward.set(nativeForward.x(), 0, nativeForward.z());
if (this.forward.lengthSq() < 0.001) this.forward.set(0, 0, 1);
this.forward.normalize();
const rearOrbitYaw = Math.atan2(-this.forward.x, -this.forward.z);
if (!this.cameraOrbitInitialized) {
this.orbitYaw = rearOrbitYaw;
this.orbitPitch = DEFAULT_ORBIT_PITCH;
this.orbitDistance = Math.max(3.2, this.settings.dimensionsMeters.length * 3.8);
this.cameraOrbitInitialized = true;
this.orbitIdleSeconds = 0;
} else if (this.orbitPointerId === null) {
this.orbitIdleSeconds += Math.max(0, deltaSeconds);
if (this.orbitIdleSeconds >= CAMERA_RETURN_DELAY_SECONDS) {
const returnDelta = Math.max(0, deltaSeconds) / CAMERA_RETURN_DURATION_SECONDS;
this.orbitYaw = approachAngle(this.orbitYaw, rearOrbitYaw, Math.PI * returnDelta);
this.orbitPitch = approach(
this.orbitPitch,
DEFAULT_ORBIT_PITCH,
Math.PI * 0.5 * returnDelta,
);
}
}
const horizontalDistance = Math.cos(this.orbitPitch) * this.orbitDistance;
this.cameraDesired.set(
this.cameraLookAt.x + Math.sin(this.orbitYaw) * horizontalDistance,
this.cameraLookAt.y + Math.sin(this.orbitPitch) * this.orbitDistance,
this.cameraLookAt.z + Math.cos(this.orbitYaw) * horizontalDistance,
);
if (!this.cameraInitialized) {
this.smoothedCamera.copy(this.cameraDesired);
this.cameraInitialized = true;
} else {
const blend = 1 - Math.exp(
-Math.max(0, deltaSeconds) * (this.orbitPointerId === null ? 8 : 18),
);
this.smoothedCamera.lerp(this.smoothedCamera, this.cameraDesired, blend);
}
this.camera.setPosition(this.smoothedCamera);
this.camera.lookAt(this.cameraLookAt);
}
private findSafeSpawnPosition(): Vec3 {
const dimensions = vehicleDimensions(this.settings);
const footprint: ReadonlyArray<readonly [number, number]> = [
[0, 0],
[dimensions.width * 0.42, dimensions.length * 0.42],
[-dimensions.width * 0.42, dimensions.length * 0.42],
[dimensions.width * 0.42, -dimensions.length * 0.42],
[-dimensions.width * 0.42, -dimensions.length * 0.42],
];
const clearanceDirections: ReadonlyArray<readonly [number, number]> = [
[dimensions.width * 0.62, 0],
[-dimensions.width * 0.62, 0],
[0, dimensions.length * 0.62],
[0, -dimensions.length * 0.62],
];
const from = new Vec3();
const to = new Vec3();
const physics = (this.app as unknown as PhysicsSystemAccess).systems.rigidbody;
for (const [offsetX, offsetZ] of UGV_SPAWN_OFFSETS) {
const heights: number[] = [];
let suitable = true;
for (const [sampleX, sampleZ] of footprint) {
const x = UGV_SPAWN_POSITION.x + offsetX + sampleX;
const z = UGV_SPAWN_POSITION.z + offsetZ + sampleZ;
from.set(x, 8, z);
to.set(x, -8, z);
const hit = physics.raycastFirst(from, to);
if (!hit || hit.normal.y < 0.72 || Math.abs(hit.point.y) > 2.5) {
suitable = false;
break;
}
heights.push(hit.point.y);
}
if (!suitable || heights.length !== footprint.length) continue;
const minimum = Math.min(...heights);
const maximum = Math.max(...heights);
if (maximum - minimum > 0.32) continue;
const x = UGV_SPAWN_POSITION.x + offsetX;
const z = UGV_SPAWN_POSITION.z + offsetZ;
const ground = heights.reduce((sum, height) => sum + height, 0) / heights.length;
const clearanceHeight = ground + dimensions.originHeight;
const origin = new Vec3(x, clearanceHeight, z);
const blocked = clearanceDirections.some(([directionX, directionZ]) => {
to.set(x + directionX, clearanceHeight, z + directionZ);
return physics.raycastFirst(origin, to) !== null;
});
if (!blocked) return new Vec3(x, ground + dimensions.originHeight, z);
}
return UGV_SPAWN_POSITION.clone();
}
private destroyVehicle(): void {
if (this.vehicle && this.dynamicsWorld) {
runCleanup("remove vehicle action", () => this.dynamicsWorld?.removeAction(this.vehicle as NativeObject));
}
if (this.vehicle) runCleanup("destroy vehicle", () => this.ammo.destroy(this.vehicle as NativeObject));
if (this.vehicleRaycaster) runCleanup("destroy vehicle raycaster", () => this.ammo.destroy(this.vehicleRaycaster as NativeObject));
if (this.vehicleTuning) runCleanup("destroy vehicle tuning", () => this.ammo.destroy(this.vehicleTuning as NativeObject));
this.vehicle = null;
this.vehicleRaycaster = null;
this.vehicleTuning = null;
this.dynamicsWorld = null;
if (this.vehicleEntity) runCleanup("destroy vehicle entity", () => this.vehicleEntity?.destroy());
this.vehicleEntity = null;
this.wheelDefinitions.length = 0;
if (this.chassisMaterial) runCleanup("destroy chassis material", () => this.chassisMaterial?.destroy());
if (this.wheelMaterial) runCleanup("destroy wheel material", () => this.wheelMaterial?.destroy());
this.chassisMaterial = null;
this.wheelMaterial = null;
this.cameraInitialized = false;
}
private isPressed(primary: string, alternative: string): boolean {
return this.pressed.has(primary) || this.pressed.has(alternative);
}
private attachInput(): void {
window.addEventListener("keydown", this.onKeyDown, true);
window.addEventListener("keyup", this.onKeyUp, true);
window.addEventListener("blur", this.onWindowBlur);
this.canvas.addEventListener("pointerdown", this.onOrbitPointerDown);
this.canvas.addEventListener("pointermove", this.onOrbitPointerMove);
this.canvas.addEventListener("pointerup", this.onOrbitPointerUp);
this.canvas.addEventListener("pointercancel", this.onOrbitPointerUp);
this.canvas.addEventListener("wheel", this.onOrbitWheel, { passive: false });
}
private detachInput(): void {
window.removeEventListener("keydown", this.onKeyDown, true);
window.removeEventListener("keyup", this.onKeyUp, true);
window.removeEventListener("blur", this.onWindowBlur);
this.canvas.removeEventListener("pointerdown", this.onOrbitPointerDown);
this.canvas.removeEventListener("pointermove", this.onOrbitPointerMove);
this.canvas.removeEventListener("pointerup", this.onOrbitPointerUp);
this.canvas.removeEventListener("pointercancel", this.onOrbitPointerUp);
this.canvas.removeEventListener("wheel", this.onOrbitWheel);
this.releaseOrbitPointer();
}
private onOrbitPointerDown = (event: PointerEvent): void => {
if (!this.active || event.button !== 0 || this.orbitPointerId !== null) return;
event.preventDefault();
this.orbitPointerId = event.pointerId;
this.orbitPointerX = event.clientX;
this.orbitPointerY = event.clientY;
this.orbitIdleSeconds = 0;
this.canvas.classList.add("is-orbiting");
try {
this.canvas.setPointerCapture(event.pointerId);
} catch {
// A browser may already have released the pointer during a window transition.
}
};
private onOrbitPointerMove = (event: PointerEvent): void => {
if (!this.active || event.pointerId !== this.orbitPointerId) return;
event.preventDefault();
const deltaX = event.clientX - this.orbitPointerX;
const deltaY = event.clientY - this.orbitPointerY;
this.orbitPointerX = event.clientX;
this.orbitPointerY = event.clientY;
this.orbitIdleSeconds = 0;
this.orbitYaw = (this.orbitYaw - deltaX * 0.006) % (Math.PI * 2);
this.orbitPitch = clamp(this.orbitPitch + deltaY * 0.0045, -0.12, 1.32);
};
private onOrbitPointerUp = (event: PointerEvent): void => {
if (event.pointerId !== this.orbitPointerId) return;
this.releaseOrbitPointer();
};
private onOrbitWheel = (event: WheelEvent): void => {
if (!this.active) return;
event.preventDefault();
this.orbitIdleSeconds = 0;
this.orbitDistance = clamp(
this.orbitDistance * Math.exp(event.deltaY * 0.0012),
1.6,
14,
);
};
private releaseOrbitPointer(): void {
const pointerId = this.orbitPointerId;
this.orbitPointerId = null;
this.canvas.classList.remove("is-orbiting");
if (pointerId === null || !this.canvas.hasPointerCapture(pointerId)) return;
try {
this.canvas.releasePointerCapture(pointerId);
} catch {
// Pointer capture is already gone; the local orbit state is still released.
}
}
private onKeyDown = (event: KeyboardEvent): void => {
if (!this.active || isTextEntry(event.target)) return;
if (!UGV_KEYS.has(event.code)) return;
event.preventDefault();
event.stopPropagation();
if (event.code === "KeyR") {
this.reset();
return;
}
this.pressed.add(event.code);
};
private onKeyUp = (event: KeyboardEvent): void => {
if (!this.active || !UGV_KEYS.has(event.code)) return;
event.preventDefault();
event.stopPropagation();
this.pressed.delete(event.code);
};
private onWindowBlur = (): void => {
this.pressed.clear();
this.releaseOrbitPointer();
};
}
const UGV_KEYS = new Set([
"KeyW",
"KeyA",
"KeyS",
"KeyD",
"ArrowUp",
"ArrowDown",
"ArrowLeft",
"ArrowRight",
"Space",
"KeyR",
]);
function createPhysicsProxyMesh(
app: Application,
source: Mesh,
localTransform: Mat4,
targetRatio: number,
): Mesh | null {
const primitive = source.primitive[0];
const sourceVertexCount = source.vertexBuffer?.numVertices ?? 0;
if (!primitive || sourceVertexCount === 0 || primitive.count < 3) return null;
const positions = new Float32Array(sourceVertexCount * 3);
const populatedVertices = source.getPositions(positions);
if (populatedVertices === 0) return null;
const transformed = new Float32Array(populatedVertices * 3);
const point = new Vec3();
for (let vertex = 0; vertex < populatedVertices; vertex += 1) {
const offset = vertex * 3;
point.set(positions[offset], positions[offset + 1], positions[offset + 2]);
localTransform.transformPoint(point, point);
transformed[offset] = point.x;
transformed[offset + 1] = point.y;
transformed[offset + 2] = point.z;
}
const requestedIndexCount = Math.max(3, primitive.count);
let indices = new Uint32Array(requestedIndexCount);
const populatedIndices = source.getIndices(indices);
if (populatedIndices === 0) {
const fallbackCount = Math.min(populatedVertices, requestedIndexCount);
indices = new Uint32Array(fallbackCount);
for (let index = 0; index < fallbackCount; index += 1) indices[index] = index;
} else if (populatedIndices !== indices.length) {
indices = indices.slice(0, populatedIndices);
}
const triangleIndexCount = Math.floor(indices.length / 3) * 3;
if (triangleIndexCount < 3) return null;
if (triangleIndexCount !== indices.length) indices = indices.slice(0, triangleIndexCount);
if (targetRatio < 1) {
const targetIndexCount = Math.max(3, Math.floor(indices.length * targetRatio / 3) * 3);
const [simplifiedIndices] = MeshoptSimplifier.simplify(
indices,
transformed,
3,
targetIndexCount,
PHYSICS_PROXY_ERROR,
);
indices = new Uint32Array(simplifiedIndices);
}
const compactedIndices = new Uint32Array(indices);
const [remap, compactedVertexCount] = MeshoptSimplifier.compactMesh(compactedIndices);
const compactedPositions = new Float32Array(compactedVertexCount * 3);
const missing = 0xffff_ffff;
for (let sourceVertex = 0; sourceVertex < remap.length; sourceVertex += 1) {
const compactedVertex = remap[sourceVertex];
if (compactedVertex === missing) continue;
const sourceOffset = sourceVertex * 3;
const compactedOffset = compactedVertex * 3;
compactedPositions[compactedOffset] = transformed[sourceOffset];
compactedPositions[compactedOffset + 1] = transformed[sourceOffset + 1];
compactedPositions[compactedOffset + 2] = transformed[sourceOffset + 2];
}
const physicsMesh = new Mesh(app.graphicsDevice);
physicsMesh.setPositions(compactedPositions);
physicsMesh.setIndices(compactedIndices);
physicsMesh.update(PRIMITIVE_TRIANGLES, true);
return physicsMesh;
}
function cloneUgvPreset(preset: SimulationUgvPreset): SimulationUgvPreset {
return {
...preset,
dimensionsMeters: { ...preset.dimensionsMeters },
};
}
function vehicleGeometryChanged(
current: SimulationUgvPreset,
next: SimulationUgvPreset,
): boolean {
return current.massKg !== next.massKg
|| current.dimensionsMeters.length !== next.dimensionsMeters.length
|| current.dimensionsMeters.width !== next.dimensionsMeters.width
|| current.dimensionsMeters.height !== next.dimensionsMeters.height
|| current.dimensionsMeters.groundClearance !== next.dimensionsMeters.groundClearance;
}
function vehicleDimensions(settings: SimulationUgvPreset) {
const { length, width, height, groundClearance } = settings.dimensionsMeters;
const bodyHeight = height - groundClearance;
const wheelRadius = clamp(
groundClearance * 0.8,
0.07,
Math.min(height * 0.4, length * 0.22),
);
const wheelThickness = clamp(width * 0.13, 0.08, 0.16);
const bodyWidth = Math.max(0.12, width - wheelThickness * 2);
const mainHeight = bodyHeight * 0.68;
const equipmentHeight = bodyHeight - mainHeight;
const suspensionRestLength = clamp(groundClearance * 0.4, 0.04, 0.16);
const originHeight = groundClearance + bodyHeight / 2;
return {
length,
width,
bodyWidth,
mainHeight,
equipmentHeight,
mainCenterY: -bodyHeight / 2 + mainHeight / 2,
equipmentCenterY: bodyHeight / 2 - equipmentHeight / 2,
wheelRadius,
wheelThickness,
wheelX: width / 2 - wheelThickness / 2,
wheelZ: Math.max(0, length / 2 - wheelRadius),
wheelConnectionY: suspensionRestLength + wheelRadius - originHeight,
suspensionRestLength,
originHeight,
};
}
function createBox(name: string, scale: Vec3, material: StandardMaterial): Entity {
const entity = new Entity(name);
entity.addComponent("render", { type: "box", castShadows: false, receiveShadows: false });
entity.setLocalScale(scale);
applyMaterial(entity, material);
return entity;
}
function applyMaterial(entity: Entity, material: StandardMaterial): void {
for (const meshInstance of entity.render?.meshInstances ?? []) meshInstance.material = material;
}
function createMaterial(diffuse: Color, emissive: Color): StandardMaterial {
const material = new StandardMaterial();
material.diffuse = diffuse;
material.emissive = emissive;
material.metalness = 0.15;
material.gloss = 0.32;
material.update();
return material;
}
function readThemeAccent(): Color {
const raw = getComputedStyle(document.documentElement).getPropertyValue("--nodedc-accent-rgb");
const channels = raw.trim().split(/[\s,]+/).map(Number);
if (channels.length < 3 || channels.some((value) => !Number.isFinite(value))) {
return new Color(0.22, 0.72, 0.38);
}
return new Color(channels[0] / 255, channels[1] / 255, channels[2] / 255);
}
function isTextEntry(target: EventTarget | null): boolean {
return target instanceof HTMLInputElement || target instanceof HTMLTextAreaElement
|| target instanceof HTMLSelectElement || target instanceof HTMLButtonElement
|| (target instanceof HTMLElement && target.isContentEditable);
}
function clamp(value: number, min: number, max: number): number { return Math.max(min, Math.min(max, value)); }
function approach(current: number, target: number, maximumDelta: number): number {
if (current < target) return Math.min(target, current + maximumDelta);
if (current > target) return Math.max(target, current - maximumDelta);
return target;
}
function approachAngle(current: number, target: number, maximumDelta: number): number {
const delta = ((target - current + Math.PI) % (Math.PI * 2) + Math.PI * 2) % (Math.PI * 2) - Math.PI;
return current + clamp(delta, -maximumDelta, maximumDelta);
}
function runCleanup(label: string, cleanup: () => void): void {
try {
cleanup();
} catch (error) {
console.warn(`[Simulation UGV] Cleanup failed: ${label}`, error);
}
}
function nextAnimationFrame(): Promise<void> { return new Promise((resolve) => requestAnimationFrame(() => resolve())); }
@@ -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 {
ActivityIndicator,
Button,
GlassMaterialSurface,
Icon,
IconButton,
SegmentedControl,
Select,
StatusBadge,
Switch,
TextField,
} from "@nodedc/ui-react";
import {
saveSimulationViewerSettings,
type SimulationEulerRotation,
type SimulationProject,
type SimulationTransformAxis,
type SimulationUgvPreset,
type SimulationViewerSettings,
} from "../../core/simulation/projects";
import {
@@ -22,16 +25,17 @@ import {
} from "../../core/runtime/latestAsyncCommitter";
import {
PlayCanvasRuntime,
playCanvasAxisTransform,
playCanvasEulerTransform,
type SimulationControlMode,
type SimulationQuality,
type SimulationViewMode,
} from "./PlayCanvasRuntime";
import {
SimulationUgvSettingsPanel,
type SimulationUgvPresetSaveState,
} from "./SimulationUgvSettingsPanel";
const AXIS_OPTIONS: Array<{ value: SimulationTransformAxis; label: string }> = [
{ value: "x", label: "X" },
{ value: "y", label: "Y" },
{ value: "z", label: "Z" },
];
const ROTATION_AXES = ["x", "y", "z"] as const;
export function SimulationViewport({
project,
@@ -44,23 +48,34 @@ export function SimulationViewport({
const runtimeRef = useRef<PlayCanvasRuntime | null>(null);
const viewerSettingsRef = useRef(project.viewerSettings);
const settingsCommitterRef = useRef<LatestAsyncCommitter<SimulationViewerSettings> | null>(null);
const settingsCommitSucceededRef = useRef(true);
const ugvSaveRequestRef = useRef(0);
const controlRequestRef = useRef(0);
const settingsId = useId();
const [state, setState] = useState<"mounting" | "loading" | "ready" | "failed">("mounting");
const [error, setError] = useState<string | null>(null);
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 [collisionState, setCollisionState] = useState<"idle" | "loading" | "ready" | "failed">("idle");
const [settingsOpen, setSettingsOpen] = useState(false);
const [visualInverted, setVisualInverted] = useState(project.viewerSettings.visual.inverted);
const [visualAxis, setVisualAxis] = useState(project.viewerSettings.visual.axis);
const [collisionInverted, setCollisionInverted] = useState(project.viewerSettings.collision.inverted);
const [collisionAxis, setCollisionAxis] = useState(project.viewerSettings.collision.axis);
const [visualRotation, setVisualRotation] = useState({
...project.viewerSettings.visual.rotationDegrees,
});
const [collisionRotation, setCollisionRotation] = useState({
...project.viewerSettings.collision.rotationDegrees,
});
const [horizontalInverted, setHorizontalInverted] = useState(
project.viewerSettings.camera.invertHorizontal,
);
const [verticalInverted, setVerticalInverted] = useState(
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 worldManifestRevision = JSON.stringify(project.worldManifest);
@@ -68,14 +83,18 @@ export function SimulationViewport({
const settings = project.viewerSettings;
viewerSettingsRef.current = settings;
setQuality(settings.quality);
setVisualInverted(settings.visual.inverted);
setVisualAxis(settings.visual.axis);
setCollisionInverted(settings.collision.inverted);
setCollisionAxis(settings.collision.axis);
setVisualRotation({ ...settings.visual.rotationDegrees });
setCollisionRotation({ ...settings.collision.rotationDegrees });
setHorizontalInverted(settings.camera.invertHorizontal);
setVerticalInverted(settings.camera.invertVertical);
setUgvDraft(cloneUgvPreset(settings.ugv));
setUgvSaveState("idle");
ugvSaveRequestRef.current += 1;
setSettingsError(null);
setSettingsOpen(false);
controlRequestRef.current += 1;
setControlMode("free");
setControlState("idle");
}, [project.projectId]);
useEffect(() => {
@@ -84,9 +103,11 @@ export function SimulationViewport({
async commit(settings) {
try {
const saved = await saveSimulationViewerSettings(project.projectId, settings);
settingsCommitSucceededRef.current = true;
if (active) onProjectChange?.(saved);
return true;
} catch (caught) {
settingsCommitSucceededRef.current = false;
if (active) {
setSettingsError(
caught instanceof Error ? caught.message : "Не удалось сохранить настройки сцены.",
@@ -122,15 +143,16 @@ export function SimulationViewport({
runtime.setQuality(settings.quality);
runtime.setCameraInversion("horizontal", settings.camera.invertHorizontal);
runtime.setCameraInversion("vertical", settings.camera.invertVertical);
runtime.setUgvPreset(settings.ugv);
setState("loading");
await runtime.loadWorld(manifest);
runtime.setLayerWorldTransform(
"visual",
playCanvasAxisTransform(settings.visual.axis, settings.visual.inverted),
playCanvasEulerTransform(settings.visual.rotationDegrees),
);
runtime.setLayerWorldTransform(
"collision",
playCanvasAxisTransform(settings.collision.axis, settings.collision.inverted),
playCanvasEulerTransform(settings.collision.rotationDegrees),
);
if (!cancelled) setState("ready");
}).catch((caught: unknown) => {
@@ -140,6 +162,7 @@ export function SimulationViewport({
});
return () => {
cancelled = true;
controlRequestRef.current += 1;
runtime.dispose();
runtimeRef.current = null;
};
@@ -151,25 +174,89 @@ export function SimulationViewport({
const collisionAvailable = project.worldManifest?.collision.available ?? false;
const commitViewerSettings = (settings: SimulationViewerSettings) => {
viewerSettingsRef.current = settings;
settingsCommitSucceededRef.current = false;
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 (
<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">
<div>
<StatusBadge tone={state === "failed" || collisionState === "failed" ? "warning" : state === "ready" && collisionState !== "loading" ? "success" : "accent"}>
{collisionState === "loading"
? "Загрузка collision"
{controlState === "loading"
? "Подготовка UGV"
: controlState === "ready"
? "UGV готов"
: controlState === "failed"
? "Ошибка UGV"
: collisionState === "loading"
? "Загрузка коллизий"
: collisionState === "failed"
? "Ошибка collision"
? "Ошибка коллизий"
: state === "ready"
? "Runtime готов"
: state === "failed"
? "Ошибка runtime"
: "Загрузка сцены"}
: "Загрузка локации"}
</StatusBadge>
<span>PlayCanvas Engine 2.21.4</span>
<span>Engine 2.21.4</span>
</div>
<div className="simulation-viewport__controls">
<Select
@@ -190,6 +277,27 @@ export function SimulationViewport({
minMenuWidth={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
label="Слой сцены"
value={viewMode}
@@ -204,7 +312,7 @@ export function SimulationViewport({
if (next !== "visual") setCollisionState("ready");
}).catch((caught: unknown) => {
setCollisionState("failed");
setError(caught instanceof Error ? caught.message : "Не удалось открыть collision GLB.");
setError(caught instanceof Error ? caught.message : "Не удалось открыть слой коллизий.");
});
}}
items={[
@@ -227,7 +335,7 @@ export function SimulationViewport({
<Icon name="settings" size={18} />
</IconButton>
{settingsOpen ? (
<div
<GlassMaterialSurface
id={settingsId}
className="simulation-viewport__settings"
role="dialog"
@@ -236,100 +344,57 @@ export function SimulationViewport({
<div className="simulation-viewport__settings-head">
<div>
<strong>Настройки сцены</strong>
<span>Gaussian-мир и камера</span>
<span>Локация, камера и UGV</span>
</div>
<IconButton label="Закрыть настройки" onClick={() => setSettingsOpen(false)}>
<Icon name="close" size={16} />
</IconButton>
</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">
<strong>Система координат</strong>
<span>Мир PlayCanvas · Y вверх</span>
<span>Мир сцены · Y вверх</span>
</div>
<p>
Коррекция слоёв не меняет координаты камеры, навигации и будущей физики.
</p>
<div className="simulation-viewport__settings-switches">
<div className="simulation-viewport__settings-layer-row">
<Switch
checked={visualInverted}
disabled={state !== "ready"}
label="Инверсия визуального слоя"
onChange={(checked) => {
setVisualInverted(checked);
runtimeRef.current?.setLayerWorldTransform(
"visual",
playCanvasAxisTransform(visualAxis, checked),
);
commitViewerSettings({
...viewerSettingsRef.current,
visual: { ...viewerSettingsRef.current.visual, inverted: checked },
});
}}
/>
<Select
className="simulation-viewport__axis-select"
label="Ось инверсии визуального слоя"
value={visualAxis}
options={AXIS_OPTIONS}
disabled={state !== "ready"}
minMenuWidth={72}
menuWidth={72}
onChange={(axis) => {
setVisualAxis(axis);
runtimeRef.current?.setLayerWorldTransform(
"visual",
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 className="simulation-viewport__settings-rotations">
<LayerRotationFields
label="Гауссовый слой"
value={visualRotation}
disabled={state !== "ready"}
onChange={(next) => {
setVisualRotation(next);
runtimeRef.current?.setLayerWorldTransform(
"visual",
playCanvasEulerTransform(next),
);
commitViewerSettings({
...viewerSettingsRef.current,
visual: { rotationDegrees: next },
});
}}
/>
<LayerRotationFields
label="Слой коллизий"
value={collisionRotation}
disabled={state !== "ready"}
onChange={(next) => {
setCollisionRotation(next);
runtimeRef.current?.setLayerWorldTransform(
"collision",
playCanvasEulerTransform(next),
);
commitViewerSettings({
...viewerSettingsRef.current,
collision: { rotationDegrees: next },
});
}}
/>
</div>
</section>
<section className="simulation-viewport__settings-group">
</section>
<section className="simulation-viewport__settings-group">
<div className="simulation-viewport__settings-group-head">
<strong>Управление камерой</strong>
<span>Мышь и тачпад</span>
@@ -362,39 +427,165 @@ export function SimulationViewport({
}}
/>
</div>
</section>
{settingsError ? (
<p className="simulation-viewport__settings-error" role="alert">
Настройки не сохранены: {settingsError}
</p>
) : null}
</div>
</section>
<SimulationUgvSettingsPanel
value={ugvDraft}
disabled={state !== "ready"}
saveState={ugvSaveState}
onChange={changeUgvDraft}
onSave={saveUgvPreset}
/>
{settingsError ? (
<p className="simulation-viewport__settings-error" role="alert">
Настройки не сохранены: {settingsError}
</p>
) : null}
</div>
</GlassMaterialSurface>
) : null}
</div>
</div>
<span className="simulation-viewport__toolbar-balance" aria-hidden="true" />
</header>
<div className="simulation-viewport__stage">
<canvas ref={canvasRef} aria-label={`PlayCanvas сцена ${project.name}`} />
<canvas ref={canvasRef} aria-label={`Сцена ${project.name}`} />
{state !== "ready" ? (
<div className="simulation-viewport__state" role={state === "failed" ? "alert" : "status"}>
{state === "failed" ? null : <ActivityIndicator label="Загрузка сцены" />}
<strong>{state === "failed" ? "Сцена не открылась" : "PlayCanvas загружает мир"}</strong>
<p>{error ?? "Streamed SOG догружается по мере готовности runtime."}</p>
{state === "failed" ? null : <ActivityIndicator label="Загрузка локации" />}
<strong>{state === "failed" ? "Локация не открылась" : "Загружаем локацию"}</strong>
<p>{error ?? "LOD-уровни загружаются по мере готовности runtime."}</p>
</div>
) : null}
</div>
{!collisionAvailable ? (
<footer className="simulation-viewport__notice">
<StatusBadge tone="warning">Collision недоступен</StatusBadge>
<span>Для этой сборки collision GLB не был запрошен; визуальный слой настоящий и не подменяется.</span>
<StatusBadge tone="warning">Коллизии недоступны</StatusBadge>
<span>Для этой сборки слой коллизий не создавался; визуальный слой остаётся доступен.</span>
</footer>
) : collisionState === "failed" ? (
<footer className="simulation-viewport__notice" role="alert">
<StatusBadge tone="warning">Collision не открылся</StatusBadge>
<span>{error ?? "PlayCanvas не смог загрузить collision GLB."}</span>
<StatusBadge tone="warning">Коллизии не открылись</StatusBadge>
<span>{error ?? "Слой коллизий не загрузился."}</span>
</footer>
) : null}
</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);
}}
/>
);
}
@@ -46,14 +46,20 @@ import { fetchM48StaticOccupancyQualification } from "./m48StaticOccupancyQualif
import { fetchM48R3StaticOccupancyShadow } from "./m48r3StaticOccupancyShadow";
import { fetchM48SFixedClassDetectorResult } from "./m48sFixedClassDetector";
import { fetchM48TRiskQualityResult } from "./m48tRiskQuality";
import { fetchM49TgsFailClosedResult } from "./m49TgsFailClosed";
import { fetchM49TgsFullShadowResult } from "./m49TgsFullShadow";
import { fetchVegetationShadowResult } from "./vegetationShadow";
export type AdvancedLaboratoryWorkId =
| "lab-v1-vegetation-shadow"
| "m48-object-centric-quality"
| "m48-small-static-passage-regression"
| "m48-static-occupancy-qualification"
| "m48r3-static-occupancy-shadow"
| "m48s-fixed-class-detector"
| "m48t-risk-quality-temporal"
| "m49-tgs-fail-closed-evidence"
| "m49-tgs-full-shadow"
| "m47-reference-graph-shadow"
| "m4-replay-threat"
| "l3-pointpillars-visual-audit"
@@ -96,12 +102,15 @@ export interface AdvancedLaboratoryIndexItem {
}
const WORK_IDS: readonly AdvancedLaboratoryWorkId[] = [
"lab-v1-vegetation-shadow",
"m48-object-centric-quality",
"m48-small-static-passage-regression",
"m48-static-occupancy-qualification",
"m48r3-static-occupancy-shadow",
"m48s-fixed-class-detector",
"m48t-risk-quality-temporal",
"m49-tgs-fail-closed-evidence",
"m49-tgs-full-shadow",
"m47-reference-graph-shadow",
"m4-replay-threat",
"l3-pointpillars-visual-audit",
@@ -139,12 +148,15 @@ const WORK_IDS: readonly AdvancedLaboratoryWorkId[] = [
];
const RESULT_PREFIX: Readonly<Record<AdvancedLaboratoryWorkId, string>> = {
"lab-v1-vegetation-shadow": "lab-v1-vegetation-shadow",
"m48-object-centric-quality": "m48-object-quality-(?:pack|result)",
"m48-small-static-passage-regression": "m48-small-static-passage-regression",
"m48-static-occupancy-qualification": "m48-static-occupancy-qualification",
"m48r3-static-occupancy-shadow": "m48r3-static-occupancy-shadow",
"m48s-fixed-class-detector": "m48s-fixed-class-detector-lab",
"m48t-risk-quality-temporal": "(?:m48t-risk-quality-temporal-lab|m48q-native-risk-quality-lab)",
"m49-tgs-fail-closed-evidence": "m49-tgs-fail-closed",
"m49-tgs-full-shadow": "m49-tgs-full-shadow",
"m47-reference-graph-shadow": "m47-reference-graph-lab",
"m4-replay-threat": "m4-threat-replay",
"l3-pointpillars-visual-audit": "l3-pointpillars-visual-audit",
@@ -189,6 +201,7 @@ export function isAdvancedLaboratoryWorkId(
export function emptyAdvancedLaboratoryResults(): AdvancedLaboratoryResults {
return {
vegetationShadow: null,
m47Graph: null,
m48: null,
m48SmallStatic: null,
@@ -196,6 +209,8 @@ export function emptyAdvancedLaboratoryResults(): AdvancedLaboratoryResults {
m48r3StaticOccupancy: null,
m48s: null,
m48t: null,
m49Tgs: null,
m49TgsFull: null,
m4Threat: null,
l3: null,
l31: null,
@@ -320,12 +335,15 @@ export function advancedLaboratoryResultAvailable(
workId: AdvancedLaboratoryWorkId,
results: AdvancedLaboratoryResults,
): boolean {
return workId === "m48-object-centric-quality" ? results.m48 !== null
return workId === "lab-v1-vegetation-shadow" ? results.vegetationShadow !== null
: workId === "m48-object-centric-quality" ? results.m48 !== null
: workId === "m48-small-static-passage-regression" ? results.m48SmallStatic !== null
: workId === "m48-static-occupancy-qualification" ? results.m48StaticOccupancy !== null
: workId === "m48r3-static-occupancy-shadow" ? results.m48r3StaticOccupancy !== null
: workId === "m48s-fixed-class-detector" ? results.m48s !== null
: workId === "m48t-risk-quality-temporal" ? results.m48t !== null
: workId === "m49-tgs-fail-closed-evidence" ? results.m49Tgs !== null
: workId === "m49-tgs-full-shadow" ? results.m49TgsFull !== null
: workId === "m47-reference-graph-shadow" ? results.m47Graph !== null
: workId === "m4-replay-threat" ? results.m4Threat !== null
: workId === "l3-pointpillars-visual-audit" ? results.l3 !== null
@@ -375,7 +393,10 @@ export async function fetchAdvancedLaboratoryResult(
} = {},
): Promise<AdvancedLaboratoryResults> {
const results = emptyAdvancedLaboratoryResults();
if (workId === "m48-object-centric-quality") {
if (workId === "lab-v1-vegetation-shadow") {
if (!resultId) throw new AdvancedLaboratoryContractError("Vegetation LAB identity не выбрана.");
results.vegetationShadow = await fetchVegetationShadowResult(resultId, { fetcher, signal });
} else if (workId === "m48-object-centric-quality") {
if (!resultId) throw new AdvancedLaboratoryContractError("M4.8 lifecycle evidence identity не выбрана.");
results.m48 = await fetchM48LifecycleResult(resultId, { fetcher, signal });
} else if (workId === "m48-small-static-passage-regression") {
@@ -393,6 +414,12 @@ export async function fetchAdvancedLaboratoryResult(
} else if (workId === "m48t-risk-quality-temporal") {
if (!resultId) throw new AdvancedLaboratoryContractError("M4.8T LAB identity не выбрана.");
results.m48t = await fetchM48TRiskQualityResult(resultId, { fetcher, signal });
} else if (workId === "m49-tgs-fail-closed-evidence") {
if (!resultId) throw new AdvancedLaboratoryContractError("M4.9 TGS LAB identity не выбрана.");
results.m49Tgs = await fetchM49TgsFailClosedResult(resultId, { fetcher, signal });
} else if (workId === "m49-tgs-full-shadow") {
if (!resultId) throw new AdvancedLaboratoryContractError("M4.9 full TGS shadow identity не выбрана.");
results.m49TgsFull = await fetchM49TgsFullShadowResult(resultId, { fetcher, signal });
} else if (workId === "m47-reference-graph-shadow") {
if (!resultId) {
throw new AdvancedLaboratoryContractError("M4.7 LAB identity не выбрана.");
@@ -40,8 +40,12 @@ import type { M48StaticOccupancyQualificationResult } from "./m48StaticOccupancy
import type { M48R3StaticOccupancyShadowResult } from "./m48r3StaticOccupancyShadow";
import type { M48SFixedClassDetectorResult } from "./m48sFixedClassDetector";
import type { M48TRiskQualityResult } from "./m48tRiskQuality";
import type { M49TgsFailClosedResult } from "./m49TgsFailClosed";
import type { M49TgsFullShadowResult } from "./m49TgsFullShadow";
import type { VegetationShadowResult } from "./vegetationShadow";
export interface AdvancedLaboratoryResults {
vegetationShadow: VegetationShadowResult | null;
m47Graph: M47ReferenceGraphLabResult | null;
m48: M48AdvancedResult | null;
m48SmallStatic: M48SmallStaticRegressionResult | null;
@@ -49,6 +53,8 @@ export interface AdvancedLaboratoryResults {
m48r3StaticOccupancy: M48R3StaticOccupancyShadowResult | null;
m48s: M48SFixedClassDetectorResult | null;
m48t: M48TRiskQualityResult | null;
m49Tgs: M49TgsFailClosedResult | null;
m49TgsFull: M49TgsFullShadowResult | null;
m4Threat: M4ThreatReplayResult | null;
l3: L3PointPillarsVisualAuditResult | null;
l31: L31PointPillarsRavnovesResult | null;
@@ -967,9 +967,10 @@ export async function fetchAdvancedLaboratoryResults({
const e39 = settledCatalogValue(settled[7]);
const e40 = settledCatalogValue(settled[8]);
return {
vegetationShadow: null,
m47Graph: null, m48: null, m48SmallStatic: null, m48StaticOccupancy: null,
m48r3StaticOccupancy: null,
m48s: null, m48t: null, m4Threat: null,
m48s: null, m48t: null, m49Tgs: null, m49TgsFull: null, m4Threat: null,
l3: null, l31: null, l32: null, l33: null,
e31,
e32,
@@ -0,0 +1,671 @@
import type { LaboratoryFetch } from "./advancedResults";
const RESULT_ID = /^m49-physical-safety-playback-[a-f0-9]{64}$/;
const SOURCE_RESULT_ID = /^m49-tgs-full-shadow-[a-f0-9]{64}$/;
const SCHEMA = "missioncore.m49-physical-safety-playback/v1";
const CHUNK_MAGIC = "MCPSCH01";
const CHUNK_HEADER_BYTES = 28;
const ENDPOINT_ROOT = "/api/v1/laboratory/m49/physical-safety-playback";
export interface M49PhysicalSafetyPlaybackFrameMetadata {
sourceSequence: number;
sourceFrameIndex: number;
sessionSeconds: number;
sampleAvailable: boolean;
metrics: Readonly<Record<string, number>>;
}
interface ArtifactDescriptor {
url: string;
mediaType: string;
byteLength: number;
sha256: string;
}
interface ChunkDescriptor extends ArtifactDescriptor {
index: number;
start: number;
count: number;
headerBytes: 28;
format: "mcpsch01-states-u8-aligned-z-bounds-f32le";
}
export interface M49PhysicalSafetyPlaybackManifest {
resultId: string;
createdAtUtc: string;
sourceResultId: string;
frameCount: number;
cellCount: number;
cellSizeM: number;
radiusM: number;
sourcePaceHz: number;
stateCodes: Readonly<Record<string, number>>;
chunkFrameCount: 32;
startupPrebufferChunkCount: 2;
residentChunkCountMax: 3;
forwardPrefetchChunkCount: 1;
centers: ArtifactDescriptor;
frames: ArtifactDescriptor;
chunks: readonly ChunkDescriptor[];
}
interface ResidentChunk {
descriptor: ChunkDescriptor;
states: Uint8Array;
zBoundsM: Float32Array;
byteLength: number;
}
export interface M49PhysicalSafetyPlaybackFrame {
metadata: M49PhysicalSafetyPlaybackFrameMetadata;
centersXyM: Float32Array;
states: Uint8Array;
zBoundsM: Float32Array;
cellSizeM: number;
radiusM: number;
}
export interface M49PhysicalSafetyPlaybackProgress {
phase: "manifest" | "static" | "prebuffer" | "ready" | "seek";
residentChunkIndexes: readonly number[];
loadedBytes: number;
}
export class M49PhysicalSafetyPlaybackContractError extends Error {}
function objectValue(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new M49PhysicalSafetyPlaybackContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function text(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new M49PhysicalSafetyPlaybackContractError(`${label}: ожидалась строка.`);
}
return value;
}
function finiteNumber(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new M49PhysicalSafetyPlaybackContractError(`${label}: ожидалось число.`);
}
return value;
}
function integer(value: unknown, label: string): number {
const parsed = finiteNumber(value, label);
if (!Number.isSafeInteger(parsed) || parsed < 0) {
throw new M49PhysicalSafetyPlaybackContractError(`${label}: ожидалось целое число.`);
}
return parsed;
}
function booleanValue(value: unknown, label: string): boolean {
if (typeof value !== "boolean") {
throw new M49PhysicalSafetyPlaybackContractError(`${label}: ожидался boolean.`);
}
return value;
}
function exact<T extends string | number | boolean>(value: unknown, expected: T, label: string): T {
if (value !== expected) {
throw new M49PhysicalSafetyPlaybackContractError(`${label}: нарушен контракт.`);
}
return expected;
}
function sha256(value: unknown, label: string): string {
const parsed = text(value, label);
if (!/^[a-f0-9]{64}$/.test(parsed)) {
throw new M49PhysicalSafetyPlaybackContractError(`${label}: SHA-256 недопустим.`);
}
return parsed;
}
function localUrl(value: unknown, expected: string, label: string): string {
const parsed = text(value, label);
if (parsed !== expected || parsed.includes("worker")) {
throw new M49PhysicalSafetyPlaybackContractError(`${label}: разрешён только sealed local API.`);
}
return parsed;
}
function descriptor(
value: unknown,
expectedUrl: string,
label: string,
): ArtifactDescriptor {
const row = objectValue(value, label);
return {
url: localUrl(row.url, expectedUrl, `${label}.url`),
mediaType: text(row.media_type, `${label}.media_type`),
byteLength: integer(row.byte_length, `${label}.byte_length`),
sha256: sha256(row.sha256, `${label}.sha256`),
};
}
async function sha256Hex(buffer: ArrayBuffer): Promise<string> {
if (!globalThis.crypto?.subtle) {
throw new M49PhysicalSafetyPlaybackContractError(
"Браузер не поддерживает проверку playback SHA-256.",
);
}
const digest = await globalThis.crypto.subtle.digest("SHA-256", buffer);
return [...new Uint8Array(digest)]
.map((value) => value.toString(16).padStart(2, "0"))
.join("");
}
async function fetchVerified(
artifact: ArtifactDescriptor,
fetcher: LaboratoryFetch,
signal?: AbortSignal,
): Promise<ArrayBuffer> {
const response = await fetcher(artifact.url, {
method: "GET",
headers: { Accept: artifact.mediaType },
signal,
});
if (!response.ok) {
throw new M49PhysicalSafetyPlaybackContractError(
`Sealed local playback недоступен: HTTP ${response.status}.`,
);
}
const buffer = await response.arrayBuffer();
if (buffer.byteLength !== artifact.byteLength) {
throw new M49PhysicalSafetyPlaybackContractError(
"Sealed local playback: получен неверный размер.",
);
}
if (await sha256Hex(buffer) !== artifact.sha256) {
throw new M49PhysicalSafetyPlaybackContractError(
"Sealed local playback: SHA-256 не совпал.",
);
}
return buffer;
}
function parseStateCodes(value: unknown): Readonly<Record<string, number>> {
const row = objectValue(value, "M49 physical-safety state codes");
const parsed: Record<string, number> = {};
for (const [name, code] of Object.entries(row)) {
const stateCode = integer(code, `M49 physical-safety state code ${name}`);
if (stateCode > 255) {
throw new M49PhysicalSafetyPlaybackContractError("M49 state code превышает uint8.");
}
parsed[name] = stateCode;
}
if (parsed.UNOBSERVED !== 0 || !Object.keys(parsed).length) {
throw new M49PhysicalSafetyPlaybackContractError("M49 UNOBSERVED state изменился.");
}
return parsed;
}
async function fetchManifest(
resultId: string,
fetcher: LaboratoryFetch,
signal?: AbortSignal,
): Promise<M49PhysicalSafetyPlaybackManifest> {
if (!RESULT_ID.test(resultId)) {
throw new M49PhysicalSafetyPlaybackContractError("M49 physical-safety identity недопустима.");
}
const base = `${ENDPOINT_ROOT}/${encodeURIComponent(resultId)}`;
const response = await fetcher(`${base}/manifest`, {
method: "GET",
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) {
throw new M49PhysicalSafetyPlaybackContractError(
`M49 physical-safety manifest недоступен: HTTP ${response.status}.`,
);
}
const payload = objectValue(await response.json(), "M49 physical-safety manifest");
exact(payload.schema_version, SCHEMA, "M49 physical-safety schema");
exact(payload.result_id, resultId, "M49 physical-safety result");
exact(payload.access, "read-only-sealed-local", "M49 physical-safety access");
const identity = objectValue(payload.identity, "M49 physical-safety identity");
const execution = objectValue(payload.execution, "M49 physical-safety execution");
exact(execution.execution_class, "local-sequential-offline", "M49 execution class");
exact(execution.worker_role, "realtime-only", "M49 worker role");
exact(execution.worker_runtime_dependency, false, "M49 worker dependency");
exact(execution.worker_requests_required, 0, "M49 worker requests");
const authority = objectValue(payload.authority, "M49 physical-safety authority");
exact(authority.commands_enabled, false, "M49 commands");
exact(authority.navigation_or_safety_accepted, false, "M49 safety authority");
exact(authority.actuation_accepted, false, "M49 actuation authority");
const playback = objectValue(payload.playback, "M49 physical-safety playback");
exact(playback.coordinate_frame, "map-gravity-local", "M49 coordinate frame");
const frameCount = integer(playback.frame_count, "M49 frame count");
const cellCount = integer(playback.cell_count, "M49 cell count");
if (!frameCount || !cellCount) {
throw new M49PhysicalSafetyPlaybackContractError("M49 playback не содержит evidence.");
}
const chunkFrameCount = exact(
integer(playback.chunk_frame_count, "M49 chunk frame count"),
32,
"M49 chunk frame count",
);
const startupPrebufferChunkCount = exact(
integer(playback.startup_prebuffer_chunk_count, "M49 startup prebuffer"),
2,
"M49 startup prebuffer",
);
const residentChunkCountMax = exact(
integer(playback.resident_chunk_count_max, "M49 resident chunks"),
3,
"M49 resident chunks",
);
const forwardPrefetchChunkCount = exact(
integer(playback.forward_prefetch_chunk_count, "M49 forward prefetch"),
1,
"M49 forward prefetch",
);
const centers = descriptor(playback.centers, `${base}/tracks/centers`, "M49 centers");
const centerRow = objectValue(playback.centers, "M49 centers");
exact(centerRow.dtype, "<f4", "M49 centers dtype");
if (!Array.isArray(centerRow.shape)
|| centerRow.shape.length !== 2
|| centerRow.shape[0] !== cellCount
|| centerRow.shape[1] !== 2
|| centers.byteLength !== cellCount * 2 * 4) {
throw new M49PhysicalSafetyPlaybackContractError("M49 centers shape изменилась.");
}
const frames = descriptor(playback.frames, `${base}/tracks/frames`, "M49 frames");
const frameRow = objectValue(playback.frames, "M49 frames");
exact(frameRow.dtype, "ndjson", "M49 frames dtype");
if (!Array.isArray(frameRow.shape)
|| frameRow.shape.length !== 1
|| frameRow.shape[0] !== frameCount) {
throw new M49PhysicalSafetyPlaybackContractError("M49 frames shape изменилась.");
}
if (!Array.isArray(playback.chunks)) {
throw new M49PhysicalSafetyPlaybackContractError("M49 chunk catalog отсутствует.");
}
const expectedChunkCount = Math.ceil(frameCount / chunkFrameCount);
if (playback.chunks.length !== expectedChunkCount) {
throw new M49PhysicalSafetyPlaybackContractError("M49 chunk accounting нарушен.");
}
const chunks = playback.chunks.map((value, index): ChunkDescriptor => {
const row = objectValue(value, `M49 chunk ${index}`);
const common = descriptor(value, `${base}/chunks/${index}`, `M49 chunk ${index}`);
const start = index * chunkFrameCount;
const count = Math.min(chunkFrameCount, frameCount - start);
exact(integer(row.index, `M49 chunk ${index}.index`), index, `M49 chunk ${index}.index`);
exact(integer(row.start, `M49 chunk ${index}.start`), start, `M49 chunk ${index}.start`);
exact(integer(row.count, `M49 chunk ${index}.count`), count, `M49 chunk ${index}.count`);
return {
...common,
index,
start,
count,
headerBytes: exact(
integer(row.header_bytes, `M49 chunk ${index}.header`),
CHUNK_HEADER_BYTES,
`M49 chunk ${index}.header`,
),
format: exact(
row.format,
"mcpsch01-states-u8-aligned-z-bounds-f32le",
`M49 chunk ${index}.format`,
),
};
});
return {
resultId,
createdAtUtc: text(payload.created_at_utc, "M49 physical-safety created"),
sourceResultId: text(identity.source_result_id, "M49 physical-safety source"),
frameCount,
cellCount,
cellSizeM: finiteNumber(playback.cell_size_m, "M49 cell size"),
radiusM: finiteNumber(playback.radius_m, "M49 radius"),
sourcePaceHz: finiteNumber(playback.source_pace_hz, "M49 source pace"),
stateCodes: parseStateCodes(playback.state_codes),
chunkFrameCount,
startupPrebufferChunkCount,
residentChunkCountMax,
forwardPrefetchChunkCount,
centers,
frames,
chunks,
};
}
export async function fetchM49PhysicalSafetyPlaybackIdForSource(
sourceResultId: string,
{
fetcher = fetch,
signal,
}: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<string | null> {
if (!SOURCE_RESULT_ID.test(sourceResultId)) {
throw new M49PhysicalSafetyPlaybackContractError("M49 source identity недопустима.");
}
const response = await fetcher(
`${ENDPOINT_ROOT}/results?limit=2&source_result_id=${encodeURIComponent(sourceResultId)}`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) {
throw new M49PhysicalSafetyPlaybackContractError(
`M49 physical-safety catalog недоступен: HTTP ${response.status}.`,
);
}
const payload = objectValue(await response.json(), "M49 physical-safety catalog");
exact(
payload.schema_version,
"missioncore.m49-physical-safety-playback-catalog/v1",
"M49 physical-safety catalog schema",
);
exact(payload.worker_runtime_dependency, false, "M49 physical-safety catalog worker");
exact(payload.access, "read-only-sealed-local", "M49 physical-safety catalog access");
if (!Array.isArray(payload.items)) {
throw new M49PhysicalSafetyPlaybackContractError("M49 physical-safety catalog изменился.");
}
if (!payload.items.length) return null;
if (payload.items.length !== 1) {
throw new M49PhysicalSafetyPlaybackContractError(
"M49 physical-safety source имеет несколько активных playback artifacts.",
);
}
const item = objectValue(payload.items[0], "M49 physical-safety catalog item");
exact(item.source_result_id, sourceResultId, "M49 physical-safety catalog source");
exact(item.worker_runtime_dependency, false, "M49 physical-safety item worker");
exact(item.navigation_or_safety_accepted, false, "M49 physical-safety item authority");
const resultId = text(item.result_id, "M49 physical-safety catalog result");
if (!RESULT_ID.test(resultId)) {
throw new M49PhysicalSafetyPlaybackContractError(
"M49 physical-safety catalog result недопустим.",
);
}
return resultId;
}
function parseFrames(
buffer: ArrayBuffer,
frameCount: number,
): readonly M49PhysicalSafetyPlaybackFrameMetadata[] {
const lines = new TextDecoder().decode(buffer).trim().split("\n");
if (lines.length !== frameCount) {
throw new M49PhysicalSafetyPlaybackContractError("M49 frame catalog изменил размер.");
}
return lines.map((line, sourceSequence) => {
const row = objectValue(JSON.parse(line), `M49 frame ${sourceSequence}`);
const metrics: Record<string, number> = {};
for (const [name, value] of Object.entries(row)) {
if (name.endsWith("_count") && typeof value === "number" && Number.isFinite(value)) {
metrics[name] = value;
}
}
return {
sourceSequence,
sourceFrameIndex: integer(row.source_frame_index, `M49 frame ${sourceSequence}.source`),
sessionSeconds: finiteNumber(row.session_seconds, `M49 frame ${sourceSequence}.time`),
sampleAvailable: booleanValue(
row.sample_available,
`M49 frame ${sourceSequence}.available`,
),
metrics,
};
});
}
function parseChunk(
buffer: ArrayBuffer,
descriptorValue: ChunkDescriptor,
cellCount: number,
frames: readonly M49PhysicalSafetyPlaybackFrameMetadata[],
admittedStateCodes: ReadonlySet<number>,
): ResidentChunk {
const bytes = new Uint8Array(buffer);
if (bytes.byteLength < CHUNK_HEADER_BYTES
|| new TextDecoder("latin1").decode(bytes.subarray(0, 8)) !== CHUNK_MAGIC) {
throw new M49PhysicalSafetyPlaybackContractError("M49 chunk signature изменилась.");
}
const view = new DataView(buffer);
const start = view.getUint32(8, true);
const count = view.getUint32(12, true);
const cells = view.getUint32(16, true);
const stateBytes = view.getUint32(20, true);
const zBytes = view.getUint32(24, true);
const expectedStateBytes = count * cellCount;
const expectedZBytes = count * cellCount * 2 * 4;
const zOffset = CHUNK_HEADER_BYTES + stateBytes
+ ((4 - ((CHUNK_HEADER_BYTES + stateBytes) % 4)) % 4);
if (start !== descriptorValue.start
|| count !== descriptorValue.count
|| cells !== cellCount
|| stateBytes !== expectedStateBytes
|| zBytes !== expectedZBytes
|| zOffset + zBytes !== bytes.byteLength) {
throw new M49PhysicalSafetyPlaybackContractError("M49 chunk layout изменился.");
}
const states = new Uint8Array(buffer, CHUNK_HEADER_BYTES, stateBytes);
const zBoundsM = new Float32Array(buffer, zOffset, count * cellCount * 2);
for (let localFrame = 0; localFrame < count; localFrame += 1) {
const stateStart = localFrame * cellCount;
const zStart = localFrame * cellCount * 2;
for (let cell = 0; cell < cellCount; cell += 1) {
const state = states[stateStart + cell];
if (!admittedStateCodes.has(state)) {
throw new M49PhysicalSafetyPlaybackContractError("M49 chunk содержит неизвестный state.");
}
if (!frames[start + localFrame].sampleAvailable
&& (state !== 0
|| Number.isFinite(zBoundsM[zStart + cell * 2])
|| Number.isFinite(zBoundsM[zStart + cell * 2 + 1]))) {
throw new M49PhysicalSafetyPlaybackContractError(
"M49 missing sample перестал быть fail-closed.",
);
}
}
}
return { descriptor: descriptorValue, states, zBoundsM, byteLength: buffer.byteLength };
}
export class M49PhysicalSafetyPlaybackBuffer {
readonly manifest: M49PhysicalSafetyPlaybackManifest;
readonly centersXyM: Float32Array;
readonly frames: readonly M49PhysicalSafetyPlaybackFrameMetadata[];
private readonly fetcher: LaboratoryFetch;
private readonly signal: AbortSignal | undefined;
private readonly onProgress: ((value: M49PhysicalSafetyPlaybackProgress) => void) | undefined;
private readonly chunks = new Map<number, ResidentChunk>();
private readonly pending = new Map<number, Promise<ResidentChunk>>();
private readonly admittedStateCodes: ReadonlySet<number>;
private staticBytes: number;
private preparedWindowKey: string | null = null;
private pendingPrepare: { key: string; promise: Promise<void> } | null = null;
private constructor(
manifest: M49PhysicalSafetyPlaybackManifest,
centersXyM: Float32Array,
frames: readonly M49PhysicalSafetyPlaybackFrameMetadata[],
fetcher: LaboratoryFetch,
signal: AbortSignal | undefined,
onProgress: ((value: M49PhysicalSafetyPlaybackProgress) => void) | undefined,
) {
this.manifest = manifest;
this.centersXyM = centersXyM;
this.frames = frames;
this.fetcher = fetcher;
this.signal = signal;
this.onProgress = onProgress;
this.admittedStateCodes = new Set(Object.values(manifest.stateCodes));
this.staticBytes = centersXyM.byteLength + manifest.frames.byteLength;
}
static async open(
resultId: string,
{
fetcher = fetch,
signal,
onProgress,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
onProgress?: (value: M49PhysicalSafetyPlaybackProgress) => void;
} = {},
): Promise<M49PhysicalSafetyPlaybackBuffer> {
onProgress?.({ phase: "manifest", residentChunkIndexes: [], loadedBytes: 0 });
const manifest = await fetchManifest(resultId, fetcher, signal);
const [centersBuffer, framesBuffer] = await Promise.all([
fetchVerified(manifest.centers, fetcher, signal),
fetchVerified(manifest.frames, fetcher, signal),
]);
onProgress?.({
phase: "static",
residentChunkIndexes: [],
loadedBytes: centersBuffer.byteLength + framesBuffer.byteLength,
});
const centersXyM = new Float32Array(centersBuffer);
const frames = parseFrames(framesBuffer, manifest.frameCount);
const playback = new M49PhysicalSafetyPlaybackBuffer(
manifest,
centersXyM,
frames,
fetcher,
signal,
onProgress,
);
const startupCount = Math.min(
manifest.startupPrebufferChunkCount,
manifest.chunks.length,
);
await Promise.all(Array.from(
{ length: startupCount },
(_, index) => playback.loadChunk(index, "prebuffer"),
));
playback.emit("ready");
return playback;
}
get residentChunkIndexes(): readonly number[] {
return [...this.chunks.keys()].sort((left, right) => left - right);
}
get residentByteLength(): number {
return this.staticBytes
+ [...this.chunks.values()].reduce((sum, chunk) => sum + chunk.byteLength, 0);
}
async prepare(sourceSequence: number): Promise<void> {
const chunkIndex = this.chunkIndex(sourceSequence);
const nextIndex = chunkIndex + this.manifest.forwardPrefetchChunkCount;
const indexes = [chunkIndex, nextIndex].filter(
(index) => index < this.manifest.chunks.length,
);
const key = indexes.join(":");
if (this.preparedWindowKey === key && indexes.every((index) => this.chunks.has(index))) {
return;
}
if (this.pendingPrepare?.key === key) return this.pendingPrepare.promise;
const promise = Promise.all(indexes.map((index) => this.loadChunk(index, "seek")))
.then(() => {
this.trim(new Set(indexes));
this.preparedWindowKey = key;
this.emit("seek");
})
.finally(() => {
if (this.pendingPrepare?.key === key) this.pendingPrepare = null;
});
this.pendingPrepare = { key, promise };
return promise;
}
async frame(sourceSequence: number): Promise<M49PhysicalSafetyPlaybackFrame> {
const chunkIndex = this.chunkIndex(sourceSequence);
const chunk = await this.loadChunk(chunkIndex, "seek");
const frame = this.frameFromChunk(sourceSequence, chunk);
const nextIndex = chunkIndex + this.manifest.forwardPrefetchChunkCount;
if (nextIndex < this.manifest.chunks.length && !this.chunks.has(nextIndex)) {
void this.loadChunk(nextIndex, "seek").catch(() => undefined);
}
return frame;
}
frameIfResident(sourceSequence: number): M49PhysicalSafetyPlaybackFrame | null {
const chunkIndex = this.chunkIndex(sourceSequence);
const chunk = this.chunks.get(chunkIndex);
return chunk ? this.frameFromChunk(sourceSequence, chunk) : null;
}
private frameFromChunk(
sourceSequence: number,
chunk: ResidentChunk,
): M49PhysicalSafetyPlaybackFrame {
const localFrame = sourceSequence - chunk.descriptor.start;
const stateOffset = localFrame * this.manifest.cellCount;
const zOffset = stateOffset * 2;
return {
metadata: this.frames[sourceSequence],
centersXyM: this.centersXyM,
states: chunk.states.subarray(stateOffset, stateOffset + this.manifest.cellCount),
zBoundsM: chunk.zBoundsM.subarray(zOffset, zOffset + this.manifest.cellCount * 2),
cellSizeM: this.manifest.cellSizeM,
radiusM: this.manifest.radiusM,
};
}
private chunkIndex(sourceSequence: number): number {
if (!Number.isSafeInteger(sourceSequence)
|| sourceSequence < 0
|| sourceSequence >= this.manifest.frameCount) {
throw new M49PhysicalSafetyPlaybackContractError("M49 source sequence недопустима.");
}
return Math.floor(sourceSequence / this.manifest.chunkFrameCount);
}
private async loadChunk(
index: number,
phase: "prebuffer" | "seek",
): Promise<ResidentChunk> {
const existing = this.chunks.get(index);
if (existing) {
this.chunks.delete(index);
this.chunks.set(index, existing);
return existing;
}
const inflight = this.pending.get(index);
if (inflight) return inflight;
const descriptorValue = this.manifest.chunks[index];
if (!descriptorValue) {
throw new M49PhysicalSafetyPlaybackContractError("M49 chunk отсутствует.");
}
const promise = fetchVerified(descriptorValue, this.fetcher, this.signal)
.then((buffer) => parseChunk(
buffer,
descriptorValue,
this.manifest.cellCount,
this.frames,
this.admittedStateCodes,
))
.then((chunk) => {
this.chunks.set(index, chunk);
this.trim(new Set([index]));
this.emit(phase);
return chunk;
})
.finally(() => this.pending.delete(index));
this.pending.set(index, promise);
return promise;
}
private trim(protectedIndexes: ReadonlySet<number>): void {
while (this.chunks.size > this.manifest.residentChunkCountMax) {
const removable = [...this.chunks.keys()].find((index) => !protectedIndexes.has(index));
if (removable === undefined) break;
this.chunks.delete(removable);
}
}
private emit(phase: M49PhysicalSafetyPlaybackProgress["phase"]): void {
this.onProgress?.({
phase,
residentChunkIndexes: this.residentChunkIndexes,
loadedBytes: this.residentByteLength,
});
}
}
@@ -0,0 +1,349 @@
import type { LaboratoryFetch } from "./advancedResults";
const RESULT_ID = /^m49-tgs-fail-closed-[a-f0-9]{64}$/;
export type M49TgsProfile = "current_increment" | "causal_rolling_1s";
export type M49TgsStateCode = 0 | 1 | 2 | 3;
export type M49TgsPointStateCode = 1 | 2 | 3;
export interface M49TgsAnchorSummary {
anchorFrameIndex: number;
slot: number;
pointCount: number;
groundPointCount: number;
nongroundPointCount: number;
rejectedPointCount: number;
groundCellCount: number;
nongroundCellCount: number;
rejectedCellCount: number;
unobservedCellCount: number;
allPointsAccounted: true;
}
export interface M49TgsFailClosedResult {
resultId: string;
createdAtUtc: string;
source: {
sourceId: "RAVNOVES00";
sourceSessionId: "20260720T065719Z_viewer_live";
sourcePackSha256: string;
linkedVisualResultId: string;
anchorFrameIndices: readonly number[];
};
configuration: {
profileId: string;
configSha256: string;
coordinateFrame: "map-gravity-local";
primaryProfile: "causal_rolling_1s";
cellSizeM: number;
radiusM: number;
};
execution: {
worker: "Worker 006";
device: "cpu";
gpuUsed: false;
wrapperElapsedSeconds: number;
};
metrics: {
anchorCount: number;
anchorProfileCount: number;
allEligiblePointsAccounted: true;
costmapCellCount: number;
processWallCurrentP50Ms: number;
processWallCurrentMaxMs: number;
processWallRollingP50Ms: number;
processWallRollingMaxMs: number;
processMaxRssKib: number;
primary: readonly M49TgsAnchorSummary[];
};
acceptance: {
representationComplete: true;
allPointsAccounted: true;
aosAbsent: true;
gpuAbsent: true;
visualQualityAccepted: false;
traversabilityAccepted: false;
};
decision: {
state: "visual-review-required";
candidateRetained: true;
nextAction: string;
};
limitations: readonly string[];
}
export interface M49TgsAnchorSpatial {
resultId: string;
anchorFrameIndex: number;
sourceSequence: number;
profile: M49TgsProfile;
coordinateFrame: "map-gravity-local";
pointsXyzM: readonly (readonly [number, number, number])[];
pointStates: readonly M49TgsPointStateCode[];
costmap: {
cellSizeM: number;
radiusM: number;
centersXyM: readonly (readonly [number, number])[];
states: readonly M49TgsStateCode[];
zBoundsM: readonly (readonly [number | null, number | null])[];
};
allPointsAccounted: true;
aosUsed: false;
}
export class M49TgsContractError extends Error {}
function objectValue(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new M49TgsContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function text(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new M49TgsContractError(`${label}: ожидалась строка.`);
}
return value;
}
function exact<T extends string | boolean>(value: unknown, expected: T, label: string): T {
if (value !== expected) throw new M49TgsContractError(`${label}: нарушен контракт.`);
return expected;
}
function numberValue(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new M49TgsContractError(`${label}: ожидалось число.`);
}
return value;
}
function integer(value: unknown, label: string): number {
const parsed = numberValue(value, label);
if (!Number.isSafeInteger(parsed) || parsed < 0) {
throw new M49TgsContractError(`${label}: ожидалось целое число.`);
}
return parsed;
}
function sha256(value: unknown, label: string): string {
const parsed = text(value, label);
if (!/^[a-f0-9]{64}$/.test(parsed)) {
throw new M49TgsContractError(`${label}: неверный SHA-256.`);
}
return parsed;
}
function resultId(value: unknown): string {
const parsed = text(value, "M49 result id");
if (!RESULT_ID.test(parsed)) throw new M49TgsContractError("M49 identity недопустима.");
return parsed;
}
function numbers(value: unknown, size: number, label: string): number[] {
if (!Array.isArray(value) || value.length !== size) {
throw new M49TgsContractError(`${label}: неверная размерность.`);
}
return value.map((item) => numberValue(item, label));
}
function anchor(value: unknown, label: string): M49TgsAnchorSummary {
const row = objectValue(value, label);
exact(row.profile_id, "causal_rolling_1s", `${label}.profile`);
exact(row.all_points_accounted, true, `${label}.accounting`);
const parsed: M49TgsAnchorSummary = {
anchorFrameIndex: integer(row.anchor_frame_index, `${label}.anchor`),
slot: integer(row.slot, `${label}.slot`),
pointCount: integer(row.point_count, `${label}.points`),
groundPointCount: integer(row.ground_point_count, `${label}.ground points`),
nongroundPointCount: integer(row.nonground_point_count, `${label}.nonground points`),
rejectedPointCount: integer(row.rejected_point_count, `${label}.rejected points`),
groundCellCount: integer(row.ground_cell_count, `${label}.ground cells`),
nongroundCellCount: integer(row.nonground_cell_count, `${label}.nonground cells`),
rejectedCellCount: integer(row.rejected_cell_count, `${label}.rejected cells`),
unobservedCellCount: integer(row.unobserved_cell_count, `${label}.unobserved cells`),
allPointsAccounted: true,
};
if (
parsed.groundPointCount + parsed.nongroundPointCount + parsed.rejectedPointCount
!== parsed.pointCount
|| parsed.groundCellCount + parsed.nongroundCellCount
+ parsed.rejectedCellCount + parsed.unobservedCellCount !== 2244
) {
throw new M49TgsContractError(`${label}: fail-closed accounting нарушен.`);
}
return parsed;
}
export async function fetchM49TgsFailClosedResult(
id: string,
{ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<M49TgsFailClosedResult> {
if (!RESULT_ID.test(id)) throw new M49TgsContractError("M49 identity недопустима.");
const response = await fetcher(
`/api/v1/laboratory/m49/tgs-fail-closed/${encodeURIComponent(id)}`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) throw new M49TgsContractError(`M49 TGS недоступен: HTTP ${response.status}.`);
const payload = objectValue(await response.json(), "M49 TGS");
exact(payload.schema_version, "missioncore.m49-tgs-fail-closed-view/v1", "M49 schema");
exact(payload.result_id, id, "M49 result");
exact(payload.ground_truth, false, "M49 ground truth");
exact(payload.access, "read-only", "M49 access");
const source = objectValue(payload.source, "M49 source");
const configuration = objectValue(payload.configuration, "M49 configuration");
const execution = objectValue(payload.execution, "M49 execution");
const metrics = objectValue(payload.metrics, "M49 metrics");
const acceptance = objectValue(payload.acceptance, "M49 acceptance");
const decision = objectValue(payload.decision, "M49 decision");
if (!Array.isArray(source.anchor_frame_indices) || !Array.isArray(metrics.primary)) {
throw new M49TgsContractError("M49 anchors: ожидался массив.");
}
if (!Array.isArray(payload.limitations)) {
throw new M49TgsContractError("M49 limitations: ожидался массив.");
}
const anchorFrameIndices = source.anchor_frame_indices.map(
(item, index) => integer(item, `M49 anchor ${index}`),
);
const primary = metrics.primary.map((item, index) => anchor(item, `M49 primary ${index}`));
if (
anchorFrameIndices.length !== 10
|| new Set(anchorFrameIndices).size !== 10
|| primary.length !== 10
|| integer(metrics.anchor_count, "M49 anchor count") !== 10
|| integer(metrics.anchor_profile_count, "M49 profile count") !== 20
|| integer(metrics.costmap_cell_count, "M49 costmap cells") !== 2244
|| primary.some((item, index) => item.anchorFrameIndex !== anchorFrameIndices[index])
) {
throw new M49TgsContractError("M49 anchor set или costmap contract нарушен.");
}
return {
resultId: resultId(payload.result_id),
createdAtUtc: text(payload.created_at_utc, "M49 created"),
source: {
sourceId: exact(source.source_id, "RAVNOVES00", "M49 source id"),
sourceSessionId: exact(
source.source_session_id,
"20260720T065719Z_viewer_live",
"M49 source session",
),
sourcePackSha256: sha256(source.source_pack_sha256, "M49 source pack"),
linkedVisualResultId: text(source.linked_visual_result_id, "M49 visual result"),
anchorFrameIndices,
},
configuration: {
profileId: text(configuration.profile_id, "M49 profile"),
configSha256: sha256(configuration.config_sha256, "M49 config"),
coordinateFrame: exact(configuration.coordinate_frame, "map-gravity-local", "M49 frame"),
primaryProfile: exact(configuration.primary_profile, "causal_rolling_1s", "M49 primary"),
cellSizeM: numberValue(configuration.cell_size_m, "M49 cell size"),
radiusM: numberValue(configuration.radius_m, "M49 radius"),
},
execution: {
worker: exact(execution.worker, "Worker 006", "M49 worker"),
device: exact(execution.device, "cpu", "M49 device"),
gpuUsed: exact(execution.gpu_used, false, "M49 GPU"),
wrapperElapsedSeconds: numberValue(execution.wrapper_elapsed_seconds, "M49 wall"),
},
metrics: {
anchorCount: 10,
anchorProfileCount: 20,
allEligiblePointsAccounted: exact(metrics.all_eligible_points_accounted, true, "M49 accounting"),
costmapCellCount: 2244,
processWallCurrentP50Ms: numberValue(metrics.process_wall_current_p50_ms, "M49 current p50"),
processWallCurrentMaxMs: numberValue(metrics.process_wall_current_max_ms, "M49 current max"),
processWallRollingP50Ms: numberValue(metrics.process_wall_rolling_p50_ms, "M49 rolling p50"),
processWallRollingMaxMs: numberValue(metrics.process_wall_rolling_max_ms, "M49 rolling max"),
processMaxRssKib: integer(metrics.process_max_rss_kib, "M49 RSS"),
primary,
},
acceptance: {
representationComplete: exact(acceptance.representation_complete, true, "M49 representation"),
allPointsAccounted: exact(acceptance.all_points_accounted, true, "M49 points"),
aosAbsent: exact(acceptance.aos_absent, true, "M49 AOS"),
gpuAbsent: exact(acceptance.gpu_absent, true, "M49 GPU absent"),
visualQualityAccepted: exact(acceptance.visual_quality_accepted, false, "M49 visual quality"),
traversabilityAccepted: exact(acceptance.traversability_accepted, false, "M49 traversability"),
},
decision: {
state: exact(decision.state, "visual-review-required", "M49 decision"),
candidateRetained: exact(decision.candidate_retained, true, "M49 retained"),
nextAction: text(decision.next_action, "M49 next action"),
},
limitations: payload.limitations.map((item, index) => text(item, `M49 limitation ${index}`)),
};
}
export async function fetchM49TgsAnchorSpatial(
id: string,
anchorFrameIndex: number,
profile: M49TgsProfile = "causal_rolling_1s",
{ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<M49TgsAnchorSpatial> {
if (!RESULT_ID.test(id) || !Number.isSafeInteger(anchorFrameIndex) || anchorFrameIndex < 0) {
throw new M49TgsContractError("M49 anchor identity недопустима.");
}
const response = await fetcher(
`/api/v1/laboratory/m49/tgs-fail-closed/${encodeURIComponent(id)}/anchors/${anchorFrameIndex}/spatial?profile=${encodeURIComponent(profile)}`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) throw new M49TgsContractError(`M49 anchor недоступен: HTTP ${response.status}.`);
const payload = objectValue(await response.json(), "M49 anchor");
exact(payload.schema_version, "missioncore.m49-tgs-anchor-spatial/v1", "M49 anchor schema");
exact(payload.result_id, id, "M49 anchor result");
exact(payload.coordinate_frame, "map-gravity-local", "M49 anchor frame");
exact(payload.all_points_accounted, true, "M49 anchor accounting");
exact(payload.aos_used, false, "M49 anchor AOS");
if (!Array.isArray(payload.points_xyz_m) || !Array.isArray(payload.point_states)) {
throw new M49TgsContractError("M49 points: ожидался массив.");
}
const costmap = objectValue(payload.costmap, "M49 costmap");
if (!Array.isArray(costmap.centers_xy_m) || !Array.isArray(costmap.states) || !Array.isArray(costmap.z_bounds_m)) {
throw new M49TgsContractError("M49 costmap arrays: нарушен контракт.");
}
const points = payload.points_xyz_m.map((item, index) => numbers(item, 3, `M49 point ${index}`) as [number, number, number]);
const pointStates = payload.point_states.map((item, index) => {
const state = integer(item, `M49 point state ${index}`);
if (state !== 1 && state !== 2 && state !== 3) throw new M49TgsContractError("M49 point state неизвестен.");
return state;
});
if (points.length !== pointStates.length) throw new M49TgsContractError("M49 point accounting нарушен.");
const centers = costmap.centers_xy_m.map((item, index) => numbers(item, 2, `M49 cell ${index}`) as [number, number]);
const states = costmap.states.map((item, index) => {
const state = integer(item, `M49 cell state ${index}`);
if (state !== 0 && state !== 1 && state !== 2 && state !== 3) throw new M49TgsContractError("M49 cell state неизвестен.");
return state;
});
const zBounds = costmap.z_bounds_m.map((item, index) => {
if (!Array.isArray(item) || item.length !== 2) throw new M49TgsContractError(`M49 z ${index}: размерность.`);
return item.map((value) => value === null ? null : numberValue(value, `M49 z ${index}`)) as [number | null, number | null];
});
if (
centers.length !== 2244
|| centers.length !== states.length
|| centers.length !== zBounds.length
|| integer(payload.anchor_frame_index, "M49 anchor frame") !== anchorFrameIndex
|| integer(payload.source_sequence, "M49 source sequence") !== anchorFrameIndex
) {
throw new M49TgsContractError("M49 costmap accounting нарушен.");
}
return {
resultId: id,
anchorFrameIndex,
sourceSequence: anchorFrameIndex,
profile: exact(payload.profile, profile, "M49 profile"),
coordinateFrame: "map-gravity-local",
pointsXyzM: points,
pointStates,
costmap: {
cellSizeM: numberValue(costmap.cell_size_m, "M49 cell size"),
radiusM: numberValue(costmap.radius_m, "M49 radius"),
centersXyM: centers,
states,
zBoundsM: zBounds,
},
allPointsAccounted: true,
aosUsed: false,
};
}
@@ -0,0 +1,658 @@
import type { LaboratoryFetch } from "./advancedResults";
const RESULT_ID = /^m49-tgs-full-shadow-[a-f0-9]{64}$/;
const SEMANTIC_RESULT_ID = /^e47-semantic-slam-[a-f0-9]{64}$/;
export type M49TgsFullShadowStateCode = 0 | 1 | 2 | 3;
export interface M49TgsFullShadowResult {
resultId: string;
createdAtUtc: string;
source: {
sourcePackSha256: string;
linkedVisualResultId: string;
linkedSemanticResultId: string;
};
configuration: {
configSha256: string;
cellSizeM: number;
radiusM: number;
historySeconds: number;
};
execution: {
wrapperElapsedSeconds: number;
};
timeline: {
frameCount: 4489;
availableLidarFrameCount: 3928;
missingLidarFrameCount: 561;
durationSeconds: number;
effectiveFps: number;
};
pointAccounting: {
eligible: number;
ground: number;
nonground: number;
rejected: number;
unaccounted: 0;
};
performance: {
candidateTgsMs: { p50: number; p95: number; p99: number; max: number };
completionAgeMs: { p50: number; p95: number; p99: number; max: number };
capacityDropCount: number;
};
acceptance: {
representationComplete: true;
visualQualityAccepted: false;
integratedGraphPerformanceAccepted: false;
allFramesAccounted: boolean;
allEligiblePointsAccounted: boolean;
};
decision: {
state: string;
candidateRetained: boolean;
nextAction: string;
};
}
export interface M49TgsFullShadowSpatial {
resultId: string;
sourceSequence: number;
sourceFrameIndex: number;
sessionSeconds: number;
sampleAvailable: boolean;
costmap: {
cellSizeM: number;
radiusM: number;
centersXyM: readonly (readonly [number, number])[];
states: readonly M49TgsFullShadowStateCode[];
zBoundsM: readonly (readonly [number | null, number | null])[];
};
metrics: {
eligiblePointCount: number;
groundPointCount: number;
nongroundPointCount: number;
rejectedPointCount: number;
occupiedCellCount: number;
};
}
export interface M49TgsFullShadowSpatialChunk {
resultId: string;
start: number;
count: number;
frames: readonly M49TgsFullShadowSpatial[];
}
export type M49TgsFullShadowPlaybackTrackId =
| "frames"
| "centers"
| "states"
| "z-bounds";
export interface M49TgsFullShadowPlaybackProgress {
phase: "manifest" | "download" | "verify" | "ready";
trackId: M49TgsFullShadowPlaybackTrackId | null;
loadedBytes: number;
totalBytes: number;
}
interface M49TgsFullShadowPlaybackTrack {
id: M49TgsFullShadowPlaybackTrackId;
url: string;
mediaType: string;
dtype: "ndjson" | "<f4" | "|u1";
shape: readonly number[];
byteLength: number;
sha256: string;
}
export interface M49TgsFullShadowPlaybackFrame {
sourceSequence: number;
sourceFrameIndex: number;
sessionSeconds: number;
sampleAvailable: boolean;
metrics: M49TgsFullShadowSpatial["metrics"];
}
export interface M49TgsFullShadowPlaybackPack {
resultId: string;
frameCount: 4489;
cellCount: 2244;
totalByteLength: number;
centersXyM: Float32Array;
states: Uint8Array;
zBoundsM: Float32Array;
frames: readonly M49TgsFullShadowPlaybackFrame[];
}
export class M49TgsFullShadowContractError extends Error {}
function objectValue(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new M49TgsFullShadowContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function text(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new M49TgsFullShadowContractError(`${label}: ожидалась строка.`);
}
return value;
}
function numberValue(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new M49TgsFullShadowContractError(`${label}: ожидалось число.`);
}
return value;
}
function integer(value: unknown, label: string): number {
const parsed = numberValue(value, label);
if (!Number.isSafeInteger(parsed) || parsed < 0) {
throw new M49TgsFullShadowContractError(`${label}: ожидалось целое число.`);
}
return parsed;
}
function booleanValue(value: unknown, label: string): boolean {
if (typeof value !== "boolean") {
throw new M49TgsFullShadowContractError(`${label}: ожидался boolean.`);
}
return value;
}
function exact<T extends string | boolean | number>(value: unknown, expected: T, label: string): T {
if (value !== expected) throw new M49TgsFullShadowContractError(`${label}: нарушен контракт.`);
return expected;
}
function timing(value: unknown, label: string) {
const row = objectValue(value, label);
return {
p50: numberValue(row.p50, `${label}.p50`),
p95: numberValue(row.p95, `${label}.p95`),
p99: numberValue(row.p99, `${label}.p99`),
max: numberValue(row.max, `${label}.max`),
};
}
export async function fetchM49TgsFullShadowResult(
id: string,
{ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<M49TgsFullShadowResult> {
if (!RESULT_ID.test(id)) throw new M49TgsFullShadowContractError("M49 full-shadow identity недопустима.");
const response = await fetcher(
`/api/v1/laboratory/m49/tgs-full-shadow/${encodeURIComponent(id)}`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) throw new M49TgsFullShadowContractError(`M49 full shadow недоступен: HTTP ${response.status}.`);
const payload = objectValue(await response.json(), "M49 full shadow");
exact(payload.schema_version, "missioncore.m49-tgs-full-shadow-view/v1", "M49 full schema");
exact(payload.result_id, id, "M49 full result");
exact(payload.ground_truth, false, "M49 full ground truth");
exact(payload.access, "read-only", "M49 full access");
const source = objectValue(payload.source, "M49 full source");
const configuration = objectValue(payload.configuration, "M49 full configuration");
const execution = objectValue(payload.execution, "M49 full execution");
const timeline = objectValue(payload.timeline, "M49 full timeline");
const accounting = objectValue(payload.point_accounting, "M49 full accounting");
const performance = objectValue(payload.performance, "M49 full performance");
const acceptance = objectValue(payload.acceptance, "M49 full acceptance");
const decision = objectValue(payload.decision, "M49 full decision");
const linkedSemanticResultId = text(
source.linked_semantic_result_id,
"M49 full semantic result",
);
if (!SEMANTIC_RESULT_ID.test(linkedSemanticResultId)) {
throw new M49TgsFullShadowContractError("M49 full semantic result: нарушена идентичность.");
}
return {
resultId: text(payload.result_id, "M49 full result"),
createdAtUtc: text(payload.created_at_utc, "M49 full created"),
source: {
sourcePackSha256: text(source.source_pack_sha256, "M49 full source pack"),
linkedVisualResultId: text(source.linked_visual_result_id, "M49 full visual result"),
linkedSemanticResultId,
},
configuration: {
configSha256: text(configuration.config_sha256, "M49 full config"),
cellSizeM: numberValue(configuration.cell_size_m, "M49 full cell size"),
radiusM: numberValue(configuration.radius_m, "M49 full radius"),
historySeconds: numberValue(configuration.history_seconds, "M49 full history"),
},
execution: { wrapperElapsedSeconds: numberValue(execution.wrapper_elapsed_seconds, "M49 full wall") },
timeline: {
frameCount: exact(integer(timeline.frame_count, "M49 full frames"), 4489, "M49 full frames"),
availableLidarFrameCount: exact(integer(timeline.available_lidar_frame_count, "M49 full available"), 3928, "M49 full available"),
missingLidarFrameCount: exact(integer(timeline.missing_lidar_frame_count, "M49 full missing"), 561, "M49 full missing"),
durationSeconds: numberValue(timeline.duration_seconds, "M49 full duration"),
effectiveFps: numberValue(timeline.effective_fps, "M49 full fps"),
},
pointAccounting: {
eligible: integer(accounting.eligible, "M49 full eligible"),
ground: integer(accounting.ground, "M49 full ground"),
nonground: integer(accounting.nonground, "M49 full nonground"),
rejected: integer(accounting.rejected, "M49 full rejected"),
unaccounted: exact(integer(accounting.unaccounted, "M49 full unaccounted"), 0, "M49 full unaccounted"),
},
performance: {
candidateTgsMs: timing(performance.candidate_tgs_ms, "M49 full TGS"),
completionAgeMs: timing(performance.completion_age_ms, "M49 full completion"),
capacityDropCount: integer(performance.capacity_drop_count, "M49 full drops"),
},
acceptance: {
representationComplete: exact(acceptance.representation_complete, true, "M49 full representation"),
visualQualityAccepted: exact(acceptance.visual_quality_accepted, false, "M49 full visual"),
integratedGraphPerformanceAccepted: exact(acceptance.integrated_graph_performance_accepted, false, "M49 full integrated"),
allFramesAccounted: booleanValue(acceptance.all_frames_accounted, "M49 full frame accounting"),
allEligiblePointsAccounted: booleanValue(acceptance.all_eligible_points_accounted, "M49 full point accounting"),
},
decision: {
state: text(decision.state, "M49 full decision"),
candidateRetained: booleanValue(decision.candidate_retained, "M49 full retained"),
nextAction: text(decision.next_action, "M49 full next action"),
},
};
}
function pair(value: unknown, label: string): readonly [number, number] {
if (!Array.isArray(value) || value.length !== 2) {
throw new M49TgsFullShadowContractError(`${label}: неверная размерность.`);
}
return [numberValue(value[0], label), numberValue(value[1], label)];
}
function parseSpatial(
id: string,
sourceSequence: number,
payload: Record<string, unknown>,
sharedCostmap?: Record<string, unknown>,
): M49TgsFullShadowSpatial {
exact(payload.source_sequence, sourceSequence, "M49 full spatial sequence");
const costmap = sharedCostmap ?? objectValue(payload.costmap, "M49 full costmap");
const metrics = objectValue(payload.metrics, "M49 full frame metrics");
const centers = costmap.centers_xy_m;
const statesRaw = sharedCostmap ? payload.states : costmap.states;
const zBoundsRaw = sharedCostmap ? payload.z_bounds_m : costmap.z_bounds_m;
if (!Array.isArray(centers) || !Array.isArray(statesRaw) || !Array.isArray(zBoundsRaw)
|| centers.length !== 2244 || statesRaw.length !== 2244 || zBoundsRaw.length !== 2244) {
throw new M49TgsFullShadowContractError("M49 full costmap: неверная размерность.");
}
const states = statesRaw.map((value, index) => {
const parsed = integer(value, `M49 full state ${index}`);
if (parsed > 3) throw new M49TgsFullShadowContractError("M49 full state недопустим.");
return parsed as M49TgsFullShadowStateCode;
});
const zBounds = zBoundsRaw.map((value, index) => {
if (!Array.isArray(value) || value.length !== 2) {
throw new M49TgsFullShadowContractError(`M49 full z ${index}: неверная размерность.`);
}
return value.map((item) => item === null
? null
: numberValue(item, `M49 full z ${index}`)) as [number | null, number | null];
});
const sampleAvailable = booleanValue(payload.sample_available, "M49 full sample available");
if (!sampleAvailable && states.some((state) => state !== 0)) {
throw new M49TgsFullShadowContractError("M49 missing LiDAR frame не остался UNOBSERVED.");
}
return {
resultId: id,
sourceSequence,
sourceFrameIndex: integer(payload.source_frame_index, "M49 full source frame"),
sessionSeconds: numberValue(payload.session_seconds, "M49 full session seconds"),
sampleAvailable,
costmap: {
cellSizeM: numberValue(costmap.cell_size_m, "M49 full cell size"),
radiusM: numberValue(costmap.radius_m, "M49 full radius"),
centersXyM: centers.map((value, index) => pair(value, `M49 full center ${index}`)),
states,
zBoundsM: zBounds,
},
metrics: {
eligiblePointCount: integer(metrics.eligible_point_count, "M49 full eligible"),
groundPointCount: integer(metrics.ground_point_count, "M49 full ground"),
nongroundPointCount: integer(metrics.nonground_point_count, "M49 full nonground"),
rejectedPointCount: integer(metrics.rejected_point_count, "M49 full rejected"),
occupiedCellCount: integer(metrics.occupied_cell_count, "M49 full occupied cells"),
},
};
}
export async function fetchM49TgsFullShadowSpatial(
id: string,
sourceSequence: number,
{ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<M49TgsFullShadowSpatial> {
if (!RESULT_ID.test(id) || !Number.isSafeInteger(sourceSequence) || sourceSequence < 0 || sourceSequence >= 4489) {
throw new M49TgsFullShadowContractError("M49 full-shadow frame identity недопустима.");
}
const response = await fetcher(
`/api/v1/laboratory/m49/tgs-full-shadow/${encodeURIComponent(id)}/frames/${sourceSequence}/spatial`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) throw new M49TgsFullShadowContractError(`M49 full-shadow frame недоступен: HTTP ${response.status}.`);
const payload = objectValue(await response.json(), "M49 full spatial");
exact(payload.schema_version, "missioncore.m49-tgs-full-shadow-spatial/v1", "M49 full spatial schema");
exact(payload.result_id, id, "M49 full spatial result");
return parseSpatial(id, sourceSequence, payload);
}
export async function fetchM49TgsFullShadowSpatialChunk(
id: string,
start: number,
count = 24,
{ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<M49TgsFullShadowSpatialChunk> {
if (!RESULT_ID.test(id) || !Number.isSafeInteger(start) || start < 0 || start >= 4489
|| !Number.isSafeInteger(count) || count < 1 || count > 24) {
throw new M49TgsFullShadowContractError("M49 full-shadow chunk identity недопустима.");
}
const response = await fetcher(
`/api/v1/laboratory/m49/tgs-full-shadow/${encodeURIComponent(id)}/spatial/chunk?start=${start}&count=${count}`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) throw new M49TgsFullShadowContractError(`M49 full-shadow chunk недоступен: HTTP ${response.status}.`);
const payload = objectValue(await response.json(), "M49 full spatial chunk");
exact(payload.schema_version, "missioncore.m49-tgs-full-shadow-spatial-chunk/v1", "M49 full chunk schema");
exact(payload.result_id, id, "M49 full chunk result");
exact(payload.start, start, "M49 full chunk start");
const returnedCount = integer(payload.count, "M49 full chunk count");
if (!Array.isArray(payload.frames) || payload.frames.length !== returnedCount
|| returnedCount !== Math.min(count, 4489 - start)) {
throw new M49TgsFullShadowContractError("M49 full chunk: неверная размерность.");
}
const costmap = objectValue(payload.costmap, "M49 full chunk costmap");
return {
resultId: id,
start,
count: returnedCount,
frames: payload.frames.map((value, index) => parseSpatial(
id,
start + index,
objectValue(value, `M49 full chunk frame ${index}`),
costmap,
)),
};
}
function playbackTrackId(value: unknown, label: string): M49TgsFullShadowPlaybackTrackId {
const parsed = text(value, label);
if (parsed !== "frames" && parsed !== "centers" && parsed !== "states" && parsed !== "z-bounds") {
throw new M49TgsFullShadowContractError(`${label}: неизвестная playback-дорожка.`);
}
return parsed;
}
function playbackDtype(value: unknown, label: string): M49TgsFullShadowPlaybackTrack["dtype"] {
const parsed = text(value, label);
if (parsed !== "ndjson" && parsed !== "<f4" && parsed !== "|u1") {
throw new M49TgsFullShadowContractError(`${label}: неизвестный dtype.`);
}
return parsed;
}
function playbackShape(value: unknown, label: string): readonly number[] {
if (!Array.isArray(value) || !value.length) {
throw new M49TgsFullShadowContractError(`${label}: ожидалась размерность.`);
}
return value.map((item, index) => integer(item, `${label}.${index}`));
}
async function fetchPlaybackManifest(
id: string,
fetcher: LaboratoryFetch,
signal: AbortSignal | undefined,
): Promise<{
frameCount: 4489;
cellCount: 2244;
totalByteLength: number;
tracks: ReadonlyMap<M49TgsFullShadowPlaybackTrackId, M49TgsFullShadowPlaybackTrack>;
}> {
const response = await fetcher(
`/api/v1/laboratory/m49/tgs-full-shadow/${encodeURIComponent(id)}/playback/manifest`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) {
throw new M49TgsFullShadowContractError(`M49 playback manifest недоступен: HTTP ${response.status}.`);
}
const payload = objectValue(await response.json(), "M49 playback manifest");
exact(payload.schema_version, "missioncore.m49-tgs-full-shadow-playback/v1", "M49 playback schema");
exact(payload.result_id, id, "M49 playback result");
exact(payload.coordinate_frame, "map-gravity-local", "M49 playback frame");
exact(payload.access, "read-only", "M49 playback access");
const frameCount = exact(integer(payload.frame_count, "M49 playback frames"), 4489, "M49 playback frames");
const cellCount = exact(integer(payload.cell_count, "M49 playback cells"), 2244, "M49 playback cells");
const totalByteLength = integer(payload.total_byte_length, "M49 playback bytes");
if (!Array.isArray(payload.tracks) || payload.tracks.length !== 4) {
throw new M49TgsFullShadowContractError("M49 playback tracks: неверная размерность.");
}
const tracks = new Map<M49TgsFullShadowPlaybackTrackId, M49TgsFullShadowPlaybackTrack>();
for (const [index, value] of payload.tracks.entries()) {
const row = objectValue(value, `M49 playback track ${index}`);
const trackId = playbackTrackId(row.id, `M49 playback track ${index}.id`);
const sha256 = text(row.sha256, `M49 playback track ${trackId}.sha256`);
if (!/^[a-f0-9]{64}$/.test(sha256) || tracks.has(trackId)) {
throw new M49TgsFullShadowContractError(`M49 playback track ${trackId}: нарушена идентичность.`);
}
tracks.set(trackId, {
id: trackId,
url: text(row.url, `M49 playback track ${trackId}.url`),
mediaType: text(row.media_type, `M49 playback track ${trackId}.media_type`),
dtype: playbackDtype(row.dtype, `M49 playback track ${trackId}.dtype`),
shape: playbackShape(row.shape, `M49 playback track ${trackId}.shape`),
byteLength: integer(row.byte_length, `M49 playback track ${trackId}.byte_length`),
sha256,
});
}
if ([...tracks.values()].reduce((sum, track) => sum + track.byteLength, 0) !== totalByteLength) {
throw new M49TgsFullShadowContractError("M49 playback bytes: нарушен accounting.");
}
return { frameCount, cellCount, totalByteLength, tracks };
}
function sha256Hex(buffer: ArrayBuffer): Promise<string> {
if (!globalThis.crypto?.subtle) {
throw new M49TgsFullShadowContractError("Браузер не поддерживает проверку playback SHA-256.");
}
return globalThis.crypto.subtle.digest("SHA-256", buffer).then((digest) => (
[...new Uint8Array(digest)].map((value) => value.toString(16).padStart(2, "0")).join("")
));
}
async function fetchPlaybackTrack(
track: M49TgsFullShadowPlaybackTrack,
fetcher: LaboratoryFetch,
signal: AbortSignal | undefined,
completedBytes: number,
totalBytes: number,
onProgress: ((progress: M49TgsFullShadowPlaybackProgress) => void) | undefined,
): Promise<ArrayBuffer> {
const response = await fetcher(track.url, {
method: "GET",
headers: { Accept: track.mediaType },
signal,
});
if (!response.ok) {
throw new M49TgsFullShadowContractError(`M49 playback ${track.id} недоступен: HTTP ${response.status}.`);
}
const bytes = new Uint8Array(track.byteLength);
if (response.body) {
const reader = response.body.getReader();
let offset = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
if (offset + value.byteLength > bytes.byteLength) {
throw new M49TgsFullShadowContractError(`M49 playback ${track.id}: превышен объявленный размер.`);
}
bytes.set(value, offset);
offset += value.byteLength;
onProgress?.({
phase: "download",
trackId: track.id,
loadedBytes: completedBytes + offset,
totalBytes,
});
}
if (offset !== bytes.byteLength) {
throw new M49TgsFullShadowContractError(`M49 playback ${track.id}: получен неполный файл.`);
}
} else {
const fallback = new Uint8Array(await response.arrayBuffer());
if (fallback.byteLength !== bytes.byteLength) {
throw new M49TgsFullShadowContractError(`M49 playback ${track.id}: получен неверный размер.`);
}
bytes.set(fallback);
}
onProgress?.({
phase: "verify",
trackId: track.id,
loadedBytes: completedBytes + bytes.byteLength,
totalBytes,
});
if (await sha256Hex(bytes.buffer) !== track.sha256) {
throw new M49TgsFullShadowContractError(`M49 playback ${track.id}: SHA-256 не совпал.`);
}
return bytes.buffer;
}
function equalShape(actual: readonly number[], expected: readonly number[]): boolean {
return actual.length === expected.length && actual.every((value, index) => value === expected[index]);
}
export function parseM49TgsNpyTrack(
buffer: ArrayBuffer,
expectedDtype: "<f4" | "|u1",
expectedShape: readonly number[],
): Float32Array | Uint8Array {
const bytes = new Uint8Array(buffer);
if (bytes.byteLength < 12
|| bytes[0] !== 0x93
|| String.fromCharCode(...bytes.subarray(1, 6)) !== "NUMPY") {
throw new M49TgsFullShadowContractError("M49 playback NPY: неверная сигнатура.");
}
const major = bytes[6];
const headerLength = major === 1
? new DataView(buffer).getUint16(8, true)
: major === 2 || major === 3
? new DataView(buffer).getUint32(8, true)
: -1;
const headerOffset = major === 1 ? 10 : 12;
if (headerLength < 0 || headerOffset + headerLength > bytes.byteLength) {
throw new M49TgsFullShadowContractError("M49 playback NPY: неверный заголовок.");
}
const header = new TextDecoder("latin1").decode(bytes.subarray(headerOffset, headerOffset + headerLength));
const dtype = header.match(/["']descr["']\s*:\s*["']([^"']+)["']/)?.[1];
const fortran = header.match(/["']fortran_order["']\s*:\s*(True|False)/)?.[1];
const rawShape = header.match(/["']shape["']\s*:\s*\(([^)]*)\)/)?.[1];
const shape = rawShape?.split(",").map((value) => value.trim()).filter(Boolean).map(Number) ?? [];
if (dtype !== expectedDtype || fortran !== "False" || !equalShape(shape, expectedShape)) {
throw new M49TgsFullShadowContractError("M49 playback NPY: dtype или shape изменились.");
}
const count = expectedShape.reduce((product, value) => product * value, 1);
const dataOffset = headerOffset + headerLength;
const bytesPerValue = expectedDtype === "<f4" ? 4 : 1;
if (dataOffset + count * bytesPerValue !== buffer.byteLength) {
throw new M49TgsFullShadowContractError("M49 playback NPY: длина payload изменилась.");
}
return expectedDtype === "<f4"
? new Float32Array(buffer, dataOffset, count)
: new Uint8Array(buffer, dataOffset, count);
}
function parsePlaybackFrames(buffer: ArrayBuffer): readonly M49TgsFullShadowPlaybackFrame[] {
const lines = new TextDecoder().decode(buffer).trim().split("\n");
if (lines.length !== 4489) {
throw new M49TgsFullShadowContractError("M49 playback frames: неверная размерность.");
}
return lines.map((line, sourceSequence) => {
const row = objectValue(JSON.parse(line), `M49 playback frame ${sourceSequence}`);
return {
sourceSequence,
sourceFrameIndex: integer(row.source_frame_index, `M49 playback frame ${sourceSequence}.source`),
sessionSeconds: numberValue(row.session_seconds, `M49 playback frame ${sourceSequence}.time`),
sampleAvailable: booleanValue(row.sample_available, `M49 playback frame ${sourceSequence}.available`),
metrics: {
eligiblePointCount: integer(row.eligible_point_count, `M49 playback frame ${sourceSequence}.eligible`),
groundPointCount: integer(row.ground_point_count, `M49 playback frame ${sourceSequence}.ground`),
nongroundPointCount: integer(row.nonground_point_count, `M49 playback frame ${sourceSequence}.nonground`),
rejectedPointCount: integer(row.rejected_point_count, `M49 playback frame ${sourceSequence}.rejected`),
occupiedCellCount: integer(row.occupied_cell_count, `M49 playback frame ${sourceSequence}.occupied`),
},
};
});
}
export async function fetchM49TgsFullShadowPlaybackPack(
id: string,
{
fetcher = fetch,
signal,
onProgress,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
onProgress?: (progress: M49TgsFullShadowPlaybackProgress) => void;
} = {},
): Promise<M49TgsFullShadowPlaybackPack> {
if (!RESULT_ID.test(id)) {
throw new M49TgsFullShadowContractError("M49 playback identity недопустима.");
}
onProgress?.({ phase: "manifest", trackId: null, loadedBytes: 0, totalBytes: 0 });
const manifest = await fetchPlaybackManifest(id, fetcher, signal);
const buffers = new Map<M49TgsFullShadowPlaybackTrackId, ArrayBuffer>();
let completedBytes = 0;
const order: readonly M49TgsFullShadowPlaybackTrackId[] = ["frames", "centers", "states", "z-bounds"];
for (const trackId of order) {
const track = manifest.tracks.get(trackId);
if (!track) throw new M49TgsFullShadowContractError(`M49 playback ${trackId}: дорожка отсутствует.`);
const buffer = await fetchPlaybackTrack(
track,
fetcher,
signal,
completedBytes,
manifest.totalByteLength,
onProgress,
);
buffers.set(trackId, buffer);
completedBytes += track.byteLength;
}
const centersTrack = manifest.tracks.get("centers")!;
const statesTrack = manifest.tracks.get("states")!;
const zBoundsTrack = manifest.tracks.get("z-bounds")!;
const centersRaw = parseM49TgsNpyTrack(
buffers.get("centers")!,
"<f4",
centersTrack.shape,
) as Float32Array;
const states = parseM49TgsNpyTrack(
buffers.get("states")!,
"|u1",
statesTrack.shape,
) as Uint8Array;
const zBoundsM = parseM49TgsNpyTrack(
buffers.get("z-bounds")!,
"<f4",
zBoundsTrack.shape,
) as Float32Array;
const frames = parsePlaybackFrames(buffers.get("frames")!);
onProgress?.({
phase: "ready",
trackId: null,
loadedBytes: manifest.totalByteLength,
totalBytes: manifest.totalByteLength,
});
return {
resultId: id,
frameCount: manifest.frameCount,
cellCount: manifest.cellCount,
totalByteLength: manifest.totalByteLength,
centersXyM: centersRaw,
states,
zBoundsM,
frames,
};
}
@@ -226,6 +226,51 @@ export interface M4ThreatTimelineChunk {
frames: readonly M4ThreatTimelineFrame[];
}
export interface M4ThreatPlaybackProgress {
phase: "manifest" | "download" | "verify" | "ready";
loadedBytes: number;
totalBytes: number;
}
export interface M4ThreatPlaybackPointPack {
resultId: string;
frameCount: 4489;
pointCount: number;
pointOffsets: Uint32Array;
pointsMapXyzM: Float32Array;
pointStart?: number;
startSequence?: number;
sequenceCount?: number;
}
export interface M4ThreatPlaybackChunkDescriptor {
index: number;
start: number;
count: number;
pointStart: number;
pointCount: number;
url: string;
mediaType: "application/octet-stream";
byteLength: number;
sha256: string;
}
export interface M4ThreatPlaybackManifest {
resultId: string;
frameCount: 4489;
pointCount: number;
pointOffsets: Uint32Array;
chunkFrameCount: 24;
residentChunkCountMax: 4;
forwardPrefetchChunkCount: 1;
chunks: readonly M4ThreatPlaybackChunkDescriptor[];
track: {
url: string;
byteLength: number;
sha256: string;
};
}
type LaboratoryFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
export const M4_THREAT_TIMELINE_ENDPOINT_ROOT = "/api/v1/laboratory/m4-threat/results";
class M4ThreatContractError extends Error {}
@@ -803,11 +848,13 @@ export async function fetchM4ThreatTimelineChunk(
signal,
endpointRoot = M4_THREAT_TIMELINE_ENDPOINT_ROOT,
cameraObstacleProjectionDelivery = null,
playbackPointPack,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
endpointRoot?: string;
cameraObstacleProjectionDelivery?: M4ThreatTimeline["cameraObstacleProjectionDelivery"];
playbackPointPack?: M4ThreatPlaybackPointPack;
} = {},
): Promise<M4ThreatTimelineChunk> {
const params = new URLSearchParams({
@@ -817,6 +864,7 @@ export async function fetchM4ThreatTimelineChunk(
if (cameraObstacleProjectionDelivery !== null) {
params.set("obstacle_projection", cameraObstacleProjectionDelivery);
}
if (playbackPointPack) params.set("include_points", "false");
const response = await fetcher(
`${endpointRoot}/${result}/timeline/chunk?${params}`,
{ headers: { Accept: "application/json" }, signal },
@@ -834,8 +882,12 @@ export async function fetchM4ThreatTimelineChunk(
if (parsedStart !== startSequence) {
throw new M4ThreatContractError("M4.6 timeline chunk start: нарушен контракт.");
}
const frames = array(payload.frames, "M4.6 timeline frames").map((raw, offset) =>
parseTimelineFrame(raw, result, parsedStart + offset, endpointRoot));
const frames = array(payload.frames, "M4.6 timeline frames").map((raw, offset) => {
const parsed = parseTimelineFrame(raw, result, parsedStart + offset, endpointRoot);
return playbackPointPack
? hydrateM4ThreatTimelineFrame(parsed, playbackPointPack)
: parsed;
});
const parsedCount = integer(payload.frame_count, "M4.6 timeline chunk count");
if (parsedCount !== frames.length || parsedCount > frameCount) {
throw new M4ThreatContractError("M4.6 timeline chunk count: нарушен контракт.");
@@ -851,6 +903,305 @@ export async function fetchM4ThreatTimelineChunk(
};
}
function playbackSha256Hex(buffer: ArrayBuffer): Promise<string> {
if (!globalThis.crypto?.subtle) {
throw new M4ThreatContractError("Браузер не поддерживает проверку playback SHA-256.");
}
return globalThis.crypto.subtle.digest("SHA-256", buffer).then((digest) => (
[...new Uint8Array(digest)].map((value) => value.toString(16).padStart(2, "0")).join("")
));
}
function roundedBodyCoordinate(value: number): number {
return Math.round(value * 1_000_000) / 1_000_000;
}
export function hydrateM4ThreatTimelineFrame(
frame: M4ThreatTimelineFrame,
pack: M4ThreatPlaybackPointPack,
): M4ThreatTimelineFrame {
if (
frame.sequence >= pack.frameCount
|| pack.resultId === ""
|| (pack.startSequence !== undefined && (
frame.sequence < pack.startSequence
|| frame.sequence >= pack.startSequence + (pack.sequenceCount ?? 0)
))
) {
throw new M4ThreatContractError("M4.6 binary playback frame: нарушена идентичность.");
}
if (!frame.sourceAvailable || !frame.spatialAvailable || !frame.bodyFrame) {
if (frame.pointCloudSourceCount !== 0) {
throw new M4ThreatContractError("M4.6 binary playback unavailable frame содержит точки.");
}
return { ...frame, pointCloudBodyXyzM: [], pointCloudSampleCount: 0 };
}
const start = pack.pointOffsets[frame.sequence];
const stop = pack.pointOffsets[frame.sequence + 1];
const pointStart = pack.pointStart ?? 0;
const pointStop = pointStart + pack.pointCount;
if (
start === undefined
|| stop === undefined
|| stop < start
|| start < pointStart
|| stop > pointStop
) {
throw new M4ThreatContractError("M4.6 binary playback offsets: нарушена размерность.");
}
const count = stop - start;
if (frame.pointCloudSourceCount !== count || frame.pointCloudSampleCount !== count) {
throw new M4ThreatContractError("M4.6 binary playback point accounting: нарушен контракт.");
}
const origin = frame.bodyFrame.originMapXyzM;
const basis = frame.bodyFrame.basisMapFromBody;
const points = new Array<M4Point3>(count);
for (let index = 0; index < count; index += 1) {
const sourceOffset = (start - pointStart + index) * 3;
const dx = pack.pointsMapXyzM[sourceOffset]! - origin[0];
const dy = pack.pointsMapXyzM[sourceOffset + 1]! - origin[1];
const dz = pack.pointsMapXyzM[sourceOffset + 2]! - origin[2];
points[index] = [
roundedBodyCoordinate(dx * basis[0][0] + dy * basis[1][0] + dz * basis[2][0]),
roundedBodyCoordinate(dx * basis[0][1] + dy * basis[1][1] + dz * basis[2][1]),
roundedBodyCoordinate(dx * basis[0][2] + dy * basis[1][2] + dz * basis[2][2]),
];
}
return { ...frame, pointCloudBodyXyzM: points, pointCloudSampleCount: count };
}
export async function fetchM4ThreatPlaybackManifest(
result: string,
{
fetcher = fetch,
signal,
endpointRoot = M4_THREAT_TIMELINE_ENDPOINT_ROOT,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
endpointRoot?: string;
} = {},
): Promise<M4ThreatPlaybackManifest> {
resultId(result);
const manifestResponse = await fetcher(`${endpointRoot}/${result}/timeline/playback`, {
headers: { Accept: "application/json" },
signal,
});
if (!manifestResponse.ok) {
throw new M4ThreatContractError(`M4.6 playback manifest: HTTP ${manifestResponse.status}.`);
}
const manifest = object(await manifestResponse.json(), "M4.6 playback manifest");
exact(manifest.schema_version, "missioncore.recorded-spatial-playback/v1", "M4.6 playback schema");
exact(manifest.result_id, result, "M4.6 playback result");
exact(manifest.coordinate_frame, "map", "M4.6 playback coordinate frame");
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 pointCount = integer(manifest.point_count, "M4.6 playback points");
const offsetsRaw = array(manifest.point_offsets, "M4.6 playback offsets");
if (offsetsRaw.length !== frameCount + 1) {
throw new M4ThreatContractError("M4.6 playback offsets: неверная размерность.");
}
const pointOffsets = Uint32Array.from(offsetsRaw, (value) => integer(value, "M4.6 playback offset"));
if (pointOffsets[0] !== 0 || pointOffsets[pointOffsets.length - 1] !== pointCount) {
throw new M4ThreatContractError("M4.6 playback offsets: нарушено замыкание.");
}
for (let index = 1; index < pointOffsets.length; index += 1) {
if (pointOffsets[index]! < pointOffsets[index - 1]!) {
throw new M4ThreatContractError("M4.6 playback offsets: нарушена монотонность.");
}
}
const track = object(manifest.track, "M4.6 playback track");
exact(track.id, "points-map-f32", "M4.6 playback track id");
exact(track.dtype, "<f4", "M4.6 playback dtype");
const shape = array(track.shape, "M4.6 playback shape").map((value) => integer(value, "M4.6 playback shape"));
if (shape.length !== 2 || shape[0] !== pointCount || shape[1] !== 3) {
throw new M4ThreatContractError("M4.6 playback shape: нарушена размерность.");
}
const byteLength = integer(track.bytes, "M4.6 playback bytes");
if (byteLength !== pointCount * 3 * Float32Array.BYTES_PER_ELEMENT) {
throw new M4ThreatContractError("M4.6 playback bytes: нарушен accounting.");
}
const sha256 = text(track.sha256, "M4.6 playback SHA-256");
if (!/^[a-f0-9]{64}$/.test(sha256)) {
throw new M4ThreatContractError("M4.6 playback SHA-256: нарушен контракт.");
}
const trackUrl = text(track.url, "M4.6 playback URL");
const chunkFrameCount = exact(
integer(manifest.chunk_frame_count, "M4.6 playback chunk frames"),
24,
"M4.6 playback chunk frames",
);
const residentChunkCountMax = exact(
integer(manifest.resident_chunk_count_max, "M4.6 playback resident chunks"),
4,
"M4.6 playback resident chunks",
);
const forwardPrefetchChunkCount = exact(
integer(manifest.forward_prefetch_chunk_count, "M4.6 playback prefetch chunks"),
1,
"M4.6 playback prefetch chunks",
);
const chunkRows = array(manifest.chunks, "M4.6 playback chunks");
const expectedChunkCount = Math.ceil(frameCount / chunkFrameCount);
if (chunkRows.length !== expectedChunkCount) {
throw new M4ThreatContractError("M4.6 playback chunk catalog: неверный размер.");
}
const chunks = chunkRows.map((value, index): M4ThreatPlaybackChunkDescriptor => {
const row = object(value, `M4.6 playback chunk ${index}`);
const start = index * chunkFrameCount;
const count = Math.min(chunkFrameCount, frameCount - start);
const pointStart = pointOffsets[start]!;
const pointStop = pointOffsets[start + count]!;
const pointChunkCount = pointStop - pointStart;
exact(integer(row.index, `M4.6 playback chunk ${index}.index`), index, `M4.6 playback chunk ${index}.index`);
exact(integer(row.start, `M4.6 playback chunk ${index}.start`), start, `M4.6 playback chunk ${index}.start`);
exact(integer(row.count, `M4.6 playback chunk ${index}.count`), count, `M4.6 playback chunk ${index}.count`);
exact(integer(row.point_start, `M4.6 playback chunk ${index}.point start`), pointStart, `M4.6 playback chunk ${index}.point start`);
exact(integer(row.point_count, `M4.6 playback chunk ${index}.point count`), pointChunkCount, `M4.6 playback chunk ${index}.point count`);
exact(row.media_type, "application/octet-stream", `M4.6 playback chunk ${index}.media type`);
exact(row.dtype, "<f4", `M4.6 playback chunk ${index}.dtype`);
const shapeValue = array(row.shape, `M4.6 playback chunk ${index}.shape`)
.map((item) => integer(item, `M4.6 playback chunk ${index}.shape`));
const chunkBytes = integer(row.bytes, `M4.6 playback chunk ${index}.bytes`);
if (
shapeValue.length !== 2
|| shapeValue[0] !== pointChunkCount
|| shapeValue[1] !== 3
|| chunkBytes !== pointChunkCount * 3 * Float32Array.BYTES_PER_ELEMENT
) {
throw new M4ThreatContractError(`M4.6 playback chunk ${index}: нарушена размерность.`);
}
const chunkSha256 = text(row.sha256, `M4.6 playback chunk ${index}.SHA-256`);
if (!/^[a-f0-9]{64}$/.test(chunkSha256)) {
throw new M4ThreatContractError(`M4.6 playback chunk ${index}: SHA-256 недопустим.`);
}
const expectedUrl = `${endpointRoot}/${result}/timeline/playback/chunks/${index}`;
const url = text(row.url, `M4.6 playback chunk ${index}.URL`);
if (url !== expectedUrl || url.includes("worker")) {
throw new M4ThreatContractError(`M4.6 playback chunk ${index}: разрешён только sealed local API.`);
}
return {
index,
start,
count,
pointStart,
pointCount: pointChunkCount,
url,
mediaType: "application/octet-stream",
byteLength: chunkBytes,
sha256: chunkSha256,
};
});
return {
resultId: result,
frameCount,
pointCount,
pointOffsets,
chunkFrameCount,
residentChunkCountMax,
forwardPrefetchChunkCount,
chunks,
track: { url: trackUrl, byteLength, sha256 },
};
}
export async function fetchM4ThreatPlaybackPointChunk(
manifest: M4ThreatPlaybackManifest,
chunkIndex: number,
{
fetcher = fetch,
signal,
onProgress,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
onProgress?: (progress: M4ThreatPlaybackProgress) => void;
} = {},
): Promise<M4ThreatPlaybackPointPack> {
const descriptor = manifest.chunks[chunkIndex];
if (!descriptor || descriptor.index !== chunkIndex) {
throw new M4ThreatContractError("M4.6 playback chunk отсутствует.");
}
onProgress?.({ phase: "download", loadedBytes: 0, totalBytes: descriptor.byteLength });
const response = await fetcher(descriptor.url, {
headers: { Accept: "application/octet-stream" },
signal,
});
if (!response.ok) {
throw new M4ThreatContractError(`M4.6 playback chunk: HTTP ${response.status}.`);
}
const buffer = await response.arrayBuffer();
if (buffer.byteLength !== descriptor.byteLength) {
throw new M4ThreatContractError("M4.6 playback chunk: неверный размер.");
}
onProgress?.({
phase: "verify",
loadedBytes: descriptor.byteLength,
totalBytes: descriptor.byteLength,
});
if (await playbackSha256Hex(buffer) !== descriptor.sha256) {
throw new M4ThreatContractError("M4.6 playback chunk: SHA-256 не совпал.");
}
onProgress?.({
phase: "ready",
loadedBytes: descriptor.byteLength,
totalBytes: descriptor.byteLength,
});
return {
resultId: manifest.resultId,
frameCount: manifest.frameCount,
pointCount: descriptor.pointCount,
pointOffsets: manifest.pointOffsets,
pointsMapXyzM: new Float32Array(buffer),
pointStart: descriptor.pointStart,
startSequence: descriptor.start,
sequenceCount: descriptor.count,
};
}
export async function fetchM4ThreatPlaybackPointPack(
result: string,
{
fetcher = fetch,
signal,
endpointRoot = M4_THREAT_TIMELINE_ENDPOINT_ROOT,
onProgress,
}: {
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
endpointRoot?: string;
onProgress?: (progress: M4ThreatPlaybackProgress) => void;
} = {},
): Promise<M4ThreatPlaybackPointPack> {
onProgress?.({ phase: "manifest", loadedBytes: 0, totalBytes: 0 });
const manifest = await fetchM4ThreatPlaybackManifest(result, {
fetcher,
signal,
endpointRoot,
});
const response = await fetcher(manifest.track.url, {
headers: { Accept: "application/octet-stream" },
signal,
});
if (!response.ok) throw new M4ThreatContractError(`M4.6 playback points: HTTP ${response.status}.`);
const buffer = await response.arrayBuffer();
if (buffer.byteLength !== manifest.track.byteLength) {
throw new M4ThreatContractError("M4.6 playback points: неверный размер.");
}
onProgress?.({ phase: "verify", loadedBytes: buffer.byteLength, totalBytes: buffer.byteLength });
if (await playbackSha256Hex(buffer) !== manifest.track.sha256) {
throw new M4ThreatContractError("M4.6 playback points: SHA-256 не совпал.");
}
onProgress?.({ phase: "ready", loadedBytes: buffer.byteLength, totalBytes: buffer.byteLength });
return {
resultId: manifest.resultId,
frameCount: manifest.frameCount,
pointCount: manifest.pointCount,
pointOffsets: manifest.pointOffsets,
pointsMapXyzM: new Float32Array(buffer),
};
}
export async function fetchM4ThreatCameraPointOverlay(
result: string,
sequence: number,
@@ -0,0 +1,432 @@
import type { LaboratoryFetch } from "./advancedResults";
const RESULT_ID = /^lab-v1-vegetation-shadow-[a-f0-9]{64}$/;
const SHA256 = /^[a-f0-9]{64}$/;
const CANDIDATES = ["ddrnet", "ppliteseg"] as const;
const ROUTE_MODES = ["source", "ddrnet", "ppliteseg", "urban", "rural", "offroad"] as const;
const VALIDATION_ASSETS = [
"source",
"truth",
"ddrnet",
"ppliteseg",
"ddrnet_error",
"ppliteseg_error",
] as const;
export type VegetationCandidateKey = typeof CANDIDATES[number];
export type VegetationRouteMode = typeof ROUTE_MODES[number];
export interface VegetationVisualFocus {
className: string;
labelId: number;
truthPixels: number;
truthFraction: number;
stratumRank: number;
}
export interface VegetationCandidateMetrics {
candidate: VegetationCandidateKey;
loadedModelName: string;
checkpointSha256: string;
meanIouPercent: number;
publishedMeanIouPercent: number;
vegetationMeanIouPercent: number;
validationLatencyP95Ms: number;
validationThroughputFps: number;
shadowLatencyP95Ms: number;
shadowThroughputFps: number;
shadowPrewarmLatencyMs: number;
peakReservedVramBytes: number;
gpuName: string;
}
export interface VegetationVisualCase {
caseId: string;
sourceKind: "goose" | "ravnoves";
width: number;
height: number;
centerCropXyxy: readonly [number, number, number, number];
outsideCropState: "undefined" | "not-applicable";
focus: VegetationVisualFocus | null;
assets: Readonly<Record<string, string>>;
}
export interface VegetationVideoSemanticClass {
classId: number;
label: string;
colorRgb: readonly [number, number, number];
disposition: "prediction" | "undefined";
}
export interface VegetationRouteVideo {
workerResultId: string;
m47ReferenceGraphResultId: string;
baseM4ResultId: string;
frameCount: 4489;
width: 800;
height: 600;
centerCropXyxy: readonly [100, 0, 700, 600];
outsideCropState: "undefined";
taxonomy: readonly VegetationVideoSemanticClass[];
aggregatePredictionPixels: readonly number[];
}
export interface VegetationShadowResult {
resultId: string;
createdAtUtc: string;
status: "visual-shadow-ready-policy-not-authorized";
selectedCandidate: VegetationCandidateKey;
candidates: readonly VegetationCandidateMetrics[];
routeCases: readonly VegetationVisualCase[];
validationCases: readonly VegetationVisualCase[];
routeVideo: VegetationRouteVideo | null;
limitations: readonly string[];
visualShadowReady: true;
missionPolicyReadyForConfiguration: true;
authority: {
commandsEnabled: false;
navigationOrSafetyAccepted: false;
actuationAccepted: false;
cameraSemanticsCanClearRigidGeometry: false;
};
}
export class VegetationShadowContractError extends Error {}
function objectValue(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new VegetationShadowContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function arrayValue(value: unknown, label: string): readonly unknown[] {
if (!Array.isArray(value)) throw new VegetationShadowContractError(`${label}: ожидался массив.`);
return value;
}
function textValue(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new VegetationShadowContractError(`${label}: ожидалась строка.`);
}
return value;
}
function numberValue(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new VegetationShadowContractError(`${label}: ожидалось число.`);
}
return value;
}
function integerValue(value: unknown, label: string): number {
const parsed = numberValue(value, label);
if (!Number.isInteger(parsed) || parsed < 0) {
throw new VegetationShadowContractError(`${label}: ожидалось неотрицательное целое.`);
}
return parsed;
}
function exact(value: unknown, expected: unknown, label: string): void {
if (value !== expected) {
throw new VegetationShadowContractError(`${label}: контракт изменён.`);
}
}
function candidateKey(value: unknown, label: string): VegetationCandidateKey {
if (value !== "ddrnet" && value !== "ppliteseg") {
throw new VegetationShadowContractError(`${label}: неизвестная модель.`);
}
return value;
}
function candidateMetricsValue(
value: unknown,
candidate: VegetationCandidateKey,
): VegetationCandidateMetrics {
const row = objectValue(value, `vegetation.metrics.${candidate}`);
const validation = objectValue(row.validation_metrics, `${candidate}.validation_metrics`);
const validationTiming = objectValue(row.validation_timing, `${candidate}.validation_timing`);
const shadowTiming = objectValue(row.shadow_timing, `${candidate}.shadow_timing`);
const resource = objectValue(row.resource, `${candidate}.resource`);
const checkpointSha256 = textValue(row.checkpoint_sha256, `${candidate}.checkpoint_sha256`);
if (!SHA256.test(checkpointSha256)) {
throw new VegetationShadowContractError(`${candidate}.checkpoint_sha256: digest invalid.`);
}
return {
candidate,
loadedModelName: textValue(row.loaded_model_name, `${candidate}.loaded_model_name`),
checkpointSha256,
meanIouPercent: numberValue(validation.mean_iou_percent, `${candidate}.mean_iou_percent`),
publishedMeanIouPercent: numberValue(
validation.published_mean_iou_percent,
`${candidate}.published_mean_iou_percent`,
),
vegetationMeanIouPercent: numberValue(validation.vegetation_mean_iou, `${candidate}.vegetation_mean_iou`) * 100,
validationLatencyP95Ms: numberValue(validationTiming.latency_ms_p95, `${candidate}.validation_latency_p95`),
validationThroughputFps: numberValue(
validationTiming.throughput_fps_from_mean_inference,
`${candidate}.validation_throughput`,
),
shadowLatencyP95Ms: numberValue(shadowTiming.latency_ms_p95, `${candidate}.shadow_latency_p95`),
shadowThroughputFps: numberValue(
shadowTiming.throughput_fps_from_mean_inference,
`${candidate}.shadow_throughput`,
),
shadowPrewarmLatencyMs: numberValue(
shadowTiming.prewarm_latency_ms,
`${candidate}.shadow_prewarm_latency`,
),
peakReservedVramBytes: integerValue(resource.peak_reserved_vram_bytes, `${candidate}.vram`),
gpuName: textValue(resource.gpu_name, `${candidate}.gpu_name`),
};
}
function visualCaseValue(
value: unknown,
resultId: string,
expectedKind: "goose" | "ravnoves",
): VegetationVisualCase {
const row = objectValue(value, `vegetation.${expectedKind}.case`);
exact(row.source_kind, expectedKind, "vegetation.case.source_kind");
const caseId = textValue(row.case_id, "vegetation.case.case_id");
const crop = arrayValue(row.center_crop_xyxy, "vegetation.case.center_crop_xyxy")
.map((item, index) => integerValue(item, `vegetation.case.crop[${index}]`));
if (crop.length !== 4) {
throw new VegetationShadowContractError("vegetation.case.center_crop_xyxy: размер изменён.");
}
const assets = objectValue(row.assets, "vegetation.case.assets");
const projected: Record<string, string> = {};
for (const [key, raw] of Object.entries(assets)) {
const descriptor = objectValue(raw, `vegetation.case.assets.${key}`);
const path = textValue(descriptor.path, `vegetation.case.assets.${key}.path`);
const sha256 = textValue(descriptor.sha256, `vegetation.case.assets.${key}.sha256`);
if (!SHA256.test(sha256) || !path.startsWith(`visual/${expectedKind}/${caseId}/`)) {
throw new VegetationShadowContractError(`vegetation.case.assets.${key}: proof invalid.`);
}
projected[key] = `/api/v1/laboratory/vegetation-shadow/${encodeURIComponent(resultId)}/assets/${path
.split("/")
.map(encodeURIComponent)
.join("/")}`;
}
const expectedAssets = expectedKind === "goose"
? VALIDATION_ASSETS
: ROUTE_MODES;
if (expectedAssets.some((key) => !projected[key])) {
throw new VegetationShadowContractError(`vegetation.case.assets: ${expectedKind} набор неполон.`);
}
const outsideCropState = row.outside_crop_state;
if (outsideCropState !== "undefined" && outsideCropState !== "not-applicable") {
throw new VegetationShadowContractError("vegetation.case.outside_crop_state: контракт изменён.");
}
let focus: VegetationVisualFocus | null = null;
if (expectedKind === "goose") {
const rawFocus = objectValue(row.focus, "vegetation.case.focus");
const truthFraction = numberValue(rawFocus.truth_fraction, "vegetation.case.focus.truth_fraction");
if (truthFraction <= 0 || truthFraction > 1) {
throw new VegetationShadowContractError("vegetation.case.focus.truth_fraction: диапазон изменён.");
}
focus = {
className: textValue(rawFocus.class_name, "vegetation.case.focus.class_name"),
labelId: integerValue(rawFocus.label_id, "vegetation.case.focus.label_id"),
truthPixels: integerValue(rawFocus.truth_pixels, "vegetation.case.focus.truth_pixels"),
truthFraction,
stratumRank: integerValue(rawFocus.stratum_rank, "vegetation.case.focus.stratum_rank"),
};
} else if (row.focus !== null) {
throw new VegetationShadowContractError("vegetation.case.focus: RAVNOVES focus отсутствует.");
}
return {
caseId,
sourceKind: expectedKind,
width: integerValue(row.width, "vegetation.case.width"),
height: integerValue(row.height, "vegetation.case.height"),
centerCropXyxy: crop as unknown as readonly [number, number, number, number],
outsideCropState,
focus,
assets: projected,
};
}
function routeVideoValue(value: unknown): VegetationRouteVideo | null {
if (value === null || value === undefined) return null;
const row = objectValue(value, "vegetation.route_video");
const workerResultId = textValue(row.worker_result_id, "vegetation.route_video.worker_result_id");
const m47ReferenceGraphResultId = textValue(
row.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");
if (
!/^lab-v1-ravnoves-video-ddrnet-[a-f0-9]{64}$/.test(workerResultId)
|| !/^m47-reference-graph-lab-[a-f0-9]{64}$/.test(m47ReferenceGraphResultId)
|| !/^m4-threat-replay-[a-f0-9]{64}$/.test(baseM4ResultId)
) {
throw new VegetationShadowContractError("vegetation.route_video: identity invalid.");
}
exact(row.frame_count, 4489, "vegetation.route_video.frame_count");
exact(row.width, 800, "vegetation.route_video.width");
exact(row.height, 600, "vegetation.route_video.height");
exact(row.outside_crop_state, "undefined", "vegetation.route_video.outside_crop_state");
exact(
row.sequence_binding,
"sequence-0-to-masks/frame-000001.png",
"vegetation.route_video.sequence_binding",
);
const crop = arrayValue(row.center_crop_xyxy, "vegetation.route_video.center_crop_xyxy")
.map((item, index) => integerValue(item, `vegetation.route_video.crop[${index}]`));
if (crop.join(",") !== "100,0,700,600") {
throw new VegetationShadowContractError("vegetation.route_video: crop contract changed.");
}
const taxonomy = objectValue(row.taxonomy, "vegetation.route_video.taxonomy");
exact(
taxonomy.schema_version,
"missioncore.lab-v1-vegetation-taxonomy/v1",
"vegetation.route_video.taxonomy.schema",
);
const classes = arrayValue(taxonomy.classes, "vegetation.route_video.taxonomy.classes")
.map((value, expectedId): VegetationVideoSemanticClass => {
const item = objectValue(value, `vegetation.route_video.taxonomy[${expectedId}]`);
const classId = integerValue(item.class_id, `vegetation.route_video.class_id[${expectedId}]`);
if (classId !== expectedId) {
throw new VegetationShadowContractError("vegetation.route_video: taxonomy order changed.");
}
const color = arrayValue(item.color_rgb, `vegetation.route_video.color[${expectedId}]`)
.map((channel, index) => integerValue(channel, `vegetation.route_video.color[${expectedId}][${index}]`));
if (color.length !== 3 || color.some((channel) => channel > 255)) {
throw new VegetationShadowContractError("vegetation.route_video: taxonomy color invalid.");
}
const disposition: VegetationVideoSemanticClass["disposition"] = expectedId === 0
? "undefined"
: "prediction";
if (item.disposition !== disposition) {
throw new VegetationShadowContractError("vegetation.route_video: taxonomy disposition changed.");
}
return {
classId,
label: textValue(item.label, `vegetation.route_video.label[${expectedId}]`),
colorRgb: color as unknown as readonly [number, number, number],
disposition,
};
});
if (classes.length !== 64) {
throw new VegetationShadowContractError("vegetation.route_video: taxonomy must contain 64 classes.");
}
const aggregatePredictionPixels = arrayValue(
row.aggregate_prediction_pixels,
"vegetation.route_video.aggregate_prediction_pixels",
).map((value, index) => integerValue(value, `vegetation.route_video.pixels[${index}]`));
if (aggregatePredictionPixels.length !== 64) {
throw new VegetationShadowContractError("vegetation.route_video: class accounting changed.");
}
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");
const archiveSha256 = textValue(maskArchive.sha256, "vegetation.route_video.mask_archive.sha256");
if (!SHA256.test(archiveSha256)) {
throw new VegetationShadowContractError("vegetation.route_video: archive digest invalid.");
}
integerValue(maskArchive.byte_length, "vegetation.route_video.mask_archive.byte_length");
return {
workerResultId,
m47ReferenceGraphResultId,
baseM4ResultId,
frameCount: 4489,
width: 800,
height: 600,
centerCropXyxy: [100, 0, 700, 600],
outsideCropState: "undefined",
taxonomy: classes,
aggregatePredictionPixels,
};
}
function parseResult(value: unknown, resultId: string): VegetationShadowResult {
const payload = objectValue(value, "Vegetation LAB");
exact(payload.schema_version, "missioncore.lab-v1-vegetation-shadow/v1", "vegetation.schema");
exact(payload.result_id, resultId, "vegetation.result_id");
exact(payload.status, "visual-shadow-ready-policy-not-authorized", "vegetation.status");
exact(payload.ground_truth, false, "vegetation.ground_truth");
exact(payload.access, "read-only", "vegetation.access");
const identity = objectValue(payload.identity, "vegetation.identity");
const metrics = objectValue(payload.metrics, "vegetation.metrics");
const candidates = objectValue(metrics.candidates, "vegetation.metrics.candidates");
const decision = objectValue(payload.decision, "vegetation.decision");
const authority = objectValue(payload.authority, "vegetation.authority");
const catalogs = objectValue(payload.catalogs, "vegetation.catalogs");
const selectedCandidate = candidateKey(identity.selected_candidate, "vegetation.selected_candidate");
exact(decision.selected_candidate, selectedCandidate, "vegetation.decision.selected_candidate");
exact(decision.visual_shadow_ready, true, "vegetation.decision.visual_shadow_ready");
exact(
decision.mission_policy_ready_for_configuration,
true,
"vegetation.decision.mission_policy_ready_for_configuration",
);
exact(decision.navigation_accepted, false, "vegetation.decision.navigation_accepted");
exact(decision.production_accepted, false, "vegetation.decision.production_accepted");
exact(authority.commands_enabled, false, "vegetation.authority.commands_enabled");
exact(
authority.navigation_or_safety_accepted,
false,
"vegetation.authority.navigation_or_safety_accepted",
);
exact(authority.actuation_accepted, false, "vegetation.authority.actuation_accepted");
exact(
authority.camera_semantics_can_clear_rigid_geometry,
false,
"vegetation.authority.camera_semantics_can_clear_rigid_geometry",
);
const routeCases = arrayValue(catalogs.ravnoves, "vegetation.catalogs.ravnoves")
.map((item) => visualCaseValue(item, resultId, "ravnoves"));
const validationCases = arrayValue(catalogs.goose, "vegetation.catalogs.goose")
.map((item) => visualCaseValue(item, resultId, "goose"));
if (routeCases.length !== 0 || validationCases.length !== 12) {
throw new VegetationShadowContractError("vegetation.catalogs: ожидалось 12 truth-backed GOOSE случаев без route viewer.");
}
return {
resultId,
createdAtUtc: textValue(payload.created_at_utc, "vegetation.created_at_utc"),
status: "visual-shadow-ready-policy-not-authorized",
selectedCandidate,
candidates: CANDIDATES.map((candidate) => candidateMetricsValue(candidates[candidate], candidate)),
routeCases,
validationCases,
routeVideo: routeVideoValue(payload.route_video),
limitations: arrayValue(payload.limitations, "vegetation.limitations")
.map((item, index) => textValue(item, `vegetation.limitations[${index}]`)),
visualShadowReady: true,
missionPolicyReadyForConfiguration: true,
authority: {
commandsEnabled: false,
navigationOrSafetyAccepted: false,
actuationAccepted: false,
cameraSemanticsCanClearRigidGeometry: false,
},
};
}
export function vegetationVideoMaskUrl(resultId: string, sequence: number): string {
if (!RESULT_ID.test(resultId) || !Number.isInteger(sequence) || sequence < 0 || sequence >= 4489) {
throw new VegetationShadowContractError("Vegetation video mask identity недопустима.");
}
return `/api/v1/laboratory/vegetation-shadow/${encodeURIComponent(resultId)}/masks/${sequence}`;
}
export async function fetchVegetationShadowResult(
resultId: string,
{
fetcher = fetch,
signal,
}: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<VegetationShadowResult> {
if (!RESULT_ID.test(resultId)) {
throw new VegetationShadowContractError("Vegetation LAB identity недопустима.");
}
const response = await fetcher(
`/api/v1/laboratory/vegetation-shadow/${encodeURIComponent(resultId)}`,
{ method: "GET", headers: { Accept: "application/json" }, signal },
);
if (!response.ok) {
throw new VegetationShadowContractError(`Vegetation LAB недоступна: HTTP ${response.status}.`);
}
return parseResult(await response.json(), resultId);
}
@@ -1,6 +1,10 @@
export type SimulationSceneType = "interior" | "outdoor" | "object";
export type SimulationTransformAxis = "x" | "y" | "z";
export type SimulationViewerQuality = "low" | "medium" | "high" | "ultra" | "maximum";
export interface SimulationEulerRotation {
x: number;
y: number;
z: number;
}
export type SimulationProjectStatus =
| "uploading"
| "queued"
@@ -35,13 +39,38 @@ export interface SimulationWorldManifest {
}
export interface SimulationViewerSettings {
schemaVersion: "missioncore.simulation-viewer-settings/v1";
schemaVersion: "missioncore.simulation-viewer-settings/v3";
quality: SimulationViewerQuality;
visual: { inverted: boolean; axis: SimulationTransformAxis };
collision: { inverted: boolean; axis: SimulationTransformAxis };
visual: { rotationDegrees: SimulationEulerRotation };
collision: { rotationDegrees: SimulationEulerRotation };
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 {
schemaVersion: "missioncore.simulation-project/v1";
projectId: string;
@@ -58,7 +87,9 @@ export interface SimulationProject {
provider: {
providerId: "gaussian-pipeline";
jobId: string | null;
jobCreatedAtUtc: string | null;
state: string | null;
stateStartedAtUtc: string | null;
progress: { completed_steps?: number; total_steps?: number; stage?: string } | null;
runtime: { source_revision?: string; image_digest?: string } | null;
};
@@ -85,11 +116,60 @@ export interface SimulationUploadProgress {
uploadedBytes: number;
totalBytes: number;
currentPath: string;
bytesPerSecond: number | null;
estimatedSecondsRemaining: number | null;
}
const API_ROOT = "/api/v1/simulation-worlds/projects";
const CHUNK_BYTES = 8 * 1024 * 1024;
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[]> {
const response = await fetch(API_ROOT, { signal, cache: "no-store" });
@@ -107,6 +187,15 @@ export async function createAndUploadSimulationProject(
sceneType: SimulationSceneType,
candidates: SimulationUploadCandidate[],
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> {
const sourceKind = sourceKindFor(candidates);
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 totalBytes = candidates.reduce((total, candidate) => total + candidate.file.size, 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) {
signal?.throwIfAborted();
const file = byPath.get(sourceFile.logicalPath);
if (!file) throw new Error(`Сервер изменил состав источника: ${sourceFile.logicalPath}`);
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;
onProgress({ uploadedBytes: confirmedBytes, totalBytes, currentPath: sourceFile.logicalPath });
sampleBytes = confirmedBytes;
sampleAt = performance.now();
reportProgress(sourceFile.logicalPath);
while (offset < file.size) {
signal?.throwIfAborted();
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;
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",
headers: { "Content-Type": "application/json" },
body: "{}",
signal,
});
project = parseProject(await jsonResponse(buildResponse));
return project;
return parseProject(await jsonResponse(buildResponse));
}
async function readUploadOffset(uploadUrl: string, fileSize: number): Promise<number> {
const head = await fetch(uploadUrl, { method: "HEAD", cache: "no-store" });
async function readUploadOffset(
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);
const value = head.headers.get("Upload-Offset");
if (!value || !/^\d+$/.test(value)) {
@@ -170,11 +308,13 @@ async function uploadChunk(
file: File,
offset: number,
nextOffset: number,
signal?: AbortSignal,
): Promise<number> {
let lastFailure: unknown = null;
for (let attempt = 1; attempt <= MAX_UPLOAD_ATTEMPTS; attempt += 1) {
let response: Response | null = null;
try {
signal?.throwIfAborted();
response = await fetch(uploadUrl, {
method: "PATCH",
headers: {
@@ -182,8 +322,10 @@ async function uploadChunk(
"Upload-Offset": String(offset),
},
body: file.slice(offset, nextOffset),
signal,
});
} catch (caught) {
if (signal?.aborted) throw caught;
lastFailure = caught;
}
if (response?.ok) {
@@ -203,7 +345,7 @@ async function uploadChunk(
lastFailure = new Error(`Mission Core временно вернул HTTP ${response.status}.`);
}
try {
const confirmed = await readUploadOffset(uploadUrl, file.size);
const confirmed = await readUploadOffset(uploadUrl, file.size, signal);
if (confirmed === nextOffset) return confirmed;
if (confirmed !== offset) {
throw new Error("Сервер подтвердил неожиданное смещение загрузки.");
@@ -246,26 +388,32 @@ export async function saveSimulationViewerSettings(
body: JSON.stringify({
schema_version: settings.schemaVersion,
quality: settings.quality,
visual: settings.visual,
collision: settings.collision,
visual: { rotation_degrees: settings.visual.rotationDegrees },
collision: { rotation_degrees: settings.collision.rotationDegrees },
camera: {
invert_horizontal: settings.camera.invertHorizontal,
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));
}
export async function retrySimulationProject(projectId: string): Promise<SimulationProject> {
const response = await fetch(`${API_ROOT}/${encodeURIComponent(projectId)}/build`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: "{}",
});
return parseProject(await jsonResponse(response));
}
export const retrySimulationProject = startSimulationProjectBuild;
export async function deleteSimulationProject(projectId: string): Promise<void> {
const response = await fetch(`${API_ROOT}/${encodeURIComponent(projectId)}`, { method: "DELETE" });
@@ -321,7 +469,9 @@ function parseProject(value: unknown): SimulationProject {
provider: {
providerId: provider.provider_id === "gaussian-pipeline" ? "gaussian-pipeline" : invalid("provider 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"),
stateStartedAtUtc: nullableString(provider.state_started_at_utc ?? null, "provider state start"),
progress: provider.progress === null ? null : objectValue(provider.progress, "provider progress"),
runtime: provider.runtime === null ? null : objectValue(provider.runtime, "provider runtime"),
},
@@ -345,16 +495,17 @@ function parseProject(value: unknown): SimulationProject {
function parseViewerSettings(value: unknown): SimulationViewerSettings {
const record = objectValue(value, "viewer settings");
exactKeys(record, ["schema_version", "quality", "visual", "collision", "camera"], "viewer settings");
if (record.schema_version !== "missioncore.simulation-viewer-settings/v1") {
exactKeys(record, ["schema_version", "quality", "visual", "collision", "camera", "ugv"], "viewer settings");
if (record.schema_version !== "missioncore.simulation-viewer-settings/v3") {
throw new Error("Настройки сцены вернули неподдерживаемую версию.");
}
const visual = parseLayerViewerSettings(record.visual, "visual settings");
const collision = parseLayerViewerSettings(record.collision, "collision settings");
const camera = objectValue(record.camera, "camera settings");
exactKeys(camera, ["invert_horizontal", "invert_vertical"], "camera settings");
const ugv = parseUgvPreset(record.ugv);
return {
schemaVersion: "missioncore.simulation-viewer-settings/v1",
schemaVersion: "missioncore.simulation-viewer-settings/v3",
quality: qualityValue(record.quality),
visual,
collision,
@@ -362,18 +513,88 @@ function parseViewerSettings(value: unknown): SimulationViewerSettings {
invertHorizontal: booleanValue(camera.invert_horizontal, "horizontal 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(
value: unknown,
label: string,
): { inverted: boolean; axis: SimulationTransformAxis } {
): { rotationDegrees: SimulationEulerRotation } {
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 {
inverted: booleanValue(record.inverted, `${label} inversion`),
axis: axisValue(record.axis),
rotationDegrees: {
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");
}
function axisValue(value: unknown): SimulationTransformAxis {
if (value === "x" || value === "y" || value === "z") return value;
return invalid("transform axis");
function rotationValue(value: unknown, label: string): number {
if (typeof value === "number" && Number.isFinite(value) && value >= -360 && value <= 360) {
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 {
@@ -488,3 +718,7 @@ function statusValue(value: unknown): SimulationProjectStatus {
function invalid(label: string): never {
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);
}
}
+1 -1
View File
@@ -161,7 +161,7 @@ export const workspaces: WorkspaceDefinition[] = [
label: "Симуляции",
title: "Симуляции",
eyebrow: "ТЕСТОВЫЙ КОНТУР / СИМУЛЯЦИИ",
description: "Каталог Gaussian-миров, переносимые сборки и интерактивные PlayCanvas-сцены.",
description: "Каталог Gaussian-миров, переносимые сборки и интерактивные сцены.",
icon: "globe",
kind: "simulations",
groups: [],
@@ -91,6 +91,11 @@
justify-content: space-between;
}
.m4-replay-threat-evidence-viewer[data-mode-controls="content"]
.m4-replay-threat-visual__pane-toolbar[data-pane-toolbar="spatial"] {
right: 3.8rem;
}
.m4-replay-threat-visual__pane-toolbar > *,
.m4-replay-threat-visual__spatial-toolbar-end > * {
pointer-events: auto;
@@ -231,9 +236,10 @@
box-sizing: border-box;
min-width: 0;
align-items: stretch;
width: max-content;
max-width: calc(var(--m4-replay-threat-overlay-pane-width, 100%) - 1.2rem);
grid-template-columns: minmax(0, max-content);
width: min(17.5rem, calc(var(--m4-replay-threat-overlay-pane-width, 100%) - 1.2rem));
max-width: none;
grid-auto-rows: 3.8rem;
grid-template-columns: minmax(0, 1fr);
gap: 0.3rem;
overflow: hidden;
background: transparent;
@@ -242,6 +248,9 @@
}
.m4-replay-threat-visual__overlay > div {
box-sizing: border-box;
height: 3.8rem;
grid-template-rows: auto auto minmax(1.32rem, 1fr);
border-radius: var(--nodedc-radius-control-compact);
background: color-mix(in srgb, var(--nodedc-floating-surface) 80%, transparent);
padding: 0.55rem 0.65rem;
@@ -249,15 +258,24 @@
}
.m4-replay-threat-visual__overlay span,
.m4-replay-threat-visual__overlay strong,
.m4-replay-threat-visual__overlay small {
.m4-replay-threat-visual__overlay strong {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.m4-replay-threat-visual__overlay small {
display: -webkit-box;
min-height: 1.32rem;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
white-space: normal;
}
@media (max-width: 900px) {
.m4-replay-threat-visual__overlay {
width: min(17.5rem, calc(100% - 1.2rem));
max-width: calc(100% - 1.2rem);
}
}
@@ -350,3 +368,30 @@
background: rgb(var(--nodedc-accent-rgb));
opacity: 0.72;
}
.laboratory-metric-evidence-scene__legend span[data-decision="ground-support"]::before {
background: rgb(var(--nodedc-success-rgb));
}
.laboratory-metric-evidence-scene__legend span[data-decision="nonground-occupied"]::before {
background: rgb(var(--nodedc-danger-rgb));
}
.laboratory-metric-evidence-scene__legend span[data-decision="unknown-rejected"]::before {
background: rgb(var(--nodedc-warning-rgb));
}
.laboratory-metric-evidence-scene__legend span[data-decision="unobserved"]::before {
background: var(--nodedc-text-muted);
opacity: 0.42;
}
.laboratory-metric-evidence-scene__legend span[data-decision="performance"] {
font-variant-numeric: tabular-nums;
}
.laboratory-metric-evidence-scene__legend span[data-decision="performance"]::before {
box-sizing: border-box;
border: 1px solid rgb(var(--nodedc-accent-rgb));
background: transparent;
}
+243 -22
View File
@@ -1,6 +1,8 @@
.simulation-workspace {
display: grid;
height: 100%;
min-width: 0;
grid-template-rows: auto minmax(0, 1fr);
gap: 0.9rem;
padding-bottom: 1rem;
}
@@ -17,6 +19,7 @@
.simulation-viewport__notice,
.simulation-catalog__summary,
.simulation-catalog__state,
.simulation-catalog__notice,
.simulation-catalog__actions,
.simulation-project-window__footer-state,
.simulation-source-drop > div,
@@ -60,7 +63,10 @@
}
.simulation-catalog {
position: relative;
display: grid;
min-height: 0;
align-content: start;
gap: 0.55rem;
min-width: 0;
}
@@ -100,23 +106,28 @@
.simulation-catalog__table-wrap {
overflow: auto;
border-radius: 1rem;
background: rgb(255 255 255 / 0.025);
background: transparent;
}
.simulation-catalog__table {
width: 100%;
min-width: 62rem;
border-collapse: collapse;
min-width: 52rem;
border-collapse: separate;
border-spacing: 0 0.34rem;
}
.simulation-catalog__table th,
.simulation-catalog__table td {
border-bottom: 1px solid var(--station-hairline);
padding: 0.72rem 0.8rem;
text-align: left;
vertical-align: middle;
}
.simulation-catalog__table th {
padding-top: 0.2rem;
padding-bottom: 0.18rem;
}
.simulation-catalog__table th:first-child,
.simulation-catalog__table td:first-child {
padding-left: 1rem;
@@ -124,6 +135,8 @@
.simulation-catalog__table th:last-child,
.simulation-catalog__table td:last-child {
width: 6.4rem;
min-width: 6.4rem;
padding-right: 1rem;
}
@@ -131,12 +144,21 @@
transition: background var(--nodedc-duration-fast) var(--nodedc-ease-standard);
}
.simulation-catalog__table tbody tr:hover {
.simulation-catalog__table tbody td {
background: rgb(255 255 255 / 0.025);
transition: background var(--nodedc-duration-fast) var(--nodedc-ease-standard);
}
.simulation-catalog__table tbody tr:last-child td {
border-bottom: 0;
.simulation-catalog__table tbody tr:hover td {
background: rgb(255 255 255 / 0.045);
}
.simulation-catalog__table tbody td:first-child {
border-radius: 0.85rem 0 0 0.85rem;
}
.simulation-catalog__table tbody td:last-child {
border-radius: 0 0.85rem 0.85rem 0;
}
.simulation-catalog__table td {
@@ -156,7 +178,7 @@
display: flex;
align-items: center;
gap: 0.65rem;
min-width: 14rem;
min-width: 11rem;
border: 0;
background: transparent;
color: inherit;
@@ -165,16 +187,26 @@
cursor: pointer;
}
.simulation-catalog__table td:first-child > button > span:last-child {
min-width: 0;
}
.simulation-catalog__table td:first-child > button strong {
display: block;
max-width: 20rem;
overflow: hidden;
overflow: clip;
color: var(--nodedc-text-primary);
font-size: 0.7rem;
text-overflow: ellipsis;
white-space: nowrap;
}
.simulation-catalog__table td:first-child > button small {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.simulation-catalog__project-icon,
.simulation-catalog__empty > span {
display: grid;
@@ -188,10 +220,53 @@
}
.simulation-catalog__actions {
display: flex;
justify-content: flex-end;
gap: 0.28rem;
}
.simulation-catalog__progress {
display: grid;
min-width: 0;
gap: 0.26rem;
}
.simulation-catalog__progress-meta,
.simulation-catalog__progress-stage {
overflow: hidden;
color: var(--nodedc-text-muted);
font-size: 0.54rem;
text-overflow: ellipsis;
white-space: nowrap;
}
.simulation-catalog__progress-track {
position: relative;
overflow: hidden;
width: 100%;
height: 0.3rem;
border-radius: 999px;
background: rgb(255 255 255 / 0.08);
}
.simulation-catalog__progress-track i {
position: absolute;
inset: 0 auto 0 0;
min-width: 0;
border-radius: inherit;
background: rgb(var(--nodedc-accent-rgb));
transition: width var(--nodedc-duration-fast) var(--nodedc-ease-standard);
}
.simulation-catalog__progress[data-indeterminate="true"] .simulation-catalog__progress-track i {
width: 28% !important;
animation: simulation-progress-pulse 1.6s ease-in-out infinite;
}
.simulation-catalog__progress-stage {
font-size: 0.52rem;
}
.simulation-catalog__state,
.simulation-catalog__empty {
min-height: 20rem;
@@ -203,6 +278,67 @@
font-size: 0.65rem;
}
.simulation-catalog__notice {
justify-content: flex-start;
gap: 0.55rem;
border-radius: 0.85rem;
background: rgb(var(--nodedc-danger-rgb) / 0.08);
color: var(--nodedc-danger);
padding: 0.7rem 0.85rem;
font-size: 0.62rem;
}
.simulation-catalog__drop-overlay {
position: absolute;
z-index: 4;
inset: 0;
display: grid;
place-content: center;
justify-items: center;
gap: 0.55rem;
border-radius: 1rem;
background: var(--nodedc-canvas);
color: var(--nodedc-text-muted);
text-align: center;
}
.simulation-catalog__drop-mark {
display: grid;
width: 4rem;
height: 4rem;
margin-bottom: 0.25rem;
place-items: center;
border-radius: 50%;
background: var(--nodedc-glass-control-active);
color: var(--nodedc-glass-control-active-text);
}
.simulation-catalog__drop-overlay strong {
color: var(--nodedc-text-primary);
font-size: 0.86rem;
}
.simulation-catalog__drop-overlay span {
font-size: 0.64rem;
}
.simulation-catalog__drop-overlay small {
color: var(--nodedc-text-muted);
font-size: 0.56rem;
}
@keyframes simulation-progress-pulse {
from { transform: translateX(-105%); }
to { transform: translateX(360%); }
}
@media (prefers-reduced-motion: reduce) {
.simulation-catalog__progress[data-indeterminate="true"] .simulation-catalog__progress-track i {
animation: none;
transform: translateX(0);
}
}
.simulation-catalog__state > div {
display: grid;
gap: 0.2rem;
@@ -342,6 +478,9 @@
}
.simulation-viewport__toolbar {
position: sticky;
z-index: 10;
top: 0;
display: grid;
grid-template-columns: minmax(12rem, 1fr) auto minmax(12rem, 1fr);
align-items: center;
@@ -379,19 +518,34 @@
top: calc(100% + 0.65rem);
right: 0;
display: grid;
grid-template-rows: auto minmax(0, 1fr);
width: min(21rem, calc(100cqw - 1.3rem));
gap: 0.75rem;
border-radius: 0.9rem;
background: rgb(25 27 31 / 0.98);
box-shadow: 0 1rem 3rem rgb(0 0 0 / 0.42);
padding: 0.8rem;
max-height: min(44rem, calc(100vh - 7rem));
overflow: clip;
gap: 0;
padding: 0;
}
.simulation-viewport__settings-head {
position: relative;
z-index: 2;
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
background: var(--nodedc-glass-panel-bg-strong);
padding: 0.8rem;
box-shadow: 0 0.55rem 1rem rgb(0 0 0 / 0.18);
backdrop-filter: blur(18px);
}
.simulation-viewport__settings-scroll {
display: grid;
min-height: 0;
overflow-y: auto;
overscroll-behavior: contain;
gap: 0.75rem;
padding: 0.75rem 0.8rem 0.8rem;
}
.simulation-viewport__settings-head > div,
@@ -440,16 +594,49 @@
gap: 0.55rem;
}
.simulation-viewport__settings-layer-row {
.simulation-viewport__settings-rotations {
display: grid;
grid-template-columns: minmax(0, 1fr) 4.5rem;
align-items: center;
gap: 0.55rem;
gap: 0.7rem;
}
.simulation-viewport__axis-select {
width: 4.5rem;
min-width: 0;
.simulation-viewport__rotation-row {
display: grid;
gap: 0.4rem;
}
.simulation-viewport__rotation-row > strong {
color: var(--nodedc-text-secondary);
font-size: 0.6rem;
}
.simulation-viewport__rotation-fields {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 0.45rem;
}
.simulation-viewport__rotation-field {
gap: 0.22rem;
}
.simulation-viewport__rotation-field .nodedc-field__label-row {
padding-inline: 0.15rem;
}
.simulation-viewport__rotation-field .nodedc-field__label {
font-size: 0.54rem;
}
.simulation-viewport__rotation-input {
min-height: 1.75rem;
padding: 0.2rem 0.55rem;
font-size: 0.64rem;
text-align: right;
}
.simulation-viewport__rotation-input:disabled {
cursor: not-allowed;
opacity: 0.45;
}
.simulation-viewport__settings-error {
@@ -459,6 +646,30 @@
padding: 0.55rem 0.65rem;
}
.simulation-ugv-settings {
gap: 0.7rem;
}
.simulation-ugv-settings__subhead {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 0.65rem;
margin-top: 0.1rem;
padding-top: 0.55rem;
border-top: 1px solid rgb(255 255 255 / 0.055);
}
.simulation-ugv-settings__subhead strong {
color: var(--nodedc-text-secondary);
font-size: 0.58rem;
}
.simulation-ugv-settings__save {
width: 100%;
margin-top: 0.15rem;
}
.simulation-viewport__toolbar-balance {
min-width: 0;
}
@@ -472,7 +683,7 @@
.simulation-viewport__stage {
position: relative;
min-width: 0;
min-height: 30rem;
min-height: 18rem;
overflow: hidden;
}
@@ -491,6 +702,16 @@
cursor: grabbing;
}
.simulation-viewport[data-control-mode="ugv"] .simulation-viewport__stage canvas,
.simulation-viewport[data-control-mode="ugv"] .simulation-viewport__stage canvas:active {
cursor: grab;
}
.simulation-viewport[data-control-mode="ugv"] .simulation-viewport__stage canvas.is-orbiting,
.simulation-viewport[data-control-mode="ugv"] .simulation-viewport__stage canvas.is-orbiting:active {
cursor: grabbing;
}
.simulation-viewport__stage canvas:focus-visible {
box-shadow: inset 0 0 0 2px rgb(var(--nodedc-accent-rgb) / 0.7);
}
@@ -48,6 +48,9 @@ import { M48StaticOccupancyQualificationResultView } from "./M48StaticOccupancyQ
import { M48R3StaticOccupancyShadowResultView } from "./M48R3StaticOccupancyShadowResult";
import { M48SFixedClassDetectorResultView } from "./M48SFixedClassDetectorResult";
import { M48TRiskQualityResultView } from "./M48TRiskQualityResult";
import { M49TgsFailClosedResultView } from "./M49TgsFailClosedResult";
import { M49TgsFullShadowResultView } from "./M49TgsFullShadowResult";
import { VegetationShadowResultView } from "./VegetationShadowResult";
export { isAdvancedLaboratoryWorkId };
export type { AdvancedLaboratoryWorkId };
@@ -90,6 +93,9 @@ export function AdvancedLaboratoryResult({
failedSessionId: string | null;
replayError: string | null;
}) {
if (workId === "lab-v1-vegetation-shadow" && results.vegetationShadow) {
return <VegetationShadowResultView rigLabel={rigLabel} result={results.vegetationShadow} />;
}
if (workId === "m48-object-centric-quality" && results.m48) {
return <M48ObjectCentricQualityResultView rigLabel={rigLabel} result={results.m48} />;
}
@@ -108,6 +114,12 @@ export function AdvancedLaboratoryResult({
if (workId === "m48t-risk-quality-temporal" && results.m48t) {
return <M48TRiskQualityResultView rigLabel={rigLabel} result={results.m48t} />;
}
if (workId === "m49-tgs-fail-closed-evidence" && results.m49Tgs) {
return <M49TgsFailClosedResultView rigLabel={rigLabel} result={results.m49Tgs} />;
}
if (workId === "m49-tgs-full-shadow" && results.m49TgsFull) {
return <M49TgsFullShadowResultView rigLabel={rigLabel} result={results.m49TgsFull} />;
}
if (workId === "m47-reference-graph-shadow" && results.m47Graph) {
return <M47ReferenceGraphResultView rigLabel={rigLabel} result={results.m47Graph} />;
}
@@ -28,6 +28,119 @@ const ATLAS_MODES = [
] as const;
type AtlasMode = typeof ATLAS_MODES[number]["value"];
const MASK_COMPARISON_MODES = [
{ value: "source", label: "SOURCE" },
{ value: "truth", label: "TRUTH" },
{ value: "prediction", label: "PREDICTION" },
{ value: "error", label: "ERROR" },
] as const;
const MASK_COMPARISON_CANDIDATES = [
{ value: "ddrnet", label: "DDRNET" },
{ value: "ppliteseg", label: "PPLITE" },
] as const;
type MaskComparisonMode = typeof MASK_COMPARISON_MODES[number]["value"];
type MaskComparisonCandidate = typeof MASK_COMPARISON_CANDIDATES[number]["value"];
export interface M48MaskComparisonCase {
caseId: string;
title: string;
context?: string;
sourceUrl: string;
truthUrl: string;
predictions: Readonly<Record<MaskComparisonCandidate, string>>;
errors: Readonly<Record<MaskComparisonCandidate, string>>;
}
function M48MaskComparisonScene({
item,
mode,
candidate,
}: {
item: M48MaskComparisonCase;
mode: MaskComparisonMode;
candidate: MaskComparisonCandidate;
}) {
const overlay = mode === "truth"
? item.truthUrl
: mode === "prediction"
? item.predictions[candidate]
: mode === "error"
? item.errors[candidate]
: null;
return (
<div className="recorded-evidence-image-scene">
<img src={item.sourceUrl} alt="" draggable={false} />
{overlay ? <img src={overlay} alt="" draggable={false} /> : null}
</div>
);
}
export function M48MaskComparisonVisual({
cases,
initialCandidate,
}: {
cases: readonly M48MaskComparisonCase[];
initialCandidate: MaskComparisonCandidate;
}) {
const [index, setIndex] = useState(0);
const [mode, setMode] = useState<MaskComparisonMode>("error");
const [candidate, setCandidate] = useState<MaskComparisonCandidate>(initialCandidate);
const [expanded, setExpanded] = useState(false);
const item = cases[index] ?? null;
return (
<LaboratoryEvidenceViewer
label="M4.8 vegetation truth comparison"
className="m48-atlas-visual"
mode={mode}
modes={MASK_COMPARISON_MODES}
secondaryMode={{
value: candidate,
modes: MASK_COMPARISON_CANDIDATES,
label: "Сравниваемая модель",
onChange: setCandidate,
}}
expanded={expanded}
onModeChange={setMode}
onExpandedChange={setExpanded}
chromeLayout="stacked"
actions={(
<>
<IconButton
label="Предыдущий vegetation hard case"
disabled={!cases.length}
onClick={() => setIndex((current) => (current - 1 + cases.length) % cases.length)}
>
<Icon name="chevron-left" size={16} />
</IconButton>
<IconButton
label="Следующий vegetation hard case"
disabled={!cases.length}
onClick={() => setIndex((current) => (current + 1) % cases.length)}
>
<Icon name="chevron-right" size={16} />
</IconButton>
</>
)}
overlay={item ? (
<div className="m48-atlas-visual__case">
<StatusBadge tone="accent">GOOSE TRUTH · {index + 1}/{cases.length}</StatusBadge>
<strong>{item.title}</strong>
{item.context ? <small>{item.context}</small> : null}
</div>
) : null}
>
{item ? (
<M48MaskComparisonScene item={item} mode={mode} candidate={candidate} />
) : (
<div className="m48-atlas-visual__state" role="alert">
<Icon name="alert" size={18} />
Vegetation hard-case каталог пуст.
</div>
)}
</LaboratoryEvidenceViewer>
);
}
function message(error: unknown): string {
return error instanceof Error && error.message.trim() ? error.message : "M4.8 evidence недоступно.";
}
@@ -0,0 +1,152 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import type {
RecordedEvidenceSemanticClass,
RecordedEvidenceSemanticPaletteEntry,
} from "../../components/laboratory/RecordedEvidenceSemanticMaskOverlay";
import {
fetchM49TgsAnchorSpatial,
type M49TgsAnchorSpatial,
type M49TgsFailClosedResult,
type M49TgsStateCode,
} from "../../core/laboratory/m49TgsFailClosed";
import {
M4ReplayThreatVisual,
type M4ReplayClassifiedSpatialFrame,
type M4ReplayThreatReviewAnchor,
} from "./M4ReplayThreatVisual";
const CLASSES: readonly RecordedEvidenceSemanticClass[] = [
{ id: 1, label: "Ground support" },
{ id: 2, label: "Non-ground occupied" },
{ id: 3, label: "Unknown / rejected" },
];
const PALETTE: readonly RecordedEvidenceSemanticPaletteEntry[] = [
{ classId: 1, color: { kind: "token", token: "--nodedc-success-rgb" } },
{ classId: 2, color: { kind: "token", token: "--nodedc-danger-rgb" } },
{ classId: 3, color: { kind: "token", token: "--nodedc-warning-rgb" } },
];
function cellState(code: M49TgsStateCode): M4ReplayClassifiedSpatialFrame["cellsMapGravityLocal"][number]["state"] {
if (code === 1) return "ground-support";
if (code === 2) return "nonground-occupied";
if (code === 3) return "unknown-rejected";
return "unobserved";
}
function message(error: unknown): string {
return error instanceof Error && error.message.trim()
? error.message
: "M49 TGS spatial evidence недоступно.";
}
export function M49TgsFailClosedEvidence({
result,
}: {
result: M49TgsFailClosedResult;
}) {
const [activeSequence, setActiveSequence] = useState<number | null>(null);
const [spatial, setSpatial] = useState<M49TgsAnchorSpatial | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const spatialCacheRef = useRef(new Map<string, M49TgsAnchorSpatial>());
const anchorSequences = useMemo(
() => new Set(result.metrics.primary.map((item) => item.anchorFrameIndex)),
[result.metrics.primary],
);
const expectedAtSequence = activeSequence !== null && anchorSequences.has(activeSequence);
useEffect(() => {
if (activeSequence === null || !anchorSequences.has(activeSequence)) {
setSpatial(null);
setLoading(false);
setError(null);
return;
}
const cacheKey = `${result.resultId}:${activeSequence}`;
const cached = spatialCacheRef.current.get(cacheKey);
if (cached) {
setSpatial(cached);
setLoading(false);
setError(null);
return;
}
const controller = new AbortController();
setSpatial(null);
setLoading(true);
setError(null);
void fetchM49TgsAnchorSpatial(
result.resultId,
activeSequence,
"causal_rolling_1s",
{ signal: controller.signal },
)
.then((next) => {
if (!controller.signal.aborted) {
spatialCacheRef.current.set(cacheKey, next);
setSpatial(next);
}
})
.catch((caught: unknown) => {
if (!controller.signal.aborted) setError(message(caught));
})
.finally(() => {
if (!controller.signal.aborted) setLoading(false);
});
return () => controller.abort();
}, [activeSequence, anchorSequences, result.resultId]);
const reviewAnchors = useMemo<readonly M4ReplayThreatReviewAnchor[]>(
() => result.metrics.primary.map((item) => ({
id: `m49-tgs-${item.anchorFrameIndex}`,
sourceSequence: item.anchorFrameIndex,
extentXyxyNormalized: [0, 0, 0, 0],
matchedAtThreshold: false,
statusLabel: "визуальная проверка",
})),
[result.metrics.primary],
);
const classifiedFrame = useMemo<M4ReplayClassifiedSpatialFrame | null>(() => {
if (!spatial) return null;
return {
sourceSequence: spatial.sourceSequence,
pointsMapGravityLocalXyzM: spatial.pointsXyzM,
pointClassIds: spatial.pointStates,
cellsMapGravityLocal: spatial.costmap.centersXyM.map((center, index) => ({
centerXyM: center,
zBoundsM: spatial.costmap.zBoundsM[index]!,
state: cellState(spatial.costmap.states[index]!),
})),
cellSizeM: spatial.costmap.cellSizeM,
classes: CLASSES,
palette: PALETTE,
};
}, [spatial]);
const handleSequenceChange = useCallback((sequence: number | null) => {
setActiveSequence(sequence);
}, []);
return (
<M4ReplayThreatVisual
resultId={result.source.linkedVisualResultId}
reviewAnchors={reviewAnchors}
showReviewAnchorBoxes={false}
reviewLabel="10 gravity-aligned TGS anchors"
evidenceLabel="M49 · TGS fail-closed"
initialSpatialMode="3d"
onActiveSequenceChange={handleSequenceChange}
classifiedSpatialLayer={{
label: "TGS fail-closed · causal rolling 1 s",
pointLayerLabel: "TGS POINTS",
cellLayerLabel: "COSTMAP",
expectedAtSequence,
frame: classifiedFrame,
loading,
error,
}}
/>
);
}
@@ -0,0 +1,92 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { M49TgsFailClosedResult } from "../../core/laboratory/m49TgsFailClosed";
import { M49TgsFailClosedEvidence } from "./M49TgsFailClosedEvidence";
function number(value: number, digits = 1): string {
return value.toLocaleString("ru-RU", { maximumFractionDigits: digits });
}
export function M49TgsFailClosedResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: M49TgsFailClosedResult;
}) {
const worst = [...result.metrics.primary].sort(
(left, right) => (
right.nongroundPointCount / Math.max(right.pointCount, 1)
- left.nongroundPointCount / Math.max(left.pointCount, 1)
),
)[0]!;
const status = "Representation complete; визуальное качество ещё не принято";
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="M4.9T4 · TRAVEL TGS fail-closed evidence"
description="TRAVEL GroundSeg запущен без AOS на десяти immutable RAVNOVES00 anchors. Вход сохранён в gravity-aligned map frame; каждый eligible point получил состояние, а каждая costmap-ячейка остаётся ground, occupied, rejected или unobserved."
status={status}
statusTone="warning"
facts={[
{ label: "Конфигурация", value: `${rigLabel} RIGHT · Camera + gravity-aligned LiDAR · 10 anchors` },
{ label: "Метод", value: "TRAVEL TGS only · AOS OFF · causal rolling 1 s" },
{ label: "Evidence", value: `${result.metrics.anchorCount} anchors · ${result.metrics.costmapCellCount.toLocaleString("ru-RU")} cells/anchor · all points accounted` },
{ label: "Нагрузка", value: `Worker 006 CPU-only · GPU 0 · wrapper ${number(result.execution.wrapperElapsedSeconds, 2)} с` },
{ label: "Authority", value: "REPLAY-SIMULATED · visual/traversability/navigation/actuation OFF" },
]}
brief={{
question: "Отделяет ли готовый TRAVEL TGS опорную поверхность от неизвестной занятой геометрии достаточно чисто, чтобы заменить самодельный static-obstacle threshold pipeline?",
approach: "На десяти сложных кадрах проверяется полный gravity-aligned point set и fail-closed costmap. Зелёное — опора, красное — non-ground occupied, жёлтое — rejected/unknown, тёмное — unobserved; камера остаётся синхронным первичным контекстом.",
principalResult: `Контракт представления закрыт: ${result.metrics.anchorProfileCount}/20 профилей, ни одной потерянной eligible point, AOS и GPU отсутствуют. Process wall rolling p50/max: ${number(result.metrics.processWallRollingP50Ms, 0)}/${number(result.metrics.processWallRollingMaxMs, 0)} мс.`,
limitation: `Качество не принято: особенно проверить кадр ${worst.anchorFrameIndex + 1}, где ${number(worst.nongroundPointCount / Math.max(worst.pointCount, 1) * 100)}% rolling points помечены non-ground. Это может быть реальная боковая геометрия либо ложная блокировка поверхности.`,
}}
method={{
completeness: "complete",
executionClass: "deterministic",
pipelineId: "travel-tgs-gravity-aligned-fail-closed/v1",
components: [
{ kind: "source", name: "RAVNOVES00", version: "10 immutable anchors", role: "camera + registered map increments", identitySha256: result.source.sourcePackSha256 },
{ kind: "algorithm", name: "TRAVEL GroundSeg", version: "95dc2fbd66a343efd9060c45a5711b6307a950a4", role: "ground/nonground separation; AOS excluded", identitySha256: result.configuration.configSha256 },
{ kind: "algorithm", name: "fail-closed complement adapter", version: "v1", role: "explicit rejected points and unobserved cells", identitySha256: result.resultId.split("-").at(-1) ?? null },
],
}}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="M4.9T4 VISUAL EVIDENCE · CAMERA + GRAVITY-ALIGNED TGS"
title="10 anchors: полный TGS point set и четырёхсостояний costmap на том же recorded timeline"
kind="recorded-replay"
resizable
>
<M49TgsFailClosedEvidence result={result} />
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
title="Что уже доказано и что проверяем глазами"
status={status}
statusTone="warning"
metrics={[
{ label: "Point accounting", value: "100%", hint: "ground + non-ground + rejected = exact eligible input" },
{ label: "Anchors", value: `${result.metrics.anchorCount}/10`, hint: "current + causal rolling 1 s" },
{ label: "Costmap", value: `${result.metrics.costmapCellCount.toLocaleString("ru-RU")} cells`, hint: `${number(result.configuration.cellSizeM, 2)} м · radius ${number(result.configuration.radiusM, 0)} м` },
{ label: "Process wall rolling", value: `${number(result.metrics.processWallRollingP50Ms, 0)} / ${number(result.metrics.processWallRollingMaxMs, 0)} мс`, hint: "p50 / max · CPU process envelope, не realtime integration" },
{ label: "GPU / AOS", value: "0 / OFF", hint: "Worker 006; Frigate budget не затронут" },
]}
conclusion={{
proved: "Готовый TGS можно встроить fail-closed: исходные точки не теряются, unknown не становится free, AOS не нужен, а вычисление укладывается в лёгкий CPU-контур на этих anchors.",
notProved: "Не доказано, что красный non-ground слой не режет дорогу, траву или допустимые просветы. Нет независимой terrain truth, полного replay, realtime graph integration и модели корпуса.",
decision: "Открыть десять anchors по очереди. Если красное остаётся на реальных препятствиях и не перекрывает видимую опорную поверхность, TGS идёт в полный shadow; иначе кандидат отклоняется без ручной подгонки порогов под эти кадры.",
}}
/>
)}
/>
);
}
@@ -0,0 +1,227 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import type {
RecordedEvidenceSemanticClass,
RecordedEvidenceSemanticPaletteEntry,
} from "../../components/laboratory/RecordedEvidenceSemanticMaskOverlay";
import {
fetchE47SemanticSlamResult,
type E47SemanticSlamResult,
} from "../../core/laboratory/e47SemanticSlam";
import {
fetchM49PhysicalSafetyPlaybackIdForSource,
M49PhysicalSafetyPlaybackBuffer,
type M49PhysicalSafetyPlaybackProgress,
} from "../../core/laboratory/m49PhysicalSafetyPlayback";
import {
fetchM49TgsFullShadowPlaybackPack,
type M49TgsFullShadowResult,
type M49TgsFullShadowPlaybackPack,
type M49TgsFullShadowPlaybackProgress,
} from "../../core/laboratory/m49TgsFullShadow";
import {
M4ReplayThreatVisual,
type M4ReplayClassifiedSpatialFrame,
} from "./M4ReplayThreatVisual";
const CLASSES: readonly RecordedEvidenceSemanticClass[] = [
{ id: 1, label: "Ground support" },
{ id: 2, label: "Non-ground occupied" },
{ id: 3, label: "Unknown / rejected" },
];
const PALETTE: readonly RecordedEvidenceSemanticPaletteEntry[] = [
{ classId: 1, color: { kind: "token", token: "--nodedc-success-rgb" } },
{ classId: 2, color: { kind: "token", token: "--nodedc-danger-rgb" } },
{ classId: 3, color: { kind: "token", token: "--nodedc-warning-rgb" } },
];
function message(error: unknown): string {
return error instanceof Error && error.message.trim()
? error.message
: "Полный TGS spatial frame недоступен.";
}
export function M49TgsFullShadowEvidence({ result }: { result: M49TgsFullShadowResult }) {
const [activeSequence, setActiveSequence] = useState<number | null>(null);
const [semantic, setSemantic] = useState<E47SemanticSlamResult | null>(null);
const [semanticError, setSemanticError] = useState<string | null>(null);
const [playbackPack, setPlaybackPack] = useState<M49TgsFullShadowPlaybackPack | null>(null);
const [physicalPlayback, setPhysicalPlayback] = useState<M49PhysicalSafetyPlaybackBuffer | null>(null);
const [physicalProgress, setPhysicalProgress] = useState<M49PhysicalSafetyPlaybackProgress | null>(null);
const [physicalRevision, setPhysicalRevision] = useState(0);
const [playbackProgress, setPlaybackProgress] = useState<M49TgsFullShadowPlaybackProgress>({
phase: "manifest",
trackId: null,
loadedBytes: 0,
totalBytes: 0,
});
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const controller = new AbortController();
setSemantic(null);
setSemanticError(null);
void fetchE47SemanticSlamResult({
resultId: result.source.linkedSemanticResultId,
signal: controller.signal,
})
.then((next) => {
if (controller.signal.aborted) return;
if (!next || next.baseM4ResultId !== result.source.linkedVisualResultId) {
throw new Error("Semantic archive не совпал с исходным M4 timeline.");
}
setSemantic(next);
})
.catch((caught: unknown) => {
if (!controller.signal.aborted) setSemanticError(message(caught));
});
return () => controller.abort();
}, [result.source.linkedSemanticResultId, result.source.linkedVisualResultId]);
useEffect(() => {
const controller = new AbortController();
setPlaybackPack(null);
setPhysicalPlayback(null);
setPhysicalProgress(null);
setPhysicalRevision(0);
setPlaybackProgress({ phase: "manifest", trackId: null, loadedBytes: 0, totalBytes: 0 });
setError(null);
void fetchM49PhysicalSafetyPlaybackIdForSource(result.resultId, {
signal: controller.signal,
}).then(async (physicalResultId) => {
if (controller.signal.aborted) return;
if (physicalResultId) {
const playback = await M49PhysicalSafetyPlaybackBuffer.open(physicalResultId, {
signal: controller.signal,
onProgress: setPhysicalProgress,
});
if (!controller.signal.aborted) setPhysicalPlayback(playback);
return;
}
const pack = await fetchM49TgsFullShadowPlaybackPack(result.resultId, {
signal: controller.signal,
onProgress: setPlaybackProgress,
});
if (!controller.signal.aborted) setPlaybackPack(pack);
}).catch((caught: unknown) => {
if (!controller.signal.aborted) setError(message(caught));
});
return () => controller.abort();
}, [result.resultId]);
useEffect(() => {
if (activeSequence === null || !physicalPlayback) return;
let active = true;
const resident = physicalPlayback.frameIfResident(activeSequence);
void physicalPlayback.prepare(activeSequence).then(() => {
if (active && !resident) setPhysicalRevision((value) => value + 1);
}).catch((caught: unknown) => {
if (active) setError(message(caught));
});
return () => {
active = false;
};
}, [activeSequence, physicalPlayback]);
const classifiedFrame = useMemo<M4ReplayClassifiedSpatialFrame | null>(() => {
if (activeSequence === null) return null;
if (physicalPlayback) {
const frame = physicalPlayback.frameIfResident(activeSequence);
if (!frame) return null;
return {
sourceSequence: activeSequence,
sampleAvailable: frame.metadata.sampleAvailable,
sourcePointCount: frame.metadata.metrics.eligible_point_count ?? 0,
pointsMapGravityLocalXyzM: [],
pointClassIds: [],
cellsMapGravityLocal: [],
packedCellsMapGravityLocal: {
centersXyM: frame.centersXyM,
stateCodes: frame.states,
zBoundsM: frame.zBoundsM,
},
cellSizeM: frame.cellSizeM,
classes: CLASSES,
palette: PALETTE,
};
}
if (!playbackPack) return null;
const frame = playbackPack.frames[activeSequence];
if (!frame) return null;
const cellOffset = activeSequence * playbackPack.cellCount;
const zOffset = cellOffset * 2;
return {
sourceSequence: activeSequence,
sampleAvailable: frame.sampleAvailable,
sourcePointCount: frame.metrics.eligiblePointCount,
pointsMapGravityLocalXyzM: [],
pointClassIds: [],
cellsMapGravityLocal: [],
packedCellsMapGravityLocal: {
centersXyM: playbackPack.centersXyM,
stateCodes: playbackPack.states.subarray(
cellOffset,
cellOffset + playbackPack.cellCount,
),
zBoundsM: playbackPack.zBoundsM.subarray(
zOffset,
zOffset + playbackPack.cellCount * 2,
),
},
cellSizeM: result.configuration.cellSizeM,
classes: CLASSES,
palette: PALETTE,
};
}, [activeSequence, physicalPlayback, physicalRevision, playbackPack, result.configuration.cellSizeM]);
const loading = activeSequence !== null && !classifiedFrame && !error;
const progressPercent = playbackProgress.totalBytes > 0
? Math.min(100, Math.round((playbackProgress.loadedBytes / playbackProgress.totalBytes) * 100))
: 0;
const loadingLabel = physicalProgress
? physicalProgress.phase === "ready"
? "Sealed TGS playback готов"
: `Буферизуем sealed TGS · ${physicalProgress.residentChunkIndexes.length}/3 chunks · ${(physicalProgress.loadedBytes / 1_048_576).toFixed(1)} МБ`
: playbackProgress.phase === "manifest"
? "Проверяем playback manifest"
: playbackProgress.phase === "ready"
? "TGS playback готов"
: `Подготавливаем TGS playback · ${progressPercent}% · ${(playbackProgress.loadedBytes / 1_048_576).toFixed(1)}/${(playbackProgress.totalBytes / 1_048_576).toFixed(1)} МБ`;
const handleSequenceChange = useCallback((sequence: number | null) => {
setActiveSequence(sequence);
}, []);
return (
<>
<M4ReplayThreatVisual
resultId={result.source.linkedVisualResultId}
semantic={semantic ? {
resultId: semantic.resultId,
taxonomy: semantic.taxonomy,
} : undefined}
showReviewAnchorBoxes={false}
reviewLabel="4 489 source-paced TGS frames"
evidenceLabel="M49 · full TGS shadow"
initialSpatialMode="3d"
onActiveSequenceChange={handleSequenceChange}
classifiedSpatialLayer={{
label: "TGS full shadow · causal rolling 1 s",
pointLayerLabel: "SOURCE POINTS",
cellLayerLabel: "TGS COSTMAP",
expectedAtSequence: true,
frame: classifiedFrame,
loading,
loadingLabel,
error,
replacePointCloud: false,
}}
/>
{semanticError ? (
<div className="m4-replay-threat-visual__pane-status" role="alert">
Semantic overlay недоступен: {semanticError}
</div>
) : null}
</>
);
}
@@ -0,0 +1,90 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import type { M49TgsFullShadowResult } from "../../core/laboratory/m49TgsFullShadow";
import { M49TgsFullShadowEvidence } from "./M49TgsFullShadowEvidence";
function number(value: number, digits = 1): string {
return value.toLocaleString("ru-RU", { maximumFractionDigits: digits });
}
export function M49TgsFullShadowResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: M49TgsFullShadowResult;
}) {
const accepted = result.decision.candidateRetained;
const status = accepted
? "Source-paced CPU shadow принят; визуальное и integrated-graph качество ещё проверяются"
: "Source-paced CPU shadow не прошёл performance gate";
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="M4.9T5 · полный source-paced TRAVEL TGS shadow"
description="Один CPU-only процесс прошёл весь recorded timeline RAVNOVES00 в исходном темпе. Камера остаётся владельцем времени; dense source cloud сохраняется, поверх него показывается четырёхсостояний TGS costmap."
status={status}
statusTone={accepted ? "success" : "danger"}
facts={[
{ label: "Конфигурация", value: `${rigLabel} RIGHT · gravity-aligned LiDAR · causal ${number(result.configuration.historySeconds)} с` },
{ label: "Timeline", value: `${result.timeline.frameCount.toLocaleString("ru-RU")} кадров · ${result.timeline.availableLidarFrameCount.toLocaleString("ru-RU")} LiDAR · ${result.timeline.missingLidarFrameCount} UNOBSERVED` },
{ label: "Нагрузка", value: `Worker 006 CPU-only · GPU 0 · ${number(result.timeline.effectiveFps, 3)} source FPS` },
{ label: "Authority", value: "REPLAY-SIMULATED · navigation/actuation OFF · integrated graph отдельно" },
]}
brief={{
question: "Удерживает ли готовый TRAVEL TGS полный десятигерцовый replay без очереди и потери кадров?",
approach: "Все 4 489 camera frames планируются по исходным timestamps. Для 3 928 доступных LiDAR frames выполняется causal rolling 1 s; 561 пропуск остаётся полностью UNOBSERVED.",
principalResult: `${result.timeline.frameCount}/4 489 frames и ${result.pointAccounting.eligible.toLocaleString("ru-RU")} eligible points учтены; TGS p95/p99 ${number(result.performance.candidateTgsMs.p95, 2)}/${number(result.performance.candidateTgsMs.p99, 2)} мс, completion age p99 ${number(result.performance.completionAgeMs.p99, 2)} мс, capacity drops ${result.performance.capacityDropCount}.`,
limitation: "Это isolated CPU shadow. Он ещё не доказывает качество красных occupied-ячеек, проходимость для конкретного корпуса или регрессию FPS полного world-state graph.",
}}
method={{
completeness: "complete",
executionClass: "deterministic",
pipelineId: "travel-tgs-full-source-paced-shadow/v1",
components: [
{ kind: "source", name: "RAVNOVES00", version: "4 489-frame recorded timeline", role: "camera-owned source clock + registered LiDAR", identitySha256: result.source.sourcePackSha256 },
{ kind: "algorithm", name: "TRAVEL GroundSeg", version: "95dc2fbd66a343efd9060c45a5711b6307a950a4", role: "gravity-aligned ground/non-ground separation; AOS OFF", identitySha256: result.configuration.configSha256 },
{ kind: "algorithm", name: "fail-closed costmap adapter", version: "v1", role: "occupied > rejected > ground > unobserved; no free inference", identitySha256: result.resultId.split("-").at(-1) ?? null },
],
}}
/>
)}
evidence={(
<LaboratoryEvidence
eyebrow="M4.9T5 VISUAL EVIDENCE · FULL CAMERA TIMELINE + SOURCE CLOUD + TGS COSTMAP"
title="Полный timeline: dense исходные точки сохранены, TGS-ячейки синхронны каждому кадру"
kind="recorded-replay"
resizable
>
<M49TgsFullShadowEvidence result={result} />
</LaboratoryEvidence>
)}
result={(
<LaboratoryResultSummary
title="Что доказал полный прогон"
status={status}
statusTone={accepted ? "success" : "danger"}
metrics={[
{ label: "Timeline", value: `${result.timeline.frameCount}/4 489`, hint: `${result.timeline.availableLidarFrameCount} LiDAR + ${result.timeline.missingLidarFrameCount} explicit UNOBSERVED` },
{ label: "TGS p95 / p99", value: `${number(result.performance.candidateTgsMs.p95, 2)} / ${number(result.performance.candidateTgsMs.p99, 2)} мс`, hint: "чистый candidate stage на CPU" },
{ label: "Completion age p99", value: `${number(result.performance.completionAgeMs.p99, 2)} мс`, hint: "от source timestamp до готового frame result" },
{ label: "Capacity drops", value: String(result.performance.capacityDropCount), hint: "кадры не отбрасывались ради темпа" },
{ label: "Point accounting", value: "100%", hint: `${result.pointAccounting.eligible.toLocaleString("ru-RU")} eligible points` },
]}
conclusion={{
proved: "Полный CPU-only TGS shadow воспроизводимо проходит recorded source clock, сохраняет fail-closed представление и не использует AOS/GPU.",
notProved: "Не приняты visual traversability, модель корпуса, камера-проекция TGS и нагрузка после встраивания в полный realtime world-state graph.",
decision: accepted
? "Кандидат остаётся. Просмотреть полный timeline, затем подключить shadow к realtime graph и измерить общий FPS/latency regression."
: "Кандидат не встраивать; сначала локализовать performance gate, который не прошёл полный replay.",
}}
/>
)}
/>
);
}
@@ -1,4 +1,4 @@
import { useEffect, useMemo, useRef, useState, type CSSProperties } from "react";
import { useCallback, useEffect, useMemo, useRef, useState, type CSSProperties } from "react";
import {
Button,
Icon,
@@ -12,7 +12,9 @@ import {
import { ObservationTimeline } from "../../components/ObservationTimeline";
import {
LaboratoryMetricEvidenceScene,
type LaboratoryMetricCellEvidence,
type LaboratoryMetricEvidenceSceneHandle,
type LaboratoryMetricPackedCellEvidence,
type LaboratoryMetricSceneMode,
} from "../../components/laboratory/LaboratoryMetricEvidenceScene";
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
@@ -95,7 +97,16 @@ function SpatialState({ message: text }: { message: string }) {
export interface M4ReplayThreatSemanticLayer {
resultId: string;
taxonomy: readonly E47SemanticClass[];
spatialResultId?: string | null;
maskUrl?: (sequence: number) => string;
label?: string;
maskAriaLabel?: string;
taxonomy: readonly {
classId: number;
label: string;
disposition: "labeled" | "ambiguous" | "prediction" | "undefined";
colorRgb: readonly [number, number, number];
}[];
}
export interface M4ReplayThreatReviewAnchor {
@@ -103,6 +114,40 @@ export interface M4ReplayThreatReviewAnchor {
sourceSequence: number;
extentXyxyNormalized: readonly [number, number, number, number];
matchedAtThreshold: boolean;
statusLabel?: string;
}
export interface M4ReplayClassifiedSpatialFrame {
sourceSequence: number;
sampleAvailable?: boolean;
sourcePointCount?: number;
pointsMapGravityLocalXyzM: readonly (readonly [number, number, number])[];
pointClassIds: readonly (number | null)[];
cellsMapGravityLocal: readonly {
centerXyM: readonly [number, number];
zBoundsM: readonly [number | null, number | null];
state: LaboratoryMetricCellEvidence["state"];
}[];
packedCellsMapGravityLocal?: {
centersXyM: Float32Array;
zBoundsM: Float32Array;
stateCodes: Uint8Array;
};
cellSizeM: number;
classes: readonly RecordedEvidenceSemanticClass[];
palette: readonly RecordedEvidenceSemanticPaletteEntry[];
}
export interface M4ReplayClassifiedSpatialLayer {
label: string;
pointLayerLabel: string;
cellLayerLabel: string;
expectedAtSequence: boolean;
frame: M4ReplayClassifiedSpatialFrame | null;
loading: boolean;
loadingLabel?: string;
error: string | null;
replacePointCloud?: boolean;
}
const EMPTY_REVIEW_ANCHORS: readonly M4ReplayThreatReviewAnchor[] = [];
@@ -115,6 +160,11 @@ export function M4ReplayThreatVisual({
reviewLabel = "Контрольные примеры M4.8R1",
timelineEndpointRoot,
evidenceLabel = "M4.6",
initialSpatialMode = null,
classifiedSpatialLayer,
showReferenceMediaLayers = true,
showSpatialOverlaySummary = true,
onActiveSequenceChange,
}: {
resultId: string;
semantic?: M4ReplayThreatSemanticLayer;
@@ -123,9 +173,16 @@ export function M4ReplayThreatVisual({
reviewLabel?: string;
timelineEndpointRoot?: string;
evidenceLabel?: string;
initialSpatialMode?: LaboratoryMetricSceneMode | null;
classifiedSpatialLayer?: M4ReplayClassifiedSpatialLayer;
showReferenceMediaLayers?: boolean;
showSpatialOverlaySummary?: boolean;
onActiveSequenceChange?: (sequence: number | null) => void;
}) {
const [mediaMode, setMediaMode] = useState<M4ThreatMediaMode | null>("video");
const [spatialMode, setSpatialMode] = useState<LaboratoryMetricSceneMode | null>(null);
const [spatialMode, setSpatialMode] = useState<LaboratoryMetricSceneMode | null>(
initialSpatialMode,
);
const [showCurrentIncrement, setShowCurrentIncrement] = useState(true);
const [showLocalSurface, setShowLocalSurface] = useState(true);
const [showRollingMap, setShowRollingMap] = useState(true);
@@ -220,6 +277,9 @@ export function M4ReplayThreatVisual({
}, [resultId]);
if (timelineFrame.activeFrame) lastFrameRef.current = timelineFrame.activeFrame;
const frame = timelineFrame.activeFrame ?? lastFrameRef.current;
useEffect(() => {
onActiveSequenceChange?.(timelineFrame.activeSequence);
}, [onActiveSequenceChange, timelineFrame.activeSequence]);
const lastSpatialFrameRef = useRef<{
resultId: string;
frame: M4ThreatTimelineFrame;
@@ -233,16 +293,30 @@ export function M4ReplayThreatVisual({
? lastSpatialFrameRef.current.frame
: null;
const cameraPointOverlay = useM4ThreatCameraPointOverlay({
enabled: showMediaPoints,
enabled: showReferenceMediaLayers && showMediaPoints,
resultId,
sequence: frame?.sequence ?? null,
endpointRoot: timelineEndpointRoot,
});
const semanticSpatialResultId = semantic
? semantic.spatialResultId === undefined ? semantic.resultId : semantic.spatialResultId
: null;
const spatialSemanticTaxonomy = useMemo<readonly E47SemanticClass[]>(
() => semanticSpatialResultId && semantic
? semantic.taxonomy.map((item) => ({
classId: item.classId,
label: item.label,
disposition: item.disposition === "ambiguous" ? "ambiguous" : "labeled",
colorRgb: item.colorRgb,
}))
: [],
[semantic, semanticSpatialResultId],
);
const semanticTimeline = useE47SemanticTimelineFrame({
resultId: semantic?.resultId ?? null,
resultId: semanticSpatialResultId,
activeSequence: frame?.sequence ?? timelineFrame.activeSequence,
frameCount: metadata.timeline?.frameCount ?? 0,
taxonomy: semantic?.taxonomy ?? [],
taxonomy: spatialSemanticTaxonomy,
});
const displayingBufferedFrame = Boolean(
frame
@@ -292,7 +366,8 @@ export function M4ReplayThreatVisual({
const staticObstacleBoxes = useMemo<readonly RecordedEvidenceBox[]>(() => {
const timeline = metadata.timeline;
if (
!frame
!showReferenceMediaLayers
|| !frame
|| !timeline?.cameraObstacleProjectionDelivery
|| !showStaticObstacles
) return [];
@@ -301,14 +376,14 @@ export function M4ReplayThreatVisual({
timeline.imageWidth,
timeline.imageHeight,
);
}, [frame, metadata.timeline, showStaticObstacles]);
}, [frame, metadata.timeline, showReferenceMediaLayers, showStaticObstacles]);
const activeBoxes = useMemo(
() => [
() => classifiedSpatialLayer || !showReferenceMediaLayers ? [] : [
...boxes(frame?.cameraProposals ?? []),
...staticObstacleBoxes,
...reviewAnchorBoxes,
],
[frame, reviewAnchorBoxes, staticObstacleBoxes],
[classifiedSpatialLayer, frame, reviewAnchorBoxes, showReferenceMediaLayers, staticObstacleBoxes],
);
const semanticClasses = useMemo<readonly RecordedEvidenceSemanticClass[]>(
() => semantic?.taxonomy.map((item) => ({
@@ -320,10 +395,14 @@ export function M4ReplayThreatVisual({
const semanticPalette = useMemo<readonly RecordedEvidenceSemanticPaletteEntry[]>(
() => semantic?.taxonomy.map((item) => ({
classId: item.classId,
color: item.disposition === "ambiguous"
color: item.disposition === "undefined"
? { kind: "transparent" as const }
: item.disposition === "ambiguous"
? { kind: "token" as const, token: "--nodedc-warning-rgb" as const }
: { kind: "diagnostic" as const, rgb: item.colorRgb },
opacity: item.disposition === "ambiguous" ? 0.52 : 0.92,
opacity: item.disposition === "undefined"
? 0
: item.disposition === "ambiguous" ? 0.52 : 0.92,
})) ?? [],
[semantic?.taxonomy],
);
@@ -363,6 +442,141 @@ export function M4ReplayThreatVisual({
return status === 2 || status === 3 ? classId : null;
});
}, [semantic, semanticIntegrityError, showSpatialSemantic, spatialFrame, spatialSemanticFrame]);
const activeSpatialFrame = spatialFrame?.sequence === timelineFrame.activeSequence
? spatialFrame
: null;
const classifiedSpatialFrame = classifiedSpatialLayer?.frame?.sourceSequence === timelineFrame.activeSequence
? classifiedSpatialLayer?.frame ?? null
: null;
const lastClassifiedSpatialFrameRef = useRef<{
resultId: string;
frame: M4ReplayClassifiedSpatialFrame;
} | null>(null);
const incomingClassifiedSpatialFrame = classifiedSpatialLayer?.frame ?? null;
if (incomingClassifiedSpatialFrame && incomingClassifiedSpatialFrame.sampleAvailable !== false) {
lastClassifiedSpatialFrameRef.current = { resultId, frame: incomingClassifiedSpatialFrame };
}
const lastAvailableClassifiedSpatialFrame = lastClassifiedSpatialFrameRef.current?.resultId === resultId
? lastClassifiedSpatialFrameRef.current.frame
: null;
// `undefined !== false` is true, so the optional-chain form here used to
// select `null` during every short chunk miss. That unmounted the WebGL
// scene, flashed the alert state and recreated OrbitControls at the default
// view on the next frame. Keep the last complete volume until the exact
// classified frame is resident again.
const displayedClassifiedSpatialFrame = classifiedSpatialFrame
&& classifiedSpatialFrame.sampleAvailable !== false
? classifiedSpatialFrame
: lastAvailableClassifiedSpatialFrame ?? classifiedSpatialFrame;
const displayedClassifiedFrameHeld = Boolean(
classifiedSpatialFrame
&& displayedClassifiedSpatialFrame
&& classifiedSpatialFrame.sourceSequence !== displayedClassifiedSpatialFrame.sourceSequence,
);
const classifiedContextSpatialFrame = displayedClassifiedSpatialFrame
? timelineFrame.availableFrames.find(
(candidate) => candidate.spatialAvailable
&& candidate.sequence === displayedClassifiedSpatialFrame.sourceSequence,
) ?? (spatialFrame?.sequence === displayedClassifiedSpatialFrame.sourceSequence
? spatialFrame
: null)
: null;
const replaceClassifiedPointCloud = classifiedSpatialLayer?.replacePointCloud ?? true;
const nominalSensorHeightM = metadata.timeline?.rig.nominalSensorHeightM ?? 0;
const mapGravityLocalSensorToBodyGround = useCallback((
point: readonly [number, number, number],
): readonly [number, number, number] => {
const basis = classifiedContextSpatialFrame?.bodyFrame?.basisMapFromBody;
const rotated: readonly [number, number, number] = basis ? [
basis[0][0] * point[0] + basis[1][0] * point[1] + basis[2][0] * point[2],
basis[0][1] * point[0] + basis[1][1] * point[1] + basis[2][1] * point[2],
basis[0][2] * point[0] + basis[1][2] * point[1] + basis[2][2] * point[2],
] : point;
// TGS evidence is translation-only map-gravity-local with the current LiDAR
// as its origin. The metric scene uses the body ground projection as z=0.
return [rotated[0], rotated[1], rotated[2] + nominalSensorHeightM];
}, [classifiedContextSpatialFrame?.bodyFrame?.basisMapFromBody, nominalSensorHeightM]);
const classifiedPointsBody = useMemo(
() => displayedClassifiedSpatialFrame?.pointsMapGravityLocalXyzM.map(
mapGravityLocalSensorToBodyGround,
) ?? [],
[displayedClassifiedSpatialFrame, mapGravityLocalSensorToBodyGround],
);
const classifiedCellsBody = useMemo<readonly LaboratoryMetricCellEvidence[]>(
() => displayedClassifiedSpatialFrame?.cellsMapGravityLocal.map((cell) => {
const body = mapGravityLocalSensorToBodyGround([
cell.centerXyM[0],
cell.centerXyM[1],
0,
]);
const [minimumSensorRelativeZ, maximumSensorRelativeZ] = cell.zBoundsM;
return {
centerBodyXyM: [body[0], body[1]],
zBoundsM: [
minimumSensorRelativeZ === null
? null
: minimumSensorRelativeZ + nominalSensorHeightM,
maximumSensorRelativeZ === null
? null
: maximumSensorRelativeZ + nominalSensorHeightM,
],
state: cell.state,
};
}) ?? [],
[displayedClassifiedSpatialFrame, mapGravityLocalSensorToBodyGround, nominalSensorHeightM],
);
const classifiedPackedCellsBody = useMemo<LaboratoryMetricPackedCellEvidence | undefined>(() => {
const packed = displayedClassifiedSpatialFrame?.packedCellsMapGravityLocal;
if (!packed) return undefined;
const cellCount = packed.stateCodes.length;
if (packed.centersXyM.length !== cellCount * 2 || packed.zBoundsM.length !== cellCount * 2) {
return undefined;
}
const centersBodyXyM = new Float32Array(cellCount * 2);
const zBoundsM = new Float32Array(cellCount * 2);
for (let index = 0; index < cellCount; index += 1) {
const body = mapGravityLocalSensorToBodyGround([
packed.centersXyM[index * 2]!,
packed.centersXyM[index * 2 + 1]!,
0,
]);
centersBodyXyM[index * 2] = body[0];
centersBodyXyM[index * 2 + 1] = body[1];
const minimum = packed.zBoundsM[index * 2]!;
const maximum = packed.zBoundsM[index * 2 + 1]!;
zBoundsM[index * 2] = Number.isFinite(minimum)
? minimum + nominalSensorHeightM
: Number.NaN;
zBoundsM[index * 2 + 1] = Number.isFinite(maximum)
? maximum + nominalSensorHeightM
: Number.NaN;
}
return { centersBodyXyM, zBoundsM, stateCodes: packed.stateCodes };
}, [displayedClassifiedSpatialFrame, mapGravityLocalSensorToBodyGround, nominalSensorHeightM]);
const classifiedCellCounts = useMemo(() => {
const counts = { ground: 0, occupied: 0, rejected: 0, unobserved: 0 };
const packed = classifiedSpatialFrame?.packedCellsMapGravityLocal;
if (packed) {
for (const code of packed.stateCodes) {
if (code === 1) counts.ground += 1;
else if (code === 2) counts.occupied += 1;
else if (code === 3) counts.rejected += 1;
else if (code === 0) counts.unobserved += 1;
else counts.rejected += 1;
}
return counts;
}
for (const cell of classifiedSpatialFrame?.cellsMapGravityLocal ?? []) {
if (cell.state === "ground-support") counts.ground += 1;
else if (cell.state === "nonground-occupied") counts.occupied += 1;
else if (cell.state === "unknown-rejected") counts.rejected += 1;
else counts.unobserved += 1;
}
return counts;
}, [classifiedSpatialFrame]);
const classifiedCellCount = classifiedSpatialFrame?.packedCellsMapGravityLocal?.stateCodes.length
?? classifiedSpatialFrame?.cellsMapGravityLocal.length
?? 0;
const sceneObstacles = useMemo(() => spatialFrame?.metricObstacles.map((obstacle) => ({
id: obstacle.componentId,
decision: obstacle.assessment.decision,
@@ -398,15 +612,17 @@ export function M4ReplayThreatVisual({
const semanticOverlay: RecordedEvidenceSemanticOverlay | undefined =
semantic && showMediaSemantic && frame
? {
src: e47SemanticMaskUrl(semantic.resultId, frame.sequence),
src: semantic.maskUrl?.(frame.sequence)
?? e47SemanticMaskUrl(semantic.resultId, frame.sequence),
prefetchSrcs: Array.from({ length: 12 }, (_, index) => index + 1)
.map((offset) => frame.sequence + offset)
.filter((sequence) => sequence < (metadata.timeline?.frameCount ?? 0))
.map((sequence) => e47SemanticMaskUrl(semantic.resultId, sequence)),
.map((sequence) => semantic.maskUrl?.(sequence)
?? e47SemanticMaskUrl(semantic.resultId, sequence)),
classes: semanticClasses,
palette: semanticPalette,
opacity: 0.9,
ariaLabel: `E47 semantic mask frame ${frame.sequence + 1}`,
ariaLabel: `${semantic.maskAriaLabel ?? "Semantic prediction"} frame ${frame.sequence + 1}`,
}
: undefined;
const accumulatedCameraPoints = cameraPointOverlay.overlay?.sequence === frame?.sequence
@@ -477,8 +693,8 @@ export function M4ReplayThreatVisual({
);
const mediaLayerControls = semantic
|| metadata.timeline?.cameraPointDelivery
|| metadata.timeline?.cameraObstacleProjectionDelivery ? (
|| (showReferenceMediaLayers && metadata.timeline?.cameraPointDelivery)
|| (showReferenceMediaLayers && metadata.timeline?.cameraObstacleProjectionDelivery) ? (
<div
className="m4-replay-threat-visual__pane-layer-controls"
role="group"
@@ -495,7 +711,7 @@ export function M4ReplayThreatVisual({
SEMANTICS
</Button>
) : null}
{metadata.timeline?.cameraPointDelivery ? (
{showReferenceMediaLayers && metadata.timeline?.cameraPointDelivery ? (
<Button
size="compact"
shape="pill"
@@ -507,7 +723,7 @@ export function M4ReplayThreatVisual({
POINTS
</Button>
) : null}
{metadata.timeline?.cameraObstacleProjectionDelivery ? (
{showReferenceMediaLayers && metadata.timeline?.cameraObstacleProjectionDelivery ? (
<Button
size="compact"
shape="pill"
@@ -522,7 +738,53 @@ export function M4ReplayThreatVisual({
</div>
) : null;
const spatialLayerControls = (
const spatialLayerControls = classifiedSpatialLayer ? (
<div
className="m4-replay-threat-visual__pane-layer-controls"
role="group"
aria-label={`Слои ${classifiedSpatialLayer.label}`}
>
<Button
size="compact"
shape="pill"
variant={showCurrentIncrement ? "primary" : "secondary"}
aria-pressed={showCurrentIncrement}
onClick={() => setShowCurrentIncrement((visible) => !visible)}
>
{classifiedSpatialLayer.pointLayerLabel}
</Button>
<Button
size="compact"
shape="pill"
variant={showLocalSurface ? "primary" : "secondary"}
aria-pressed={showLocalSurface}
title="Bounded local SLAM surface · visual-derived"
onClick={() => setShowLocalSurface((visible) => !visible)}
>
LOCAL SLAM
</Button>
<Button
size="compact"
shape="pill"
variant={showRollingMap ? "primary" : "secondary"}
aria-pressed={showRollingMap}
onClick={() => setShowRollingMap((visible) => !visible)}
>
{classifiedSpatialLayer.cellLayerLabel}
</Button>
{semanticSpatialResultId ? (
<Button
size="compact"
shape="pill"
variant={showSpatialSemantic ? "primary" : "secondary"}
aria-pressed={showSpatialSemantic}
onClick={() => setShowSpatialSemantic((visible) => !visible)}
>
SEMANTICS
</Button>
) : null}
</div>
) : (
<div
className="m4-replay-threat-visual__pane-layer-controls"
role="group"
@@ -568,7 +830,7 @@ export function M4ReplayThreatVisual({
LOW-STEP
</Button>
) : null}
{semantic ? (
{semanticSpatialResultId ? (
<Button
size="compact"
shape="pill"
@@ -626,7 +888,7 @@ export function M4ReplayThreatVisual({
value={String(selectedReviewAnchorIndex)}
options={reviewAnchors.map((anchor, index) => ({
value: String(index),
label: `${index + 1}/${reviewAnchors.length} · кадр ${anchor.sourceSequence + 1} · ${anchor.matchedAtThreshold ? "покрыт" : "пропуск"}`,
label: `${index + 1}/${reviewAnchors.length} · кадр ${anchor.sourceSequence + 1} · ${anchor.statusLabel ?? (anchor.matchedAtThreshold ? "покрыт" : "пропуск")}`,
}))}
variant="split"
menuWidth="anchor"
@@ -652,7 +914,11 @@ export function M4ReplayThreatVisual({
? splitPrimarySize
: 100;
const overlay = metadata.timeline && frame ? (
const overlaySequence = timelineFrame.activeSequence ?? frame?.sequence ?? null;
const overlaySessionSeconds = overlaySequence === null
? null
: (metadata.timeline?.frameTimesNs[overlaySequence] ?? 0) / 1_000_000_000;
const overlay = metadata.timeline && overlaySequence !== null ? (
<div
className="l3-visual-audit__overlay m4-replay-threat-visual__overlay"
style={{
@@ -661,50 +927,75 @@ export function M4ReplayThreatVisual({
>
<div>
<span>RAVNOVES00 · recorded realtime</span>
<strong>frame {frame.sequence + 1}/{metadata.timeline.frameCount}</strong>
<strong>frame {overlaySequence + 1}/{metadata.timeline.frameCount}</strong>
<small>
+{(frame.sessionSeconds - metadata.timeline.timelineStartSeconds).toFixed(3)} с
+{((overlaySessionSeconds ?? metadata.timeline.timelineStartSeconds) - metadata.timeline.timelineStartSeconds).toFixed(3)} с
· {displayingBufferedFrame
? "держим последний кадр, следующий в буфере"
: playbackController.playback.playing ? "воспроизведение" : "пауза / seek"}
</small>
</div>
<div>
<span>Spatial evidence</span>
<strong>
{currentIncrementObstacles.length} current · {rollingMapObstacles.length} rolling
{metadata.timeline.occupancyProvenanceDelivery
? ` · ${lowStepObstacles.length} low-step`
: ""}
</strong>
<small>
{spatialFrame
? `${spatialFrame.pointCloudSampleCount}/${spatialFrame.pointCloudSourceCount} exact · ${localSurface.pointsBodyXyzM.length} local SLAM / ${localSurface.sourceFrameCount} frames`
: "квалифицированный spatial frame ещё не получен"}
{frame.worldStateAvailable
? " · world-state delivered"
: ` · world-state gap (${frame.terminalOutcome})`}
{accumulatedCameraPoints
? ` · camera points ${accumulatedCameraPoints.sampleCount}/${accumulatedCameraPoints.projectedPointCount} · causal ${accumulatedCameraPoints.windowSeconds.toFixed(1)} с / ${accumulatedCameraPoints.sourceFrameCount} frames`
: pointCloudOverlay
? ` · camera points ${frame.cameraProjectedSampleCount}/${frame.cameraProjectedPointCount} exact-current · накопление загружается`
: showMediaPoints && cameraPointOverlay.error
? " · накопленное camera cloud недоступно"
: ""}
{semantic && spatialSemanticFrame
? ` · semantic L ${spatialSemanticFrame.counts.labeled} · A ${spatialSemanticFrame.counts.ambiguous} · U ${spatialSemanticFrame.counts.unprojected} · Ø ${spatialSemanticFrame.counts.absent}`
: semantic ? " · semantic buffer" : ""}
</small>
</div>
<div>
<span>Virtual corridor</span>
<strong>
{spatialFrame?.decisionCounts.threat ?? 0} threat · nearest {nearest === null ? "—" : `${nearest.toFixed(2)} м`}
</strong>
<small>
{metadata.timeline.corridor.forwardLengthM} м · body {metadata.timeline.rig.lengthM}×{metadata.timeline.rig.widthM} м · REPLAY-SIMULATED
</small>
</div>
{showSpatialOverlaySummary ? (
<>
<div>
<span>Spatial evidence</span>
<strong>{classifiedSpatialLayer
? classifiedSpatialFrame
? replaceClassifiedPointCloud
? `${classifiedSpatialFrame.pointsMapGravityLocalXyzM.length.toLocaleString("ru-RU")} TGS points · ${classifiedCellCount.toLocaleString("ru-RU")} cells`
: `${(activeSpatialFrame?.pointCloudSourceCount ?? classifiedSpatialFrame.sourcePointCount ?? 0).toLocaleString("ru-RU")} source points · ${classifiedCellCount.toLocaleString("ru-RU")} TGS cells`
: "TGS spatial buffer"
: `${currentIncrementObstacles.length} current · ${rollingMapObstacles.length} rolling${metadata.timeline.occupancyProvenanceDelivery ? ` · ${lowStepObstacles.length} low-step` : ""}`}</strong>
<small>{classifiedSpatialLayer
? classifiedSpatialFrame
? classifiedSpatialFrame.sampleAvailable === false
? displayedClassifiedFrameHeld && displayedClassifiedSpatialFrame
? `LiDAR отсутствует · current UNOBSERVED · объёмный контекст удержан с frame ${displayedClassifiedSpatialFrame.sourceSequence + 1}`
: "LiDAR отсутствует · все ячейки принудительно UNOBSERVED · causal rolling 1 s"
: activeSpatialFrame
? "map-gravity-local · all eligible points accounted · causal rolling 1 s"
: "TGS рассчитан · linked source cloud недоступен для этого кадра"
: classifiedSpatialLayer.error
?? classifiedSpatialLayer.loadingLabel
?? `Открываем ${classifiedSpatialLayer.label}`
: (
<>
{spatialFrame
? `${spatialFrame.pointCloudSampleCount}/${spatialFrame.pointCloudSourceCount} exact · ${localSurface.pointsBodyXyzM.length} local SLAM / ${localSurface.sourceFrameCount} frames`
: "квалифицированный spatial frame ещё не получен"}
{frame
? frame.worldStateAvailable
? " · world-state delivered"
: ` · world-state gap (${frame.terminalOutcome})`
: " · world-state frame unavailable"}
{accumulatedCameraPoints
? ` · camera points ${accumulatedCameraPoints.sampleCount}/${accumulatedCameraPoints.projectedPointCount} · causal ${accumulatedCameraPoints.windowSeconds.toFixed(1)} с / ${accumulatedCameraPoints.sourceFrameCount} frames`
: pointCloudOverlay && frame
? ` · camera points ${frame.cameraProjectedSampleCount}/${frame.cameraProjectedPointCount} exact-current · накопление загружается`
: showMediaPoints && cameraPointOverlay.error
? " · накопленное camera cloud недоступно"
: ""}
{semanticSpatialResultId && spatialSemanticFrame
? ` · semantic L ${spatialSemanticFrame.counts.labeled} · A ${spatialSemanticFrame.counts.ambiguous} · U ${spatialSemanticFrame.counts.unprojected} · Ø ${spatialSemanticFrame.counts.absent}`
: semanticSpatialResultId ? " · semantic buffer" : ""}
</>
)}</small>
</div>
<div>
<span>{classifiedSpatialLayer ? "TGS fail-closed" : "Virtual corridor"}</span>
<strong>{classifiedSpatialLayer
? classifiedSpatialFrame
? `${classifiedCellCounts.occupied} occupied · ${classifiedCellCounts.rejected} rejected · ${classifiedCellCounts.unobserved} unobserved`
: classifiedSpatialLayer.loading || displayingBufferedFrame ? "loading" : "unavailable"
: `${spatialFrame?.decisionCounts.threat ?? 0} threat · nearest ${nearest === null ? "—" : `${nearest.toFixed(2)} м`}`}</strong>
<small>{classifiedSpatialLayer
? classifiedSpatialFrame
? `${classifiedCellCounts.ground} ground-support · visual review only · navigation authority OFF`
: "visual review only · navigation authority OFF"
: `${metadata.timeline.corridor.forwardLengthM} м · body ${metadata.timeline.rig.lengthM}×${metadata.timeline.rig.widthM} м · REPLAY-SIMULATED`}</small>
</div>
</>
) : null}
</div>
) : undefined;
@@ -802,27 +1093,61 @@ export function M4ReplayThreatVisual({
</div>
</div>
) : null}
{spatialFrame ? (
<LaboratoryMetricEvidenceScene
ref={metricSceneRef}
pointCloudBodyXyzM={spatialFrame.pointCloudBodyXyzM}
localSurfaceBodyXyzM={localSurface.pointsBodyXyzM}
obstacles={sceneObstacles}
rig={timeline.rig}
corridor={timeline.corridor}
occupiedVoxelSizeM={timeline.occupiedVoxelSizeM}
mode={spatialMode}
label={`${evidenceLabel} exact current increment, bounded local SLAM surface and rolling occupancy`}
showCurrentIncrement={showCurrentIncrement}
showLocalSurface={showLocalSurface}
showRollingMap={showRollingMap}
showLowStep={showLowStep}
pointSemanticClassIds={alignedSemanticPointIds}
semanticClasses={semanticClasses}
semanticPalette={semanticPalette}
/>
<LaboratoryMetricEvidenceScene
ref={metricSceneRef}
pointCloudBodyXyzM={displayedClassifiedSpatialFrame && replaceClassifiedPointCloud
? classifiedPointsBody
: classifiedContextSpatialFrame?.pointCloudBodyXyzM ?? []}
localSurfaceBodyXyzM={localSurface.pointsBodyXyzM}
obstacles={classifiedSpatialLayer ? [] : sceneObstacles}
rig={timeline.rig}
corridor={timeline.corridor}
occupiedVoxelSizeM={displayedClassifiedSpatialFrame?.cellSizeM ?? timeline.occupiedVoxelSizeM}
mode={spatialMode}
label={`${evidenceLabel} exact current increment, bounded local SLAM surface and rolling occupancy`}
showCurrentIncrement={showCurrentIncrement}
showLocalSurface={showLocalSurface}
showRollingMap={showRollingMap}
showLowStep={classifiedSpatialLayer ? false : showLowStep}
pointSemanticClassIds={displayedClassifiedSpatialFrame && replaceClassifiedPointCloud
? displayedClassifiedSpatialFrame.pointClassIds
: alignedSemanticPointIds}
semanticClasses={displayedClassifiedSpatialFrame && replaceClassifiedPointCloud
? displayedClassifiedSpatialFrame.classes
: semanticClasses}
semanticPalette={displayedClassifiedSpatialFrame && replaceClassifiedPointCloud
? displayedClassifiedSpatialFrame.palette
: semanticPalette}
classifiedCells={classifiedCellsBody}
classifiedPackedCells={classifiedPackedCellsBody}
classifiedCellSizeM={displayedClassifiedSpatialFrame?.cellSizeM}
showClassifiedCells={showRollingMap}
/>
{classifiedSpatialLayer && !displayedClassifiedSpatialFrame ? (
<div className="l3-visual-audit__state" role={classifiedSpatialLayer.error ? "alert" : "status"}>
{classifiedSpatialLayer.loading || displayingBufferedFrame
? <span className="busy-indicator" aria-hidden="true" />
: <Icon name="alert" size={18} />}
<span>{classifiedSpatialLayer.error
?? (classifiedSpatialLayer.loading || displayingBufferedFrame
? classifiedSpatialLayer.loadingLabel ?? `Открываем ${classifiedSpatialLayer.label}`
: classifiedSpatialLayer.expectedAtSequence
? `Открываем ${classifiedSpatialLayer.label}`
: `${classifiedSpatialLayer.label} рассчитан только на 10 контрольных кадров.`)}</span>
</div>
) : null}
{frame && !frame.spatialAvailable ? (
{classifiedSpatialFrame?.sampleAvailable === false ? (
<div className="m4-replay-threat-visual__pane-status" role="status">
Кадр {classifiedSpatialFrame.sourceSequence + 1}: LiDAR отсутствует; current safety — все {classifiedCellCount.toLocaleString("ru-RU")} TGS-ячейки UNOBSERVED
{displayedClassifiedFrameHeld && displayedClassifiedSpatialFrame
? `; для ориентации удержан последний объёмный контекст кадра ${displayedClassifiedSpatialFrame.sourceSequence + 1}.`
: "."}
</div>
) : classifiedSpatialFrame && !activeSpatialFrame ? (
<div className="m4-replay-threat-visual__pane-status" role="status">
Кадр {classifiedSpatialFrame.sourceSequence + 1}: TGS costmap показан cell-only; linked source cloud для отрисовки отсутствует.
</div>
) : frame && !frame.spatialAvailable ? (
<div className="m4-replay-threat-visual__pane-status" role="status">
{spatialFrame
? `На кадре ${frame.sequence + 1} нет body frame; держим spatial evidence кадра ${spatialFrame.sequence + 1}.`
@@ -857,7 +1182,7 @@ export function M4ReplayThreatVisual({
{timelineFrame.loading || displayingBufferedFrame ? (
<div className="m4-replay-threat-visual__buffering" role="status">
<span className="busy-indicator" aria-hidden="true" />
<span>Догружаем следующий spatial-буфер без сброса сцены</span>
<span>{timelineFrame.loadingLabel ?? "Догружаем следующий spatial-буфер без сброса сцены"}</span>
</div>
) : null}
{timelineFrame.error ? (
@@ -866,13 +1191,13 @@ export function M4ReplayThreatVisual({
<span>{timelineFrame.error}</span>
</div>
) : null}
{semantic && semanticTimeline.loading ? (
{semanticSpatialResultId && semanticTimeline.loading ? (
<div className="m4-replay-threat-visual__buffering" role="status">
<span className="busy-indicator" aria-hidden="true" />
<span>Догружаем semantic-point evidence E47</span>
</div>
) : null}
{semanticTimeline.error ? (
{semanticSpatialResultId && semanticTimeline.error ? (
<div className="m4-replay-threat-visual__buffering" role="alert">
<Icon name="alert" size={16} />
<span>{semanticTimeline.error}</span>
@@ -914,7 +1239,7 @@ export function M4ReplayThreatVisual({
<div className="l3-visual-audit m4-replay-threat-visual">
<LaboratoryEvidenceViewer
label={semantic
? "E47 semantic + SLAM diagnostic replay"
? semantic.label ?? "Semantic diagnostic replay"
: `${evidenceLabel} recorded-realtime replay`}
className="m4-replay-threat-evidence-viewer"
mode={mediaMode ?? "none"}
@@ -0,0 +1,201 @@
import {
LaboratoryEvidence,
LaboratoryResultSummary,
LaboratorySummary,
LaboratoryWorkTemplate,
} from "../../components/laboratory/LaboratoryPresentation";
import {
vegetationVideoMaskUrl,
type VegetationShadowResult,
} from "../../core/laboratory/vegetationShadow";
import {
M48MaskComparisonVisual,
type M48MaskComparisonCase,
} from "./M48FailureAtlasVisual";
import { M4ReplayThreatVisual } from "./M4ReplayThreatVisual";
function decimal(value: number, digits = 1): string {
return value.toLocaleString("ru-RU", { maximumFractionDigits: digits });
}
const VEGETATION_LABELS: Readonly<Record<string, string>> = {
high_grass: "Высокая трава",
low_grass: "Низкая трава",
bush: "Куст",
tree_trunk: "Ствол дерева",
tree_crown: "Крона дерева",
hedge: "Живая изгородь",
forest: "Лесная растительность",
crops: "Посевы",
};
function comparisonCases(result: VegetationShadowResult): readonly M48MaskComparisonCase[] {
return result.validationCases.map((item) => {
const focus = item.focus!;
return {
caseId: item.caseId,
title: `${VEGETATION_LABELS[focus.className] ?? focus.className} · truth ${decimal(focus.truthFraction * 100, 1)}% кадра`,
sourceUrl: item.assets.source,
truthUrl: item.assets.truth,
predictions: {
ddrnet: item.assets.ddrnet,
ppliteseg: item.assets.ppliteseg,
},
errors: {
ddrnet: item.assets.ddrnet_error,
ppliteseg: item.assets.ppliteseg_error,
},
};
});
}
export function VegetationShadowResultView({
rigLabel,
result,
}: {
rigLabel: string;
result: VegetationShadowResult;
}) {
const selected = result.candidates.find(
(candidate) => candidate.candidate === result.selectedCandidate,
)!;
const alternative = result.candidates.find(
(candidate) => candidate.candidate !== result.selectedCandidate,
)!;
return (
<LaboratoryWorkTemplate
summary={(
<LaboratorySummary
title="LAB V1 · готовые модели растительности"
description={result.routeVideo
? "M4.8 сохраняет truth-backed сравнение моделей, а штатный M4.7 viewer показывает фактический DDRNet prediction на всей записи RAVNOVES00. Все 4489 масок запечатаны локально и открываются без Worker 006."
: "Штатный M4.8-инструмент сравнивает две готовые fine-64 модели на полном GOOSE validation split и на 12 truth-backed hard cases, выбранных только по наличию нужной растительности. Sealed evidence открывается локально без Worker 006."}
status={result.routeVideo
? "DDRNet full-video prediction ready · route truth отсутствует"
: "Truth-backed model comparison · route transfer не принят"}
statusTone="warning"
facts={[
{ label: "Источник", value: "GOOSE validation · 962 размеченных кадра · 12 vegetation hard cases" },
{ label: "Сравнение", value: "DDRNet-39 vs PPLiteSeg · official fine-64 weights" },
{ label: "Кейсы", value: "трава · куст · ствол · крона · изгородь · лес · посевы" },
...(result.routeVideo ? [{
label: "Видео",
value: "RAVNOVES00 · 4489/4489 DDRNet masks · exact recorded sequence",
}] : []),
{ label: "Authority", value: `${rigLabel} · MODEL QUALIFICATION ONLY · commands OFF` },
]}
brief={{
question: "Какие готовые веса лучше различают проезжаемую траву, кусты и стволы на размеченных off-road кадрах?",
approach: "Обе модели последовательно прогнаны в одном изолированном CUDA-runtime на 962 кадрах. 12 визуальных кейсов выбраны детерминированно по truth-поддержке восьми растительных классов; один M4.8 viewer показывает source, truth, prediction и material-error для выбранной модели.",
principalResult: `${selected.loadedModelName} лидирует по vegetation IoU: ${decimal(selected.vegetationMeanIouPercent, 2)}% против ${decimal(alternative.vegetationMeanIouPercent, 2)}%. ${result.routeVideo ? "Его фактическая temporal stability теперь видна на всех 4489 кадрах штатного recorded viewer." : "Ошибки по каждому типу проверяются в одном штатном инструменте."}`,
limitation: "GOOSE — внешний размеченный домен; RAVNOVES00 — наш fisheye, но без ручной truth-разметки. Full-video слой показывает prediction, а не доказывает правильность. Папоротник отдельным классом отсутствует.",
}}
method={{
completeness: "complete",
executionClass: "ai-inference",
pipelineId: "goose-fine64-ready-weights-to-ravnoves-policy-shadow/v1",
components: result.candidates.map((candidate) => ({
kind: "model" as const,
name: candidate.loadedModelName,
version: candidate.candidate,
role: candidate.candidate === result.selectedCandidate ? "selected policy provider" : "comparison candidate",
identitySha256: candidate.checkpointSha256,
})),
}}
/>
)}
evidence={(
<>
<LaboratoryEvidence
eyebrow="M4.8 · GOOSE VEGETATION HARD CASES"
title="ERROR: красный — пропуск · жёлтый — лишнее · фиолетовый — перепутан тип · зелёный — совпадение"
kind="diagnostic-model"
resizable
>
<M48MaskComparisonVisual
cases={comparisonCases(result)}
initialCandidate={result.selectedCandidate}
/>
</LaboratoryEvidence>
{result.routeVideo ? (
<LaboratoryEvidence
eyebrow="M4.7 · RAVNOVES00 FULL VIDEO"
title="DDRNet PREDICTION · 4489/4489 кадров · TRUTH для этой записи отсутствует"
kind="diagnostic-model"
resizable
>
<M4ReplayThreatVisual
resultId={result.routeVideo.baseM4ResultId}
evidenceLabel="LAB V1 · DDRNet"
showReferenceMediaLayers={false}
showSpatialOverlaySummary={false}
semantic={{
resultId: result.routeVideo.workerResultId,
spatialResultId: null,
taxonomy: result.routeVideo.taxonomy,
maskUrl: (sequence) => vegetationVideoMaskUrl(result.resultId, sequence),
label: "DDRNet vegetation prediction · recorded video",
maskAriaLabel: "DDRNet vegetation prediction",
}}
/>
</LaboratoryEvidence>
) : null}
</>
)}
result={(
<LaboratoryResultSummary
title="DDRNet — стартовые веса; перенос на ровер ещё не доказан"
status={`${selected.loadedModelName} выбран только как vegetation candidate`}
statusTone="warning"
metrics={[
{
label: "GOOSE mIoU",
value: `${decimal(selected.meanIouPercent, 2)}% / ${decimal(alternative.meanIouPercent, 2)}%`,
hint: `${selected.candidate} / ${alternative.candidate} · полный validation split`,
},
{
label: "Vegetation IoU",
value: `${decimal(selected.vegetationMeanIouPercent, 2)}% / ${decimal(alternative.vegetationMeanIouPercent, 2)}%`,
hint: "агрегация классов grass/vegetation/bush/tree и родственных fine-64 labels",
},
{
label: "Worker shadow p95",
value: `${decimal(selected.shadowLatencyP95Ms, 2)} / ${decimal(alternative.shadowLatencyP95Ms, 2)} ms`,
hint: "чистый inference · одна тяжёлая модель за раз",
},
{
label: "Cold prewarm",
value: `${decimal(selected.shadowPrewarmLatencyMs, 1)} / ${decimal(alternative.shadowPrewarmLatencyMs, 1)} ms`,
hint: "один явный inference до допуска кадров; исключён из steady-state p95",
},
{
label: "Worker throughput",
value: `${decimal(selected.shadowThroughputFps, 1)} / ${decimal(alternative.shadowThroughputFps, 1)} FPS`,
hint: "изолированный Worker 006 · не realtime graph целиком",
},
{
label: "Peak VRAM",
value: `${decimal(selected.peakReservedVramBytes / 1024 ** 3, 2)} / ${decimal(alternative.peakReservedVramBytes / 1024 ** 3, 2)} GiB`,
hint: `${selected.candidate} / ${alternative.candidate} · RTX 4090`,
},
{
label: "Hard-case evidence",
value: "12 truth-backed cases",
hint: "8 vegetation strata · Worker для открытия не требуется",
},
...(result.routeVideo ? [{
label: "Route video",
value: "4489/4489 masks",
hint: "DDRNet prediction · exact sequence · Worker-independent playback",
}] : []),
]}
conclusion={{
proved: "Обе официальные fine-64 модели воспроизводимо запускаются на Worker 006; DDRNet лучше по aggregate vegetation IoU. Truth-backed hard cases прямо показывают траву, кусты и стволы, а не случайные автомобили и здания.",
notProved: "Не доказаны accuracy на нашем fisheye-домене, папоротник как отдельный материал, collision safety и physical-live поведение ровера. Видео позволяет увидеть temporal stability, но без truth не превращает её в метрику качества.",
decision: "Смотреть полный prediction на видео и собирать конкретные temporal/domain failure cases. DDRNet остаётся diagnostic candidate; LiDAR/TGS fail-closed геометрию не ослаблять.",
}}
/>
)}
/>
);
}
@@ -63,6 +63,13 @@ interface KnownWorkDefinition {
const rig = (rigLabel: string): string => rigLabel.trim() || "Сенсорный риг";
const KNOWN_WORKS: Readonly<Record<Exclude<LaboratoryWorkId, `session:${string}`>, KnownWorkDefinition>> = {
"lab-v1-vegetation-shadow": {
profileId: "rig-ravnoves-perception-gate-v1",
profileName: (rigLabel) => `${rig(rigLabel)} · GOOSE vegetation qualification`,
experimentId: "lab-v1-vegetation-mission-policy",
experimentName: "DDRNet vs PPLiteSeg · truth-backed vegetation hard cases",
variantName: "LAB V1 · готовые vegetation weights · GOOSE truth",
},
"m48-object-centric-quality": {
profileId: "rig-dual-evidence-virtual-corridor-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · Camera + prediction-free spatial evidence`,
@@ -105,6 +112,20 @@ const KNOWN_WORKS: Readonly<Record<Exclude<LaboratoryWorkId, `session:${string}`
experimentName: "RF-DETR native risk review and temporal identity",
variantName: "M4.8Q · native raw KB4 review · quality not adjudicated",
},
"m49-tgs-fail-closed-evidence": {
profileId: "rig-dual-evidence-virtual-corridor-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · Camera + gravity-aligned LiDAR`,
experimentId: "m49-tgs-fail-closed-evidence",
experimentName: "TRAVEL TGS fail-closed traversability evidence",
variantName: "M4.9T4 · 10 anchors · causal rolling 1 s · AOS OFF",
},
"m49-tgs-full-shadow": {
profileId: "rig-dual-evidence-virtual-corridor-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · Camera + gravity-aligned LiDAR`,
experimentId: "m49-tgs-full-shadow",
experimentName: "TRAVEL TGS complete source-paced shadow",
variantName: "M4.9T5 · 4 489 frames · causal rolling 1 s · CPU-only",
},
"m47-reference-graph-shadow": {
profileId: "rig-dual-evidence-virtual-corridor-v1",
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · Camera + LiDAR dual evidence`,
@@ -18,6 +18,7 @@ function mergeResults(
next: AdvancedLaboratoryResults,
): AdvancedLaboratoryResults {
return {
vegetationShadow: next.vegetationShadow ?? current.vegetationShadow,
m47Graph: next.m47Graph ?? current.m47Graph,
m48: next.m48 ?? current.m48,
m48SmallStatic: next.m48SmallStatic ?? current.m48SmallStatic,
@@ -25,6 +26,8 @@ function mergeResults(
m48r3StaticOccupancy: next.m48r3StaticOccupancy ?? current.m48r3StaticOccupancy,
m48s: next.m48s ?? current.m48s,
m48t: next.m48t ?? current.m48t,
m49Tgs: next.m49Tgs ?? current.m49Tgs,
m49TgsFull: next.m49TgsFull ?? current.m49TgsFull,
m4Threat: next.m4Threat ?? current.m4Threat,
l3: next.l3 ?? current.l3,
l31: next.l31 ?? current.l31,
@@ -119,11 +122,14 @@ export function useAdvancedLaboratoryCatalog({
const indexedResultId = index.find((item) => item.workId === selectedWorkId)?.resultId;
if (
[
"lab-v1-vegetation-shadow",
"m47-reference-graph-shadow",
"m48-object-centric-quality",
"m48-small-static-passage-regression",
"m48-static-occupancy-qualification",
"m48r3-static-occupancy-shadow",
"m49-tgs-fail-closed-evidence",
"m49-tgs-full-shadow",
].includes(selectedWorkId)
&& !indexedResultId
) return;
@@ -2,10 +2,15 @@ import { useEffect, useMemo, useRef, useState } from "react";
import {
fetchM4ThreatCameraPointOverlay,
fetchM4ThreatPlaybackManifest,
fetchM4ThreatPlaybackPointChunk,
fetchM4ThreatTimeline,
fetchM4ThreatTimelineChunk,
M4_THREAT_TIMELINE_ENDPOINT_ROOT,
selectM4ThreatTimelineSequence,
type M4ThreatCameraPointOverlay,
type M4ThreatPlaybackManifest,
type M4ThreatPlaybackProgress,
type M4ThreatTimeline,
type M4ThreatTimelineChunk,
type M4ThreatTimelineFrame,
@@ -82,11 +87,48 @@ export function useM4ThreatTimelineFrame({
() => new Map(),
);
const [error, setError] = useState<string | null>(null);
const [playbackManifest, setPlaybackManifest] = useState<M4ThreatPlaybackManifest | null>(null);
const [playbackProgress, setPlaybackProgress] = useState<M4ThreatPlaybackProgress>({
phase: "manifest",
loadedBytes: 0,
totalBytes: 0,
});
const [playbackError, setPlaybackError] = useState<string | null>(null);
const binaryPlayback = endpointRoot === undefined
|| endpointRoot === M4_THREAT_TIMELINE_ENDPOINT_ROOT;
const inFlight = useRef(new Map<number, AbortController>());
const chunksRef = useRef(chunks);
const activeChunkStartRef = useRef<number | null>(null);
chunksRef.current = chunks;
useEffect(() => {
const controller = new AbortController();
setPlaybackManifest(null);
setPlaybackError(null);
setPlaybackProgress({ phase: "manifest", loadedBytes: 0, totalBytes: 0 });
if (!timeline) return () => controller.abort();
if (!binaryPlayback) {
setPlaybackProgress({ phase: "ready", loadedBytes: 0, totalBytes: 0 });
return () => controller.abort();
}
void fetchM4ThreatPlaybackManifest(resultId, {
signal: controller.signal,
endpointRoot,
})
.then((manifest) => {
if (!controller.signal.aborted) {
setPlaybackManifest(manifest);
setPlaybackProgress({ phase: "ready", loadedBytes: 0, totalBytes: 0 });
}
})
.catch((caught: unknown) => {
if (!controller.signal.aborted) {
setPlaybackError(errorMessage(caught, "Бинарный spatial playback M4.6 недоступен."));
}
});
return () => controller.abort();
}, [binaryPlayback, endpointRoot, resultId, timeline]);
useEffect(() => {
for (const controller of inFlight.current.values()) controller.abort();
inFlight.current.clear();
@@ -116,7 +158,7 @@ export function useM4ThreatTimelineFrame({
activeChunkStartRef.current = activeChunkStart;
useEffect(() => {
if (!timeline || activeChunkStart === null) return;
if (!timeline || activeChunkStart === null || (binaryPlayback && !playbackManifest)) return;
const starts = m4ThreatChunkWindowStarts(
activeChunkStart,
chunkSize,
@@ -127,11 +169,28 @@ export function useM4ThreatTimelineFrame({
if (chunksRef.current.has(start) || inFlight.current.has(start)) continue;
const controller = new AbortController();
inFlight.current.set(start, controller);
void fetchM4ThreatTimelineChunk(resultId, start, chunkSize, {
signal: controller.signal,
endpointRoot,
cameraObstacleProjectionDelivery: timeline.cameraObstacleProjectionDelivery,
})
void (async () => {
const playbackPointPack = binaryPlayback && playbackManifest
? await fetchM4ThreatPlaybackPointChunk(
playbackManifest,
Math.floor(start / playbackManifest.chunkFrameCount),
{
signal: controller.signal,
onProgress: (progress) => {
if (!controller.signal.aborted && start === activeChunkStartRef.current) {
setPlaybackProgress(progress);
}
},
},
)
: undefined;
return fetchM4ThreatTimelineChunk(resultId, start, chunkSize, {
signal: controller.signal,
endpointRoot,
cameraObstacleProjectionDelivery: timeline.cameraObstacleProjectionDelivery,
playbackPointPack,
});
})()
.then((chunk) => {
if (controller.signal.aborted) return;
setChunks((current) => {
@@ -161,7 +220,7 @@ export function useM4ThreatTimelineFrame({
// loaded first, then the next chunk is prefetched on the following render.
break;
}
}, [activeChunkStart, chunkSize, chunks, endpointRoot, resultId, timeline]);
}, [activeChunkStart, binaryPlayback, chunkSize, chunks, endpointRoot, playbackManifest, resultId, timeline]);
const activeFrame: M4ThreatTimelineFrame | null = useMemo(() => {
if (activeSequence === null || activeChunkStart === null) return null;
@@ -181,8 +240,17 @@ export function useM4ThreatTimelineFrame({
activeSequence,
activeFrame,
availableFrames,
loading: error === null && Boolean(timeline) && !activeFrame,
error,
loading: error === null && playbackError === null && Boolean(timeline) && !activeFrame,
loadingLabel: !binaryPlayback
? "Догружаем следующий spatial-буфер без сброса сцены"
: playbackProgress.phase === "manifest"
? "Проверяем M4 playback manifest"
: playbackProgress.phase === "ready"
? "Подготавливаем текущий spatial-кадр"
: `${playbackProgress.phase === "verify" ? "Проверяем" : "Буферизуем"} текущий spatial chunk · ${playbackProgress.totalBytes > 0
? `${Math.min(100, Math.round(playbackProgress.loadedBytes / playbackProgress.totalBytes * 100))}% · ${(playbackProgress.loadedBytes / 1_048_576).toFixed(1)} МБ`
: "0%"}`,
error: playbackError ?? error,
};
}
@@ -1,25 +1,45 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import {
useCallback,
useEffect,
useMemo,
useRef,
useState,
type DragEvent,
} from "react";
import {
ActivityIndicator,
Button,
ConfirmationModal,
GlassSurface,
Icon,
IconButton,
StatusBadge,
} from "@nodedc/ui-react";
import { SimulationCatalog } from "../../components/simulation/SimulationCatalog";
import { SimulationProjectWindow } from "../../components/simulation/SimulationProjectWindow";
import { SimulationViewport } from "../../components/simulation/SimulationViewport";
import {
createSimulationProject,
deleteSimulationProject,
fetchSimulationProjects,
retrySimulationProject,
uploadSimulationProjectSource,
type SimulationProject,
type SimulationProjectStatus,
type SimulationUploadProgress,
} from "../../core/simulation/projects";
import {
archiveProjectName,
archivesFromDrop,
hasDroppedFiles,
} from "../../core/simulation/sourceFiles";
const ACTIVE_STATUSES = new Set<SimulationProjectStatus>(["queued", "processing", "importing"]);
const BUILD_STATUSES = new Set<SimulationProjectStatus>(["queued", "processing", "importing"]);
interface BrowserUpload {
project: SimulationProject;
file: File;
}
export function SimulationWorkspace() {
const [projects, setProjects] = useState<SimulationProject[]>([]);
@@ -30,6 +50,15 @@ export function SimulationWorkspace() {
const [editing, setEditing] = useState<SimulationProject | null>(null);
const [deleting, setDeleting] = useState<SimulationProject | null>(null);
const [retryingId, setRetryingId] = useState<string | null>(null);
const [pendingUploads, setPendingUploads] = useState<BrowserUpload[]>([]);
const [uploadProgress, setUploadProgress] = useState<Map<string, SimulationUploadProgress>>(
() => new Map(),
);
const [queueError, setQueueError] = useState<string | null>(null);
const [dropping, setDropping] = useState(false);
const activeUploadRef = useRef<{ projectId: string; controller: AbortController } | null>(null);
const intakeChainRef = useRef<Promise<void>>(Promise.resolve());
const dragDepthRef = useRef(0);
const load = useCallback(async (signal?: AbortSignal) => {
try {
@@ -51,11 +80,60 @@ export function SimulationWorkspace() {
}, [load]);
useEffect(() => {
if (!projects.some((project) => ACTIVE_STATUSES.has(project.status))) return;
if (!projects.some((project) => BUILD_STATUSES.has(project.status))) return;
const timer = window.setInterval(() => void load(), 2_000);
return () => window.clearInterval(timer);
}, [load, projects]);
useEffect(() => {
const next = pendingUploads[0];
if (!next || activeUploadRef.current) return;
const controller = new AbortController();
activeUploadRef.current = { projectId: next.project.projectId, controller };
void uploadSimulationProjectSource(
next.project,
[{ file: next.file, logicalPath: next.file.name }],
(progress) => {
setUploadProgress((current) => {
const updated = new Map(current);
updated.set(next.project.projectId, progress);
return updated;
});
setProjects((current) => current.map((project) => (
project.projectId === next.project.projectId
? {
...project,
source: { ...project.source, uploadedByteLength: progress.uploadedBytes },
}
: project
)));
},
controller.signal,
).then((queued) => {
setProjects((current) => current.map((project) => (
project.projectId === queued.projectId ? queued : project
)));
setQueueError(null);
}).catch((caught) => {
if (controller.signal.aborted) return;
setQueueError(caught instanceof Error
? `${next.file.name}: ${caught.message}`
: `${next.file.name}: загрузка остановлена.`);
}).finally(() => {
setPendingUploads((current) => current.filter(
(item) => item.project.projectId !== next.project.projectId,
));
setUploadProgress((current) => {
const updated = new Map(current);
updated.delete(next.project.projectId);
return updated;
});
activeUploadRef.current = null;
});
}, [pendingUploads]);
useEffect(() => () => activeUploadRef.current?.controller.abort(), []);
const selected = useMemo(
() => projects.find((project) => project.projectId === selectedId) ?? null,
[projects, selectedId],
@@ -72,8 +150,9 @@ export function SimulationWorkspace() {
};
const acceptSaved = (project: SimulationProject) => {
const wasEditing = editing !== null;
setProjects((current) => [project, ...current.filter((item) => item.projectId !== project.projectId)]);
setSelectedId(project.projectId);
if (!wasEditing) setSelectedId(project.projectId);
setWindowOpen(false);
setEditing(null);
};
@@ -86,10 +165,77 @@ export function SimulationWorkspace() {
const confirmDelete = async () => {
if (!deleting) return;
await deleteSimulationProject(deleting.projectId);
setProjects((current) => current.filter((project) => project.projectId !== deleting.projectId));
if (selectedId === deleting.projectId) setSelectedId(null);
setDeleting(null);
const pending = pendingUploads.find((item) => item.project.projectId === deleting.projectId);
if (activeUploadRef.current?.projectId === deleting.projectId) {
activeUploadRef.current.controller.abort();
}
setPendingUploads((current) => current.filter(
(item) => item.project.projectId !== deleting.projectId,
));
try {
await deleteSimulationProject(deleting.projectId);
setProjects((current) => current.filter((project) => project.projectId !== deleting.projectId));
setUploadProgress((current) => {
const updated = new Map(current);
updated.delete(deleting.projectId);
return updated;
});
if (selectedId === deleting.projectId) setSelectedId(null);
setDeleting(null);
} catch (caught) {
if (pending) setPendingUploads((current) => [pending, ...current]);
const message = caught instanceof Error ? caught.message : "Не удалось удалить проект.";
setQueueError(message);
throw caught;
}
};
const enqueueDroppedArchives = useCallback((files: File[]) => {
intakeChainRef.current = intakeChainRef.current.then(async () => {
for (const file of files) {
try {
const project = await createSimulationProject(
archiveProjectName(file.name),
"outdoor",
[{ file, logicalPath: file.name }],
);
setProjects((current) => [
project,
...current.filter((item) => item.projectId !== project.projectId),
]);
setPendingUploads((current) => [...current, { project, file }]);
} catch (caught) {
setQueueError(caught instanceof Error
? `${file.name}: ${caught.message}`
: `${file.name}: не удалось создать проект.`);
}
}
});
}, []);
const handleDrop = async (transfer: DataTransfer) => {
dragDepthRef.current = 0;
setDropping(false);
setQueueError(null);
try {
enqueueDroppedArchives(await archivesFromDrop(transfer));
} catch (caught) {
setQueueError(caught instanceof Error ? caught.message : "Архивы не приняты.");
}
};
const handleDragEnter = (event: DragEvent<HTMLDivElement>) => {
if (!hasDroppedFiles(event.dataTransfer)) return;
event.preventDefault();
dragDepthRef.current += 1;
setDropping(true);
};
const handleDragLeave = (event: DragEvent<HTMLDivElement>) => {
if (!hasDroppedFiles(event.dataTransfer)) return;
event.preventDefault();
dragDepthRef.current = Math.max(0, dragDepthRef.current - 1);
if (dragDepthRef.current === 0) setDropping(false);
};
const retry = async (project: SimulationProject) => {
@@ -141,7 +287,7 @@ export function SimulationWorkspace() {
</div>
<div className="simulation-workspace__processing-actions">
<StatusBadge tone={selected.status === "failed" ? "warning" : "accent"}>
{selected.provider.state ?? selected.status}
{providerStateLabel(selected.provider.state, selected.status)}
</StatusBadge>
{selected.status === "failed" ? (
<Button
@@ -164,93 +310,45 @@ export function SimulationWorkspace() {
}
return (
<div className="simulation-workspace">
<div
className="simulation-workspace"
onDragEnter={handleDragEnter}
onDragOver={(event) => {
if (!hasDroppedFiles(event.dataTransfer)) return;
event.preventDefault();
event.dataTransfer.dropEffect = "copy";
}}
onDragLeave={handleDragLeave}
onDrop={(event) => {
if (!hasDroppedFiles(event.dataTransfer)) return;
event.preventDefault();
void handleDrop(event.dataTransfer);
}}
>
<header className="simulation-workspace__head">
<div>
<span className="section-eyebrow">ТЕСТОВЫЙ КОНТУР / СИМУЛЯЦИИ</span>
<h2>Gaussian-миры</h2>
<p>Каталог исходников, сборок Worker 006 и полнофункциональных PlayCanvas-сцен.</p>
<p>Каталог исходников, сборок Worker 006 и полнофункциональных интерактивных сцен.</p>
</div>
<Button variant="primary" icon={<Icon name="plus" size={16} />} onClick={openCreate}>
Новый проект
</Button>
</header>
<section className="simulation-catalog" aria-label="Проекты симуляции">
<header className="simulation-catalog__summary">
<div><span>Проектов</span><strong>{projects.length.toLocaleString("ru-RU")}</strong></div>
<div><span>Готово</span><strong>{projects.filter((project) => project.status === "ready").length}</strong></div>
<div><span>В работе</span><strong>{projects.filter((project) => ACTIVE_STATUSES.has(project.status)).length}</strong></div>
<div><span>Исходники</span><strong>{formatBytes(projects.reduce((sum, project) => sum + project.source.totalByteLength, 0))}</strong></div>
</header>
{loading ? (
<div className="simulation-catalog__state" role="status">
<ActivityIndicator label="Загрузка каталога" />
<span>Читаем каталог сцен</span>
</div>
) : error ? (
<div className="simulation-catalog__state" role="alert">
<Icon name="alert" size={20} />
<div><strong>Каталог недоступен</strong><span>{error}</span></div>
<Button size="compact" onClick={() => void load()}>Повторить</Button>
</div>
) : projects.length === 0 ? (
<div className="simulation-catalog__empty">
<span><Icon name="globe" size={20} /></span>
<strong>Проектов пока нет</strong>
<p>Создайте первый мир из папки LCC/LCC2 или архива ZIP, RAR, 7z.</p>
<Button variant="primary" icon={<Icon name="upload" size={16} />} onClick={openCreate}>
Загрузить исходник
</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) => {
const status = statusPresentation(project.status);
return (
<tr key={project.projectId} data-status={project.status}>
<td>
<button type="button" onClick={() => setSelectedId(project.projectId)}>
<span className="simulation-catalog__project-icon"><Icon name="globe" size={18} /></span>
<span><strong>{project.name}</strong><small>{sceneTypeLabel(project.sceneType)}</small></span>
</button>
</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}`} disabled={ACTIVE_STATUSES.has(project.status)} onClick={() => setDeleting(project)}>
<Icon name="trash" size={16} />
</IconButton>
<IconButton label={`Редактировать ${project.name}`} onClick={() => openEdit(project)}>
<Icon name="edit" size={16} />
</IconButton>
</div>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
)}
</section>
<SimulationCatalog
projects={projects}
uploadProgress={uploadProgress}
loading={loading}
loadError={error}
queueError={queueError}
dropping={dropping}
onCreate={openCreate}
onRetryLoad={() => void load()}
onSelect={(project) => setSelectedId(project.projectId)}
onEdit={openEdit}
onDelete={setDeleting}
/>
<SimulationProjectWindow
open={windowOpen}
@@ -280,7 +378,7 @@ function statusPresentation(status: SimulationProjectStatus): {
label: string;
tone: "success" | "accent" | "warning" | "neutral";
} {
if (status === "ready") return { label: "Готово", tone: "success" };
if (status === "ready") return { label: "Визуал готов", tone: "success" };
if (status === "failed") return { label: "Ошибка", tone: "warning" };
if (status === "uploading") return { label: "Загрузка", tone: "neutral" };
if (status === "importing") return { label: "Импорт", tone: "accent" };
@@ -294,28 +392,20 @@ function processingMessage(project: SimulationProject): string {
if (typeof completed === "number" && typeof total === "number") {
return `Этап ${completed.toLocaleString("ru-RU")} из ${total.toLocaleString("ru-RU")}. Каталог обновляется автоматически.`;
}
return "Исходник подтверждён; конвертация SOG и Streamed SOG выполняется в переносимом контейнере.";
return "Исходник подтверждён; локация и её LOD-уровни собираются в переносимом контейнере.";
}
function sceneTypeLabel(value: SimulationProject["sceneType"]): string {
if (value === "interior") return "Интерьер";
if (value === "object") return "Объект";
return "Улица";
}
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 })} ГБ`;
function providerStateLabel(
state: string | null,
projectStatus: SimulationProjectStatus,
): string {
if (state === "queued") return "Ожидание";
if (state === "verifying_source") return "Проверка исходника";
if (state === "inspecting") return "Анализ локации";
if (state === "building_preview") return "Подготовка локации";
if (state === "building_streamed_sog") return "Сборка LOD";
if (state === "building_collision") return "Сборка коллизий";
if (state === "ready") return "Визуал готов";
if (state === "failed") return "Ошибка";
return statusPresentation(projectStatus).label;
}
@@ -7,6 +7,7 @@ let server;
let fetchAdvancedLaboratoryResults;
let fetchAdvancedLaboratoryIndex;
let fetchAdvancedLaboratoryResult;
let fetchM49TgsAnchorSpatial;
let AdvancedLaboratoryContractError;
let buildLaboratoryCatalog;
let buildLaboratoryProfiles;
@@ -836,6 +837,77 @@ function e40() {
};
}
function m49View() {
const anchors = [171, 306, 368, 402, 450, 509, 525, 744, 1122, 1856];
return {
schema_version: "missioncore.m49-tgs-fail-closed-view/v1",
result_id: `m49-tgs-fail-closed-${"2".repeat(64)}`,
created_at_utc: "2026-08-26T17:09:43Z",
source: {
source_id: "RAVNOVES00",
source_session_id: "20260720T065719Z_viewer_live",
source_pack_sha256: "3".repeat(64),
linked_visual_result_id: `m4-threat-replay-${"4".repeat(64)}`,
anchor_frame_indices: anchors,
},
configuration: {
profile_id: "m49-ravnoves00-tgs-fail-closed-evidence/v1",
config_sha256: "5".repeat(64),
coordinate_frame: "map-gravity-local",
primary_profile: "causal_rolling_1s",
cell_size_m: 0.45,
radius_m: 12,
},
execution: {
worker: "Worker 006",
device: "cpu",
gpu_used: false,
wrapper_elapsed_seconds: 12.1,
},
metrics: {
anchor_count: 10,
anchor_profile_count: 20,
all_eligible_points_accounted: true,
costmap_cell_count: 2244,
process_wall_current_p50_ms: 20,
process_wall_current_max_ms: 30,
process_wall_rolling_p50_ms: 30,
process_wall_rolling_max_ms: 40,
process_max_rss_kib: 9292,
primary: anchors.map((anchor, slot) => ({
anchor_frame_index: anchor,
slot,
profile_id: "causal_rolling_1s",
point_count: 3,
ground_point_count: 1,
nonground_point_count: 1,
rejected_point_count: 1,
ground_cell_count: 1,
nonground_cell_count: 1,
rejected_cell_count: 1,
unobserved_cell_count: 2241,
all_points_accounted: true,
})),
},
acceptance: {
representation_complete: true,
all_points_accounted: true,
aos_absent: true,
gpu_absent: true,
visual_quality_accepted: false,
traversability_accepted: false,
},
decision: {
state: "visual-review-required",
candidate_retained: true,
next_action: "Review the ten anchors.",
},
limitations: ["bounded diagnostic evidence"],
ground_truth: false,
access: "read-only",
};
}
before(async () => {
server = await createServer({
appType: "custom",
@@ -850,6 +922,9 @@ before(async () => {
fetchAdvancedLaboratoryIndex,
fetchAdvancedLaboratoryResult,
} = await server.ssrLoadModule("/src/core/laboratory/advancedIndex.ts"));
({ fetchM49TgsAnchorSpatial } = await server.ssrLoadModule(
"/src/core/laboratory/m49TgsFailClosed.ts",
));
({
buildLaboratoryCatalog,
buildLaboratoryProfiles,
@@ -1596,6 +1671,62 @@ test("selected advanced LAB fetches only its own strict catalog", async () => {
]);
});
test("selected M49 LAB preserves the sealed fail-closed contract", async () => {
const payload = m49View();
const requests = [];
const decoded = await fetchAdvancedLaboratoryResult(
"m49-tgs-fail-closed-evidence",
{
resultId: payload.result_id,
fetcher: async (input, init) => {
requests.push({ input: String(input), method: init?.method });
return new Response(JSON.stringify(payload), { status: 200 });
},
},
);
assert.equal(decoded.m49Tgs.metrics.anchorCount, 10);
assert.equal(decoded.m49Tgs.metrics.costmapCellCount, 2244);
assert.equal(decoded.m49Tgs.acceptance.visualQualityAccepted, false);
assert.deepEqual(requests, [{
input: `/api/v1/laboratory/m49/tgs-fail-closed/${payload.result_id}`,
method: "GET",
}]);
});
test("M49 anchor fetch keeps every point and all four costmap states", async () => {
const resultId = m49View().result_id;
const centers = Array.from({ length: 2244 }, (_, index) => [index * 0.45, 0]);
const states = Array.from({ length: 2244 }, (_, index) => index % 4);
const zBounds = states.map((state) => state === 0 ? [null, null] : [0, 0.2]);
const decoded = await fetchM49TgsAnchorSpatial(resultId, 171, "causal_rolling_1s", {
fetcher: async () => new Response(JSON.stringify({
schema_version: "missioncore.m49-tgs-anchor-spatial/v1",
result_id: resultId,
anchor_frame_index: 171,
source_sequence: 171,
profile: "causal_rolling_1s",
coordinate_frame: "map-gravity-local",
points_xyz_m: [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
point_states: [1, 2, 3],
costmap: {
cell_size_m: 0.45,
radius_m: 12,
centers_xy_m: centers,
states,
z_bounds_m: zBounds,
},
all_points_accounted: true,
aos_used: false,
access: "read-only",
}), { status: 200 }),
});
assert.deepEqual(decoded.pointStates, [1, 2, 3]);
assert.equal(decoded.costmap.states.length, 2244);
assert.deepEqual(new Set(decoded.costmap.states), new Set([0, 1, 2, 3]));
});
test("keeps valid LAB catalogs available when one transport endpoint fails", async () => {
const decoded = await fetchAdvancedLaboratoryResults({
fetcher: async (input) => {
@@ -691,3 +691,18 @@ test("metric evidence keeps one WebGL renderer while frame labels advance", () =
/querySelector\("canvas"\)[\s\S]*?setAttribute\("aria-label", label\)[\s\S]*?\}, \[label\]\);/,
);
});
test("metric evidence renders on demand while preserving damped camera interaction", () => {
const source = readFileSync(
new URL("../src/components/laboratory/LaboratoryMetricEvidenceScene.tsx", import.meta.url),
"utf8",
);
assert.match(source, /const requestRenderRef = useRef/);
assert.match(source, /controls\.addEventListener\("change", requestRender\)/);
assert.match(source, /requestRenderRef\.current\(\)/);
assert.doesNotMatch(
source,
/const render = \(\) => \{\s*animationFrame = window\.requestAnimationFrame\(render\)/,
);
});
@@ -0,0 +1,234 @@
import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { after, before, test } from "node:test";
import { createServer } from "vite";
let server;
let M49PhysicalSafetyPlaybackBuffer;
let fetchM49PhysicalSafetyPlaybackIdForSource;
const resultId = `m49-physical-safety-playback-${"a".repeat(64)}`;
const endpoint = `/api/v1/laboratory/m49/physical-safety-playback/${resultId}`;
const frameCount = 160;
const cellCount = 4;
before(async () => {
server = await createServer({
appType: "custom",
logLevel: "silent",
server: { middlewareMode: true },
});
({
M49PhysicalSafetyPlaybackBuffer,
fetchM49PhysicalSafetyPlaybackIdForSource,
} = await server.ssrLoadModule(
"/src/core/laboratory/m49PhysicalSafetyPlayback.ts",
));
});
after(async () => {
await server?.close();
});
function sha256(buffer) {
return createHash("sha256").update(Buffer.from(buffer)).digest("hex");
}
function chunk(index) {
const start = index * 32;
const count = Math.min(32, frameCount - start);
const stateBytes = count * cellCount;
const zBytes = count * cellCount * 2 * 4;
const headerBytes = 28;
const padding = (4 - ((headerBytes + stateBytes) % 4)) % 4;
const buffer = new ArrayBuffer(headerBytes + stateBytes + padding + zBytes);
const bytes = new Uint8Array(buffer);
bytes.set(new TextEncoder().encode("MCPSCH01"));
const view = new DataView(buffer);
view.setUint32(8, start, true);
view.setUint32(12, count, true);
view.setUint32(16, cellCount, true);
view.setUint32(20, stateBytes, true);
view.setUint32(24, zBytes, true);
const states = new Uint8Array(buffer, headerBytes, stateBytes);
states.fill(1);
const z = new Float32Array(buffer, headerBytes + stateBytes + padding, count * cellCount * 2);
for (let local = 0; local < count; local += 1) {
const sequence = start + local;
for (let cell = 0; cell < cellCount; cell += 1) {
z[(local * cellCount + cell) * 2] = 0;
z[(local * cellCount + cell) * 2 + 1] = 0.12;
}
if (sequence === 17) {
states.fill(0, local * cellCount, (local + 1) * cellCount);
z.fill(Number.NaN, local * cellCount * 2, (local + 1) * cellCount * 2);
}
}
return buffer;
}
function fixture() {
const centers = new Float32Array([0, 0, 0.15, 0, 0.3, 0, 0.45, 0]).buffer;
const frames = new TextEncoder().encode(Array.from({ length: frameCount }, (_, sequence) => (
JSON.stringify({
source_frame_index: sequence,
session_seconds: sequence / 10,
sample_available: sequence !== 17,
eligible_point_count: sequence === 17 ? 0 : cellCount,
})
)).join("\n") + "\n").buffer;
const chunks = Array.from({ length: Math.ceil(frameCount / 32) }, (_, index) => chunk(index));
const manifest = {
schema_version: "missioncore.m49-physical-safety-playback/v1",
result_id: resultId,
created_at_utc: "2026-08-27T12:00:00Z",
access: "read-only-sealed-local",
identity: { source_result_id: `m49-tgs-full-shadow-${"b".repeat(64)}` },
execution: {
execution_class: "local-sequential-offline",
worker_role: "realtime-only",
worker_runtime_dependency: false,
worker_requests_required: 0,
},
authority: {
commands_enabled: false,
navigation_or_safety_accepted: false,
actuation_accepted: false,
},
playback: {
coordinate_frame: "map-gravity-local",
source_pace_hz: 10,
frame_count: frameCount,
cell_count: cellCount,
cell_size_m: 0.15,
radius_m: 12,
state_codes: {
UNOBSERVED: 0,
GROUND_SUPPORT: 1,
NONGROUND_OCCUPIED: 2,
UNKNOWN_REJECTED: 3,
},
chunk_frame_count: 32,
startup_prebuffer_chunk_count: 2,
resident_chunk_count_max: 3,
forward_prefetch_chunk_count: 1,
centers: {
url: `${endpoint}/tracks/centers`,
media_type: "application/octet-stream",
byte_length: centers.byteLength,
sha256: sha256(centers),
dtype: "<f4",
shape: [cellCount, 2],
},
frames: {
url: `${endpoint}/tracks/frames`,
media_type: "application/x-ndjson",
byte_length: frames.byteLength,
sha256: sha256(frames),
dtype: "ndjson",
shape: [frameCount],
},
chunks: chunks.map((buffer, index) => ({
index,
start: index * 32,
count: Math.min(32, frameCount - index * 32),
url: `${endpoint}/chunks/${index}`,
media_type: "application/octet-stream",
byte_length: buffer.byteLength,
sha256: sha256(buffer),
header_bytes: 28,
format: "mcpsch01-states-u8-aligned-z-bounds-f32le",
})),
},
};
const responses = new Map([
[`${endpoint}/tracks/centers`, centers],
[`${endpoint}/tracks/frames`, frames],
...chunks.map((buffer, index) => [`${endpoint}/chunks/${index}`, buffer]),
]);
return { manifest, responses };
}
test("physical-safety playback opens and seeks with bounded local chunks only", async () => {
const { manifest, responses } = fixture();
const requested = [];
const progress = [];
const fetcher = async (input) => {
const url = String(input);
requested.push(url);
if (url === `${endpoint.replace(`/${resultId}`, "")}/results?limit=2&source_result_id=${manifest.identity.source_result_id}`) {
return Response.json({
schema_version: "missioncore.m49-physical-safety-playback-catalog/v1",
worker_runtime_dependency: false,
access: "read-only-sealed-local",
items: [{
result_id: resultId,
source_result_id: manifest.identity.source_result_id,
worker_runtime_dependency: false,
navigation_or_safety_accepted: false,
}],
});
}
if (url === `${endpoint}/manifest`) {
return Response.json(manifest);
}
const payload = responses.get(url);
return payload
? new Response(payload, { headers: { "Content-Type": "application/octet-stream" } })
: new Response(null, { status: 404 });
};
const resolvedResultId = await fetchM49PhysicalSafetyPlaybackIdForSource(
manifest.identity.source_result_id,
{ fetcher },
);
assert.equal(resolvedResultId, resultId);
const playback = await M49PhysicalSafetyPlaybackBuffer.open(resolvedResultId, {
fetcher,
onProgress: (value) => progress.push(value),
});
assert.deepEqual(playback.residentChunkIndexes, [0, 1]);
assert.equal(progress.at(-1).phase, "ready");
assert.equal(requested.some((url) => url.includes("worker")), false);
const startupRequestCount = requested.length;
await playback.prepare(0);
await playback.prepare(0);
assert.equal(requested.length, startupRequestCount);
const missing = await playback.frame(17);
assert.equal(missing.metadata.sampleAvailable, false);
assert.deepEqual(new Set(missing.states), new Set([0]));
assert.equal([...missing.zBoundsM].every((value) => Number.isNaN(value)), true);
await playback.prepare(100);
assert.equal(playback.residentChunkIndexes.length <= 3, true);
assert.equal(playback.residentChunkIndexes.includes(3), true);
assert.equal(playback.residentChunkIndexes.includes(4), true);
const selected = await playback.frame(100);
assert.deepEqual([...selected.states], [1, 1, 1, 1]);
const maximumResidentBytes = manifest.playback.centers.byte_length
+ manifest.playback.frames.byte_length
+ 3 * Math.max(...manifest.playback.chunks.map((value) => value.byte_length));
assert.equal(playback.residentByteLength <= maximumResidentBytes, true);
await playback.prepare(0);
assert.equal(playback.residentChunkIndexes.length <= 3, true);
assert.equal(playback.residentChunkIndexes.includes(0), true);
assert.equal(playback.residentChunkIndexes.includes(1), true);
assert.equal(
requested.every((url) => url.startsWith("/api/v1/laboratory/m49/physical-safety-playback")),
true,
);
});
test("physical-safety playback rejects a manifest that points at Worker", async () => {
const { manifest } = fixture();
manifest.playback.centers.url = "http://worker-006/centers";
await assert.rejects(
M49PhysicalSafetyPlaybackBuffer.open(resultId, {
fetcher: async () => Response.json(manifest),
}),
/sealed local API/,
);
});
@@ -0,0 +1,150 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { after, before, test } from "node:test";
import { createServer } from "vite";
let server;
let fetchM49TgsFullShadowSpatialChunk;
let parseM49TgsNpyTrack;
const resultId = `m49-tgs-full-shadow-${"a".repeat(64)}`;
before(async () => {
server = await createServer({
appType: "custom",
logLevel: "silent",
server: { middlewareMode: true },
});
({ fetchM49TgsFullShadowSpatialChunk, parseM49TgsNpyTrack } = await server.ssrLoadModule(
"/src/core/laboratory/m49TgsFullShadow.ts",
));
});
after(async () => {
await server?.close();
});
test("M4.9T5 chunk keeps every missing-LiDAR cell explicitly UNOBSERVED", async () => {
let requestedUrl = "";
const centers = Array.from({ length: 2244 }, (_, index) => [index * 0.45, 0]);
const unobserved = Array.from({ length: 2244 }, () => 0);
const zBounds = Array.from({ length: 2244 }, () => [null, null]);
const metrics = {
eligible_point_count: 0,
ground_point_count: 0,
nonground_point_count: 0,
rejected_point_count: 0,
occupied_cell_count: 0,
};
const chunk = await fetchM49TgsFullShadowSpatialChunk(resultId, 7, 1, {
fetcher: async (url) => {
requestedUrl = String(url);
return new Response(JSON.stringify({
schema_version: "missioncore.m49-tgs-full-shadow-spatial-chunk/v1",
result_id: resultId,
start: 7,
count: 1,
coordinate_frame: "map-gravity-local",
costmap: {
cell_size_m: 0.45,
radius_m: 12,
centers_xy_m: centers,
},
frames: [{
source_sequence: 7,
source_frame_index: 7,
session_seconds: 36.119857292,
sample_available: false,
states: unobserved,
z_bounds_m: zBounds,
metrics,
}],
}), { status: 200, headers: { "Content-Type": "application/json" } });
},
});
assert.equal(
requestedUrl,
`/api/v1/laboratory/m49/tgs-full-shadow/${resultId}/spatial/chunk?start=7&count=1`,
);
assert.equal(chunk.count, 1);
assert.equal(chunk.frames[0].sampleAvailable, false);
assert.equal(chunk.frames[0].costmap.states.length, 2244);
assert.deepEqual(new Set(chunk.frames[0].costmap.states), new Set([0]));
});
test("M4.9T5 viewer prefers autonomous chunks and keeps a sealed legacy fallback", async () => {
const [source, visual, scene, contract] = await Promise.all([
readFile(
new URL("../src/workspaces/laboratory/M49TgsFullShadowEvidence.tsx", import.meta.url),
"utf8",
),
readFile(
new URL("../src/workspaces/laboratory/M4ReplayThreatVisual.tsx", import.meta.url),
"utf8",
),
readFile(
new URL("../src/components/laboratory/LaboratoryMetricEvidenceScene.tsx", import.meta.url),
"utf8",
),
readFile(
new URL("../src/core/laboratory/m49TgsFullShadow.ts", import.meta.url),
"utf8",
),
]);
assert.match(source, /fetchM49PhysicalSafetyPlaybackIdForSource/);
assert.match(source, /M49PhysicalSafetyPlaybackBuffer\.open/);
assert.match(source, /physicalPlayback\.frameIfResident/);
assert.match(source, /physicalPlayback\.prepare/);
assert.match(source, /fetchM49TgsFullShadowPlaybackPack/);
assert.match(source, /playbackProgress/);
assert.doesNotMatch(source, /fetchM49TgsFullShadowSpatialChunk/);
assert.match(source, /sampleAvailable: frame\.sampleAvailable/);
assert.match(source, /packedCellsMapGravityLocal/);
assert.match(source, /playbackPack\.states\.subarray/);
assert.match(source, /playbackPack\.zBoundsM\.subarray/);
assert.doesNotMatch(source, /Array\.from\(\s*playbackPack\.states/);
assert.doesNotMatch(source, /centersXyM\.map\(/);
assert.match(source, /fetchE47SemanticSlamResult/);
assert.match(source, /next\.baseM4ResultId !== result\.source\.linkedVisualResultId/);
assert.match(source, /semantic=\{semantic \? \{/);
assert.match(
visual,
/classifiedSpatialFrame\s*&&\s*classifiedSpatialFrame\.sampleAvailable !== false/,
);
assert.doesNotMatch(visual, /classifiedSpatialFrame\?\.sampleAvailable !== false/);
assert.match(visual, /timelineFrame\.availableFrames\.find/);
assert.match(visual, /<LaboratoryMetricEvidenceScene/);
assert.doesNotMatch(
visual,
/\{\(!classifiedSpatialLayer \? spatialFrame : displayedClassifiedSpatialFrame\) \? \(/,
);
assert.match(scene, /useEffect\(\(\) => resetView\(\), \[mode, resetView\]\)/);
assert.match(contract, /linked_semantic_result_id/);
assert.match(contract, /missioncore\.m49-tgs-full-shadow-playback\/v1/);
});
function npyFloat32(values, shape) {
const shapeText = shape.length === 1 ? `${shape[0]},` : shape.join(", ");
const prefixLength = 10;
let header = `{'descr': '<f4', 'fortran_order': False, 'shape': (${shapeText}), }`;
const padding = (16 - ((prefixLength + header.length + 1) % 16)) % 16;
header += " ".repeat(padding) + "\n";
const buffer = new ArrayBuffer(prefixLength + header.length + values.length * 4);
const bytes = new Uint8Array(buffer);
bytes.set([0x93, 0x4e, 0x55, 0x4d, 0x50, 0x59, 1, 0]);
new DataView(buffer).setUint16(8, header.length, true);
bytes.set(new TextEncoder().encode(header), prefixLength);
new Float32Array(buffer, prefixLength + header.length, values.length).set(values);
return buffer;
}
test("M4.9T5 parses sealed NPY tracks without JSON point arrays", () => {
const parsed = parseM49TgsNpyTrack(
npyFloat32([1.25, -2.5, 3.75, 4.5], [2, 2]),
"<f4",
[2, 2],
);
assert.deepEqual([...parsed], [1.25, -2.5, 3.75, 4.5]);
});
@@ -1,4 +1,5 @@
import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import { readFile } from "node:fs/promises";
import { after, before, test } from "node:test";
@@ -9,7 +10,10 @@ let fetchM4ThreatReplayResult;
let fetchM4ThreatVisual;
let fetchM4ThreatTimeline;
let fetchM4ThreatTimelineChunk;
let fetchM4ThreatPlaybackManifest;
let fetchM4ThreatPlaybackPointChunk;
let fetchM4ThreatCameraPointOverlay;
let hydrateM4ThreatTimelineFrame;
let selectM4ThreatTimelineFrame;
let selectM4ThreatTimelineSequence;
let advanceRecordedEvidencePlayback;
@@ -32,7 +36,10 @@ before(async () => {
fetchM4ThreatVisual,
fetchM4ThreatTimeline,
fetchM4ThreatTimelineChunk,
fetchM4ThreatPlaybackManifest,
fetchM4ThreatPlaybackPointChunk,
fetchM4ThreatCameraPointOverlay,
hydrateM4ThreatTimelineFrame,
selectM4ThreatTimelineFrame,
selectM4ThreatTimelineSequence,
} = await server.ssrLoadModule("/src/core/laboratory/m4ReplayThreat.ts"));
@@ -332,6 +339,115 @@ test("M4.6 timeline keeps only a compact index and decodes bounded spatial chunk
assert.equal(selectM4ThreatTimelineFrame(chunk.frames, 35.50).sequence, 1);
});
test("M4.6 hydrates a lightweight timeline frame from one retained binary point track", async () => {
const raw = timelineFrame(0, 35.421857292, {
body_frame: {
origin_map_xyz_m: [10, 20, 30],
basis_map_from_body: [[1, 0, 0], [0, 1, 0], [0, 0, 1]],
},
point_cloud_body_xyz_m: [],
point_cloud_source_count: 2,
point_cloud_sample_count: 2,
});
const parsed = await fetchM4ThreatTimelineChunk(resultId, 0, 1, {
playbackPointPack: {
resultId,
frameCount: 4489,
pointCount: 2,
pointOffsets: new Uint32Array([0, 2, ...Array(4488).fill(2)]),
pointsMapXyzM: new Float32Array([11, 20, 30.25, 12, 19.5, 30]),
},
fetcher: async (input) => {
assert.match(String(input), /include_points=false/);
return new Response(JSON.stringify({
schema_version: "missioncore.recorded-spatial-evidence-chunk/v1",
result_id: resultId,
start_sequence: 0,
frame_count: 1,
next_sequence: 1,
frames: [raw],
authority: "replay-simulated",
}), { status: 200 });
},
});
assert.deepEqual(parsed.frames[0].pointCloudBodyXyzM, [
[1, 0, 0.25],
[2, -0.5, 0],
]);
assert.equal(parsed.frames[0].pointCloudSampleCount, 2);
assert.equal(typeof hydrateM4ThreatTimelineFrame, "function");
});
test("M4.6 source cloud opens from one verified bounded chunk instead of the 105 MiB track", async () => {
const pointOffsets = [0, ...Array(4489).fill(2)];
const points = new Float32Array([11, 20, 30.25, 12, 19.5, 30]);
const pointBytes = points.buffer;
const pointSha256 = createHash("sha256").update(Buffer.from(pointBytes)).digest("hex");
const endpointRoot = "/api/v1/laboratory/m4-threat/results";
const chunks = Array.from({ length: 188 }, (_, index) => {
const start = index * 24;
const count = Math.min(24, 4489 - start);
const pointStart = pointOffsets[start];
const pointStop = pointOffsets[start + count];
const pointCount = pointStop - pointStart;
return {
index,
start,
count,
point_start: pointStart,
point_count: pointCount,
url: `${endpointRoot}/${resultId}/timeline/playback/chunks/${index}`,
media_type: "application/octet-stream",
dtype: "<f4",
shape: [pointCount, 3],
bytes: pointCount * 3 * 4,
sha256: index === 0 ? pointSha256 : "0".repeat(64),
};
});
const manifestPayload = {
schema_version: "missioncore.recorded-spatial-playback/v1",
result_id: resultId,
frame_count: 4489,
point_count: 2,
point_offsets: pointOffsets,
chunk_frame_count: 24,
resident_chunk_count_max: 4,
forward_prefetch_chunk_count: 1,
chunks,
track: {
id: "points-map-f32",
url: `${endpointRoot}/${resultId}/timeline/playback/tracks/points-map-f32`,
media_type: "application/octet-stream",
dtype: "<f4",
shape: [2, 3],
bytes: pointBytes.byteLength,
sha256: pointSha256,
},
coordinate_frame: "map",
access: "read-only-sealed-binary-playback",
};
const requested = [];
const fetcher = async (input) => {
const url = String(input);
requested.push(url);
if (url.endsWith("/timeline/playback")) return Response.json(manifestPayload);
if (url.endsWith("/timeline/playback/chunks/0")) return new Response(pointBytes.slice(0));
return new Response(null, { status: 404 });
};
const manifest = await fetchM4ThreatPlaybackManifest(resultId, { fetcher });
const chunk = await fetchM4ThreatPlaybackPointChunk(manifest, 0, { fetcher });
assert.deepEqual(requested, [
`${endpointRoot}/${resultId}/timeline/playback`,
`${endpointRoot}/${resultId}/timeline/playback/chunks/0`,
]);
assert.equal(chunk.pointCount, 2);
assert.equal(chunk.pointStart, 0);
assert.equal(chunk.sequenceCount, 24);
assert.equal(chunk.pointsMapXyzM.byteLength, 24);
assert.equal(requested.some((url) => url.endsWith("points-map-f32")), false);
});
test("M4.8S timeline binds factory-KB4 camera points through its exact endpoint", async () => {
const replayResultId = `m48s-fixed-class-detector-lab-${"b".repeat(64)}`;
const endpointRoot = "/api/v1/laboratory/m48s/fixed-class-detector";
@@ -784,10 +900,15 @@ test("M4.6 viewer keeps media and spatial panes on one playback clock", async ()
assert.match(visualCss, /width: 33\.333333%/);
assert.match(visualCss, /flex-flow: column nowrap/);
assert.match(visualCss, /m4-replay-threat-visual__overlay > div/);
assert.match(visualCss, /grid-template-columns: minmax\(0, max-content\)/);
assert.match(visualCss, /width: max-content/);
assert.match(visualCss, /grid-template-columns: minmax\(0, 1fr\)/);
assert.match(visualCss, /width: min\(17\.5rem/);
assert.match(visualCss, /grid-auto-rows: 3\.8rem/);
assert.match(visualCss, /--m4-replay-threat-overlay-pane-width/);
assert.match(visualCss, /white-space: nowrap/);
assert.match(visualCss, /-webkit-line-clamp: 2/);
assert.match(visual, /classifiedCellCount/);
assert.match(visual, /lastAvailableClassifiedSpatialFrame/);
assert.match(visual, /current UNOBSERVED/);
assert.match(visualCss, /laboratory-metric-evidence-scene__legend/);
assert.match(visualCss, /bottom: auto/);
assert.match(videoScene, /<RecordedFmp4Player/);
@@ -799,7 +920,29 @@ test("M4.6 viewer keeps media and spatial panes on one playback clock", async ()
assert.match(metricScene, /OrbitControls/);
assert.match(visual, /LOCAL SLAM/);
assert.match(visual, /showLocalSurface/);
assert.match(visual, /pointCloudBodyXyzM=\{spatialFrame\.pointCloudBodyXyzM\}/);
assert.match(
visual,
/pointCloudBodyXyzM=\{displayedClassifiedSpatialFrame && replaceClassifiedPointCloud[\s\S]*\? classifiedPointsBody[\s\S]*: classifiedContextSpatialFrame\?\.pointCloudBodyXyzM \?\? \[\]\}/,
);
assert.match(
visual,
/const classifiedSpatialFrame = classifiedSpatialLayer\?\.frame\?\.sourceSequence === timelineFrame\.activeSequence[\s\S]*lastClassifiedSpatialFrameRef[\s\S]*const displayedClassifiedSpatialFrame = classifiedSpatialFrame\s*&&\s*classifiedSpatialFrame\.sampleAvailable !== false/,
);
assert.doesNotMatch(visual, /classifiedSpatialFrame\?\.sampleAvailable !== false/);
assert.match(visual, /timelineFrame\.availableFrames\.find/);
assert.match(visual, /localSurfaceBodyXyzM=\{localSurface\.pointsBodyXyzM\}/);
assert.match(visual, /showLocalSurface=\{showLocalSurface\}/);
assert.match(visual, /\{semantic \? \([\s\S]*>\s*SEMANTICS\s*<\/Button>/);
assert.match(visual, /current safety — все \{classifiedCellCount\.toLocaleString\("ru-RU"\)\} TGS-ячейки UNOBSERVED/);
assert.match(
visual,
/mapGravityLocalSensorToBodyGround[\s\S]*rotated\[2\] \+ nominalSensorHeightM/,
);
assert.match(
visual,
/zBoundsM: \[[\s\S]*minimumSensorRelativeZ \+ nominalSensorHeightM[\s\S]*maximumSensorRelativeZ \+ nominalSensorHeightM/,
);
assert.match(visual, /classifiedCells=\{classifiedCellsBody\}/);
assert.match(metricScene, /Локальная SLAM-поверхность/);
assert.match(visual, /showJumpToEnd=\{false\}/);
assert.doesNotMatch(visual, /Назад на 5 секунд/);
@@ -173,6 +173,9 @@ test("recorded player keeps full-archive range fallback and uses bounded generat
assert.match(source, /pumpRecordedSegmentWindow/);
assert.match(source, /waitForRecordedVideoTarget/);
assert.match(source, /removeRecordedMediaRange/);
assert.match(source, /hasPresentedFrame/);
assert.match(source, /retainForwardFrame/);
assert.match(source, /candidateTarget\.sequence >= previousTarget\.sequence/);
assert.equal(recordedMediaDecodeStartSequence([1, 1491, 1501], 1500), 1491);
assert.deepEqual(
@@ -28,6 +28,8 @@ test("semantic evidence mask is GPU-colored, bounded, cancelled and object-conta
assert.match(source, /Math\.min\(width \/ mask\.width, height \/ mask\.height\)/);
assert.match(source, /decoded\.key !== expectedKey/);
assert.match(source, /mask\?\.key === expectedKey/);
assert.match(source, /lastReadyMaskRef/);
assert.match(source, /data-state=\{failure \? "error" : exactMask \? "ready" : renderMask \? "stale" : "loading"\}/);
assert.match(source, /recorded-evidence-semantic-mask-overlay__error/);
assert.match(source, /role="alert"/);
});
@@ -53,6 +55,12 @@ test("metric evidence keeps missing semantic assignments as context and exposes
assert.match(source, /classId === null \? undefined : colorsByClassId\.get\(classId\)/);
assert.match(source, /data-decision="semantic"/);
assert.match(source, /recordedEvidenceSemanticCssColor/);
assert.match(source, /DynamicDrawUsage/);
assert.match(source, /classifiedMeshesRef/);
assert.match(source, /classifiedPackedCells/);
assert.match(source, /updatePointPositions/);
assert.match(source, /renderer\.info\.render\.calls/);
assert.match(source, /data-decision="performance"/);
});
test("semantic point alignment follows the last qualified spatial increment", async () => {
@@ -9,37 +9,61 @@ async function read(relativePath) {
}
test("simulation is a dedicated Polygon workspace with a bounded feature slice", async () => {
const [productModel, workspaceHub, workspace, styles] = await Promise.all([
const [productModel, workspaceHub, workspace, catalog, styles] = await Promise.all([
read("productModel.ts"),
read("workspaces/Workspaces.tsx"),
read("workspaces/simulation/SimulationWorkspace.tsx"),
read("components/simulation/SimulationCatalog.tsx"),
read("styles.css"),
]);
assert.match(productModel, /id: "simulations"[\s\S]*root: "polygon"[\s\S]*kind: "simulations"/);
assert.match(workspaceHub, /case "simulations":[\s\S]*<SimulationWorkspace \/>/);
assert.match(workspace, /simulation-catalog__table/);
assert.match(catalog, /simulation-catalog__table/);
assert.match(workspace, /archivesFromDrop/);
assert.match(workspace, /uploadSimulationProjectSource/);
assert.match(catalog, /colSpan=\{3\}/);
assert.match(catalog, /Ожидает обработки/);
assert.match(catalog, /Прогресс \$\{roundedPercent\}% \|/);
assert.match(catalog, /Осталось примерно \$\{minutes\} мин/);
assert.match(catalog, /simulation-catalog__progress-track/);
assert.doesNotMatch(catalog, /disabled=\{ACTIVE_STATUSES\.has/);
assert.match(workspace, /<SimulationViewport[\s\S]*project=\{selected\}/);
assert.match(workspace, /<ConfirmationModal/);
assert.match(workspace, /if \(state === "building_streamed_sog"\) return "Сборка LOD"/);
assert.doesNotMatch(workspace, /\{selected\.provider\.state \?\? selected\.status\}/);
assert.match(styles, /styles\/simulation\.css/);
});
test("browser upload remains same-origin, resumable and token-free", async () => {
const [core, window] = await Promise.all([
const [core, window, sourceFiles] = await Promise.all([
read("core/simulation/projects.ts"),
read("components/simulation/SimulationProjectWindow.tsx"),
read("core/simulation/sourceFiles.ts"),
]);
assert.match(core, /const API_ROOT = "\/api\/v1\/simulation-worlds\/projects"/);
assert.match(core, /method: "HEAD"/);
assert.match(core, /method: "PATCH"/);
assert.match(core, /"Upload-Offset"/);
assert.match(core, /estimatedSecondsRemaining/);
assert.match(core, /smoothedBytesPerSecond \* 0\.7 \+ currentBytesPerSecond \* 0\.3/);
assert.match(core, /estimateSimulationProcessing/);
assert.match(core, /jobCreatedAtUtc/);
assert.match(core, /stateStartedAtUtc/);
assert.match(core, /REFERENCE_STREAM_SECONDS = 2_317/);
assert.doesNotMatch(core, /Authorization|Bearer|18090|Worker 006/);
assert.match(window, /accept="\.zip,\.rar,\.7z"/);
assert.match(window, /webkitdirectory/);
assert.match(window, /webkitGetAsEntry/);
assert.match(window, /ровно одна сцена \.lcc или \.lcc2/);
assert.match(sourceFiles, /webkitGetAsEntry/);
assert.match(sourceFiles, /entry\?\.isDirectory/);
assert.match(sourceFiles, /const file = item\.getAsFile\(\)/);
assert.match(sourceFiles, /MAX_SIMULATION_SOURCE_BYTES = 16 \* 1024 \*\* 3/);
assert.match(sourceFiles, /item\.kind === "file"/);
assert.match(sourceFiles, /ровно одна сцена \.lcc или \.lcc2/);
assert.match(sourceFiles, /отдельные архивы ZIP, RAR или 7z/);
assert.doesNotMatch(window, /Загружаем источник/);
assert.match(window, /считаем время/);
});
test("failed simulation can restart from its retained Mission Core source", async () => {
@@ -54,9 +78,13 @@ test("failed simulation can restart from its retained Mission Core source", asyn
});
test("PlayCanvas owns the realtime scene graph without an iframe or React entity tree", async () => {
const [runtime, viewport, packageDocument] = await Promise.all([
const [runtime, ugv, viewport, ugvSettings, projects, styles, packageDocument] = await Promise.all([
read("components/simulation/PlayCanvasRuntime.ts"),
read("components/simulation/SimulationUgvController.ts"),
read("components/simulation/SimulationViewport.tsx"),
read("components/simulation/SimulationUgvSettingsPanel.tsx"),
read("core/simulation/projects.ts"),
read("styles/simulation.css"),
readFile(new URL("../package.json", import.meta.url), "utf8"),
]);
@@ -72,41 +100,118 @@ test("PlayCanvas owns the realtime scene graph without an iframe or React entity
assert.match(runtime, /maximum: \{[^}]*lodRangeMin: 0, lodRangeMax: 5, pixelRatio: 2, splatBudget: 4_000_000, targetFps: 60/);
assert.match(runtime, /app\.scene\.gsplat\.splatBudget = profile\.splatBudget/);
assert.match(runtime, /app\.autoRender = false/);
assert.match(runtime, /app\.on\("update", this\.updateFrame\)/);
assert.match(runtime, /if \(!cameraChanged && this\.controlMode === "free"\) return/);
assert.doesNotMatch(runtime, /app\.on\("frameupdate"/);
assert.match(runtime, /WEBGL_lose_context/);
assert.match(runtime, /selected\.horizontal[^\n]+frame\.mouse\[0\] \*= -1/);
assert.match(runtime, /selected\.vertical[^\n]+frame\.mouse\[1\] \*= -1/);
assert.match(runtime, /setCameraInversion/);
assert.match(runtime, /async setViewMode\([\s\S]*this\.resetCameraInteraction\(\);[\s\S]*this\.viewMode = mode/);
assert.match(runtime, /input\._pointerId = -1/);
assert.match(runtime, /async setViewMode\([\s\S]*this\.viewMode = mode/);
assert.doesNotMatch(runtime.match(/async setViewMode\([\s\S]*?\n }/)?.[0] ?? "", /resetCameraInteraction/);
assert.match(runtime, /controller\.fire\("state"\)/);
assert.match(runtime, /state\.mouse\.fill\(0\)/);
assert.match(runtime, /new Asset\([\s\S]*"container"/);
assert.match(runtime, /instantiateRenderEntity/);
assert.match(runtime, /instantiateModelEntity/);
assert.match(runtime, /applyWorldTransform/);
assert.match(runtime, /setLayerWorldTransform/);
assert.match(runtime, /PLAYCANVAS_X_180_TRANSFORM/);
assert.match(runtime, /PLAYCANVAS_Y_180_TRANSFORM/);
assert.match(runtime, /PLAYCANVAS_Z_180_TRANSFORM/);
assert.match(runtime, /playCanvasEulerTransform/);
assert.match(runtime, /setFromEulerAngles/);
assert.match(runtime, /DracoDecoderModule/);
assert.match(runtime, /draco_decoder_gltf\.wasm/);
assert.match(runtime, /ensureCollision/);
assert.match(runtime, /setControlMode/);
assert.match(runtime, /setUgvPreset/);
assert.match(runtime, /SimulationUgvController/);
assert.match(runtime, /for \(const model of this\.collisionVisuals\) model\.enabled = visible/);
assert.match(runtime, /instantiateModelEntity/);
assert.match(runtime, /this\.ugvController\?\.disable\(\)/);
assert.match(runtime, /this\.collisionMaterial\.blendType = combined \? BLEND_NORMAL : BLEND_NONE/);
assert.match(runtime, /private requestRender\(\): void/);
assert.match(runtime, /app\.root\.addChild/);
assert.match(runtime, /dispose\(\)/);
assert.doesNotMatch(`${runtime}\n${viewport}`, /iframe|<GSplat/);
assert.match(viewport, /Collision недоступен/);
assert.match(viewport, /Для этой сборки collision GLB не был запрошен/);
assert.match(viewport, /Коллизии недоступны/);
assert.match(viewport, /Для этой сборки слой коллизий не создавался/);
assert.match(viewport, /<Select[\s\S]*Качество Gaussian-сцены/);
assert.match(viewport, /label="Режим управления"/);
assert.match(viewport, /label: "Свободно"/);
assert.match(viewport, /label: "UGV"/);
assert.match(viewport, /WASD\/стрелки · пробел — тормоз · R — домой/);
assert.match(viewport, /data-control-mode=\{controlMode\}/);
assert.match(viewport, /useState<SimulationQuality>\(project\.viewerSettings\.quality\)/);
assert.match(viewport, /runtimeRef\.current\?\.home\(\)/);
assert.match(viewport, /Настройки сцены/);
assert.match(viewport, /Инверсия визуального слоя/);
assert.match(viewport, /Инверсия collision-слоя/);
assert.match(viewport, /Гауссовый слой/);
assert.match(viewport, /Слой коллизий/);
assert.match(viewport, /Инверсия управления по горизонтали/);
assert.match(viewport, /Инверсия управления по вертикали/);
assert.match(viewport, /Ось инверсии визуального слоя/);
assert.match(viewport, /Ось инверсии collision-слоя/);
assert.match(viewport, /ROTATION_AXES/);
assert.match(viewport, /<GlassMaterialSurface/);
assert.match(viewport, /<TextField/);
assert.match(viewport, /nextDraft\.trim\(\) !== ""/);
assert.match(viewport, /saveSimulationViewerSettings/);
assert.match(viewport, /SimulationUgvSettingsPanel/);
assert.match(viewport, /runtime\.setUgvPreset\(settings\.ugv\)/);
assert.match(viewport, /onProjectChange\?\.\(saved\)/);
assert.match(viewport, /const worldManifestRevision = JSON\.stringify\(project\.worldManifest\)/);
assert.match(viewport, /\[project\.projectId, worldManifestRevision\]/);
assert.doesNotMatch(viewport, /\[project\.projectId, project\.worldManifest\]/);
assert.doesNotMatch(viewport, />PlayCanvas|PlayCanvas загружает|Streamed SOG/);
assert.match(ugv, /AMMO_MODULE_ROOT = "\/vendor\/playcanvas\/ammo\/2026-08-25"/);
assert.match(ugv, /new this\.ammo\.btRaycastVehicle/);
assert.match(ugv, /type: "mesh"/);
assert.match(ugv, /MeshoptSimplifier\.simplify/);
assert.match(ugv, /PHYSICS_PROXY_MAX_TRIANGLES = 240_000/);
assert.match(ugv, /createPhysicsProxyMesh/);
assert.match(ugv, /findSafeSpawnPosition/);
assert.match(ugv, /physics\.raycastFirst/);
assert.match(ugv, /runCleanup\("destroy static physics mesh", \(\) => mesh\.destroy\(\)\)/);
assert.match(ugv, /type: "static"/);
assert.match(ugv, /mass: this\.settings\.massKg/);
assert.match(ugv, /suspensionRestLength/);
assert.match(ugv, /requestedTurn = Number\(this\.isPressed\("KeyA", "ArrowLeft"\)\) - Number\(this\.isPressed\("KeyD", "ArrowRight"\)\)/);
assert.match(ugv, /leftCommand = clamp\(forwardCommand - turnCommand/);
assert.match(ugv, /rightCommand = clamp\(forwardCommand \+ turnCommand/);
assert.match(ugv, /this\.settings\.invertSteering/);
assert.match(ugv, /this\.settings\.maxSpeedMetersPerSecond/);
assert.match(ugv, /this\.settings\.maxTurnRateDegrees/);
assert.match(ugv, /desiredSpeed = forwardInput \* maxSpeed/);
assert.match(ugv, /maximumAcceleration = clamp\(1\.4 \+ maxSpeed \* 0\.35, 1\.8, 4\.2\)/);
assert.match(ugv, /SERVICE_BRAKE_DECELERATION_MPS2 = 1\.8/);
assert.match(ugv, /horizontalSpeed - deceleration \* Math\.max\(0, deltaSeconds\)/);
assert.match(ugv, /this\.vehicle\.setBrake\(0, index\)/);
assert.doesNotMatch(ugv, /massKg \* 3/);
assert.match(ugv, /pureTurn = !braking && forwardInput === 0 && turnInput !== 0/);
assert.match(ugv, /desiredYawRate = -turnInput \* maxTurnRate/);
assert.match(ugv, /this\.orbitYaw = \(this\.orbitYaw - deltaX \* 0\.006\)/);
assert.match(ugv, /CAMERA_RETURN_DELAY_SECONDS = 1\.2/);
assert.match(ugv, /CAMERA_RETURN_DURATION_SECONDS = 2/);
assert.match(ugv, /approachAngle\(this\.orbitYaw, rearOrbitYaw/);
assert.match(ugv, /this\.canvas\.addEventListener\("pointerdown", this\.onOrbitPointerDown\)/);
assert.match(ugv, /this\.canvas\.addEventListener\("wheel", this\.onOrbitWheel/);
assert.match(ugv, /vehicleGeometryChanged/);
assert.match(ugv, /removeAction\(this\.vehicle as NativeObject\)/);
assert.match(ugv, /runCleanup\("destroy static physics proxy", \(\) => entity\.destroy\(\)\)/);
assert.match(ugv, /runCleanup\("destroy vehicle entity"/);
assert.match(ugvSettings, /Физика UGV/);
assert.match(ugvSettings, /Дифференциальный привод/);
assert.match(ugvSettings, /Масса конструкции/);
assert.match(ugvSettings, /Общая высота/);
assert.match(ugvSettings, /Клиренс/);
assert.match(ugvSettings, /Скорость движения/);
assert.match(ugvSettings, /Скорость разворота/);
assert.match(ugvSettings, /exactValueBounds=\{SIMULATION_UGV_EXACT_VALUE_LIMITS\.maxSpeedMetersPerSecond\}/);
assert.match(projects, /maxSpeedMetersPerSecond: \{ min: 0, max: 1_000_000_000 \}/);
assert.match(ugvSettings, /Инверсия управления A\/D/);
assert.match(ugvSettings, /Сохранить пресет/);
assert.match(ugvSettings, /<RangeControl/);
assert.match(ugvSettings, /<Switch/);
assert.match(ugvSettings, /<TextField/);
assert.match(projects, /missioncore\.simulation-viewer-settings\/v3/);
assert.match(projects, /ugv: SimulationUgvPreset/);
assert.match(styles, /\.simulation-viewport__settings \{[\s\S]*grid-template-rows: auto minmax\(0, 1fr\);[\s\S]*overflow: clip;/);
assert.match(styles, /\.simulation-viewport__settings-scroll \{[\s\S]*overflow-y: auto;/);
assert.match(styles, /\.simulation-viewport__toolbar \{[\s\S]*position: sticky;[\s\S]*top: 0;/);
assert.match(styles, /canvas\.is-orbiting[\s\S]*cursor: grabbing/);
});
@@ -0,0 +1,182 @@
import assert from "node:assert/strict";
import { access, readFile } from "node:fs/promises";
import { after, before, test } from "node:test";
import { createServer } from "vite";
let server;
let fetchVegetationShadowResult;
before(async () => {
server = await createServer({
appType: "custom",
logLevel: "silent",
server: { middlewareMode: true },
});
({ fetchVegetationShadowResult } = await server.ssrLoadModule(
"/src/core/laboratory/vegetationShadow.ts",
));
});
after(async () => {
await server?.close();
});
const resultId = `lab-v1-vegetation-shadow-${"a".repeat(64)}`;
function candidate(candidateKey, vegetationIou) {
return {
loaded_model_name: candidateKey === "ddrnet" ? "ddrnet_39" : "pp_lite_t_seg",
checkpoint_sha256: (candidateKey === "ddrnet" ? "b" : "c").repeat(64),
validation_metrics: {
mean_iou_percent: 44.2,
published_mean_iou_percent: 46.53,
vegetation_mean_iou: vegetationIou,
},
validation_timing: {
latency_ms_p95: 22.4,
throughput_fps_from_mean_inference: 48.1,
},
shadow_timing: {
prewarm_latency_ms: 612.4,
latency_ms_p95: 21.8,
throughput_fps_from_mean_inference: 49.2,
},
resource: {
peak_reserved_vram_bytes: 2_000_000_000,
gpu_name: "NVIDIA GeForce RTX 4090",
},
};
}
function visualCase(sourceKind, index) {
const caseId = `case-${index}`;
const keys = sourceKind === "goose"
? ["source", "truth", "ddrnet", "ppliteseg", "ddrnet_error", "ppliteseg_error"]
: ["source", "ddrnet", "ppliteseg", "urban", "rural", "offroad"];
return {
case_id: caseId,
source_kind: sourceKind,
width: sourceKind === "goose" ? 512 : 800,
height: sourceKind === "goose" ? 512 : 600,
center_crop_xyxy: sourceKind === "goose" ? [0, 0, 512, 512] : [100, 0, 700, 600],
outside_crop_state: sourceKind === "goose" ? "not-applicable" : "undefined",
focus: sourceKind === "goose" ? {
class_name: "high_grass",
label_id: 51,
truth_pixels: 16384,
truth_fraction: 0.0625,
stratum_rank: index + 1,
} : null,
assets: Object.fromEntries(keys.map((key) => [key, {
path: `visual/${sourceKind}/${caseId}/${key}.png`,
sha256: "d".repeat(64),
}])),
};
}
function routeVideo() {
return {
worker_result_id: `lab-v1-ravnoves-video-ddrnet-${"e".repeat(64)}`,
m47_reference_graph_result_id: `m47-reference-graph-lab-${"f".repeat(64)}`,
base_m4_result_id: `m4-threat-replay-${"1".repeat(64)}`,
frame_count: 4489,
width: 800,
height: 600,
center_crop_xyxy: [100, 0, 700, 600],
outside_crop_state: "undefined",
sequence_binding: "sequence-0-to-masks/frame-000001.png",
taxonomy: {
schema_version: "missioncore.lab-v1-vegetation-taxonomy/v1",
classes: Array.from({ length: 64 }, (_, classId) => ({
class_id: classId,
label: classId === 0 ? "undefined" : `class-${classId}`,
color_rgb: [classId, classId, classId],
disposition: classId === 0 ? "undefined" : "prediction",
})),
},
aggregate_prediction_pixels: Array(64).fill(0),
mask_archive: {
path: "video/ddrnet-semantic-masks.zip",
sha256: "9".repeat(64),
byte_length: 1024,
},
};
}
test("vegetation LAB keeps autonomous assets and fail-closed authority", async () => {
let requestedUrl = "";
const result = await fetchVegetationShadowResult(resultId, {
fetcher: async (url) => {
requestedUrl = String(url);
return new Response(JSON.stringify({
schema_version: "missioncore.lab-v1-vegetation-shadow/v1",
result_id: resultId,
created_at_utc: "2026-08-27T20:00:00Z",
status: "visual-shadow-ready-policy-not-authorized",
ground_truth: false,
identity: { selected_candidate: "ddrnet" },
metrics: {
candidates: {
ddrnet: candidate("ddrnet", 0.64),
ppliteseg: candidate("ppliteseg", 0.61),
},
},
decision: {
selected_candidate: "ddrnet",
visual_shadow_ready: true,
mission_policy_ready_for_configuration: true,
navigation_accepted: false,
production_accepted: false,
},
limitations: ["shadow only"],
authority: {
commands_enabled: false,
navigation_or_safety_accepted: false,
actuation_accepted: false,
camera_semantics_can_clear_rigid_geometry: false,
},
catalogs: {
goose: Array.from({ length: 12 }, (_, index) => visualCase("goose", index)),
ravnoves: [],
},
route_video: routeVideo(),
access: "read-only",
}), { status: 200, headers: { "Content-Type": "application/json" } });
},
});
assert.equal(
requestedUrl,
`/api/v1/laboratory/vegetation-shadow/${resultId}`,
);
assert.equal(result.selectedCandidate, "ddrnet");
assert.equal(result.candidates[0].vegetationMeanIouPercent, 64);
assert.equal(result.routeCases.length, 0);
assert.equal(result.validationCases.length, 12);
assert.equal(result.routeVideo.frameCount, 4489);
assert.equal(result.routeVideo.taxonomy[0].disposition, "undefined");
assert.equal(result.validationCases[0].focus.className, "high_grass");
assert.match(result.validationCases[0].assets.ddrnet_error, /\/assets\/visual\/goose\//);
assert.deepEqual(result.authority, {
commandsEnabled: false,
navigationOrSafetyAccepted: false,
actuationAccepted: false,
cameraSemanticsCanClearRigidGeometry: false,
});
});
test("vegetation LAB reuses the admitted M4.8 and M4.7 instruments", async () => {
const resultSource = await readFile(
new URL("../src/workspaces/laboratory/VegetationShadowResult.tsx", import.meta.url),
"utf8",
);
assert.match(resultSource, /M48MaskComparisonVisual/);
assert.match(resultSource, /M4ReplayThreatVisual/);
assert.equal(resultSource.match(/<LaboratoryEvidence\b/g)?.length, 2);
assert.match(resultSource, /showReferenceMediaLayers=\{false\}/);
assert.doesNotMatch(resultSource, /VegetationRouteVisual|urban\/rural\/off-road presets/);
await assert.rejects(
access(new URL("../src/workspaces/laboratory/VegetationShadowVisual.tsx", import.meta.url)),
{ code: "ENOENT" },
);
});
@@ -0,0 +1,10 @@
{
"schema_version": "missioncore.laboratory-evidence-definition/v1",
"work_id": "lab-v1-vegetation-shadow",
"evidence": {
"runtime_relative_root": "lab-v1-vegetation/results",
"result_id_prefix": "lab-v1-vegetation-shadow",
"document_name": "result.json",
"schema_version": "missioncore.lab-v1-vegetation-shadow/v1"
}
}
@@ -0,0 +1,10 @@
{
"schema_version": "missioncore.laboratory-evidence-definition/v1",
"work_id": "m49-tgs-fail-closed-evidence",
"evidence": {
"runtime_relative_root": "m49/tgs-fail-closed-results",
"result_id_prefix": "m49-tgs-fail-closed",
"document_name": "manifest.json",
"schema_version": "missioncore.m49-tgs-fail-closed-result/v1"
}
}
@@ -0,0 +1,10 @@
{
"schema_version": "missioncore.laboratory-evidence-definition/v1",
"work_id": "m49-tgs-full-shadow",
"evidence": {
"runtime_relative_root": "m49/tgs-full-shadow-results",
"result_id_prefix": "m49-tgs-full-shadow",
"document_name": "manifest.json",
"schema_version": "missioncore.m49-tgs-full-shadow-lab/v1"
}
}
+47
View File
@@ -162,6 +162,53 @@
"run": "missioncore.laboratory-run/v1",
"evidence": "missioncore.m48t-risk-quality-temporal-lab/v1"
}
},
{
"work_id": "m49-tgs-fail-closed-evidence",
"lifecycle": "experimental",
"isolation": "bounded-adapter",
"adapter_id": "experimental.m49-tgs-fail-closed-evidence/v1",
"input_roles": ["repository_root"],
"contracts": {
"source": "missioncore.m49-tgs-worker-evidence/v1",
"provider": "missioncore.travel-tgs-ground-segmentation/v1",
"graph": "missioncore.m49-fail-closed-costmap-evidence/v1",
"run": "missioncore.laboratory-run/v1",
"evidence": "missioncore.m49-tgs-fail-closed-result/v1"
}
},
{
"work_id": "m49-tgs-full-shadow",
"lifecycle": "experimental",
"isolation": "bounded-adapter",
"adapter_id": "experimental.m49-tgs-full-shadow/v1",
"input_roles": ["repository_root"],
"contracts": {
"source": "missioncore.m49-tgs-full-shadow-result/v1",
"provider": "missioncore.travel-tgs-ground-segmentation/v1",
"graph": "missioncore.m49-tgs-full-source-paced-shadow/v1",
"run": "missioncore.laboratory-run/v1",
"evidence": "missioncore.m49-tgs-full-shadow-lab/v1"
}
},
{
"work_id": "lab-v1-vegetation-shadow",
"lifecycle": "experimental",
"isolation": "bounded-adapter",
"adapter_id": "experimental.lab-v1-vegetation-shadow/v1",
"input_roles": [
"ddrnet_goose_root",
"ppliteseg_goose_root",
"ddrnet_ravnoves_root",
"ppliteseg_ravnoves_root"
],
"contracts": {
"source": "missioncore.goose-ravnoves-vegetation-source-set/v1",
"provider": "missioncore.goose-fine64-ready-weight-provider/v1",
"graph": "missioncore.vegetation-mission-policy-shadow-graph/v1",
"run": "missioncore.laboratory-run/v1",
"evidence": "missioncore.lab-v1-vegetation-shadow/v1"
}
}
],
"legacy_work_ids": [
+22 -1
View File
@@ -1,6 +1,6 @@
{
"schema_version": "missioncore.laboratory-value-review-registry/v1",
"reviewed_at_utc": "2026-08-26T08:34:34Z",
"reviewed_at_utc": "2026-08-27T20:59:10Z",
"entries": [
{
"catalog_id": "e28-local-surface",
@@ -267,6 +267,27 @@
"signal": "progress",
"lifecycle": "current",
"visual_evidence": "available"
},
{
"catalog_id": "m49-tgs-fail-closed-evidence",
"evidence_id": "m49-tgs-fail-closed-9d5cb089bb5cc23f829acb47eda52eaa886db3b60f8fa7d57e02e27f642e837b",
"signal": "progress",
"lifecycle": "current",
"visual_evidence": "available"
},
{
"catalog_id": "m49-tgs-full-shadow",
"evidence_id": "m49-tgs-full-shadow-0faeaaf3aba8dccae974eab51ff9cccf264a13ec285abb1920c1e5a09a7e87bc",
"signal": "progress",
"lifecycle": "current",
"visual_evidence": "available"
},
{
"catalog_id": "lab-v1-vegetation-shadow",
"evidence_id": "lab-v1-vegetation-shadow-ad4d9fbbb21ff8a270b77f559b4e78dcdaf0455afd61afb5033009623984e554",
"signal": "failed",
"lifecycle": "current",
"visual_evidence": "available"
}
]
}
@@ -0,0 +1,119 @@
{
"schema_version": "missioncore.lab-v1-goose-vegetation-benchmark/v1",
"lab_id": "LAB-V1",
"worker_id": "worker-006",
"runtime": {
"super_gradients_version": "3.2.0",
"super_gradients_revision": "54d062ecb1081944a672ce447cf3e96a36708ff9",
"python_version": "3.9",
"numpy_version": "1.23.0",
"cmake_version": "3.31.6",
"onnxsim_version": "0.4.36",
"opencv_python_version": "4.8.1.78",
"pytorch_version": "1.13.1",
"torchvision_version": "0.14.1",
"pytorch_cuda_version": "11.7",
"container_base": "nvidia/cuda:12.8.1-cudnn-devel-ubuntu22.04@sha256:ad6d59a3bbf3e82c1c849c9ac09cfc2a3e0bbb8655042fd899be6681b3fe2a85",
"miniconda_installer": "Miniconda3-py39_24.11.1-0-Linux-x86_64.sh",
"miniconda_installer_sha256": "3ea8373098d72140e08aac9217822b047ec094eb457e7f73945af7c6f68bf6f5"
},
"dataset": {
"dataset_id": "goose-2d-validation-visible-rgb",
"relative_root": "goose-2d/validation",
"mapping_relative_path": "goose_label_mapping.csv",
"mapping_sha256": "88ae319ba5a3877dd3ae0773f693a6a5fdc283934140de9dfaff029108aefd7f",
"image_glob": "images/val/**/*_windshield_vis.png",
"expected_pair_count": 962,
"input_size": [512, 512],
"preprocessing": [
"center-square-crop",
"nearest-neighbor-resize",
"rgb-to-tensor-0-1"
]
},
"candidates": {
"ddrnet": {
"candidate_id": "goose-ddrnet-class-512",
"model_names": ["ddrnet_39"],
"checkpoint_relative_path": "models/goose/ddrnet_class_512.pth",
"checkpoint_size_bytes": 259419077,
"checkpoint_sha256": "b99c2838051bcd7b092fd3970aa62a77d5c0bbb809c9b9afb2ff4b0ebdaa4ee6",
"published_validation_miou_percent": 46.53
},
"ppliteseg": {
"candidate_id": "goose-ppliteseg-class-512",
"model_names": [
"pp_lite_t_seg",
"pp_lite_t_seg50",
"pp_lite_t_seg75",
"pp_lite_b_seg",
"pp_lite_b_seg50",
"pp_lite_b_seg75"
],
"checkpoint_relative_path": "models/goose/ppliteseg_class_512.pth",
"checkpoint_size_bytes": 98208249,
"checkpoint_sha256": "6dd412c0c99115e359896c4cab43a8e6bce9e09b843e7fa885fe597b0a6121cd",
"published_validation_miou_percent": 45.09
}
},
"ravnoves": {
"source_id": "RAVNOVES00/right-cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8",
"source_sha256": "cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8",
"base_m4_result_id": "m4-threat-replay-2a953c5f27f2a5b1dddc5c658c1de2c323d7796084a099c024987a1da03aa324",
"expected_width": 800,
"expected_height": 600,
"expected_frame_count": 4489,
"frame_indices": [0, 253, 512, 768, 1024, 1536, 2048, 2560, 3072, 3584, 4096, 4488],
"crop_contract": "center-600-square-to-512; outside-crop-is-undefined"
},
"vegetation_class_names": [
"leaves",
"forest",
"bush",
"moss",
"tree_crown",
"tree_trunk",
"crops",
"low_grass",
"high_grass",
"scenery_vegetation",
"hedge",
"tree_root"
],
"visual_case_contract": {
"selection_basis": "ground-truth-class-support-only",
"case_count": 12,
"minimum_focus_pixels": 2048,
"strata": [
{ "class_name": "high_grass", "count": 2 },
{ "class_name": "low_grass", "count": 2 },
{ "class_name": "bush", "count": 2 },
{ "class_name": "tree_trunk", "count": 2 },
{ "class_name": "tree_crown", "count": 1 },
{ "class_name": "hedge", "count": 1 },
{ "class_name": "forest", "count": 1 },
{ "class_name": "crops", "count": 1 }
],
"error_overlay": {
"correct_material_rgba": [34, 197, 94, 72],
"missed_vegetation_rgba": [239, 68, 68, 220],
"false_vegetation_rgba": [245, 158, 11, 220],
"wrong_vegetation_material_rgba": [168, 85, 247, 220]
}
},
"policy_action_colors": {
"ALLOW": "#22c55e",
"HIGH_COST": "#f59e0b",
"NO_GO": "#ef4444"
},
"invariants": {
"one_heavy_candidate_at_a_time": true,
"raw_fisheye_is_immutable": true,
"outside_center_crop_is_free": false,
"missing_or_unknown_is_free": false,
"camera_semantics_can_clear_rigid_geometry": false,
"navigation_authority": false,
"actuation_authority": false,
"canonical_triton_mutation_allowed": false
}
}
@@ -0,0 +1,174 @@
{
"schema_version": "missioncore.vegetation-candidate-manifest/v1",
"lab_id": "LAB-V1",
"ops_issue_id": "88aff22a-43aa-48fb-b7eb-ac424d987d37",
"frozen_utc": "2026-08-27T18:39:00Z",
"worker": {
"worker_id": "worker-006",
"candidate_root": "D:\\NDC_MISSIONCORE\\datasets\\vegetation-v1\\observed-2026-08-27",
"canonical_runtime_mutation_allowed": false
},
"upstream_sources": [
{
"source_id": "wildscenes",
"repository": "https://github.com/csiro-robotics/WildScenes.git",
"revision": "9eb4e10b4483a634159e2b371be0437e465fe218",
"worker_relative_path": "sources/wildscenes"
},
{
"source_id": "rellis-3d",
"repository": "https://github.com/unmannedlab/RELLIS-3D.git",
"revision": "c17a118fcaed1559f03cc32cc3a91dedc557f8b8",
"worker_relative_path": "sources/rellis-3d"
},
{
"source_id": "orfd-offnet",
"repository": "https://github.com/chaytonmin/Off-Road-Freespace-Detection.git",
"revision": "50e63d24836198e8fb5af707e521f414104b4876",
"worker_relative_path": "sources/orfd"
}
],
"candidate_assets": [
{
"candidate_id": "goose-ppliteseg-category-512",
"modality": "camera-2d",
"family": "ppliteseg",
"output_contract": "goose-category-12",
"input_resolution": [512, 512],
"relative_path": "models/goose/ppliteseg_category_512.pth",
"size_bytes": 98180357,
"sha256": "87bf55a1ae994a4bdf3a4d30c8f0dec2916994b37d433f0b5b694d63157ee0ab",
"asset_status": "accepted",
"runner_status": "super-gradients-compatibility-probe-required"
},
{
"candidate_id": "goose-ppliteseg-class-512",
"modality": "camera-2d",
"family": "ppliteseg",
"output_contract": "goose-class-64",
"input_resolution": [512, 512],
"relative_path": "models/goose/ppliteseg_class_512.pth",
"size_bytes": 98208249,
"sha256": "6dd412c0c99115e359896c4cab43a8e6bce9e09b843e7fa885fe597b0a6121cd",
"asset_status": "accepted",
"runner_status": "super-gradients-compatibility-probe-required"
},
{
"candidate_id": "goose-ddrnet-category-512",
"modality": "camera-2d",
"family": "ddrnet",
"output_contract": "goose-category-12",
"input_resolution": [512, 512],
"relative_path": "models/goose/ddrnet_category_512.pth",
"size_bytes": 259313579,
"sha256": "ddab58d3b27309e4e1545ecb0eafbe077f3ebd58fe7120defe44478424c2a20e",
"asset_status": "accepted",
"runner_status": "super-gradients-compatibility-probe-required"
},
{
"candidate_id": "goose-ddrnet-class-512",
"modality": "camera-2d",
"family": "ddrnet",
"output_contract": "goose-class-64",
"input_resolution": [512, 512],
"relative_path": "models/goose/ddrnet_class_512.pth",
"size_bytes": 259419077,
"sha256": "b99c2838051bcd7b092fd3970aa62a77d5c0bbb809c9b9afb2ff4b0ebdaa4ee6",
"asset_status": "accepted",
"runner_status": "super-gradients-compatibility-probe-required"
},
{
"candidate_id": "rellis-hrnet-ocr",
"modality": "camera-2d",
"family": "hrnet-ocr",
"output_contract": "rellis-20-class",
"relative_path": "models/rellis-3d/hrnet_best.zip",
"size_bytes": 788183119,
"sha256": "2842b7c427d9abb3f34725dea306b72422af5293cd8289da3035edff81deac27",
"asset_status": "accepted",
"runner_status": "upstream-validation-probe-required"
},
{
"candidate_id": "rellis-gscnn",
"modality": "camera-2d",
"family": "gscnn",
"output_contract": "rellis-20-class",
"relative_path": "models/rellis-3d/gscnn_best.pth",
"size_bytes": 1098515419,
"sha256": "d77a83032ff9b3dea74bdbdd9bb12c0810ddff0dce6a02d42264c592fbdc0b18",
"asset_status": "accepted-size-and-hash-legacy-pickle",
"runner_status": "upstream-validation-probe-required"
},
{
"candidate_id": "rellis-salsanext",
"modality": "lidar-3d",
"family": "salsanext",
"output_contract": "rellis-20-class",
"relative_path": "models/rellis-3d/salsanext_best.zip",
"size_bytes": 164312336,
"sha256": "6cab4f0c4ac556f9597d338692f2dee1a9e9438941f9d85159685616b948fafb",
"asset_status": "accepted",
"runner_status": "native-scan-compatibility-probe-required"
},
{
"candidate_id": "rellis-kpconv",
"modality": "lidar-3d",
"family": "kpconv",
"output_contract": "rellis-20-class",
"relative_path": "models/rellis-3d/kpconv_best.zip",
"size_bytes": 1522250650,
"sha256": "d2a11b7a66290cce3d592e830889c3bcb188c62ced83092a7201765621b8bfc3",
"asset_status": "accepted",
"runner_status": "native-scan-compatibility-probe-required"
},
{
"candidate_id": "wildscenes-model-bundle",
"modality": "camera-2d-and-lidar-3d",
"family": "mmsegmentation-and-mmdetection3d",
"output_contract": "wildscenes-natural-environment",
"bundle_entries": [
"spvcnn_wildscenes.pth",
"minkunet_wildscenes.pth",
"deeplabv3_wildscenes.pth",
"segformer_wildscenes.pth",
"cylinder3d_wildscenes.pth",
"mask2former_swin_wildscenes.pth",
"upernet_convnext_wildscenes.pth",
"mask2former_resnet_wildscenes.pth"
],
"relative_path": "models/wildscenes/WildScenes-Models.zip",
"size_bytes": 8637507567,
"sha256": "0b5040b157e928263bdbefeaffbd36414c2cb641ebc70713b3be3460c1feaf2b",
"asset_status": "accepted",
"runner_status": "upstream-docker-validation-probe-required"
},
{
"candidate_id": "orfd-offnet-roadseg",
"modality": "camera-lidar-fusion",
"family": "offnet",
"output_contract": "binary-freespace",
"relative_path": "models/orfd-offnet/OFF-Net/best_net_RoadSeg.pth",
"size_bytes": 100976531,
"sha256": "4d82a9ba1c5411a8159e1a262f1741a6a163c8bd9751d52a90fb10deb237650d",
"asset_status": "accepted",
"runner_status": "upstream-validation-probe-required"
}
],
"execution_sequence": [
"upstream-runner-reproduction",
"official-validation-sample",
"native-fisheye-or-lidar-compatibility",
"shared-vegetation-label-mapping",
"recorded-shadow-quality-and-resource-tournament",
"mission-policy-composition-without-actuation"
],
"invariants": {
"one_heavy_candidate_at_a_time": true,
"raw_fisheye_is_immutable": true,
"missing_or_unknown_is_free": false,
"semantic_label_can_clear_rigid_geometry": false,
"preset_can_override_explicit_mission_rule": false,
"navigation_authority": false,
"actuation_authority": false
}
}
@@ -0,0 +1,108 @@
{
"schema_version": "missioncore.vegetation-mission-policy/v1",
"profile_id": "lab-v1-vegetation-mission-policy/v1",
"status": "planning-shadow-only-unqualified",
"vehicle_profile": {
"path": "config/perception/m49-physical-safety-shadow-v0.json",
"profile_id": "m49-mission-core-rover-physical-safety-shadow/v0"
},
"actions": [
"ALLOW",
"HIGH_COST",
"NO_GO"
],
"precedence": [
"hard_safety_interlock",
"explicit_mission_rule",
"selected_preset_default"
],
"material_classes": [
"hard_surface",
"bare_soil",
"grass",
"fern",
"herbaceous_vegetation",
"cultivated_vegetation",
"woody_shrub",
"tree_or_trunk",
"vegetation_unknown"
],
"mission_overridable_materials": [
"grass",
"fern",
"herbaceous_vegetation",
"cultivated_vegetation"
],
"presets": {
"urban": {
"hard_surface": "ALLOW",
"bare_soil": "HIGH_COST",
"grass": "NO_GO",
"fern": "NO_GO",
"herbaceous_vegetation": "NO_GO",
"cultivated_vegetation": "NO_GO",
"woody_shrub": "NO_GO",
"tree_or_trunk": "NO_GO",
"vegetation_unknown": "NO_GO"
},
"rural": {
"hard_surface": "ALLOW",
"bare_soil": "ALLOW",
"grass": "HIGH_COST",
"fern": "HIGH_COST",
"herbaceous_vegetation": "HIGH_COST",
"cultivated_vegetation": "NO_GO",
"woody_shrub": "NO_GO",
"tree_or_trunk": "NO_GO",
"vegetation_unknown": "NO_GO"
},
"offroad": {
"hard_surface": "ALLOW",
"bare_soil": "ALLOW",
"grass": "HIGH_COST",
"fern": "HIGH_COST",
"herbaceous_vegetation": "HIGH_COST",
"cultivated_vegetation": "NO_GO",
"woody_shrub": "NO_GO",
"tree_or_trunk": "NO_GO",
"vegetation_unknown": "NO_GO"
}
},
"evidence_policy": {
"policy_eligible_states": [
"SUPPORTED_GROUND",
"VEGETATION_POTENTIALLY_TRAVERSABLE"
],
"force_no_go_states": [
"UNOBSERVED",
"NEGATIVE_OR_UNSUPPORTED",
"RIGID_OR_UNKNOWN_OBSTACLE",
"VEGETATION_UNKNOWN",
"VEGETATION_WITH_RIGID_GEOMETRY"
],
"force_no_go_materials": [
"woody_shrub",
"tree_or_trunk",
"vegetation_unknown"
],
"missing_material_is": "vegetation_unknown",
"missing_evidence_is": "UNOBSERVED",
"camera_semantics_can_clear_rigid_geometry": false,
"mission_rule_can_override_hard_safety_interlock": false
},
"mission_configuration_contract": {
"preset_is_required": true,
"explicit_rules_are_sparse_overrides": true,
"preset_may_be_suggested_automatically": true,
"automatic_preset_switch_requires_operator_opt_in": true,
"automatic_switch_may_weaken_explicit_mission_rule": false,
"operator_must_review_effective_rules_before_start": true
},
"authority": {
"perception_selects_mission_action": false,
"preset_grants_actuation": false,
"navigation_accepted": false,
"safety_accepted": false,
"actuation_accepted": false
}
}
@@ -0,0 +1,84 @@
{
"schema_version": "missioncore.vegetation-provider-label-map/v1",
"profile_id": "lab-v1-vegetation-provider-label-map/v1",
"policy_profile_id": "lab-v1-vegetation-mission-policy/v1",
"providers": {
"goose-fine-64": {
"unmapped_policy": "no-material-claim",
"class_granularity": "fine",
"can_distinguish_low_and_high_grass": true,
"can_distinguish_tree_trunk": true,
"labels": {
"asphalt": "hard_surface",
"bikeway": "hard_surface",
"cobble": "hard_surface",
"sidewalk": "hard_surface",
"gravel": "bare_soil",
"soil": "bare_soil",
"low_grass": "grass",
"high_grass": "herbaceous_vegetation",
"moss": "herbaceous_vegetation",
"crops": "cultivated_vegetation",
"bush": "woody_shrub",
"hedge": "woody_shrub",
"tree_trunk": "tree_or_trunk",
"tree_root": "tree_or_trunk",
"forest": "vegetation_unknown",
"leaves": "vegetation_unknown",
"scenery_vegetation": "vegetation_unknown",
"tree_crown": "vegetation_unknown"
}
},
"goose-category-12": {
"unmapped_policy": "no-material-claim",
"class_granularity": "coarse",
"can_distinguish_low_and_high_grass": false,
"can_distinguish_tree_trunk": false,
"labels": {
"artificial_ground": "hard_surface",
"vegetation": "vegetation_unknown"
}
},
"wildscenes-19": {
"unmapped_policy": "no-material-claim",
"class_granularity": "medium",
"can_distinguish_low_and_high_grass": false,
"can_distinguish_tree_trunk": true,
"labels": {
"asphalt/concrete": "hard_surface",
"dirt": "bare_soil",
"gravel": "bare_soil",
"grass": "grass",
"bush": "woody_shrub",
"tree-trunk": "tree_or_trunk",
"tree-foliage": "vegetation_unknown"
}
},
"rellis-20": {
"unmapped_policy": "no-material-claim",
"class_granularity": "medium",
"can_distinguish_low_and_high_grass": false,
"can_distinguish_tree_trunk": false,
"labels": {
"asphalt": "hard_surface",
"concrete": "hard_surface",
"grass": "grass",
"bush": "woody_shrub",
"tree": "tree_or_trunk"
}
},
"orfd-binary-freespace": {
"unmapped_policy": "no-material-claim",
"class_granularity": "binary-geometry-only",
"can_distinguish_low_and_high_grass": false,
"can_distinguish_tree_trunk": false,
"labels": {}
}
},
"invariants": {
"unmapped_label_means_free": false,
"coarse_vegetation_means_grass": false,
"binary_freespace_selects_material": false,
"provider_label_grants_actuation": false
}
}
@@ -0,0 +1,59 @@
{
"schema_version": "missioncore.camera-lidar-traversability/v1",
"roles": {
"camera_semantics": "material-and-risk-meaning-primary",
"lidar": "metric-geometry-and-positive-support-primary",
"arbitration": "preserve-contradiction-never-unilateral-clear"
},
"camera_contract": {
"raster": "native-800x600-fisheye",
"rectification_allowed": false,
"new_model_allowed": false,
"vegetation_classes": [
"grass_low_vegetation",
"tree_woody_vegetation"
]
},
"states": [
{
"id": "RIGID_OR_UNKNOWN_OBSTACLE",
"required_evidence": "stable-metric-nonground-without-vegetation-support",
"cost": "lethal-or-unknown"
},
{
"id": "VEGETATION_UNKNOWN",
"required_evidence": "camera-vegetation-without-positive-ground-support",
"cost": "unknown-never-free"
},
{
"id": "VEGETATION_POTENTIALLY_TRAVERSABLE",
"required_evidence": "camera-low-vegetation-plus-positive-ground-without-concentrated-rigid-return",
"cost": "high-cost-terrain"
},
{
"id": "VEGETATION_WITH_RIGID_GEOMETRY",
"required_evidence": "camera-vegetation-plus-concentrated-persistent-vertical-geometry",
"cost": "lethal"
},
{
"id": "SUPPORTED_GROUND",
"required_evidence": "positive-ground-support-without-blocking-geometry",
"cost": "candidate-traversable"
}
],
"invariants": {
"camera_can_clear_positive_rigid_geometry": false,
"lidar_nonground_alone_means_rigid_object": false,
"missing_lidar_support_means_free": false,
"vegetation_is_binary_free_or_lethal": false,
"physical_navigation_authority": false
},
"deferred_robot_profile": [
"footprint",
"ground_clearance",
"traction",
"maximum_slope",
"admissible_vegetation_height",
"vegetation_contact_allowed"
]
}
@@ -0,0 +1,107 @@
{
"schema_version": "missioncore.m49-physical-safety-shadow-profile/v1",
"profile_id": "m49-mission-core-rover-physical-safety-shadow/v0",
"status": "planning-only-unqualified",
"vehicle": {
"source": "operator-supplied-2026-08-27-unverified",
"body_length_m": 1.0,
"body_width_m": 0.8,
"body_height_m": 0.4,
"ground_clearance_m": 0.15,
"nominal_mass_kg": 100.0,
"mass_qualification": "estimate-not-weighed",
"provisional_centered_footprint_body_xy_m": [
[0.5, 0.4],
[0.5, -0.4],
[-0.5, -0.4],
[-0.5, 0.4]
],
"nominal_speed_mps": 0.2777777778,
"maximum_operating_speed_mps": 0.5555555556,
"drive": {
"motor_count": 2,
"rated_power_per_motor_w": 500.0,
"motor_per_side": true,
"steering_model": "unqualified-likely-differential"
}
},
"unresolved_physical_inputs": [
"measured-mass-and-center-of-gravity",
"wheel-diameter-and-width",
"wheelbase-track-and-overhangs",
"qualified-base-link-origin",
"controller-braking-and-loss-of-power-behavior",
"measured-stopping-envelope",
"maximum-step-slope-and-side-slope",
"lidar-camera-to-body-extrinsics",
"negative-obstacle-sensor-coverage"
],
"terrain_evidence": {
"accepted_baseline_profile": "m49-tgs-full-shadow-v1.json",
"accepted_baseline_cell_size_m": 0.45,
"baseline_mutation_allowed": false,
"challenger_cell_sizes_m": [0.15, 0.1],
"challenger_radius_m": 12.0,
"missing_support_policy": "stop-never-free",
"required_states": [
"SUPPORTED",
"RIGID_OR_UNKNOWN_OBSTACLE",
"NEGATIVE_OR_UNSUPPORTED",
"VEGETATION_UNKNOWN",
"VEGETATION_POTENTIALLY_TRAVERSABLE",
"VEGETATION_WITH_RIGID_GEOMETRY"
]
},
"operator_visual": {
"host_profile": "14-inch-2023-macbook-pro-18-gib",
"worker_role": "realtime-only",
"lab_artifact_generation": "local-sequential-offline-or-imported-sealed",
"lab_build_may_take_longer_than_source_duration": true,
"source_pace_hz": 10.0,
"render_policy": "on-demand-with-interaction-damping",
"transport": "sealed-local-binary-chunks-no-per-cell-json",
"worker_runtime_dependency": false,
"sealed_artifact_required": true,
"offline_worker_open_required": true,
"full_session_high_resolution_preload_allowed": false,
"playback_buffer": {
"chunk_frame_count": 32,
"startup_prebuffer_chunk_count": 2,
"startup_prebuffer_seconds": 6.4,
"resident_chunk_count_max": 3,
"resident_window_seconds_max": 9.6,
"forward_prefetch_chunk_count": 1,
"backward_seek_policy": "load-target-and-next-from-sealed-local-artifact"
},
"lod": {
"safety_computation_reduced_by_visual_lod": false,
"near_field_radius_m": 4.0,
"near_field_cell_size_m": 0.15,
"mid_field_radius_m": 8.0,
"mid_field_cell_size_m": 0.3,
"far_field_radius_m": 12.0,
"far_field_cell_size_m": 0.45,
"paused_active_frame_full_resolution_available": true
},
"acceptance_budget": {
"main_thread_frame_prepare_p95_ms_max": 10.0,
"webgl_render_submit_p95_ms_target": 16.7,
"webgl_render_submit_p95_ms_max": 33.3,
"evidence_to_visible_p95_ms_max": 100.0,
"incremental_resident_memory_mib_max": 256.0,
"post_warmup_memory_growth_mib_max": 32.0,
"long_task_over_50ms_count_max": 0,
"webgl_context_loss_count_max": 0,
"worker_unavailable_open_failure_count_max": 0,
"worker_request_count_during_lab_open_max": 0,
"worker_request_count_during_lab_build_max": 0
}
},
"authority": {
"commands_enabled": false,
"navigation_accepted": false,
"safety_accepted": false,
"actuation_accepted": false,
"physical_test_accepted": false
}
}
@@ -0,0 +1,69 @@
{
"schema_version": "missioncore.m49-tgs-fail-closed-evidence-profile/v1",
"profile_id": "m49-ravnoves00-tgs-fail-closed-evidence/v1",
"source": {
"source_id": "RAVNOVES00",
"session_id": "20260720T065719Z_viewer_live",
"source_pack_id": "e10-lidar-pack-576c994a6c814e2592dd6240ace3902a5db94843312c759a73ba0c9166157d2b",
"source_pack_sha256": "0685d24219d8236caf8b7f1685e93f6d6b59e7fd015a768d88a92bbe8b154944",
"travel_revision": "95dc2fbd66a343efd9060c45a5711b6307a950a4",
"compatibility_decision_sha256": "ff0b27f469cccad258c53ae07cb04ad00e696d82b32021c4f1f38ba7fa39d66a",
"input_coordinate_frame": "map-gravity-local-translation-only"
},
"anchors": [171, 306, 368, 402, 450, 509, 525, 744, 1122, 1856],
"tgs": {
"max_range_m": 80.0,
"min_range_m": 1.0,
"resolution_m": 8.0,
"num_iterations": 3,
"num_lowest_representative_points": 5,
"minimum_points": 10,
"seed_threshold_m": 0.5,
"distance_threshold_m": 0.125,
"outlier_threshold_m": 0.3,
"normal_threshold": 0.94,
"weight_threshold": 200.0,
"lcc_normal_similarity": 0.03,
"lcc_planar_distance_m": 0.1,
"obstacle_height_m": 1.0,
"refine_mode": true
},
"profiles": {
"current_increment": {
"role": "diagnostic-current-evidence"
},
"causal_rolling_1s": {
"role": "primary-local-evidence",
"history_seconds": 1.0,
"local_radius_m": 12.0
}
},
"costmap": {
"coordinate_frame": "map-gravity-local",
"cell_size_m": 0.45,
"radius_m": 12.0,
"state_priority": [
"NONGROUND_OCCUPIED",
"UNKNOWN_REJECTED",
"GROUND_SUPPORT",
"UNOBSERVED"
]
},
"state_codes": {
"UNOBSERVED": 0,
"GROUND_SUPPORT": 1,
"NONGROUND_OCCUPIED": 2,
"UNKNOWN_REJECTED": 3
},
"invariants": {
"all_eligible_input_points_accounted": true,
"aos_allowed": false,
"lidar_orientation_applied_to_tgs_input": false,
"map_gravity_axis_preserved": true,
"missing_support_means_free": false,
"unobserved_cells_are_emitted": true,
"camera_projection_is_authoritative": false,
"gpu_allowed": false,
"navigation_or_actuation_allowed": false
}
}
@@ -0,0 +1,77 @@
{
"schema_version": "missioncore.m49-tgs-full-shadow-profile/v1",
"profile_id": "m49-ravnoves00-tgs-full-shadow/v1",
"source": {
"source_id": "RAVNOVES00",
"session_id": "20260720T065719Z_viewer_live",
"source_pack_id": "e10-lidar-pack-576c994a6c814e2592dd6240ace3902a5db94843312c759a73ba0c9166157d2b",
"source_pack_sha256": "0685d24219d8236caf8b7f1685e93f6d6b59e7fd015a768d88a92bbe8b154944",
"travel_revision": "95dc2fbd66a343efd9060c45a5711b6307a950a4",
"input_coordinate_frame": "map-gravity-local-translation-only",
"expected_timeline_frames": 4489,
"expected_available_lidar_frames": 3928
},
"tgs": {
"max_range_m": 80.0,
"min_range_m": 1.0,
"resolution_m": 8.0,
"num_iterations": 3,
"num_lowest_representative_points": 5,
"minimum_points": 10,
"seed_threshold_m": 0.5,
"distance_threshold_m": 0.125,
"outlier_threshold_m": 0.3,
"normal_threshold": 0.94,
"weight_threshold": 200.0,
"lcc_normal_similarity": 0.03,
"lcc_planar_distance_m": 0.1,
"obstacle_height_m": 1.0,
"refine_mode": true
},
"profile": {
"id": "causal_rolling_1s",
"history_seconds": 1.0,
"local_radius_m": 12.0,
"missing_lidar_policy": "all-cells-unobserved"
},
"costmap": {
"coordinate_frame": "map-gravity-local",
"cell_size_m": 0.45,
"radius_m": 12.0,
"state_priority": [
"NONGROUND_OCCUPIED",
"UNKNOWN_REJECTED",
"GROUND_SUPPORT",
"UNOBSERVED"
]
},
"state_codes": {
"UNOBSERVED": 0,
"GROUND_SUPPORT": 1,
"NONGROUND_OCCUPIED": 2,
"UNKNOWN_REJECTED": 3
},
"acceptance": {
"recorded_source_rate_hz": 10.0,
"minimum_effective_timeline_fps": 10.0,
"candidate_stage_p95_ms_max": 25.0,
"candidate_stage_p99_ms_max": 50.0,
"completion_age_p99_ms_max": 100.0,
"capacity_drop_count_max": 0,
"unaccounted_available_frame_count_max": 0,
"unaccounted_eligible_point_count_max": 0
},
"invariants": {
"all_eligible_input_points_accounted": true,
"aos_allowed": false,
"lidar_orientation_applied_to_tgs_input": false,
"map_gravity_axis_preserved": true,
"missing_support_means_free": false,
"missing_lidar_means_unobserved": true,
"unobserved_cells_are_emitted": true,
"future_frames_used": false,
"camera_projection_is_authoritative": false,
"gpu_allowed": false,
"navigation_or_actuation_allowed": false
}
}
@@ -0,0 +1,73 @@
{
"schema_version": "missioncore.m49-tgs-integrated-graph-shadow-profile/v1",
"profile_id": "m49-ravnoves00-tgs-native-risk-integrated-shadow/v1",
"source": {
"source_id": "RAVNOVES00",
"expected_timeline_frames": 4489,
"expected_available_lidar_frames": 3928,
"source_pack_sha256": "0685d24219d8236caf8b7f1685e93f6d6b59e7fd015a768d88a92bbe8b154944",
"requested_source_rate_hz": 12.0,
"shared_start_barrier": true
},
"stages": {
"reference_graph": {
"graph_config": "m48n-rf-detr-native-reference-graph-shadow-v0.json",
"graph_config_sha256": "1db6f6fa0561d819505a5e4f62fe256dab6fe4d9da6b4f1a9a5073bfcf772c90",
"detector_profile": "rf-detr-large-native-kb4-risk-shadow-v0.json",
"detector_profile_sha256": "dbf4da5dbad6c3c22b1280b46ffcad81719bd183c81c263a4859847d829019b6",
"detector_provider_id": "triton-rf-detr-large-coco-native-kb4-risk-fp16-shadow/v0",
"single_inference_pass": true
},
"tgs": {
"profile": "m49-tgs-full-shadow-v1.json",
"profile_sha256": "c2e07010aaee78259d36c057962d6bfb885349251ff7356d867e5813e632881c",
"candidate_id": "travel-tgs-only-gravity-aligned",
"linked_accepted_result_id": "m49-tgs-full-shadow-ef98de7db7596d48e8c8c0549ce68e6704ee03e87c8c4bcf1e3e748b7ccb032e",
"aos_allowed": false,
"gpu_allowed": false,
"states_remain_separate": true
}
},
"acceptance": {
"minimum_delivery_ratio": 1.0,
"reference_world_state_fps": 11.79902,
"maximum_world_state_fps_regression_fraction": 0.05,
"minimum_effective_world_state_fps": 11.209069,
"maximum_world_state_completion_p95_ms": 125.0,
"candidate_stage_p95_ms_max": 25.0,
"candidate_stage_p99_ms_max": 50.0,
"combined_output_age_p99_ms_max": 125.0,
"capacity_drop_count_max": 0,
"unaccounted_frame_count_max": 0
},
"telemetry": {
"sample_interval_seconds": 1.0,
"required_products": [
"graph_pipeline_timing",
"graph_queue_high_watermarks",
"tgs_stage_timing",
"host_container_cpu_memory",
"gpu_utilization_memory_power_temperature"
]
},
"invariants": {
"native_fisheye_raster_unchanged": true,
"camera_rectification_allowed": false,
"camera_resize_allowed": false,
"tgs_candidate_parameters_unchanged": true,
"tgs_modifies_reference_graph_state": false,
"missing_lidar_means_unobserved": true,
"future_frames_used": false,
"low_step_used": false,
"gauss_or_playcanvas_in_scope": false
},
"authority": {
"visual_quality_accepted": false,
"traversability_accepted": false,
"physical_free_space_accepted": false,
"commands_enabled": false,
"actuation_allowed": false,
"navigation_or_safety_accepted": false,
"production_accepted": false
}
}
@@ -0,0 +1,56 @@
{
"schema_version": "missioncore.m49-travel-ravnoves-compatibility/v1",
"profile_id": "m49-travel-ravnoves-compatibility/v1",
"candidate": {
"id": "travel",
"revision": "95dc2fbd66a343efd9060c45a5711b6307a950a4",
"worker_image_id": "sha256:7b412020f4d8392d1d1ed1b33beadc44140f0ea8f781e62dd69796042334300f",
"upstream_profile": "kitti-64x4500"
},
"source": {
"source_id": "RAVNOVES00",
"session_id": "20260720T065719Z_viewer_live",
"representation": "registered-map-increment-v1",
"source_pack_id": "e10-lidar-pack-576c994a6c814e2592dd6240ace3902a5db94843312c759a73ba0c9166157d2b",
"source_pack_sha256": "0685d24219d8236caf8b7f1685e93f6d6b59e7fd015a768d88a92bbe8b154944",
"intensity_available": false,
"ring_available": false,
"per_point_time_available": false,
"native_ray_model_available": false
},
"anchors": [171, 306, 368, 402, 450, 509, 525, 744, 1122, 1856],
"input_profiles": [
{
"id": "current_increment",
"description": "Exact current registered map increment transformed into the current lidar frame.",
"history_seconds": 0.0,
"local_radius_m": null,
"travel_min_range_m": 1.0,
"travel_max_range_m": 80.0
},
{
"id": "causal_rolling_1s",
"description": "Current and past registered increments from one second, bounded by the accepted local-map radius.",
"history_seconds": 1.0,
"local_radius_m": 12.0,
"travel_min_range_m": 1.0,
"travel_max_range_m": 80.0
}
],
"order_variants": ["source", "reversed"],
"compatibility_acceptance": {
"ground_and_nonground_account_for_all_in_range_input_points": true,
"minimum_partition_multiset_jaccard": 0.99,
"minimum_labeled_multiset_jaccard": 0.99,
"all_outputs_must_be_finite": true
},
"invariants": {
"future_frames_allowed": false,
"missing_support_means_free": false,
"rolling_support_can_clear_space": false,
"zero_filled_intensity_has_semantic_authority": false,
"camera_resize_or_rectification_allowed": false,
"gpu_allowed": false,
"navigation_or_actuation_allowed": false
}
}
@@ -15,6 +15,12 @@
"candidate_id": "gseg3d-ground-consistency",
"role": "primary",
"components": [
{
"repository": "https://github.com/dfki-ric/ground_segmentation.git",
"revision": "116f510485ea5b4707e2b476958d34a5a067f647",
"observed_ref": "refs/heads/master",
"license": "BSD-3-Clause"
},
{
"repository": "https://github.com/dfki-ric/ground_segmentation_ros2.git",
"revision": "5c5ba6f5ca0d682db2d59b2ad2f6a317ee938b95",
+22 -11
View File
@@ -1,6 +1,7 @@
# Simulation Worlds — product-surface brief
Status: accepted for the first working vertical slice on 2026-08-26.
Status: accepted for the first working vertical slice on 2026-08-26; visual/physical stage boundary
revised on 2026-08-27.
## Job story
@@ -57,8 +58,10 @@ Mission Core backend
DC Gaussian Pipeline
├─ TUS bundle or archive admission
├─ secure ZIP/RAR/7z normalization
├─ native Vulkan SplatTransform build on Worker 006
└─ bounded collision simplification + mandatory Draco publication
├─ native Vulkan SplatTransform visual build on Worker 006
└─ optional Mesh_Files/*.ply source-collision path
├─ conservative small-hole repair and topology audit
└─ mandatory Draco, digest-bound publication
```
The browser never receives the Worker token. Mission Core does not embed archive-format behavior
@@ -76,13 +79,18 @@ SplatTransform GPU command through a confined filesystem spool to the pinned nat
on the RTX 4090. It does not install into or share the Python, CUDA, Triton or computer-vision
environments on the host.
Collision publication is deliberately two-stage. SplatTransform generates smooth collision and
SVO query data at a scene-type voxel size (`0.2 m` outdoors, `0.1 m` indoors/objects). The isolated
pipeline container then bounds the debug/physics mesh to a default 250,000-triangle target and
always writes `KHR_draco_mesh_compression` before the artifact digest is sealed. Simplification
controls decoded geometry cost; Draco controls transfer and stored bytes and is never treated as a
replacement for a physics mesh budget. The browser loads the result through a pinned local Draco
WASM decoder with no external decoder request.
Project processing never generates collision geometry from Gaussian data by default. When the
normalized source contains exactly one PLY below `Mesh_Files`, the same queued build automatically
selects the provider's `source` collision profile. The original indexed mesh is retained; only
strictly admitted small internal boundary loops on approximately planar Z-up surfaces receive new
triangles. Vertices and existing faces are never moved, welded, smoothed, simplified or remeshed.
The derived GLB then passes the mandatory Draco publication gate and carries a separate repair
report. A source with no admitted PLY remains visual-only and still reaches `ready` normally.
The conservative repair rejects outer borders, branched boundaries, large or non-planar loops,
vertical openings, mixed orientation and failed/self-intersecting triangulations. Ambiguous holes
remain open and are counted in the report instead of being silently capped. Explicit mesh
generation from Gaussian data remains a later experiment, not a fallback in this ingestion path.
Visual and collision layers retain independent X/Y/Z correction settings, while PlayCanvas world,
camera, navigation and future physics stay in the canonical Y-up coordinate system. Quality, both
@@ -105,7 +113,10 @@ Primary implementation references:
- encrypted, linked, traversing, duplicate and over-limit archive entries fail closed;
- project status is durable and reflects provider state without fabricated percentages;
- ready artifacts are imported digest-bound and served from Mission Core same-origin URLs;
- every published collision GLB is simplified to the configured budget and requires Draco;
- the build always requests preview and streamed Gaussian outputs;
- exactly one `Mesh_Files/*.ply` automatically selects repaired source-mesh collision plus Draco;
- archives without that mesh remain visual-only and reach ready without a collision artifact;
- generated Gaussian/voxel collision is never used as an implicit fallback;
- edit changes project metadata; delete removes both the Mission Core project and terminal provider
job;
- a ready project mounts direct PlayCanvas Engine and loads Streamed SOG with preview fallback;
@@ -572,6 +572,16 @@ Exit:
### M4.9 — recorded-realtime release candidate and cutover
The first complete integrated source-paced load gate passed on `2026-08-27`:
the frozen RF-DETR world-state graph and unchanged CPU TGS candidate accounted
for all `4,489` frames at `11.860865 FPS`, with joined output p95/p99 of
`46.825886/63.94485 ms` and zero capacity drops. The immutable identities and
resource envelope are recorded in
[`experiments/perception/M49_TGS_INTEGRATED_GRAPH_ACCEPTANCE_2026-08-27.md`](../experiments/perception/M49_TGS_INTEGRATED_GRAPH_ACCEPTANCE_2026-08-27.md).
This closes only the one-loop integrated load gate. Visual traversability,
uncapped parity, repeated-loop soak, restart/recovery, navigation, actuation
and production remain open.
Execute on Worker 006 with the frozen RAVNOVES00 source:
1. one full `1.0×` 448.723-second replay;
@@ -33,8 +33,13 @@ The sequential evaluation order is:
RF-DETR remains the semantic risk provider for people, children, animals,
cars, trucks, motorcycles and bicycles. It does not decide the presence or
shape of an unknown static obstacle. Native `800x600` fisheye input remains
unchanged.
shape of an unknown static obstacle. The admitted camera semantic layer owns
surface/material hints such as low vegetation and woody vegetation. Native
`800x600` fisheye input remains unchanged.
There is no single globally primary sensor. The camera is primary for semantic
meaning; LiDAR is primary for metric geometry and positive support. Neither is
allowed to erase contradictory evidence from the other.
## Required product representation
@@ -55,12 +60,42 @@ robot-footprint collision cost + traversability
+----> camera projection (diagnostic visualization only)
camera RF-DETR ----> dynamic semantic risk hints ----------------+
camera semantics --> material / vegetation hint -----------------+
```
The canonical safety output is a costmap/elevation/occupancy product. Camera
boxes may be derived for operator review, but a box must never be the source of
occupied cells, clearance, passage width or a control action.
## Camera/LiDAR arbitration and vegetation
Tall grass demonstrates why a binary obstacle mask is insufficient. LiDAR may
correctly report many non-ground returns while being unable to tell whether
they came from compressible grass, a rigid trunk hidden inside the grass, or a
drop-off with no positive support. Camera semantics can identify vegetation,
but a camera label cannot prove that the ground below is load-bearing or free
of a hidden rigid object.
Mission Core therefore preserves at least these distinct states:
| Evidence | Traversability state |
| --- | --- |
| Stable metric non-ground evidence without vegetation support | `RIGID_OR_UNKNOWN_OBSTACLE`; lethal or unknown according to support confidence |
| Camera vegetation plus diffuse LiDAR returns, but no positive ground support below | `VEGETATION_UNKNOWN`; never silently free |
| Camera low vegetation plus positive local ground support and no concentrated rigid return | `VEGETATION_POTENTIALLY_TRAVERSABLE`; high-cost terrain, not a wall |
| Camera vegetation plus concentrated persistent vertical geometry | `VEGETATION_WITH_RIGID_GEOMETRY`; lethal |
| Positive ground support without blocking geometry | `SUPPORTED_GROUND`; candidate traversable terrain |
`VEGETATION_POTENTIALLY_TRAVERSABLE` is a representation decision, not an
authorization to drive through it. The later robot profile must still declare
ground clearance, footprint, traction, admissible slope, vegetation height and
whether contact with vegetation is allowed. Until that profile exists, the
planner may prefer a supported hard path and retain vegetation as a higher-cost
or unknown alternative.
The executable evidence contract is
[`config/perception/m49-camera-lidar-traversability-v1.json`](../config/perception/m49-camera-lidar-traversability-v1.json).
## Candidate A — GSeg3D and Ground Consistency
### GSeg3D
@@ -78,6 +113,11 @@ publishes separate ground and obstacle point clouds and provides:
Mission Core evaluates it as the first replacement for the current local
height connected-component heuristic.
The upstream GSeg3D documentation explicitly lists recall degradation in dense
vegetation as a remaining challenge and semantic-aware refinement as future
work. Candidate A can therefore qualify the metric ground/non-ground seam, but
cannot alone close the vegetation product gate.
### Nav2 Ground Consistency
[DFKI Ground Consistency](https://github.com/dfki-ric/nav2_ground_consistency_costmap_plugin)
@@ -239,6 +279,13 @@ failure.
- Run upstream sample/demo data first to prove the unmodified components.
- Measure component latency and memory before adding Mission Core adapters.
T1 completed on `2026-08-26` with an upstream candidate rejection. The pinned
sources build, but the Ground Consistency suite passes only `11/12` gtests;
`testBoundsReflectFootprint` fails because the test fixture has no configured
footprint. The sealed evidence and complete run ledger are recorded in
[`experiments/perception/M49_T1_GSEG3D_UPSTREAM_QUALIFICATION_2026-08-26.md`](../experiments/perception/M49_T1_GSEG3D_UPSTREAM_QUALIFICATION_2026-08-26.md).
Candidate A receives no replay or runtime authority in this revision set.
### T2 — Candidate A RAVNOVES00 replay
- Feed the bounded causal local cloud with exact pose lineage.
@@ -246,6 +293,8 @@ failure.
- Review the ten mandatory anchors and compute full-run stability/resource
metrics.
- Do not draw system camera boxes until the costmap result is sealed.
- Replay the existing camera semantic vegetation evidence as an independent
arbitration input; do not let it clear unsupported or concentrated geometry.
### T3 — Candidate B challenger
@@ -255,6 +304,68 @@ failure.
- Stop if Candidate A already passes and TRAVEL adds no measurable value that
justifies GPL runtime implications.
The upstream T3 qualification passed on `2026-08-26`: exact-source KITTI
regression was bit-identical to the committed gold result, the standalone
example produced all outputs and the ROS 2 Jazzy node was discoverable. See
[`experiments/perception/M49_T3_TRAVEL_UPSTREAM_QUALIFICATION_2026-08-26.md`](../experiments/perception/M49_T3_TRAVEL_UPSTREAM_QUALIFICATION_2026-08-26.md).
The direct RAVNOVES00 compatibility gate then rejected the combined TGS+AOS
profile. TGS ground/non-ground membership was invariant under point-order
reversal, but AOS failed all `20/20` order comparisons with labeled-point
Jaccard as low as `0.469`; its `64 × 4,500`, `-24.8°…+2.0°` KITTI scan model is
not the K1 registered-map-increment contract. The complete evidence is in
[`experiments/perception/M49_T3_TRAVEL_RAVNOVES_COMPATIBILITY_2026-08-26.md`](../experiments/perception/M49_T3_TRAVEL_RAVNOVES_COMPATIBILITY_2026-08-26.md).
Direct AOS admission is closed. TGS alone remains eligible for a bounded
ground/non-ground adapter that retains the exact unreturned complement as
unknown evidence. GPL runtime, visual quality and full-source realtime remain
open gates.
The gravity-aligned TGS-only adapter completed on the same ten anchors with all
eligible input points accounted as ground, non-ground or rejected and no AOS
invocation. It emits a deterministic `0.45 m`, `12 m` local evidence grid while
preserving unobserved cells. See
[`experiments/perception/M49_TGS_FAIL_CLOSED_EVIDENCE_2026-08-26.md`](../experiments/perception/M49_TGS_FAIL_CLOSED_EVIDENCE_2026-08-26.md).
Representation/accounting is accepted for the visual gate; obstacle and
traversability quality are not yet accepted. The sealed result is now published
in the canonical LAB as
`m49-tgs-fail-closed-9d5cb089bb5cc23f829acb47eda52eaa886db3b60f8fa7d57e02e27f642e837b`,
linked to the current M4 v3 recorded timeline. Publication adds no navigation
or actuation authority.
The subsequent complete source-paced TGS-only shadow passed on `2026-08-26`.
It accounted for all `4,489` timeline frames and all `63,646,163` eligible
points, emitted the `561` missing-LiDAR frames as explicit all-cell
`UNOBSERVED`, sustained `10.003945 FPS`, measured TGS `p95/p99` at
`1.694/2.08973 ms`, completion-age `p99` at `41.42845548 ms`, and recorded zero
capacity drops. The immutable result and canonical LAB acceptance are recorded
in
[`experiments/perception/M49_TGS_FULL_SHADOW_ACCEPTANCE_2026-08-26.md`](../experiments/perception/M49_TGS_FULL_SHADOW_ACCEPTANCE_2026-08-26.md).
This accepts the CPU source-paced shadow and retains the candidate. It does not
accept visual traversability quality, integrated graph performance, navigation
or actuation.
The product-integration load gate then passed on `2026-08-27`. The unchanged
TGS candidate ran concurrently with the frozen RF-DETR world-state graph for
all `4,489` frames at a requested `12 Hz`: every graph frame was delivered,
every TGS frame was accounted and capacity drops remained zero. The joined
output sustained `11.860865 FPS`, measured `46.825886/63.94485 ms` at p95/p99,
and the CPU-only TGS stage measured `1.735/2.14019 ms` at p95/p99 with a
`22.6 MiB` memory maximum. See
[`experiments/perception/M49_TGS_INTEGRATED_GRAPH_ACCEPTANCE_2026-08-27.md`](../experiments/perception/M49_TGS_INTEGRATED_GRAPH_ACCEPTANCE_2026-08-27.md).
Integrated runtime capacity is accepted; visual traversability, physical free
space, navigation, actuation and production remain unaccepted.
The canonical LAB playback data plane was then replaced on `2026-08-27` without
changing the sealed TGS or reference-graph results. Dense M4 points and complete
TGS numeric products are now loaded once as immutable digest-verified binary
tracks; bounded JSON windows retain metadata only, and the existing Three.js
scene updates persistent GPU buffers instead of remounting per frame. The exact
products, loopback delivery measurements, seek/play browser acceptance and
authority boundary are recorded in
[`experiments/perception/M49_LAB_PLAYBACK_DATA_PLANE_ACCEPTANCE_2026-08-27.md`](../experiments/perception/M49_LAB_PLAYBACK_DATA_PLANE_ACCEPTANCE_2026-08-27.md).
This is a playback and evidence-delivery acceptance, not a new perception or
navigation-quality gate.
### T4 — Candidate C occupancy/ESDF probe
- Run nvblox separately with RF-DETR disabled for the isolated probe.
@@ -272,7 +383,16 @@ failure.
## Immediate next action
Resume T1 for Candidate A only after the Worker resource gate in the T0 report
passes. No LOW-STEP tuning, new object model, camera resize, fisheye
Keep the accepted integrated graph frozen and review the published full
gravity-aligned TGS timeline in camera, metric 3D and costmap space. Preserve
`GROUND_SUPPORT`, `NONGROUND_OCCUPIED`, `UNKNOWN_REJECTED` and `UNOBSERVED` as
separate products; do not use AOS or infer free cells from absent
republication. Integrated FPS, latency, resources and queue behavior are now
accepted for the one-loop source-paced gate. If occupied evidence carpets the
route, soft traversable vegetation or usable gaps, reject TGS without tuning it
against the review evidence. Close and induce-test the Worker inference-runtime
restart guard, then proceed to repeated-loop soak only if visual review also passes.
Candidate A T2 replay remains blocked by the failed unmodified T1 gate. No
LOW-STEP tuning, new object model, camera resize, fisheye
rectification, manual dataset or parallel heavy Worker job is authorized by
this decision.
@@ -0,0 +1,186 @@
# ADR 0043: Physical safety shadow and bounded operator visual
Date: 2026-08-27
Status: autonomous transport slice implemented; 0.15 m challenger and measured acceptance incomplete
## Context
Mission Core has accepted a source-paced RF-DETR-native plus TRAVEL TGS
recorded shadow on RAVNOVES00. The retained TGS result uses a `0.45 m` cell,
`12 m` radius and four explicit states. It is useful operator evidence but is
not a planner-authoritative terrain provider. The integrated acceptance joins
TGS and the reference graph as evidence; TGS does not modify graph state.
The intended physical rover is not a lightweight laboratory fixture. The
operator supplied the following planning facts, not yet independently measured:
- body `1.0 x 0.8 x 0.4 m`;
- ground clearance `0.15 m`;
- mass approximately `100 kg`;
- one nominal `500 W` motor per side;
- nominal speed `0.2778 m/s` (`1 km/h`);
- maximum operating speed `0.5556 m/s` (`2 km/h`).
Controller, remote dead-man, braking behavior, physical E-stop, body/sensor
extrinsics, center of gravity and negative-obstacle coverage are unavailable.
Commands therefore remain disabled.
The current full TGS playback pack is about `88 MiB`: approximately `9.6 MiB`
states, `77 MiB` z-bounds and `1.1 MiB` frame metadata. Holding the same
`12 m` disk for all `4,489` frames would scale to roughly `0.8 GiB` at `0.15 m`
and `1.8 GiB` at `0.10 m`, before richer terrain features, video, semantics and
renderer allocations. Full-session high-resolution browser preload is not an
admitted design for the current 18 GiB MacBook host.
## Decision
Recorded LAB and realtime perception are different runtime contours. Worker
006 is reserved for realtime perception. A new recorded LAB is built by a
bounded sequential offline builder on the MacBook, or admits an already sealed
immutable artifact. The offline build may take longer than source duration. It
must not become a hidden Worker job. An opened LAB never depends on Worker 006.
```text
recorded LAB publication, Worker 006 absent
recorded source -> local sequential offline build
-> seal immutable result + binary LOD tracks
|
v
local/CAS artifact store
|
Worker 006 remains absent
|
v
canonical local backend, read-only
|
v
MacBook LAB CAMERA + 3D + PLAN
separate future realtime contour
K1 -> Worker 006 -> live world-state/planner shadow
```
Opening, seeking, looping and visually inspecting a sealed LAB must perform
zero requests to Worker 006. If the worker, its host, Docker or the network path
to it is unavailable, a locally present sealed result opens unchanged. If a
manifest-declared local object is absent or corrupt, LAB fails with an explicit
artifact error; it does not start compute, scan worker results or silently
rebuild evidence. This preserves ADR 0033.
Publishing a new LAB also performs zero Worker requests. Slow local preparation
is acceptable, but it is explicit and bounded: build first, seal second, open
third. Historical LAB artifacts that were originally computed on Worker 006
remain valid after sealing because replay depends only on their immutable
artifacts; they do not justify a Worker dependency for new LABs.
Visual performance may never change a safety state, erase unknown space or
reduce the resolution used by the safety consumer. Conversely, the MacBook does
not need every cell of every high-resolution frame resident at once in order to
provide a complete operator explanation.
The retained `0.45 m` M49T5 profile and sealed result remain immutable. A new
physical-safety challenger may evaluate `0.15 m` and `0.10 m` cells without
rewriting that baseline. The first candidate resolution is `0.15 m`; `0.10 m`
is admitted only if LiDAR density improves the evidence. Worker compute
acceptance and autonomous LAB viewer acceptance are measured separately.
The operator transport uses binary typed arrays. It must not serialize one JSON
object per cell or expand binary frames into nested JavaScript objects before
updating GPU instances. Recorded high-resolution inspection reads only sealed
local binary chunks and active-frame detail, not a full-session high-resolution
preload.
The proposed spatial chunk is `32` source frames (`3.2 s` at `10 Hz`). Before
the spatial scene declares itself ready, it may preload the first two chunks,
or `6.4 s`, from the local artifact. The browser retains at most three chunks
(`9.6 s`), normally previous/current/next, and prefetches one forward chunk.
A backward seek loads the target and next chunk from the same sealed artifact.
This bounded startup delay is preferable to either Worker dependency or a
multi-gigabyte browser preload.
## Implemented autonomous transport slice
The first executable transport slice is now present without creating a new LAB
surface or transport clock:
- `m49_physical_safety_playback.py` is a local, sequential, filesystem-only
sealer. It accepts an already sealed source, verifies its declared hashes,
and writes immutable binary chunks atomically. It contains no Worker client,
job submission or network path.
- `m49_physical_safety_playback_api.py` exposes only the configured local
artifact root. Manifest, shared centers, frame catalog and every requested
chunk are hash-verified. Corruption returns an explicit artifact failure.
- `m49PhysicalSafetyPlayback.ts` rejects non-canonical URLs, verifies byte
length and SHA-256 in the browser, prebuffers two chunks, retains no more than
three and loads target plus next on seek.
- the existing M49 evidence composition resolves this transport by exact
`source_result_id`. If no chunked derivative has been published, it may still
read the old sealed local full pack; neither path uses Worker 006.
A real transport canary was generated from the accepted immutable `0.45 m`
M49T5 result without recomputing TGS or changing that source result. The canary
identity is
`m49-physical-safety-playback-0d0e41a995a902a4701935a79655d93f2f1898806f8fa82ebbd207129f111d69`.
It contains all `4,489` frames and `2,244` cells in `141` chunks. A normal
32-frame chunk is `646,300` bytes; two startup chunks are about `1.23 MiB` and
three spatial chunks are about `1.85 MiB`, plus the shared centers and frame
catalog. This is the transport canary only. It is not a `0.15 m` safety result
and no upsampled baseline is presented as new evidence.
The `0.15 m` shape, fail-closed missing-sample rule, bounded seek behavior and
zero-Worker URL policy are covered with synthetic contract fixtures. A real
`0.15 m` challenger still requires the local sequential terrain computation
and a separately sealed result.
The visual uses explicit LOD:
- `0-4 m`: `0.15 m` near-field cells;
- `4-8 m`: `0.30 m` explanatory cells;
- `8-12 m`: `0.45 m` context cells;
- pause/anchor inspection: full available active-frame resolution.
LOD changes only presentation. The sealed result retains the full candidate
evidence needed to reproduce its decisions; a future realtime worker separately
retains the full live safety grid across the configured safety radius.
The metric Three.js scene retains instanced meshes and all evidence layers. It
renders on evidence, camera interaction, resize or layer changes. It does not
run a permanent animation loop while idle. The scene exposes render-pass time,
draw calls, triangles, cell count and pixel ratio in the operator surface.
## Acceptance budget
Acceptance is measured on the named 14-inch 2023 MacBook Pro with 18 GiB RAM,
with recorded video, semantic overlay and TGS 3D/PLAN evidence available:
- source pace: `10 Hz` without evidence loss;
- main-thread frame preparation p95 at most `10 ms`;
- CPU-side WebGL render submission p95 target `16.7 ms`, hard maximum
`33.3 ms`; GPU completion requires a separate supported timer query;
- evidence-to-visible p95 at most `100 ms`;
- incremental visual resident memory at most `256 MiB`;
- post-warmup growth at most `32 MiB` over a ten-minute soak;
- zero browser long tasks above `50 ms` during source-paced playback;
- zero WebGL context losses;
- zero Worker 006 requests while opening, seeking or playing a LAB;
- zero Worker 006 requests while building a new recorded LAB;
- the sealed LAB opens with Worker 006 unavailable;
- idle metric scene performs no continuous render loop.
These are proposed thresholds until a canonical browser run emits a sealed
performance ledger. Passing them does not grant navigation, safety or actuation
authority.
## Consequences
- The operator keeps the full explanatory visual instead of receiving a
stripped-down safety UI.
- High-resolution terrain work does not turn the MacBook into a second
perception worker.
- Historical LAB remains usable during Worker maintenance, Docker failure or
loss of the Worker network path.
- Evidence identity and safety decisions remain independent of display LOD.
- The next implementation boundary is a packed, chunked terrain contract plus
footprint-aware states; no physical connection or command path is introduced.
- Physical authority remains blocked by controller/E-stop, measured braking,
vehicle geometry, sensor extrinsics and negative-obstacle coverage.
@@ -0,0 +1,100 @@
# M4.9 LAB sealed playback data plane — 2026-08-27
Status: **accepted for local recorded-evidence playback on the canonical Mission
Core service**. This acceptance covers playback delivery, timeline interaction
and renderer lifecycle. It does not change TGS visual-quality, navigation,
actuation or production authority.
## Decision
The LAB no longer transports source point arrays or the full TGS costmap as
per-frame JSON. The canonical playback data plane now downloads sealed numeric
tracks once, verifies their declared SHA-256 identities, retains them in the
browser and derives the current frame from immutable offsets. JSON remains only
for bounded frame metadata that is not a dense numeric point track.
The renderer keeps one Three.js scene mounted. Point `BufferGeometry`,
classified-cell `InstancedMesh` objects and semantic-mask presentation are
updated in place. A qualified previous semantic mask may remain visible while
the exact next mask is loading; it is marked stale and is never presented as
the new exact frame. Rapid seeks cancel obsolete metadata windows.
This removes the observed frame-by-frame JSON parsing and scene-remount path.
It also disables dynamic gzip for local numeric tracks and compact frame
metadata. During diagnosis, on-the-fly compression of the M4 point product took
approximately `14.4 s` and placed the backend in zlib work. The accepted
delivery uses `Content-Encoding: identity`, immutable cache headers and the
sealed digest instead.
## Sealed playback products
| Product | Shape / count | Bytes | Delivery |
| --- | ---: | ---: | --- |
| M4 map points | `9,207,270 × 3`, float32 | `110,487,240` | one raw binary track + `4,490` immutable offsets |
| M49 cell centers | `2,244 × 2`, float32 NPY | `18,080` | one immutable track |
| M49 states | `4,489 × 2,244`, uint8 NPY | `10,073,444` | one immutable track |
| M49 z-bounds | `4,489 × 2,244 × 2`, float32 NPY | `80,586,656` | one immutable track |
| M49 frame ledger | `4,489` NDJSON records | `1,178,262` | one immutable track |
| M49 total | four tracks | `91,856,442` | preloaded once |
M4 points remain in the exact registered-map point index space. A lightweight
M4 metadata window declares body transforms, camera proposals, obstacles and
point accounting without duplicating point arrays. The browser hydrates a
qualified current increment from the retained binary track. M49 states and
z-bounds are addressed directly by `source_sequence`; all `2,244` cells remain
explicit for every one of the `4,489` frames.
## Measured local delivery
One hot local-loopback measurement on the canonical `127.0.0.1:8000` service:
| Request | Bytes | Wall time |
| --- | ---: | ---: |
| M4 point track | `110,487,240` | `0.162 s` |
| M49 centers | `18,080` | `0.009 s` |
| M49 states | `10,073,444` | `0.046 s` |
| M49 z-bounds | `80,586,656` | `0.256 s` |
| M49 frame ledger | `1,178,262` | `0.007 s` |
| M4 lightweight metadata, frames `1104–1127` | `891,543` | `0.160 s` |
These loopback values describe delivery only; they are not a network SLA. The
UI separately exposes exact download percentage and MiB progress while tracks
are transferred and verified.
## Browser acceptance
Production assets were opened through the canonical service. An arbitrary seek
from the beginning to `110.330 s` produced exact frame `1105`, its E47 semantic
mask, qualified source cloud, bounded local SLAM surface and the same-frame TGS
costmap. The qualified scene was visible within the `1.2 s` observation wait.
Playback then advanced through frame `1141` across multiple 24-frame metadata
windows. The final DOM and visual evidence agreed on frame `1141`; the semantic
mask was frame `1141`; `map-gravity-local · all eligible points accounted` was
visible; no spatial buffering state or missing-linked-cloud warning remained.
The browser console contained zero errors.
Frames without a qualified body frame still render honestly as TGS cell-only.
They do not reuse a source cloud from another frame and do not invent geometry.
## Runtime and validation
- Canonical service: exactly one listener on `127.0.0.1:8000`; no listener on
`8765`.
- Backend: Ruff passed; `11` focused M49/M4 tests passed.
- Frontend: `28` focused playback, semantic and architecture tests passed;
TypeScript typecheck and production build passed.
- Delivery commits: `bf76304` (`sealed spatial playback tracks`) and `7aeea4e`
(`retained spatial scenes during replay`).
- GAUSS and PlayCanvas changes were excluded from both commits and from this
acceptance.
## Authority boundary
Accepted: local recorded-evidence delivery, exact sealed identity, bounded
metadata windows, fast seek behavior, retained renderer state and synchronized
camera/semantic/spatial presentation.
Not accepted: visual traversability quality, physical free space, a vehicle
envelope, collision clearance, navigation, actuation, live-stream transport or
production soak. Those gates remain separate from the LAB playback data plane.
@@ -61,6 +61,7 @@ This T0 check does not reopen or alter that evidence.
| Order | Candidate | Exact revision | Admission role |
| ---: | --- | --- | --- |
| 1 | DFKI GSeg3D core | `116f510485ea5b4707e2b476958d34a5a067f647` | Pinned core dependency |
| 1 | DFKI GSeg3D | `5c5ba6f5ca0d682db2d59b2ad2f6a317ee938b95` | Primary terrain-relative ground/non-ground provider |
| 1 | DFKI Nav2 Ground Consistency | `41cec620efba6c370dccfc59a6ec1134775ff48a` | Primary temporal costmap evidence layer |
| 2 | TRAVEL | `95dc2fbd66a343efd9060c45a5711b6307a950a4` | Class-free challenger |
@@ -86,6 +87,28 @@ Candidate A may start only when all of the following are true:
4. canonical Mission Core Triton is healthy and is not stopped or replaced;
5. the candidate uses a new `ndc-` isolated build/runtime contour.
## Resource incident localized before T1
The apparent continued Worker pressure was not caused by an active GAUSS job.
A PowerShell process from an earlier Mission Core diagnostic had been orphaned
after loading and recursively serializing the complete nested M4.8R3
`result.json`. At discovery it held approximately `74,912.6 MiB` working set
and `193,275.8 MiB` private memory. The pressure killed
`ndc-mission-core-triton` with exit code `137`.
The orphaned diagnostic process was terminated at
`2026-08-26T15:17:39Z`. Free physical memory subsequently recovered to
`83,374,248 KiB`. The existing canonical Triton container was started without
replacement or rebuild and returned to `healthy` at
`2026-08-26T15:18:10Z`.
Future Worker inspection must not pass a complete nested replay result through
PowerShell `ConvertFrom-Json` followed by deep `ConvertTo-Json`. Diagnostics
must select bounded scalar fields before serialization and must have a wall
timeout plus process cleanup. A separate compute-runtime change is required to
give canonical Triton a declared restart policy; this report does not mutate
the Worker Compose source.
## Next command boundary
The next authorized action is T1 Candidate A upstream qualification: build the
@@ -0,0 +1,93 @@
# M4.9 T1 GSeg3D upstream qualification — 2026-08-26
Status: **rejected as an unmodified Candidate A stack**; RAVNOVES00 replay and
all navigation or actuation authority remain disabled
## Decision
The pinned GSeg3D, ROS 2 wrapper and Nav2 Ground Consistency sources build in
an isolated Worker 006 container. The unmodified Ground Consistency test suite
does not pass: `11/12` gtests pass and
`GroundConsistencyLayerTest.testBoundsReflectFootprint` fails.
The failure is inside the pinned upstream candidate rather than the Mission
Core replay adapter. The layer logs that no footprint is configured, skips
footprint clearing and leaves `max_x/max_y` at `0`, while its own test requires
both bounds to be at least `1.0`.
Mission Core therefore does not call this revision set plug-and-play ready,
does not patch upstream silently and does not start Candidate A RAVNOVES00
replay. This result does not prove that the GSeg3D algorithm is unusable. It
proves that the exact unmodified stack frozen for T1 fails its admission gate.
## Frozen identity
| Component | Revision |
| --- | --- |
| DFKI `ground_segmentation` | `116f510485ea5b4707e2b476958d34a5a067f647` |
| DFKI `ground_segmentation_ros2` | `5c5ba6f5ca0d682db2d59b2ad2f6a317ee938b95` |
| DFKI `nav2_ground_consistency_costmap_plugin` | `41cec620efba6c370dccfc59a6ec1134775ff48a` |
| Final Mission Core qualification source | `316f0d7f8582f47cdd2a05e7e7404cfe33a40d6d` |
| Final deterministic Worker artifact | `f681ba58aa3c69b7cd3572fda4abc1c4303bf73d62262fb1c534e220f7136801` |
| Worker image | `sha256:57a481faa9ad50d69a5aae537ed24c1770867413b38e91399060c5dd448b3471` |
The final artifact was built twice locally with identical SHA-256, copied to a
new immutable Worker release directory, verified again before extraction and
run through the interactive Worker executor. The image size is
`1,172,862,313` bytes.
## Run ledger
| Run | Outcome | Interpretation |
| --- | --- | --- |
| `upstream-001` | pre-build failure | Non-interactive Docker credential helper; no candidate result |
| `upstream-002` | pre-build failure | Isolated Docker config was still evaluated in the non-interactive SSH context; no candidate result |
| `upstream-003` | harness failure | Image built; strict shell nounset conflicted with the standard ROS 2 setup hook |
| `upstream-004` | harness failure | Test command omitted the merged-install layout used during the image build |
| `upstream-005` | **candidate test failure** | Harness reached the pinned upstream gtests; `11/12` passed and footprint-bound test failed |
The two harness failures were corrected in new content-addressed artifacts and
were never presented as candidate failures. No identical failed artifact was
retried.
## Sealed final evidence
Worker output root:
`D:\NDC_MISSIONCORE\runtime\results\m49-t1-gseg3d\upstream-005`
| File | Bytes | SHA-256 |
| --- | ---: | --- |
| `result.json` | `640` | `6ff189d7c160fe98063672a848ced205199e7bd3ed3de568b1638b7e325848ac` |
| `qualification.log` | `37,813` | `ffafad5cc09ec3d46302111ed0fe9ec6c02d0dbd982bc7307c28b44db071abba` |
| `resource.txt` | `772` | `5955b1eb01a7f587efaeb3f9d547b80e44c1d6651f4b2c9906b47d11e8a5f894` |
The GSeg3D core checkout exposes no CTest tests in the selected build, so its
`ctest` step reports `No tests were found` and returns success. The decisive
executable evidence is the Ground Consistency suite, which does contain twelve
tests and returns one failure.
## Resource and isolation result
- Worker 006 remained the only execution host.
- The candidate build/test used CPU and did not request CUDA, Vulkan or a GPU
lease.
- The concurrent approximately `47–51%` GPU utilization and roughly `9 GiB`
VRAM belonged to `sentinel-frigate`, not GAUSS or this qualification.
- Canonical `ndc-mission-core-triton` remained `running/healthy` after the
final run.
- Temporary candidate containers and the one-shot scheduled task were removed
after evidence capture.
- No Mission Core source evidence, GAUSS process, Frigate process or canonical
Triton image was stopped, rebuilt or replaced.
## Authority boundary and next action
Candidate A has no RAVNOVES00 quality, realtime, navigation, collision-safety
or actuation authority. T2 is blocked for this exact unmodified revision set.
The next sequential action is an isolated upstream qualification of Candidate
B, TRAVEL, at its frozen revision. Its GPL-3.0 license limits it to evaluation
until a separate distribution decision is made. Camera/LiDAR vegetation
arbitration remains an independent product requirement and is not waived by
any terrain candidate.
@@ -0,0 +1,148 @@
# M4.9 T3 TRAVEL compatibility on RAVNOVES00 — 2026-08-26
Status: **direct TRAVEL TGS+AOS profile rejected**; TGS retained for a bounded
ground/non-ground adapter probe; visual quality, realtime integration,
navigation and actuation remain disabled
## Decision
The unmodified TRAVEL KITTI profile cannot consume the RAVNOVES00 `lio_pcl`
representation as a complete ground-and-instance provider. Ground segmentation
is deterministic and fast on the tested inputs, but the above-ground object
segmentation changes materially when the exact same points are presented in
reverse order. The fixed KITTI vertical-angle profile also does not describe
the K1 registered-map increments.
Mission Core therefore rejects direct admission of TRAVEL AOS. It retains the
unmodified TGS stage for the next bounded adapter test because its ground and
non-ground partitions were bit-set invariant across all order comparisons. The
adapter must preserve TGS-rejected points as explicit unknown evidence rather
than silently dropping them. No AOS cluster, camera box, free cell or command
receives authority from this run.
## Frozen identity
| Item | Identity |
| --- | --- |
| TRAVEL source | `95dc2fbd66a343efd9060c45a5711b6307a950a4` |
| Mission Core probe source | `28d52972370097d3f3e376e17c9fdb388b0e7ebf` |
| Deterministic Worker artifact | `c726018a62f602ff1f51432fbdf4bba67aa969735f9798a55472b079eff8a0fb` |
| TRAVEL image | `sha256:7b412020f4d8392d1d1ed1b33beadc44140f0ea8f781e62dd69796042334300f` |
| CPU preparation/analysis image | `sha256:ceb13548617e4bd3f619766bfdff00af3fa5160946b367828da6d2233dcdcba0` |
| RAVNOVES00 LiDAR pack | `0685d24219d8236caf8b7f1685e93f6d6b59e7fd015a768d88a92bbe8b154944` |
| Compatibility profile | [`config/perception/m49-travel-ravnoves-compatibility-v1.json`](../../config/perception/m49-travel-ravnoves-compatibility-v1.json) |
The release artifact was built from a clean committed revision, copied into a
new Worker release root and verified before extraction. The run used the two
already-qualified images by exact image ID. It performed no image build,
network access, CUDA/Vulkan request or GPU lease.
## Input contract
The probe used the ten operator-reviewed anchors `171`, `306`, `368`, `402`,
`450`, `509`, `525`, `744`, `1122` and `1856` with two causal representations:
1. `current_increment`: the exact registered map increment transformed into
the current LiDAR frame;
2. `causal_rolling_1s`: current and past increments from the preceding one
second, bounded to the accepted `12 m` local-map radius and transformed into
the current LiDAR frame.
No future frame was used. Missing republication was not interpreted as free
space. The source pack has no retained intensity, ring/channel, per-point time
or native ray model; intensity was zero-filled only to satisfy the XYZI input
ABI.
Each representation was run in source order and exact reverse order. This
produced `10 × 2 × 2 = 40` isolated CPU invocations.
## Result
### TGS ground/non-ground stage
| Measurement | Current increment | Causal rolling 1 s |
| --- | ---: | ---: |
| Eligible points over ten source-order anchors | `20,468` | `144,872` |
| Ground + non-ground points returned | `20,337` | `144,871` |
| Unreturned eligible points | `131` | `1` |
| Anchors with complete accounting | `7/10` | `9/10` |
| Ground multiset order Jaccard, minimum | `1.000` | `1.000` |
| Non-ground multiset order Jaccard, minimum | `1.000` | `1.000` |
| TGS stage p50 | `0.269 ms` | `1.775 ms` |
| TGS stage p95 | `0.329 ms` | `4.379 ms` |
| TGS stage maximum | `0.583 ms` | `4.807 ms` |
TGS classification is order-invariant on all twenty source/reverse
comparisons. The missing points are not random process loss. TRAVEL internally
routes below-plane or filtered points into its outlier cloud, while the public
example and ROS wrapper publish only `ground`, `nonground` and AOS `labeled`.
Frame `171` accounts for `129` of the `131` missing current-increment points;
they lie approximately `2.59–5.09 m` from the sensor. A safety adapter must
retain the exact input complement as `UNKNOWN_REJECTED`, never as free.
### AOS above-ground object stage
| Measurement | Result |
| --- | ---: |
| Accepted source/reverse comparisons | `0/20` |
| Labeled-point multiset Jaccard, minimum | `0.469` |
| Labeled-point multiset Jaccard, median | `0.663` |
| Labeled-point multiset Jaccard, maximum | `0.963` |
| Current-increment non-ground points retained as labeled, median | `20.6%` |
| Causal-rolling non-ground points retained as labeled, median | `38.4%` |
This is a representation incompatibility, not a threshold-quality judgment.
AOS projects points into a `64 × 4,500` spherical range matrix and keeps the
first point that reaches an occupied angular bin. A registered-map increment
does not carry an upstream ring order, so reversing an otherwise identical
array changes which points survive projection and changes cluster membership.
The pinned KITTI profile declares a vertical range of `-24.8°…+2.0°`. Only
`26.7%` of current-increment non-ground points and `34.4%` of causal-rolling
non-ground points lie inside that declared range. The pinned implementation's
row lookup also has no valid end-of-range branch for an angle above the maximum.
Changing the angle table and inventing a pseudo-ring layout would create a new
K1-specific algorithm profile; it is not accepted as the ready upstream system
tested here.
### Wrapper and resource envelope
- Forty isolated invocations completed in `6.654335 s`, including container
startup, input preparation and analysis.
- Per-invocation wall p50 was `50 ms`; p95 was `70.5 ms`. These include
process startup and are not the candidate-stage realtime measurement.
- Maximum measured process RSS was `18,140 KiB`.
- Canonical `ndc-mission-core-triton` retained the same container ID and stayed
`running/healthy`.
- The one-shot scheduled task and all three exact candidate container names
were removed. Worker free memory after cleanup was approximately `52.82 GiB`.
- Frigate, GAUSS/PlayCanvas, the GPU allocation and the canonical port `8000`
were not modified.
## Sealed evidence
Worker root:
`D:\NDC_MISSIONCORE\runtime\results\m49-t3-travel-ravnoves\ravnoves-compat-001`
| File | Bytes | SHA-256 |
| --- | ---: | --- |
| `result.json` | `26,810` | `ff0b27f469cccad258c53ae07cb04ad00e696d82b32021c4f1f38ba7fa39d66a` |
| `worker-summary.json` | `1,007` | `007eb400f6a12c950e81a631dc1c7824bc10c0024327fb9965f5689d3b4c6a22` |
| `inputs/input-manifest.json` | `22,967` | `1239f2517c5dfcb46827cfd8f370e642a70eac3a83e39d6b769564321e2ff930` |
| `travel-timing.tsv` | `1,606` | `959b82372dbb197525071f030a4be1d6f3ff848e32b62b4b47b4f2bf2fa9d3d7` |
## Next gate
Build a TGS-only recorded adapter with four explicit outputs:
1. positive `GROUND_SUPPORT` from TGS ground;
2. `NONGROUND_OCCUPIED` from every TGS non-ground point, without AOS;
3. `UNKNOWN_REJECTED` from the exact eligible-input complement;
4. `UNOBSERVED` for cells with no positive support, never free by omission.
The first review remains the ten anchors in a 3D occupancy/costmap view. Camera
projection is diagnostic only and follows after the metric output is sealed.
The adapter is admitted only if compact obstacles and the passages between them
remain explicit without large vegetation/curb walls. GPL distribution and
full-source realtime remain separate gates.
@@ -0,0 +1,99 @@
# M4.9 T3 TRAVEL upstream qualification — 2026-08-26
Status: **upstream candidate qualified**; RAVNOVES00 quality, realtime,
navigation and actuation remain disabled
## Decision
The frozen TRAVEL revision builds and passes its official C++ regression path
on Worker 006. The run also passes the standalone example smoke and produces a
discoverable ROS 2 Jazzy `travel_node`.
This accepts TRAVEL for the next recorded-replay compatibility gate. It does
not yet accept the RAVNOVES00 `lio_pcl` representation, Mission Core latency,
vegetation handling, obstacle quality, navigation or control.
## Frozen identity
| Item | Identity |
| --- | --- |
| TRAVEL source | `95dc2fbd66a343efd9060c45a5711b6307a950a4` |
| Mission Core qualification source | `67578f4ce16316dbaa562cba466ca9ab820fa380` |
| Deterministic Worker artifact | `01cb4954de748d2e23567734a5dc5043ebfa7d1596313c3ddba792fa94ac3cb7` |
| Worker image | `sha256:7b412020f4d8392d1d1ed1b33beadc44140f0ea8f781e62dd69796042334300f` |
| Image size | `1,030,088,960` bytes |
| KITTI fixture | `1,994,688` bytes; SHA-256 `bf272996d5b6d25cc5589e1089137cb20a98b63bd4823a7fea5631b359f6d68c` |
| Committed gold result | SHA-256 `8aaacaa57d17a8a2f043c3ea0a0c134d36a3c50da2385f2364ca4eff53ea9b9f` |
The release artifact was built twice with identical SHA-256, copied into a new
Worker release directory and verified again before extraction. The run used
the exact upstream revision and did not modify its source.
## Upstream result
- `124,668` KITTI points loaded.
- Official C++ regression output was bit-identical to the committed gold dump.
- Ground segmentation produced `70,175` ground and `54,462` non-ground points.
- Above-ground clustering received `54,462` points, projected `47,311`,
labeled `45,792` and produced `185` canonical clusters in the regression
representation.
- The standalone example wrote non-empty ground, non-ground and labeled
outputs.
- ROS 2 Jazzy discovered `travel_ros travel_node` from the isolated merged
install.
The example prints `The # of point clouds and # of labels are not same`
because the official smoke fixture intentionally contains a Velodyne frame but
no optional KITTI labels directory. The example returns success and writes all
three required outputs; the official upstream CI uses the same unlabeled smoke
shape. This diagnostic is recorded rather than hidden.
## Timing and memory
| Measurement | Observation |
| --- | ---: |
| Worker image build | `290.528115 s` |
| Worker qualification wrapper | `1.667882 s` |
| Sealed in-container qualification section | `617 ms` |
| C++ bit-regression wall time | `0.04 s` |
| C++ bit-regression maximum RSS | `39,504 KiB` |
| Reported TGS time | `11.168 ms` |
| Reported AOS range-map / clustering / post stages | `3 / 6 / 1 ms` |
These numbers are promising but are not Mission Core realtime acceptance. They
cover one native KITTI scan and include neither RAVNOVES00 causal support
assembly nor pose transforms, camera arbitration, costmap publication or full
graph load.
## Sealed evidence
Worker output root:
`D:\NDC_MISSIONCORE\runtime\results\m49-t3-travel\upstream-001`
| File | Bytes | SHA-256 |
| --- | ---: | --- |
| `result.json` | `674` | `d3fb582b96f907c92156767bf0186fa565897f593bcfd89943ad6a639c0aed38` |
| `worker-summary.json` | `824` | `baedc1b292691be50c5ef70169df1ff43f99f0e122f527a557a599a906a90d28` |
| `qualification.log` | `3,557` | `7dc5b8ecb1cb8ad91fc08edbfcc6ab818e4524e271b0524bb02914cf5f96d933` |
| `resource.txt` | `893` | `dc5638295170cb1edd9c16e2c4ae9b53a89b22b014d27a847c127d6b9afa541c` |
After capture, the candidate container and one-shot scheduled task were absent.
Canonical `ndc-mission-core-triton` remained `running/healthy`. The build and
qualification requested no CUDA, Vulkan or GPU lease; the concurrent Frigate
GPU allocation was not modified.
## Compatibility gate before RAVNOVES00 replay
TRAVEL's accepted KITTI profile assumes an organized spinning-LiDAR geometry
(`64` vertical scans, `4,500` horizontal bins and a fixed vertical angle
range). RAVNOVES00 supplies registered vendor-map increments rather than one
conventional complete raw scan per video frame. The next step must therefore
prove that a bounded causal body-frame representation preserves the angular
and temporal information TRAVEL requires. Missing returns may not be invented,
treated as free, or filled from future frames.
TRAVEL remains GPL-3.0-or-later and is evaluation-only until a separate
distribution decision. Camera semantics remain primary for material meaning;
LiDAR/TRAVEL remains metric evidence and cannot independently declare tall
vegetation traversable.
@@ -0,0 +1,200 @@
# M4.9 gravity-aligned TGS fail-closed evidence — 2026-08-26
Status: **evidence adapter and canonical LAB publication completed**; visual
obstacle/traversability quality, full-source realtime, navigation and actuation
remain disabled
## Decision
The TGS-only adapter now produces a complete, fail-closed metric evidence pack
for the ten RAVNOVES00 review anchors. It preserves the SLAM map gravity axis,
applies only a translation into the current local origin, never invokes AOS and
accounts for every point inside TRAVEL's declared `1–80 m` processing range.
This closes the representation and accounting gate. It does not accept visual
quality. Several anchors still contain a high non-ground share, so the next
gate is a 3D/costmap review of the sealed output rather than threshold tuning or
camera boxes. The metric output is now available in the canonical LAB on port
`8000` as an operator-review surface; publication does not upgrade it to an
accepted terrain provider.
## Frozen identity
| Item | Identity |
| --- | --- |
| TRAVEL source | `95dc2fbd66a343efd9060c45a5711b6307a950a4` |
| Mission Core source | `941fb8961693c7e1e122921842d547d9fe9bb94f` |
| Deterministic Worker artifact | `f52ea1995e973872ef4050de03fcef1aafa58a278d16506b05b8378102fd5781` |
| TRAVEL image | `sha256:7b412020f4d8392d1d1ed1b33beadc44140f0ea8f781e62dd69796042334300f` |
| CPU preparation/analysis image | `sha256:ceb13548617e4bd3f619766bfdff00af3fa5160946b367828da6d2233dcdcba0` |
| RAVNOVES00 LiDAR pack | `0685d24219d8236caf8b7f1685e93f6d6b59e7fd015a768d88a92bbe8b154944` |
| Executable profile | [`config/perception/m49-tgs-fail-closed-evidence-v1.json`](../../config/perception/m49-tgs-fail-closed-evidence-v1.json) |
The artifact used a new Worker release root and exact image IDs. The Worker
compiled a bounded TGS-only entrypoint against the pinned upstream headers; no
TRAVEL source file was changed. The entrypoint calls `TravelGroundSeg` and does
not construct or invoke `ObjectCluster`.
## Representation
Two source profiles were prepared for anchors `171`, `306`, `368`, `402`,
`450`, `509`, `525`, `744`, `1122` and `1856`:
- `current_increment`: exact current registered map increment;
- `causal_rolling_1s`: current and preceding one second of positive map
support, bounded to `12 m`, with no future frame.
For both profiles the current map position is subtracted, but the map axes are
not rotated into the handheld LiDAR orientation. This preserves the verified
SLAM gravity axis required by a terrain model. The previous sensor-frame probe
remains valid only for rejecting AOS's scan/ring assumptions; it is not used as
the TGS quality result.
The evidence pack has four explicit cell states:
| State | Meaning |
| --- | --- |
| `GROUND_SUPPORT` | positive TGS ground evidence |
| `NONGROUND_OCCUPIED` | every TGS non-ground point; no instance filter |
| `UNKNOWN_REJECTED` | exact in-range input complement not returned by TGS |
| `UNOBSERVED` | no positive evidence in the bounded local cell; never free by omission |
The local review grid is `0.45 m` with a `12 m` radius. State priority is
non-ground, rejected, ground, then unobserved. This is evidence precedence, not
a robot-footprint collision policy.
## Anchor result
Primary profile: `causal_rolling_1s`.
| Anchor | Ground points | Non-ground points | Rejected points | Ground cells | Non-ground cells | Unobserved cells |
| ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| `171` | `8,304` | `18,479` | `0` | `495` | `413` | `1,336` |
| `306` | `5,646` | `13,821` | `0` | `694` | `265` | `1,285` |
| `368` | `4,323` | `2,482` | `0` | `600` | `196` | `1,448` |
| `402` | `5,782` | `3,190` | `0` | `641` | `260` | `1,343` |
| `450` | `6,933` | `5,058` | `0` | `451` | `304` | `1,489` |
| `509` | `5,941` | `11,460` | `0` | `481` | `287` | `1,476` |
| `525` | `5,797` | `14,441` | `0` | `473` | `270` | `1,501` |
| `744` | `2,900` | `16,754` | `3` | `388` | `324` | `1,532` |
| `1122` | `2,422` | `3,403` | `0` | `599` | `228` | `1,417` |
| `1856` | `4,967` | `2,759` | `0` | `693` | `181` | `1,370` |
All eligible points across both profiles and all twenty anchor/profile records
are represented as ground, non-ground or rejected. A rejected point can share
a cell with higher-priority non-ground evidence; therefore the point count may
be non-zero while the final rejected-cell count is zero.
The gravity-aligned result is materially different from the diagnostic
sensor-frame run. At anchor `171`, rolling ground evidence increases from `626`
to `8,304` points. This confirms that TGS quality must not be judged in the
handheld sensor orientation.
The result is still not visually accepted. Anchors `171`, `509`, `525` and
especially `744` retain large non-ground populations. The 3D review must decide
whether these are correct buildings/vegetation/curbs outside the passage,
terrain-model overclassification inside the passage, or both.
## Timing and resources
| Measurement | Current increment | Causal rolling 1 s |
| --- | ---: | ---: |
| TGS p50 | `0.275 ms` | `1.378 ms` |
| TGS maximum | `0.327 ms` | `3.807 ms` |
- End-to-end Worker wrapper: `12.085689 s`, including source verification,
preparation, one C++ compile, twenty process starts, evidence packing and
analysis.
- Per-process wall p50: `20 ms`; p95: `40 ms`.
- Maximum process RSS: `9,292 KiB`.
- No GPU flag, CUDA/Vulkan context, image build or network access was used.
- Canonical Triton retained the same container ID and stayed
`running/healthy`.
- All exact candidate containers and the one-shot scheduled task were removed;
Worker free memory after cleanup was approximately `52.945 GiB`.
- Frigate, GAUSS/PlayCanvas and port `8000` were not modified.
## Sealed evidence
Worker root:
`D:\NDC_MISSIONCORE\runtime\results\m49-tgs-fail-closed\gravity-anchors-001`
| File | Bytes | SHA-256 |
| --- | ---: | --- |
| `evidence/result.json` | `9,137` | `0cfefcfa7e5e5ba015442446a830c7d0c0c93ac40b0b1e8888686590184c666a` |
| `evidence/evidence.npz` | `1,599,830` | `eaf1cd94cf7d3956e6bfebe4038ea18e6824fb6df5806e4c3b2d5b1c838f3b7b` |
| `worker-summary.json` | `975` | `ad8b2a23a46dd0c8039b07136fdb6e6cfe579fe6d4bbf3696ea2a16089174fbe` |
| `inputs/input-manifest.json` | `13,357` | `7a22e61606e1fa9ab61d47417f941b590a68b29e4529d4c290992ce7eb89a642` |
| `tgs-timing.tsv` | `638` | `a8f439996205286335ac392b37f51b54056d04e9cb07e03648c52c1e8f2f6ae8` |
## Canonical LAB publication
- LAB result:
`m49-tgs-fail-closed-9d5cb089bb5cc23f829acb47eda52eaa886db3b60f8fa7d57e02e27f642e837b`;
- linked current visual timeline:
`m4-threat-replay-2a953c5f27f2a5b1dddc5c658c1de2c323d7796084a099c024987a1da03aa324`;
- source LiDAR pack SHA-256 remains
`0685d24219d8236caf8b7f1685e93f6d6b59e7fd015a768d88a92bbe8b154944`;
- the earlier immutable seal beginning `200b57cc` is superseded only because it
linked the retired M4 v2 visual profile, whose timeline is incompatible with
the current v3 API. No Worker evidence byte or TGS result was changed.
The LAB reuses the canonical recorded-realtime camera and spatial instrument.
It exposes the ten anchors, the full classified TGS point set, the four-state
costmap, `VIDEO/CAMERA`, `3D/PLAN`, independent point/costmap visibility and
exact per-anchor counts. Camera proposal boxes, AOS clusters, future frames and
navigation authority are absent. Browser acceptance confirmed that every
operator control is independent and that a loading anchor cannot temporarily
display unrelated M4 obstacle statistics.
The published TGS coordinates are intentionally sensor-origin
`map-gravity-local`: the ground is therefore approximately `-1.25 m` below the
current LiDAR. The canonical LAB presentation converts these immutable values
to the scene's body-ground origin by adding the rig's sealed
`nominal_sensor_height_m = 1.25` after the gravity-to-body rotation. The same
offset is applied to point heights and costmap z-bounds; x/y evidence and the
sealed Worker result remain unchanged. A regression check across all ten
anchors puts the median displayed ground points in `-0.299…+0.135 m` and the
median ground-cell centres in `-0.308…+0.237 m`. On anchor `1856`, where the
presentation defect was reported, those medians are `+0.004 m` and `-0.063 m`.
The residual variation is measured terrain/pose variation, not the previous
systematic `1.25 m` lift.
## Preliminary corridor triage
The published evidence was also projected into the frozen virtual body
corridor (`-0.5…8.0 m` longitudinal, `±0.5 m` lateral) without changing the
sealed result. The table counts costmap cell centres inside that corridor:
| Anchor | Current occupied | Rolling occupied | Rolling unobserved | Preliminary reading |
| ---: | ---: | ---: | ---: | --- |
| `171` | `0` | `8` | `6` | unresolved rolling band at `5.63…7.90 m` |
| `306` | `0` | `0` | `12` | no corridor obstruction |
| `368` | `1` | `1` | `16` | compact occupied support at `6.34 m` |
| `402` | `0` | `1` | `10` | compact occupied support at `1.89 m` |
| `450` | `0` | `0` | `8` | no corridor obstruction |
| `509` | `0` | `0` | `7` | no corridor obstruction |
| `525` | `0` | `0` | `9` | no corridor obstruction |
| `744` | `0` | `3` | `25` | weakly observed; requires explicit review |
| `1122` | `0` | `2` | `24` | rolling recovers compact obstacle support |
| `1856` | `1` | `1` | `13` | compact occupied support at `4.14 m` |
This is a diagnostic, not ground truth. It shows why the causal window cannot
simply be removed: anchor `1122` is entirely unobserved in the current-only
corridor but gains ground and occupied evidence in rolling. It also identifies
the opposing risk: at anchor `171`, rolling adds eight occupied cells where the
camera/PLAN comparison appears to show an open route apart from side geometry.
Anchor `744` remains too weakly observed for acceptance. These two anchors keep
the visual gate open even though the remaining frames do not show a continuous
occupied carpet.
## Next gate
Review the published ten anchors in both metric `3D` and `PLAN`. The operator
must focus on the mandatory obstacle/gap cases and the large non-ground
populations listed above. Accept TGS for a full recorded-source shadow only if
red non-ground evidence remains on real geometry without carpeting the visible
route, soft traversable vegetation or usable gaps. Otherwise reject this
candidate without tuning thresholds against these ten anchors. Camera
projection follows only if the metric evidence is accepted.
@@ -0,0 +1,105 @@
# M4.9T5 full source-paced TRAVEL TGS shadow — 2026-08-26
Status: **recorded source-paced CPU shadow accepted; candidate retained**.
Visual traversability quality, full-graph performance, navigation and actuation
remain unaccepted.
## Decision
The gravity-aligned TGS-only candidate completed the entire `RAVNOVES00`
recorded timeline on Worker 006. Every source timeline frame and every eligible
LiDAR point is represented. No frame was dropped for capacity, AOS was never
invoked, no GPU was requested and the canonical Triton container remained
healthy with the same identity.
This accepts TGS as a bounded CPU shadow candidate. It does not yet make TGS a
navigation authority or prove that its occupied/ground interpretation is
correct for vegetation, terrain, gaps or the future vehicle envelope.
## Frozen identity
| Item | Identity |
| --- | --- |
| LAB result | `m49-tgs-full-shadow-ef98de7db7596d48e8c8c0549ce68e6704ee03e87c8c4bcf1e3e748b7ccb032e` |
| Worker run | `Worker 006 / ravnoves00-full-001` |
| Mission Core revision used by Worker | `40c850b167dda366d8aa45d828520168affaf9fd` |
| Deterministic Worker artifact | `5e0ea16c7a5cc760463836718b0cd8b0006ffc4b202e5f706a20a86ef2f912ab` |
| Source pack SHA-256 | `0685d24219d8236caf8b7f1685e93f6d6b59e7fd015a768d88a92bbe8b154944` |
| TGS config SHA-256 | `c2e07010aaee78259d36c057962d6bfb885349251ff7356d867e5813e632881c` |
| Linked visual result | `m4-threat-replay-2a953c5f27f2a5b1dddc5c658c1de2c323d7796084a099c024987a1da03aa324` |
| Linked semantic result | `e47-semantic-slam-f8c53ba12e9719856195890c7dcd2ba6c114434674159f948064368012e6b3bc` |
The profile is a `0.45 m`, `12 m` radius, `1 s` causal rolling
`map-gravity-local` costmap. The four states remain separate:
`GROUND_SUPPORT`, `NONGROUND_OCCUPIED`, `UNKNOWN_REJECTED` and `UNOBSERVED`.
## Full-run result
| Measurement | Result |
| --- | ---: |
| Timeline | `4,489 / 4,489` frames |
| LiDAR available / missing | `3,928 / 561` |
| Recorded duration / effective rate | `448.623 s / 10.003945 FPS` |
| Eligible points | `63,646,163` |
| Ground / non-ground / rejected | `16,579,467 / 47,046,815 / 19,881` |
| Unaccounted points | `0` |
| TGS p50 / p95 / p99 / max | `1.190 / 1.694 / 2.08973 / 9.106 ms` |
| Completion age p50 / p95 / p99 / max | `17.920792 / 29.140775 / 41.428455 / 659.980295 ms` |
| Capacity drops | `0` |
| Worker wrapper wall time | `569.401084 s` |
All formal source-paced, point-accounting and capacity gates passed. The
completion-age maximum is retained as an outlier; the accepted gate is the
measured `p99 = 41.43 ms`, not the maximum.
## Fail-closed behavior
The `561` timeline frames without a LiDAR sample are not removed, interpolated
or copied from the preceding frame. Each is emitted as a complete `2,244`-cell
costmap with all states explicitly `UNOBSERVED`, zero occupied cells and zero
source points. This preserves chronology without inventing free space.
## Canonical LAB publication and UI acceptance
The immutable result is published on the canonical Mission Core service at
port `8000`. The LAB reuses the recorded camera timeline and exposes independent
`SOURCE POINTS`, `LOCAL SLAM`, `TGS COSTMAP`, `SEMANTICS`, `3D` and `PLAN`
controls. The semantic mask is the exact E47 full-route archive bound to the
same M4 camera result; it remains diagnostic and does not change TGS states.
The first implementation fetched one large JSON frame at a time. Browser QA
showed that this was only intermittently exact at `1×`. The published viewer
therefore uses immutable chunks of `24` spatial frames and prefetches the next
chunk. A final `1×` browser acceptance sampled twenty consecutive positions
across chunk boundaries: all twenty displayed the exact TGS frame and none
showed the loading placeholder. A real missing-LiDAR frame was separately
accepted with `0 source points`, `2,244 unobserved`, `0 occupied` and no retained
previous cloud.
A follow-up playback acceptance fixed a UI lifecycle defect that remounted the
Three.js scene while the next TGS frame crossed the React buffer boundary. The
scene now retains the last sealed classified frame until the exact next frame
arrives, so orbit controls and the operator-selected view survive continuous
playback. Twelve consecutive `250 ms` playback samples kept one mounted 3D
canvas, a visible bounded `LOCAL SLAM` surface and the synchronized E47 video
mask. Pausing is no longer required to rotate, pan or zoom the 3D view.
## Sealed evidence
| File | Bytes | SHA-256 |
| --- | ---: | --- |
| `costmap-states.npy` | `10,073,444` | `4173f8b6b743755d6d6cbd863c433fe44853974bdd4898002e08574a65783a1a` |
| `costmap-z-bounds-m.npy` | `80,586,656` | `f8d68b5ca09860ef857a22e15e114bbeabde32492d5c821ee59dfa62ae40373a` |
| `frames.ndjson` | `1,178,262` | `878e20806d55cdc1fa16692397e11a0e56af760a83c173c8fc73319a39b0e779` |
| `worker-summary.json` | `1,076` | `487d4be6f3d724bca647aa2e48ba89e7ffd0c78f1bfc5e7e5a01e1db82379dc7` |
## Next gate
Attach this unchanged TGS stage to the realtime world-state graph in shadow
mode alongside the frozen camera semantic-risk provider. Measure complete-graph
FPS, end-to-end latency, CPU/GPU/VRAM and queue drops against the accepted
baseline. Keep navigation authority off. In parallel, review the full camera +
source cloud + TGS timeline for false occupied carpets, missed compact
obstacles, vegetation and usable gaps. Only the combination of acceptable
visual behavior and acceptable integrated-graph regression can promote the
candidate beyond shadow.
@@ -0,0 +1,123 @@
# M4.9T5 integrated TGS + reference graph shadow — 2026-08-27
Status: **complete source-paced integrated shadow accepted**. Integrated
runtime capacity is accepted for the frozen RAVNOVES00 graph. Visual
traversability quality, physical free space, navigation, actuation and
production remain unaccepted.
## Decision
The unchanged gravity-aligned TGS candidate and the frozen RF-DETR
world-state graph completed the same `4,489`-frame RAVNOVES00 timeline from a
shared `12 Hz` start barrier on Worker 006. Every graph frame was delivered,
every TGS frame was accounted, all queues remained bounded and no capacity
drop or supersession occurred.
The integrated graph sustained `11.860865 FPS`, compared with the frozen
reference result at `11.79902 FPS`. TGS added `1.735 ms` at p95 and used at
most `22.6 MiB` of container memory; it did not add a GPU model. The exact
per-frame join of graph and TGS outputs measured `46.825886 ms` at p95 and
`63.94485 ms` at p99. This accepts the runtime integration gate without
changing any navigation or safety authority.
## Frozen identity
| Item | Identity |
| --- | --- |
| Integrated result | `m49-tgs-integrated-graph-shadow-75e48fd8acf0246be16613e087fcf26fe909f7834718d217c74785a57f494b24` |
| Worker run | `Worker 006 / ravnoves00-integrated-12fps-003` |
| Mission Core revision used by Worker | `f153d671d316ea7b2200c0edcb80206d896ffcd5` |
| Deterministic Worker artifact | `1818ea57887db819e9d986713b050bd15eae1a43ee4af70c5b8b5d39f4cd8522` |
| Worker wheel | `b9414a1f2f1998543aeb63824d3cdfe55bbc6e4eecc3146d65962ebc7da86b8b` |
| TGS native binary | `2c3b4c7e6b66d19d2e32ee3ef867fa8c14073be29441bcd19fe565d9632c6538` |
| Integrated profile | `b61e018b2d04eec58802e2d4186ce7a3dd3a15b254db106b57b609e903eeef80` |
| Frozen graph config | `1db6f6fa0561d819505a5e4f62fe256dab6fe4d9da6b4f1a9a5073bfcf772c90` |
| Frozen TGS profile | `c2e07010aaee78259d36c057962d6bfb885349251ff7356d867e5813e632881c` |
| Linked accepted TGS result | `m49-tgs-full-shadow-ef98de7db7596d48e8c8c0549ce68e6704ee03e87c8c4bcf1e3e748b7ccb032e` |
The release was built from `git archive` of the declared revision. Uncommitted
GAUSS and PlayCanvas files were neither packaged nor changed by the run.
## Full-run result
| Measurement | Result |
| --- | ---: |
| Graph admitted / delivered | `4,489 / 4,489` |
| TGS timeline accounted | `4,489 / 4,489` |
| LiDAR available | `3,928` |
| Delivery ratio | `1.0` |
| Capacity drops / supersessions / failures | `0 / 0 / 0` |
| Effective world-state rate | `11.860865 FPS` |
| Frozen reference rate | `11.79902 FPS` |
| World-state completion p50 / p95 / p99 / max | `28.437492 / 46.696395 / 63.94485 / 170.202343 ms` |
| Joined graph+TGS output p50 / p95 / p99 / max | `28.57367 / 46.825886 / 63.94485 / 170.202343 ms` |
| TGS stage p50 / p95 / p99 / max | `1.186 / 1.735 / 2.14019 / 6.151 ms` |
| TGS completion p50 / p95 / p99 / max | `17.605461 / 31.771832 / 42.957350 / 70.851977 ms` |
| Queue high-water marks | `2` for every bounded stage |
| Worker wrapper wall time | `544.887447 s` |
All nineteen formal identity, accounting, throughput, latency, telemetry,
capacity and false-authority checks passed.
## Resource envelope
| Resource | Result |
| --- | ---: |
| GPU utilization mean / p95 / max | `43.114973 / 53 / 57 %` |
| GPU memory mean / p95 / max | `9,737.144 / 9,744 / 9,751 MiB` |
| GPU power p95 / max | `149.342 / 153.81 W` |
| TGS CPU mean / p95 / max | `3.474793 / 5.27 / 6.04 %` |
| TGS memory mean / p95 / max | `20.571818 / 21.84 / 22.6 MiB` |
| Graph container memory max | `1,769.472 MiB` |
| Triton container memory max | `582.9 MiB` |
The measured GPU envelope belongs to the already accepted reference graph.
TGS is a CPU-only stage and does not request CUDA, Vulkan or a second model.
## LAB publication boundary
No third visual viewer is created for this gate. The TGS cells, source points,
camera frames and semantic archive are byte-identical to the already published
`m49-tgs-full-shadow` LAB. This run proves concurrent runtime behavior, so its
new evidence is the immutable timing, queue and resource report above. Visual
quality review continues in the existing full-shadow LAB.
## Runtime recovery finding
Preflight found the canonical `ndc-mission-core-triton` container stopped after
the Docker runtime restarted. Docker reported exit code `255`, no OOM event and
Compose configured `restart: "no"`. The accepted run began only after the exact
same container was started and healthy; its container ID remained
`4232fb040062a8384809e73612baa92b343ddcb60f00c23e7091eb4909959223`
throughout the run.
This is a deployment-lifecycle defect, not a perception overload result. The
container must use a durable restart policy and an unhealthy-state watchdog;
changing the TGS or detector graph cannot fix it.
The live container was subsequently changed in place to
`restart: unless-stopped`. Docker confirmed the service remained `running` and
`healthy` with the same container ID, so applying the mitigation caused no
inference restart. This protects the current container from process and Docker
daemon restarts. The versioned Worker Compose source still declares
`restart: "no"`; closing that source-level drift and inducing a controlled
recovery remain a separate deployment gate.
## Sealed compact evidence
| File | SHA-256 |
| --- | --- |
| `result.json` | `bfc881aa943d46582a248b6024a413c3c4b3a6959fa4a674f6ec33d658fdcbdf` |
| `graph-result.json` | `784d27b908137843f6adf08d267576a74b0676f5f98226923399ae59f538b78a` |
| `tgs-result.json` | `c0245744892b66c7f24af3cd7ea9c8cd8d543114fffa70e32d8d5fef81054716` |
| `worker-summary.json` | `c449246d14fb927b6490bcf657950b354f101fd3eb4b296bc0f6dd8543aafe10` |
## Next gate
Keep this integrated graph frozen. Complete the independent visual
traversability review in the existing TGS LAB, concentrating on vegetation,
compact obstacles, false occupied carpets, usable gaps and missing-LiDAR
behavior. In parallel, close the Worker inference-runtime restart policy and
verify an induced stop/recovery without changing result identity. Only after
both gates pass may the candidate advance to repeated-loop soak and recovery
acceptance; navigation and actuation remain off.
@@ -67,6 +67,27 @@ AUTHORITY: Final = {
LOAD_PURPOSES: Final = ("production-rate", "reserve-gate", "limit-discovery")
def wait_for_shared_start(
*,
ready_file: Path,
start_file: Path,
timeout_seconds: float,
) -> None:
"""Join an external source-admission barrier after local warmup is complete."""
if timeout_seconds <= 0:
raise RuntimeError("shared-start timeout must be positive")
if ready_file.exists():
raise RuntimeError("shared-start ready file already exists")
ready_file.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
ready_file.write_text("ready\n", encoding="utf-8")
deadline = time.monotonic() + timeout_seconds
while not start_file.is_file():
if time.monotonic() >= deadline:
raise RuntimeError("shared-start barrier timed out")
time.sleep(0.01)
class GpuTelemetry:
def __init__(self, interval_seconds: float) -> None:
self.interval_seconds = interval_seconds
@@ -394,6 +415,9 @@ def main() -> int:
parser.add_argument("--runtime-artifact-sha256", required=True)
parser.add_argument("--runner-sha256", required=True)
parser.add_argument("--telemetry-interval-seconds", type=float, default=1.0)
parser.add_argument("--shared-start-ready-file", type=Path)
parser.add_argument("--shared-start-file", type=Path)
parser.add_argument("--shared-start-timeout-seconds", type=float, default=600.0)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--progress", type=Path, required=True)
parser.add_argument("--frame-ledger", type=Path, required=True)
@@ -404,6 +428,17 @@ def main() -> int:
raise RuntimeError("maximum frame count must be positive")
if arguments.telemetry_interval_seconds <= 0:
raise RuntimeError("telemetry interval must be positive")
shared_start_requested = (
arguments.shared_start_ready_file is not None or arguments.shared_start_file is not None
)
if shared_start_requested and (
arguments.shared_start_ready_file is None or arguments.shared_start_file is None
):
raise RuntimeError("shared-start ready and start files must be configured together")
if shared_start_requested and arguments.loops != 1:
raise RuntimeError("shared-start barrier requires exactly one loop")
if arguments.shared_start_timeout_seconds <= 0:
raise RuntimeError("shared-start timeout must be positive")
if arguments.source_rate_hz is not None and (
not np.isfinite(arguments.source_rate_hz) or arguments.source_rate_hz <= 0
):
@@ -506,6 +541,12 @@ def main() -> int:
)
detector_warmup = runtime.warm_up_detector()
source_prefetch = runtime.prepare_source()
if shared_start_requested:
wait_for_shared_start(
ready_file=arguments.shared_start_ready_file,
start_file=arguments.shared_start_file,
timeout_seconds=arguments.shared_start_timeout_seconds,
)
gc_policy = CyclicGcHotLoopPolicy()
with gc_policy:
runtime.mark_source_admission_started()
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
"""Publish the verified Worker TGS pack as immutable Mission Core LAB evidence."""
from __future__ import annotations
import argparse
from pathlib import Path
from k1link.laboratory.m49_tgs_fail_closed import (
M49TgsFailClosedError,
seal_m49_tgs_fail_closed,
)
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--source-root", type=Path, required=True)
parser.add_argument("--destination-root", type=Path, required=True)
parser.add_argument("--profile", type=Path, required=True)
parser.add_argument("--linked-visual-result-id", required=True)
arguments = parser.parse_args()
try:
result = seal_m49_tgs_fail_closed(
source_root=arguments.source_root,
destination_root=arguments.destination_root,
profile_path=arguments.profile,
linked_visual_result_id=arguments.linked_visual_result_id,
)
except (M49TgsFailClosedError, OSError, ValueError) as exc:
parser.error(str(exc))
print(result.result_id)
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,492 @@
[CmdletBinding()]
param(
[ValidateSet("Start", "Run", "Status", "Manifest", "VerifyModels", "Verify")]
[string]$Mode = "Status",
[string]$Root = "D:\NDC_MISSIONCORE\datasets\vegetation-v1\observed-2026-08-27",
[int]$DownloadLimitMiB = 30
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$canonicalPrefix = "D:\NDC_MISSIONCORE\datasets\"
if (-not $Root.StartsWith($canonicalPrefix, [System.StringComparison]::OrdinalIgnoreCase)) {
throw "LAB V1 assets must stay under $canonicalPrefix"
}
if ($DownloadLimitMiB -lt 1 -or $DownloadLimitMiB -gt 100) {
throw "DownloadLimitMiB must be between 1 and 100"
}
$stateRoot = "D:\NDC_MISSIONCORE\datasets\state\lab-v1-vegetation"
$toolRoot = "D:\NDC_MISSIONCORE\datasets\tooling\aria2-1.37.0"
$ariaArchive = Join-Path $toolRoot "aria2-1.37.0-win-64bit-build1.zip"
$ariaExecutable = Join-Path $toolRoot "aria2-1.37.0-win-64bit-build1\aria2c.exe"
$ariaUrl = "https://github.com/aria2/aria2/releases/download/release-1.37.0/aria2-1.37.0-win-64bit-build1.zip"
$taskName = "MissionCore-LabV1-VegetationAssets"
$stagePath = Join-Path $stateRoot "download-stage.json"
$assetManifestPath = Join-Path $stateRoot "asset-manifest.json"
$verificationPath = Join-Path $stateRoot "verification.jsonl"
$bulkVerificationPath = Join-Path $stateRoot "bulk-verification.json"
$gooseInputPath = Join-Path $stateRoot "goose-and-models.input.txt"
$wildScenesInputPath = Join-Path $stateRoot "wildscenes-61541v003.input.txt"
$orfdTorrentPath = Join-Path $stateRoot "orfd-ec5ccf4b8e49271ee3b63660383facf43063f2f2.torrent"
$wildScenesDataFileCount = 76525
$wildScenesMetadataFileCount = 4
$wildScenesInputEntryCount = $wildScenesDataFileCount + $wildScenesMetadataFileCount
$orfdTorrentExpectedBytes = 4918171
$orfdTorrentSha256 = "b886dc76d169c924d619ff07baf8a0cd2364ca6f9c4b36d8777f2971fd78bc92"
$orfdPublishedFileCount = 70983
$orfdPublishedSizeBytes = 45941056780
$wildScenesPublishedSizeBytes = 196358841020
$assets = @(
[pscustomobject]@{ group = "models"; id = "goose-ppliteseg-category-512"; url = "https://goose-dataset.de/models/ppliteseg_category_512.pth"; relative_path = "models\goose\ppliteseg_category_512.pth"; expected_bytes = $null },
[pscustomobject]@{ group = "models"; id = "goose-ppliteseg-class-512"; url = "https://goose-dataset.de/models/ppliteseg_class_512.pth"; relative_path = "models\goose\ppliteseg_class_512.pth"; expected_bytes = $null },
[pscustomobject]@{ group = "models"; id = "goose-ddrnet-category-512"; url = "https://goose-dataset.de/models/ddrnet_category_512.pth"; relative_path = "models\goose\ddrnet_category_512.pth"; expected_bytes = $null },
[pscustomobject]@{ group = "models"; id = "goose-ddrnet-class-512"; url = "https://goose-dataset.de/models/ddrnet_class_512.pth"; relative_path = "models\goose\ddrnet_class_512.pth"; expected_bytes = $null },
[pscustomobject]@{ group = "models"; id = "rellis-hrnet-ocr"; url = "https://drive.usercontent.google.com/download?id=137Lfw6HcDmdEReu_R7Q_I-zmRvvqFys3&export=download&confirm=t"; relative_path = "models\rellis-3d\hrnet_best.zip"; expected_bytes = 788183119 },
[pscustomobject]@{ group = "models"; id = "rellis-gscnn"; url = "https://drive.usercontent.google.com/download?id=1Z8OlstkdzDrY9k-yxMQmVB192ac8j4MD&export=download&confirm=t"; relative_path = "models\rellis-3d\gscnn_best.pth"; expected_bytes = 1098515419 },
[pscustomobject]@{ group = "models"; id = "rellis-salsanext"; url = "https://drive.usercontent.google.com/download?id=1DxuzlnFKnU8EpSuODRywJUrJlieUUheg&export=download&confirm=t"; relative_path = "models\rellis-3d\salsanext_best.zip"; expected_bytes = 164312336 },
[pscustomobject]@{ group = "models"; id = "rellis-kpconv"; url = "https://drive.usercontent.google.com/download?id=1Exrt4yWDhgucx_vr08hAuXTcaLUcpXXm&export=download&confirm=t"; relative_path = "models\rellis-3d\kpconv_best.zip"; expected_bytes = 1522250650 },
[pscustomobject]@{ group = "models"; id = "wildscenes-models"; url = "https://www.dropbox.com/scl/fo/1za8yjfj0dl8fsddbl9ns/AAZ4xc2PkwcPc14sjY_GhVA?rlkey=3yqq5lpiicjtqqau4cljl66y5&st=fwhd1l86&dl=1"; relative_path = "models\wildscenes\WildScenes-Models.zip"; expected_bytes = 8637507567 },
[pscustomobject]@{ group = "goose"; id = "goose-2d-train"; url = "https://goose-dataset.de/storage/goose_2d_train.zip"; relative_path = "goose-2d\archives\goose_2d_train.zip"; expected_bytes = $null },
[pscustomobject]@{ group = "goose"; id = "goose-2d-val"; url = "https://goose-dataset.de/storage/goose_2d_val.zip"; relative_path = "goose-2d\archives\goose_2d_val.zip"; expected_bytes = $null },
[pscustomobject]@{ group = "goose"; id = "goose-2d-test"; url = "https://goose-dataset.de/storage/goose_2d_test.zip"; relative_path = "goose-2d\archives\goose_2d_test.zip"; expected_bytes = $null },
[pscustomobject]@{ group = "goose"; id = "goose-3d-train"; url = "https://goose-dataset.de/storage/goose_3d_train.zip"; relative_path = "goose-3d\archives\goose_3d_train.zip"; expected_bytes = $null },
[pscustomobject]@{ group = "goose"; id = "goose-3d-test"; url = "https://goose-dataset.de/storage/goose_3d_test.zip"; relative_path = "goose-3d\archives\goose_3d_test.zip"; expected_bytes = $null },
[pscustomobject]@{ group = "goose"; id = "goose-ex-2d-train"; url = "https://goose-dataset.de/storage/gooseEx_2d_train.zip"; relative_path = "goose-ex-2d\archives\gooseEx_2d_train.zip"; expected_bytes = $null },
[pscustomobject]@{ group = "goose"; id = "goose-ex-2d-val"; url = "https://goose-dataset.de/storage/gooseEx_2d_val.zip"; relative_path = "goose-ex-2d\archives\gooseEx_2d_val.zip"; expected_bytes = $null },
[pscustomobject]@{ group = "goose"; id = "goose-ex-2d-test"; url = "https://goose-dataset.de/storage/gooseEx_2d_test.zip"; relative_path = "goose-ex-2d\archives\gooseEx_2d_test.zip"; expected_bytes = $null },
[pscustomobject]@{ group = "goose"; id = "goose-ex-3d-train"; url = "https://goose-dataset.de/storage/gooseEx_3d_train.zip"; relative_path = "goose-ex-3d\archives\gooseEx_3d_train.zip"; expected_bytes = $null },
[pscustomobject]@{ group = "goose"; id = "goose-ex-3d-val"; url = "https://goose-dataset.de/storage/gooseEx_3d_val.zip"; relative_path = "goose-ex-3d\archives\gooseEx_3d_val.zip"; expected_bytes = $null },
[pscustomobject]@{ group = "goose"; id = "goose-ex-3d-test"; url = "https://goose-dataset.de/storage/gooseEx_3d_test.zip"; relative_path = "goose-ex-3d\archives\gooseEx_3d_test.zip"; expected_bytes = $null }
)
function Write-JsonFile {
param([string]$Path, [object]$Value)
$parent = Split-Path -Parent $Path
New-Item -ItemType Directory -Force -Path $parent | Out-Null
$json = $Value | ConvertTo-Json -Depth 12
[System.IO.File]::WriteAllText($Path, $json + [Environment]::NewLine, [System.Text.UTF8Encoding]::new($false))
}
function Set-Stage {
param([string]$Stage, [string]$Status, [string]$Detail)
Write-JsonFile -Path $stagePath -Value ([ordered]@{
schema_version = "missioncore.lab-v1-asset-download-stage/v1"
observed_at_utc = [DateTime]::UtcNow.ToString("o")
worker_id = "worker-006"
stage = $Stage
status = $Status
detail = $Detail
})
}
function Invoke-CurlDownload {
param([string]$Url, [string]$Destination)
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $Destination) | Out-Null
& curl.exe --location --fail --retry 8 --retry-all-errors --continue-at - --output $Destination $Url
if ($LASTEXITCODE -ne 0) {
throw "curl failed for $Url with exit code $LASTEXITCODE"
}
}
function Test-AriaInputList {
param([string]$Path, [int]$ExpectedEntries)
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
return $false
}
$urlCount = 0
$dirCount = 0
foreach ($line in [System.IO.File]::ReadLines($Path)) {
if ($line.StartsWith("https://", [System.StringComparison]::OrdinalIgnoreCase)) {
$urlCount++
}
elseif ($line.StartsWith(" dir=", [System.StringComparison]::Ordinal)) {
$dirCount++
}
}
return $urlCount -eq $ExpectedEntries -and $dirCount -eq $ExpectedEntries
}
function Test-FileIntegrity {
param([string]$Path, [long]$ExpectedBytes, [string]$ExpectedSha256)
$file = Get-Item -LiteralPath $Path -ErrorAction SilentlyContinue
if ($null -eq $file -or $file.Length -ne $ExpectedBytes) {
return $false
}
$actualSha256 = (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
return $actualSha256 -eq $ExpectedSha256
}
function Initialize-Aria2 {
New-Item -ItemType Directory -Force -Path $toolRoot | Out-Null
if (-not (Test-Path -LiteralPath $ariaExecutable -PathType Leaf)) {
Invoke-CurlDownload -Url $ariaUrl -Destination $ariaArchive
Expand-Archive -LiteralPath $ariaArchive -DestinationPath $toolRoot -Force
}
if (-not (Test-Path -LiteralPath $ariaExecutable -PathType Leaf)) {
throw "aria2 executable was not installed at $ariaExecutable"
}
}
function Write-DirectInput {
$lines = [System.Collections.Generic.List[string]]::new()
foreach ($asset in $assets) {
$destination = Join-Path $Root $asset.relative_path
New-Item -ItemType Directory -Force -Path (Split-Path -Parent $destination) | Out-Null
$lines.Add([string]$asset.url)
$lines.Add(" dir=" + (Split-Path -Parent $destination))
$lines.Add(" out=" + (Split-Path -Leaf $destination))
}
[System.IO.File]::WriteAllLines($gooseInputPath, $lines, [System.Text.UTF8Encoding]::new($false))
}
function Invoke-AriaInput {
param([string]$InputPath, [int]$ConcurrentDownloads, [int]$ConnectionsPerServer)
$downloadLimit = "{0}M" -f $DownloadLimitMiB
& $ariaExecutable `
--input-file=$InputPath `
--continue=true `
--max-concurrent-downloads=$ConcurrentDownloads `
--max-connection-per-server=$ConnectionsPerServer `
--split=$ConnectionsPerServer `
--min-split-size=16M `
"--max-overall-download-limit=$downloadLimit" `
--file-allocation=none `
--disk-cache=64M `
--auto-file-renaming=false `
--allow-overwrite=false `
--retry-wait=5 `
--max-tries=0 `
--summary-interval=60 `
--console-log-level=notice
if ($LASTEXITCODE -ne 0) {
throw "aria2 input run failed for $InputPath with exit code $LASTEXITCODE"
}
}
function Get-DownloadTask {
return Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
}
function Get-AssetSnapshot {
$rows = foreach ($asset in $assets) {
$path = Join-Path $Root $asset.relative_path
$file = Get-Item -LiteralPath $path -ErrorAction SilentlyContinue
$partial = Get-Item -LiteralPath ($path + ".aria2") -ErrorAction SilentlyContinue
[ordered]@{
id = $asset.id
group = $asset.group
relative_path = $asset.relative_path
status = if ($null -ne $file -and $null -eq $partial) { "downloaded" } elseif ($null -ne $file) { "partial" } else { "pending" }
size_bytes = if ($null -ne $file) { $file.Length } else { 0 }
expected_bytes = $asset.expected_bytes
}
}
return @($rows)
}
function Invoke-Verify {
param([ValidateSet("All", "Models")][string]$Scope = "All")
New-Item -ItemType Directory -Force -Path $stateRoot | Out-Null
$writer = [System.IO.StreamWriter]::new($verificationPath, $false, [System.Text.UTF8Encoding]::new($false))
try {
foreach ($asset in $assets) {
if ($Scope -eq "Models" -and $asset.group -ne "models") {
continue
}
$path = Join-Path $Root $asset.relative_path
if (-not (Test-Path -LiteralPath $path -PathType Leaf)) {
continue
}
if (Test-Path -LiteralPath ($path + ".aria2") -PathType Leaf) {
throw "Cannot verify partial asset $($asset.id)"
}
$file = Get-Item -LiteralPath $path
$hash = Get-FileHash -LiteralPath $path -Algorithm SHA256
$sizeMatches = $null
if ($null -ne $asset.expected_bytes) {
$sizeMatches = $file.Length -eq [long]$asset.expected_bytes
if (-not $sizeMatches) {
throw "Size mismatch for $($asset.id): expected $($asset.expected_bytes), got $($file.Length)"
}
}
$row = [ordered]@{
schema_version = "missioncore.lab-v1-asset-verification/v1"
observed_at_utc = [DateTime]::UtcNow.ToString("o")
id = $asset.id
relative_path = $asset.relative_path
size_bytes = $file.Length
expected_bytes = $asset.expected_bytes
size_matches = $sizeMatches
sha256 = $hash.Hash.ToLowerInvariant()
source_url = $asset.url
}
$writer.WriteLine(($row | ConvertTo-Json -Compress -Depth 8))
$writer.Flush()
}
$offnetRoot = Join-Path $Root "models\orfd-offnet"
if (Test-Path -LiteralPath $offnetRoot -PathType Container) {
Get-ChildItem -LiteralPath $offnetRoot -File -Recurse | Sort-Object FullName | ForEach-Object {
$row = [ordered]@{
schema_version = "missioncore.lab-v1-asset-verification/v1"
observed_at_utc = [DateTime]::UtcNow.ToString("o")
id = "orfd-offnet/" + $_.FullName.Substring($offnetRoot.Length + 1).Replace("\", "/")
relative_path = $_.FullName.Substring($Root.Length + 1)
size_bytes = $_.Length
expected_bytes = $null
size_matches = $null
sha256 = (Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
source_url = "https://drive.google.com/drive/folders/1lnm2M1HEkVs9W3-FSEX3ddE9GYz4rqCU"
}
$writer.WriteLine(($row | ConvertTo-Json -Compress -Depth 8))
$writer.Flush()
}
}
}
finally {
$writer.Dispose()
}
}
function Measure-PayloadTree {
param([string]$Path)
[long]$bytes = 0
[long]$fileCount = 0
[long]$partialCount = 0
Get-ChildItem -LiteralPath $Path -File -Recurse | ForEach-Object {
if ($_.Name.EndsWith(".aria2", [System.StringComparison]::OrdinalIgnoreCase)) {
$partialCount++
}
else {
$fileCount++
$bytes += $_.Length
}
}
return [ordered]@{ file_count = $fileCount; size_bytes = $bytes; partial_count = $partialCount }
}
function Invoke-BulkVerify {
$wildScenesRoot = Join-Path $Root "wildscenes"
$orfdRoot = Join-Path $Root "orfd"
$wildScenes = Measure-PayloadTree -Path $wildScenesRoot
$orfd = Measure-PayloadTree -Path $orfdRoot
if (
$wildScenes.file_count -ne $wildScenesInputEntryCount -or
$wildScenes.size_bytes -ne $wildScenesPublishedSizeBytes -or
$wildScenes.partial_count -ne 0
) {
throw "WildScenes tree failed count/size/partial verification"
}
if (
$orfd.file_count -ne $orfdPublishedFileCount -or
$orfd.size_bytes -ne $orfdPublishedSizeBytes -or
$orfd.partial_count -ne 0
) {
throw "ORFD tree failed count/size/partial verification"
}
Write-JsonFile -Path $bulkVerificationPath -Value ([ordered]@{
schema_version = "missioncore.lab-v1-bulk-verification/v1"
observed_at_utc = [DateTime]::UtcNow.ToString("o")
worker_id = "worker-006"
wildscenes = $wildScenes
orfd = $orfd
canonical_triton_action = "none"
})
}
function Write-AssetManifest {
if (-not (Test-AriaInputList -Path $wildScenesInputPath -ExpectedEntries $wildScenesInputEntryCount)) {
throw "WildScenes input list is not admitted"
}
if (-not (Test-FileIntegrity -Path $orfdTorrentPath -ExpectedBytes $orfdTorrentExpectedBytes -ExpectedSha256 $orfdTorrentSha256)) {
throw "ORFD torrent source is not admitted"
}
Write-JsonFile -Path $assetManifestPath -Value ([ordered]@{
schema_version = "missioncore.lab-v1-asset-manifest/v1"
observed_at_utc = [DateTime]::UtcNow.ToString("o")
worker_id = "worker-006"
storage_policy = "worker-d-only"
root = $Root
runtime_models_mutated = $false
canonical_triton_action = "none"
existing_admitted_assets = @(
[ordered]@{ id = "goose-3d-val"; path = "D:\NDC_MISSIONCORE\datasets\goose-3d\v2025-08-22\archives\goose_3d_val.zip"; sha256 = "0be9e0f8459bafcbc92ff7c3cc366e9b4e2f6e1e9bdf50e6439e1557e864c26f" },
[ordered]@{ id = "rellis-3d-v1.1"; path = "D:\NDC_MISSIONCORE\datasets\rellis-3d\v1.1"; admission_identity = "a3f3f161a5a7edccdf66cea75ecf9004b8f6a895282faabe661c075c15a82774" }
)
direct_assets = $assets
wildscenes = [ordered]@{ collection_id = 63154; version = 3; published_size_bytes = $wildScenesPublishedSizeBytes; data_file_count = $wildScenesDataFileCount; metadata_file_count = $wildScenesMetadataFileCount; input_entry_count = $wildScenesInputEntryCount; url_list = $wildScenesInputPath; url_list_sha256 = (Get-FileHash -LiteralPath $wildScenesInputPath -Algorithm SHA256).Hash.ToLowerInvariant() }
orfd = [ordered]@{ info_hash = "ec5ccf4b8e49271ee3b63660383facf43063f2f2"; published_size_bytes = $orfdPublishedSizeBytes; published_file_count = $orfdPublishedFileCount; torrent = $orfdTorrentPath; torrent_size_bytes = $orfdTorrentExpectedBytes; torrent_sha256 = $orfdTorrentSha256 }
offnet_weights = [ordered]@{ source_folder = "https://drive.google.com/drive/folders/1lnm2M1HEkVs9W3-FSEX3ddE9GYz4rqCU"; relative_path = "models\orfd-offnet"; downloader = "gdown==5.2.0"; isolated_image = "nvcr.io/nvidia/tritonserver@sha256:58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794"; runtime_models_mutated = $false }
})
}
New-Item -ItemType Directory -Force -Path $Root, $stateRoot | Out-Null
switch ($Mode) {
"Start" {
$existingTask = Get-DownloadTask
if ($null -ne $existingTask -and $existingTask.State -eq "Running") {
throw "LAB V1 downloader task is already running"
}
$drive = Get-PSDrive -Name D
$currentUsage = Measure-PayloadTree -Path $Root
[long]$remainingPayloadBytes = 360GB - [long]$currentUsage.size_bytes
if ($remainingPayloadBytes -lt 0) {
$remainingPayloadBytes = 0
}
[long]$requiredFreeBytes = 10GB + $remainingPayloadBytes
if ($drive.Free -lt $requiredFreeBytes) {
throw "Worker D: has only $([math]::Round($drive.Free / 1GB, 2)) GiB free; $([math]::Round($requiredFreeBytes / 1GB, 2)) GiB is required for remaining payload plus reserve"
}
Set-Stage -Stage "preflight" -Status "running" -Detail "Installing bounded downloader and generating immutable source lists"
Initialize-Aria2
Write-DirectInput
if (-not (Test-AriaInputList -Path $wildScenesInputPath -ExpectedEntries $wildScenesInputEntryCount)) {
Invoke-CurlDownload -Url "https://data.csiro.au/dap/ws/v2/collections/63154/files/list" -Destination $wildScenesInputPath
}
if (-not (Test-AriaInputList -Path $wildScenesInputPath -ExpectedEntries $wildScenesInputEntryCount)) {
throw "WildScenes input list is incomplete: expected $wildScenesInputEntryCount URL/dir entries"
}
if (-not (Test-FileIntegrity -Path $orfdTorrentPath -ExpectedBytes $orfdTorrentExpectedBytes -ExpectedSha256 $orfdTorrentSha256)) {
Remove-Item -LiteralPath $orfdTorrentPath -Force -ErrorAction SilentlyContinue
Invoke-CurlDownload -Url "https://academictorrents.com/download/ec5ccf4b8e49271ee3b63660383facf43063f2f2.torrent" -Destination $orfdTorrentPath
}
if (-not (Test-FileIntegrity -Path $orfdTorrentPath -ExpectedBytes $orfdTorrentExpectedBytes -ExpectedSha256 $orfdTorrentSha256)) {
throw "ORFD torrent source failed size/SHA-256 verification"
}
Write-AssetManifest
$powershell = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
$arguments = @(
"-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass",
"-File", "`"$PSCommandPath`"",
"-Mode", "Run",
"-Root", "`"$Root`"",
"-DownloadLimitMiB", $DownloadLimitMiB
) -join " "
$action = New-ScheduledTaskAction -Execute $powershell -Argument $arguments -WorkingDirectory (Split-Path -Parent $PSCommandPath)
$principal = New-ScheduledTaskPrincipal -UserId ([System.Security.Principal.WindowsIdentity]::GetCurrent().Name) -LogonType Interactive -RunLevel Limited
$trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date).AddMinutes(30))
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -ExecutionTimeLimit ([TimeSpan]::FromHours(72))
Register-ScheduledTask -TaskName $taskName -Action $action -Principal $principal -Trigger $trigger -Settings $settings -Description "Mission Core LAB V1 isolated vegetation dataset and model admission." -Force | Out-Null
Start-ScheduledTask -TaskName $taskName
Set-Stage -Stage "queued" -Status "running" -Detail "Bounded downloader started as scheduled task $taskName"
[ordered]@{
ok = $true
mode = $Mode
task_name = $taskName
task_state = (Get-ScheduledTask -TaskName $taskName).State.ToString()
root = $Root
d_free_gib_before = [math]::Round($drive.Free / 1GB, 2)
download_limit_mib = $DownloadLimitMiB
canonical_triton_action = "none"
} | ConvertTo-Json -Depth 6
}
"Run" {
try {
Initialize-Aria2
Set-Stage -Stage "models-and-goose" -Status "running" -Detail "Downloading official pretrained weights and prepared GOOSE/GOOSE-Ex splits"
Invoke-AriaInput -InputPath $gooseInputPath -ConcurrentDownloads 2 -ConnectionsPerServer 4
Set-Stage -Stage "orfd" -Status "running" -Detail "Downloading the 45.94 GB Academic Torrents ORFD release"
$orfdRoot = Join-Path $Root "orfd"
$downloadLimit = "{0}M" -f $DownloadLimitMiB
New-Item -ItemType Directory -Force -Path $orfdRoot | Out-Null
& $ariaExecutable `
--dir=$orfdRoot `
--continue=true `
--seed-time=0 `
"--max-overall-download-limit=$downloadLimit" `
--file-allocation=none `
--disk-cache=64M `
--summary-interval=60 `
--console-log-level=notice `
$orfdTorrentPath
if ($LASTEXITCODE -ne 0) {
throw "ORFD torrent run failed with exit code $LASTEXITCODE"
}
Set-Stage -Stage "wildscenes" -Status "running" -Detail "Downloading the complete public WildScenes v3 file list"
$wildScenesRoot = Join-Path $Root "wildscenes"
New-Item -ItemType Directory -Force -Path $wildScenesRoot | Out-Null
Push-Location $wildScenesRoot
try {
Invoke-AriaInput -InputPath $wildScenesInputPath -ConcurrentDownloads 8 -ConnectionsPerServer 2
}
finally {
Pop-Location
}
Set-Stage -Stage "verification" -Status "running" -Detail "Computing SHA-256 for direct archive and model assets"
Invoke-Verify
Invoke-BulkVerify
Set-Stage -Stage "complete" -Status "complete" -Detail "All scheduled assets downloaded and verified by direct hashes or published tree totals"
}
catch {
Set-Stage -Stage "failed" -Status "failed" -Detail $_.Exception.Message
throw
}
}
"Status" {
$task = Get-DownloadTask
$stage = if (Test-Path -LiteralPath $stagePath -PathType Leaf) { Get-Content -LiteralPath $stagePath -Raw | ConvertFrom-Json } else { $null }
$snapshot = Get-AssetSnapshot
$drive = Get-PSDrive -Name D
[ordered]@{
ok = $true
mode = $Mode
running = $null -ne $task -and $task.State -eq "Running"
task_name = $taskName
task_state = if ($null -ne $task) { $task.State.ToString() } else { $null }
stage = $stage
direct_assets = $snapshot
downloaded_direct_asset_count = @($snapshot | Where-Object { $_.status -eq "downloaded" }).Count
partial_direct_asset_count = @($snapshot | Where-Object { $_.status -eq "partial" }).Count
d_free_gib = [math]::Round($drive.Free / 1GB, 2)
canonical_triton_action = "none"
} | ConvertTo-Json -Depth 10
}
"Manifest" {
Write-AssetManifest
[ordered]@{
ok = $true
mode = $Mode
manifest_path = $assetManifestPath
manifest_sha256 = (Get-FileHash -LiteralPath $assetManifestPath -Algorithm SHA256).Hash.ToLowerInvariant()
canonical_triton_action = "none"
} | ConvertTo-Json -Depth 6
}
"VerifyModels" {
Invoke-Verify -Scope "Models"
[ordered]@{
ok = $true
mode = $Mode
verification_path = $verificationPath
verification_sha256 = (Get-FileHash -LiteralPath $verificationPath -Algorithm SHA256).Hash.ToLowerInvariant()
verified_asset_count = (Get-Content -LiteralPath $verificationPath).Count
canonical_triton_action = "none"
} | ConvertTo-Json -Depth 6
}
"Verify" {
Set-Stage -Stage "verification" -Status "running" -Detail "Manual direct-asset and published-tree verification requested"
Invoke-Verify
Invoke-BulkVerify
Set-Stage -Stage "verification" -Status "complete" -Detail "Direct assets hashed and bulk trees match published totals"
[ordered]@{
ok = $true
mode = $Mode
verification_path = $verificationPath
bulk_verification_path = $bulkVerificationPath
verified_asset_count = (Get-Content -LiteralPath $verificationPath).Count
} | ConvertTo-Json -Depth 6
}
}
@@ -0,0 +1,255 @@
[CmdletBinding()]
param(
[ValidateSet("Build", "Probe", "Validate", "Ravnoves", "RavnovesVideo", "Status")]
[string]$Mode = "Status",
[ValidateSet("Ddrnet", "Ppliteseg")]
[string]$Candidate = "Ddrnet",
[string]$AssetRoot = "D:\NDC_MISSIONCORE\datasets\vegetation-v1\observed-2026-08-27",
[string]$ToolRoot = "D:\NDC_MISSIONCORE\datasets\tooling\lab-v1-vegetation-goose",
[string]$OutputRoot = "D:\NDC_MISSIONCORE\runtime\experiments\lab-v1-vegetation",
[string]$RavnovesVideo = "D:\NDC_MISSIONCORE\runtime\experiments\e46e\inputs\right-cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8.mp4"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$datasetPrefix = "D:\NDC_MISSIONCORE\datasets\"
$runtimePrefix = "D:\NDC_MISSIONCORE\runtime\experiments\"
if (-not $AssetRoot.StartsWith($datasetPrefix, [StringComparison]::OrdinalIgnoreCase)) {
throw "AssetRoot must stay under $datasetPrefix"
}
if (-not $ToolRoot.StartsWith($datasetPrefix, [StringComparison]::OrdinalIgnoreCase)) {
throw "ToolRoot must stay under $datasetPrefix"
}
if (-not $OutputRoot.StartsWith($runtimePrefix, [StringComparison]::OrdinalIgnoreCase)) {
throw "OutputRoot must stay under $runtimePrefix"
}
$image = "ndc/mission-core-lab-v1-goose:sg3.2.0-cu117-v1"
$canonicalContainer = "ndc-mission-core-triton"
$candidateKey = $Candidate.ToLowerInvariant()
$contextRoot = Join-Path $ToolRoot "context"
$configRoot = Join-Path $ToolRoot "config"
$benchmarkConfig = Join-Path $configRoot "lab-v1-goose-vegetation-benchmark-v1.json"
$policyConfig = Join-Path $configRoot "lab-v1-vegetation-mission-policy-v1.json"
$providerMapConfig = Join-Path $configRoot "lab-v1-vegetation-provider-label-map-v1.json"
$datasetRoot = Join-Path $AssetRoot "goose-2d\validation"
$checkpointRelative = if ($candidateKey -eq "ddrnet") {
"models\goose\ddrnet_class_512.pth"
} else {
"models\goose\ppliteseg_class_512.pth"
}
$checkpoint = Join-Path $AssetRoot $checkpointRelative
$expectedCheckpointSha256 = if ($candidateKey -eq "ddrnet") {
"b99c2838051bcd7b092fd3970aa62a77d5c0bbb809c9b9afb2ff4b0ebdaa4ee6"
} else {
"6dd412c0c99115e359896c4cab43a8e6bce9e09b843e7fa885fe597b0a6121cd"
}
$expectedCheckpointBytes = if ($candidateKey -eq "ddrnet") { 259419077 } else { 98208249 }
$ravnovesSha256 = "cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8"
$frameIndices = @(0, 253, 512, 768, 1024, 1536, 2048, 2560, 3072, 3584, 4096, 4488)
$dockerConfig = "D:\NDC_MISSIONCORE\datasets\state\lab-v1-vegetation\docker-config"
function Get-CanonicalTritonIdentity {
$identity = & docker inspect $canonicalContainer --format "{{.Id}}|{{.Config.Image}}|{{.State.Status}}|{{if .State.Health}}{{.State.Health.Status}}{{end}}"
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($identity)) {
throw "Canonical Triton is unavailable"
}
$parts = $identity.Split("|")
if ($parts.Count -ne 4 -or $parts[2] -ne "running" -or $parts[3] -ne "healthy") {
throw "Canonical Triton is not running and healthy: $identity"
}
return $identity
}
function Assert-FileIdentity {
param([string]$Path, [long]$ExpectedBytes, [string]$ExpectedSha256)
$file = Get-Item -LiteralPath $Path -ErrorAction SilentlyContinue
if ($null -eq $file -or $file.Length -ne $ExpectedBytes) {
throw "File identity changed: $Path"
}
$actualSha256 = (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
if ($actualSha256 -ne $ExpectedSha256) {
throw "File digest changed: $Path"
}
}
function Assert-RunnerInputs {
foreach ($path in @($benchmarkConfig, $policyConfig, $providerMapConfig)) {
if (-not (Test-Path -LiteralPath $path -PathType Leaf)) {
throw "Runner config is unavailable: $path"
}
}
if (-not (Test-Path -LiteralPath (Join-Path $contextRoot "Dockerfile") -PathType Leaf)) {
throw "Runner Dockerfile is unavailable"
}
if (-not (Test-Path -LiteralPath (Join-Path $contextRoot "run_goose_vegetation_benchmark.py") -PathType Leaf)) {
throw "Runner source is unavailable"
}
Assert-FileIdentity -Path $checkpoint -ExpectedBytes $expectedCheckpointBytes -ExpectedSha256 $expectedCheckpointSha256
}
function New-RunRoot {
param([string]$Kind)
$stamp = [DateTime]::UtcNow.ToString("yyyyMMddTHHmmssfffZ")
$path = Join-Path $OutputRoot ("{0}-{1}-{2}" -f $Kind, $candidateKey, $stamp)
New-Item -ItemType Directory -Path $path | Out-Null
return $path
}
function Invoke-IsolatedRun {
param(
[ValidateSet("goose", "ravnoves", "ravnoves-video")][string]$RunMode,
[string]$RunRoot,
[int]$Limit,
[string]$FramesRoot = ""
)
$containerName = "ndc-lab-v1-goose-$candidateKey-$([Guid]::NewGuid().ToString('N').Substring(0, 10))"
$visualCount = if ($RunMode -eq "ravnoves-video") { 0 } else { 12 }
$arguments = @(
"run", "--rm", "--name", $containerName,
"--gpus", "all",
"--network", "none",
"--read-only",
"--cap-drop", "ALL",
"--security-opt", "no-new-privileges",
"--memory", "10g",
"--cpus", "8",
"--pids-limit", "512",
"--tmpfs", "/tmp:rw,noexec,nosuid,size=2g",
"--env", "HOME=/tmp",
"--mount", "type=bind,src=$datasetRoot,dst=/data/goose,readonly",
"--mount", "type=bind,src=$checkpoint,dst=/models/candidate.pth,readonly",
"--mount", "type=bind,src=$configRoot,dst=/config,readonly",
"--mount", "type=bind,src=$RunRoot,dst=/output",
$image,
"--mode", $RunMode,
"--candidate", $candidateKey,
"--config", "/config/lab-v1-goose-vegetation-benchmark-v1.json",
"--policy", "/config/lab-v1-vegetation-mission-policy-v1.json",
"--provider-map", "/config/lab-v1-vegetation-provider-label-map-v1.json",
"--checkpoint", "/models/candidate.pth",
"--dataset-root", "/data/goose",
"--output", "/output/result",
"--limit", $Limit.ToString(),
"--visual-count", $visualCount.ToString()
)
if ($RunMode -in @("ravnoves", "ravnoves-video")) {
$arguments = @($arguments[0..($arguments.Count - 1)])
$arguments += @("--frames-root", "/input")
$mountIndex = [Array]::IndexOf($arguments, $image)
$head = @($arguments[0..($mountIndex - 1)])
$tail = @($arguments[$mountIndex..($arguments.Count - 1)])
$arguments = $head + @("--mount", "type=bind,src=$FramesRoot,dst=/input,readonly") + $tail
}
& docker @arguments
if ($LASTEXITCODE -ne 0) {
throw "LAB V1 container failed with exit code $LASTEXITCODE"
}
}
function Export-RavnovesFrames {
param([string]$Destination)
Assert-FileIdentity -Path $RavnovesVideo -ExpectedBytes (Get-Item -LiteralPath $RavnovesVideo).Length -ExpectedSha256 $ravnovesSha256
New-Item -ItemType Directory -Path $Destination | Out-Null
$expression = ($frameIndices | ForEach-Object { "eq(n\,$_ )" }) -join "+"
$temporaryPattern = Join-Path $Destination "selected-%03d.png"
& ffmpeg -hide_banner -loglevel error -i $RavnovesVideo -vf "select='$expression'" -fps_mode vfr $temporaryPattern
if ($LASTEXITCODE -ne 0) {
throw "RAVNOVES exact frame extraction failed"
}
$selected = @(Get-ChildItem -LiteralPath $Destination -Filter "selected-*.png" | Sort-Object Name)
if ($selected.Count -ne $frameIndices.Count) {
throw "RAVNOVES frame island changed: expected $($frameIndices.Count), got $($selected.Count)"
}
for ($index = 0; $index -lt $selected.Count; $index++) {
$target = Join-Path $Destination ("frame-{0:D6}.png" -f $frameIndices[$index])
Move-Item -LiteralPath $selected[$index].FullName -Destination $target
}
}
function Export-RavnovesVideoFrames {
param([string]$Destination)
Assert-FileIdentity -Path $RavnovesVideo -ExpectedBytes (Get-Item -LiteralPath $RavnovesVideo).Length -ExpectedSha256 $ravnovesSha256
New-Item -ItemType Directory -Path $Destination | Out-Null
& ffmpeg -hide_banner -loglevel error -i $RavnovesVideo -map 0:v:0 -fps_mode passthrough (Join-Path $Destination "frame-%06d.png")
if ($LASTEXITCODE -ne 0) {
throw "RAVNOVES full-video frame extraction failed"
}
$frames = @(Get-ChildItem -LiteralPath $Destination -File -Filter "frame-*.png" | Sort-Object Name)
if ($frames.Count -ne 4489 -or $frames[0].Name -ne "frame-000001.png" -or $frames[-1].Name -ne "frame-004489.png") {
throw "RAVNOVES full-video frame sequence changed"
}
}
if ($Mode -eq "Status") {
$imageIdentity = & docker image inspect $image --format "{{.Id}}" 2>$null
[ordered]@{
schema_version = "missioncore.lab-v1-goose-runner-status/v1"
observed_at_utc = [DateTime]::UtcNow.ToString("o")
worker_id = "worker-006"
image = $image
image_id = if ($LASTEXITCODE -eq 0) { $imageIdentity } else { $null }
canonical_triton = Get-CanonicalTritonIdentity
asset_root = $AssetRoot
output_root = $OutputRoot
candidate = $candidateKey
} | ConvertTo-Json -Depth 6
exit 0
}
Assert-RunnerInputs
$canonicalBefore = Get-CanonicalTritonIdentity
try {
if ($Mode -eq "Build") {
if (-not (Test-Path -LiteralPath (Join-Path $dockerConfig "config.json") -PathType Leaf)) {
throw "Isolated Docker client configuration is unavailable"
}
$previousDockerConfig = $env:DOCKER_CONFIG
try {
$env:DOCKER_CONFIG = $dockerConfig
& docker build --pull=false --label "com.nodedc.component=mission-core-lab-v1-goose" --label "com.nodedc.authority=shadow-only" --tag $image $contextRoot
if ($LASTEXITCODE -ne 0) {
throw "LAB V1 image build failed with exit code $LASTEXITCODE"
}
}
finally {
$env:DOCKER_CONFIG = $previousDockerConfig
}
}
elseif ($Mode -eq "Probe") {
$runRoot = New-RunRoot -Kind "probe"
Invoke-IsolatedRun -RunMode "goose" -RunRoot $runRoot -Limit 8
}
elseif ($Mode -eq "Validate") {
$runRoot = New-RunRoot -Kind "validation"
Invoke-IsolatedRun -RunMode "goose" -RunRoot $runRoot -Limit 0
}
elseif ($Mode -eq "Ravnoves") {
$runRoot = New-RunRoot -Kind "ravnoves"
$framesRoot = Join-Path $runRoot "input-frames"
Export-RavnovesFrames -Destination $framesRoot
Invoke-IsolatedRun -RunMode "ravnoves" -RunRoot $runRoot -Limit 0 -FramesRoot $framesRoot
}
elseif ($Mode -eq "RavnovesVideo") {
if ($candidateKey -ne "ddrnet") {
throw "Full-video shadow is admitted only for the selected DDRNet candidate"
}
$runRoot = New-RunRoot -Kind "ravnoves-video"
$framesRoot = Join-Path $runRoot "input-frames"
Export-RavnovesVideoFrames -Destination $framesRoot
Invoke-IsolatedRun -RunMode "ravnoves-video" -RunRoot $runRoot -Limit 0 -FramesRoot $framesRoot
Remove-Item -LiteralPath $framesRoot -Recurse -Force
}
}
finally {
$canonicalAfter = Get-CanonicalTritonIdentity
if ($canonicalAfter -ne $canonicalBefore) {
throw "Canonical Triton identity changed during LAB V1 work"
}
}
@@ -0,0 +1,120 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$BuildContext,
[Parameter(Mandatory = $true)]
[ValidatePattern("^[A-Za-z0-9._-]{1,96}$")]
[string]$RunId,
[string]$OutputRoot = "D:\NDC_MISSIONCORE\runtime\results\m49-t1-gseg3d"
)
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
function Assert-LastExitCode([string]$Operation) {
if ($LASTEXITCODE -ne 0) { throw "$Operation failed with exit code $LASTEXITCODE" }
}
function Resolve-DDirectory([string]$Path, [string]$Label, [bool]$Create) {
if ($Create -and -not (Test-Path -LiteralPath $Path)) {
$null = New-Item -ItemType Directory -Path $Path
}
$item = Get-Item -LiteralPath (Resolve-Path -LiteralPath $Path).Path -Force
if (
-not $item.PSIsContainer -or
($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
[IO.Path]::GetPathRoot($item.FullName).TrimEnd("\") -ine "D:"
) {
throw "$Label must be a real D: directory"
}
return $item.FullName
}
function Get-Container([string]$Name) {
$rows = @((& docker inspect $Name) | ConvertFrom-Json)
Assert-LastExitCode "Docker inspection for $Name"
if ($rows.Count -ne 1) { throw "Container identity for $Name is not unique" }
return $rows[0]
}
if ($env:COMPUTERNAME -cne "DESKTOP-OPJ8J04") {
throw "M49 T1 is pinned to Worker 006"
}
$context = Resolve-DDirectory $BuildContext "M49 T1 build context" $false
$output = Resolve-DDirectory $OutputRoot "M49 T1 output root" $true
$runOutput = Join-Path $output $RunId
if (Test-Path -LiteralPath $runOutput) { throw "M49 T1 output already exists" }
$null = New-Item -ItemType Directory -Path $runOutput
$runOutput = Resolve-DDirectory $runOutput "M49 T1 run output" $false
$dockerConfig = Join-Path $runOutput "docker-config"
$null = New-Item -ItemType Directory -Path $dockerConfig
'{"auths":{}}' | Set-Content -LiteralPath (Join-Path $dockerConfig "config.json") -Encoding ascii
$env:DOCKER_CONFIG = $dockerConfig
$os = Get-CimInstance Win32_OperatingSystem
$freeMemoryGiB = [double]$os.FreePhysicalMemory / 1MB
if ($freeMemoryGiB -lt 16.0) {
throw ("M49 T1 requires 16 GiB free memory; observed {0:N2} GiB" -f $freeMemoryGiB)
}
$canonicalTriton = Get-Container "ndc-mission-core-triton"
if (-not $canonicalTriton.State.Running -or $canonicalTriton.State.Health.Status -cne "healthy") {
throw "Canonical Mission Core Triton must remain healthy during M49 T1"
}
$containerName = "ndc-mission-core-m49-t1-gseg3d-$RunId"
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$containerName$") {
throw "M49 T1 container name already exists"
}
$imageTag = "ndc/mission-core-m49-t1-gseg3d:20260826"
$buildStarted = [DateTimeOffset]::UtcNow
& docker build --pull=false --tag $imageTag $context
Assert-LastExitCode "M49 T1 image build"
$buildCompleted = [DateTimeOffset]::UtcNow
$runStarted = [DateTimeOffset]::UtcNow
try {
& docker run --rm --name $containerName --cpus 16 --memory 24g `
--volume ((($runOutput -replace "\\", "/")) + ":/evidence") `
$imageTag
Assert-LastExitCode "M49 T1 upstream qualification"
} finally {
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$containerName$") {
& docker rm --force $containerName *> $null
}
}
$runCompleted = [DateTimeOffset]::UtcNow
$image = @((& docker image inspect $imageTag) | ConvertFrom-Json)[0]
Assert-LastExitCode "M49 T1 image inspection"
$resultPath = Join-Path $runOutput "result.json"
if (-not (Test-Path -LiteralPath $resultPath -PathType Leaf)) {
throw "M49 T1 result.json is missing"
}
$result = Get-Content -LiteralPath $resultPath -Raw | ConvertFrom-Json
if ($result.status -cne "passed") { throw "M49 T1 qualification did not pass" }
$summary = [ordered]@{
schema_version = "missioncore.m49-t1-worker-summary/v1"
worker_id = "worker-006"
run_id = $RunId
image_tag = $imageTag
image_id = [string]$image.Id
image_size_bytes = [long]$image.Size
build_started_utc = $buildStarted.ToString("o")
build_wall_seconds = [math]::Round(($buildCompleted - $buildStarted).TotalSeconds, 6)
qualification_wall_seconds = [math]::Round(($runCompleted - $runStarted).TotalSeconds, 6)
free_memory_gib_before = [math]::Round($freeMemoryGiB, 6)
canonical_triton_id = [string]$canonicalTriton.Id
canonical_triton_health = [string]$canonicalTriton.State.Health.Status
candidate_accepted = $true
ravnoves00_quality_accepted = $false
realtime_accepted = $false
navigation_or_actuation_allowed = $false
}
$summary | ConvertTo-Json -Depth 3 | Set-Content -LiteralPath (
Join-Path $runOutput "worker-summary.json"
) -Encoding utf8
$summary | ConvertTo-Json -Depth 3
@@ -0,0 +1,62 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ReleaseRoot,
[Parameter(Mandatory = $true)]
[ValidatePattern("^[A-Za-z0-9._-]{1,96}$")]
[string]$RunId
)
$ErrorActionPreference = "Stop"
$taskName = "MissionCore-M49T1GSeg3D"
$release = (Resolve-Path -LiteralPath $ReleaseRoot).Path
$payload = Join-Path $release "payload"
$runner = Join-Path $payload "Invoke-M49T1GSeg3DQualification.ps1"
if (-not (Test-Path -LiteralPath $runner -PathType Leaf)) {
throw "M49 T1 runner is missing"
}
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($existing -and $existing.State -eq "Running") {
throw "$taskName is already running"
}
$powerShell = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
$arguments = @(
"-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass",
"-File", "`"$runner`"",
"-BuildContext", "`"$payload`"",
"-RunId", "`"$RunId`""
) -join " "
$userId = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$action = New-ScheduledTaskAction `
-Execute $powerShell `
-Argument $arguments `
-WorkingDirectory $payload
$principal = New-ScheduledTaskPrincipal `
-UserId $userId `
-LogonType Interactive `
-RunLevel Limited
$trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date).AddMinutes(30))
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit ([TimeSpan]::FromHours(3))
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Principal $principal `
-Trigger $trigger `
-Settings $settings `
-Description "One-shot M49 T1 pinned GSeg3D upstream qualification." `
-Force | Out-Null
Start-ScheduledTask -TaskName $taskName
[pscustomobject]@{
task_name = $taskName
run_id = $RunId
release_root = $release
state = (Get-ScheduledTask -TaskName $taskName).State.ToString()
} | ConvertTo-Json -Compress
@@ -0,0 +1,120 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$BuildContext,
[Parameter(Mandatory = $true)]
[ValidatePattern("^[A-Za-z0-9._-]{1,96}$")]
[string]$RunId,
[string]$OutputRoot = "D:\NDC_MISSIONCORE\runtime\results\m49-t3-travel"
)
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
function Assert-LastExitCode([string]$Operation) {
if ($LASTEXITCODE -ne 0) { throw "$Operation failed with exit code $LASTEXITCODE" }
}
function Resolve-DDirectory([string]$Path, [string]$Label, [bool]$Create) {
if ($Create -and -not (Test-Path -LiteralPath $Path)) {
$null = New-Item -ItemType Directory -Path $Path
}
$item = Get-Item -LiteralPath (Resolve-Path -LiteralPath $Path).Path -Force
if (
-not $item.PSIsContainer -or
($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
[IO.Path]::GetPathRoot($item.FullName).TrimEnd("\") -ine "D:"
) {
throw "$Label must be a real D: directory"
}
return $item.FullName
}
function Get-Container([string]$Name) {
$rows = @((& docker inspect $Name) | ConvertFrom-Json)
Assert-LastExitCode "Docker inspection for $Name"
if ($rows.Count -ne 1) { throw "Container identity for $Name is not unique" }
return $rows[0]
}
if ($env:COMPUTERNAME -cne "DESKTOP-OPJ8J04") {
throw "M49 T3 is pinned to Worker 006"
}
$context = Resolve-DDirectory $BuildContext "M49 T3 build context" $false
$output = Resolve-DDirectory $OutputRoot "M49 T3 output root" $true
$runOutput = Join-Path $output $RunId
if (Test-Path -LiteralPath $runOutput) { throw "M49 T3 output already exists" }
$null = New-Item -ItemType Directory -Path $runOutput
$runOutput = Resolve-DDirectory $runOutput "M49 T3 run output" $false
$dockerConfig = Join-Path $runOutput "docker-config"
$null = New-Item -ItemType Directory -Path $dockerConfig
'{"auths":{}}' | Set-Content -LiteralPath (Join-Path $dockerConfig "config.json") -Encoding ascii
$env:DOCKER_CONFIG = $dockerConfig
$os = Get-CimInstance Win32_OperatingSystem
$freeMemoryGiB = [double]$os.FreePhysicalMemory / 1MB
if ($freeMemoryGiB -lt 16.0) {
throw ("M49 T3 requires 16 GiB free memory; observed {0:N2} GiB" -f $freeMemoryGiB)
}
$canonicalTriton = Get-Container "ndc-mission-core-triton"
if (-not $canonicalTriton.State.Running -or $canonicalTriton.State.Health.Status -cne "healthy") {
throw "Canonical Mission Core Triton must remain healthy during M49 T3"
}
$containerName = "ndc-mission-core-m49-t3-travel-$RunId"
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$containerName$") {
throw "M49 T3 container name already exists"
}
$imageTag = "ndc/mission-core-m49-t3-travel:20260826"
$buildStarted = [DateTimeOffset]::UtcNow
& docker build --pull=false --tag $imageTag $context
Assert-LastExitCode "M49 T3 image build"
$buildCompleted = [DateTimeOffset]::UtcNow
$runStarted = [DateTimeOffset]::UtcNow
try {
& docker run --rm --name $containerName --cpus 16 --memory 24g `
--volume ((($runOutput -replace "\\", "/")) + ":/evidence") `
$imageTag
Assert-LastExitCode "M49 T3 upstream qualification"
} finally {
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$containerName$") {
& docker rm --force $containerName *> $null
}
}
$runCompleted = [DateTimeOffset]::UtcNow
$image = @((& docker image inspect $imageTag) | ConvertFrom-Json)[0]
Assert-LastExitCode "M49 T3 image inspection"
$resultPath = Join-Path $runOutput "result.json"
if (-not (Test-Path -LiteralPath $resultPath -PathType Leaf)) {
throw "M49 T3 result.json is missing"
}
$result = Get-Content -LiteralPath $resultPath -Raw | ConvertFrom-Json
if ($result.status -cne "passed") { throw "M49 T3 qualification did not pass" }
$summary = [ordered]@{
schema_version = "missioncore.m49-t3-worker-summary/v1"
worker_id = "worker-006"
run_id = $RunId
image_tag = $imageTag
image_id = [string]$image.Id
image_size_bytes = [long]$image.Size
build_started_utc = $buildStarted.ToString("o")
build_wall_seconds = [math]::Round(($buildCompleted - $buildStarted).TotalSeconds, 6)
qualification_wall_seconds = [math]::Round(($runCompleted - $runStarted).TotalSeconds, 6)
free_memory_gib_before = [math]::Round($freeMemoryGiB, 6)
canonical_triton_id = [string]$canonicalTriton.Id
canonical_triton_health = [string]$canonicalTriton.State.Health.Status
candidate_accepted = $true
ravnoves00_quality_accepted = $false
realtime_accepted = $false
navigation_or_actuation_allowed = $false
}
$summary | ConvertTo-Json -Depth 3 | Set-Content -LiteralPath (
Join-Path $runOutput "worker-summary.json"
) -Encoding utf8
$summary | ConvertTo-Json -Depth 3
@@ -0,0 +1,62 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ReleaseRoot,
[Parameter(Mandatory = $true)]
[ValidatePattern("^[A-Za-z0-9._-]{1,96}$")]
[string]$RunId
)
$ErrorActionPreference = "Stop"
$taskName = "MissionCore-M49T3Travel"
$release = (Resolve-Path -LiteralPath $ReleaseRoot).Path
$payload = Join-Path $release "payload"
$runner = Join-Path $payload "Invoke-M49T3TravelQualification.ps1"
if (-not (Test-Path -LiteralPath $runner -PathType Leaf)) {
throw "M49 T3 runner is missing"
}
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($existing -and $existing.State -eq "Running") {
throw "$taskName is already running"
}
$powerShell = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
$arguments = @(
"-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass",
"-File", "`"$runner`"",
"-BuildContext", "`"$payload`"",
"-RunId", "`"$RunId`""
) -join " "
$userId = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$action = New-ScheduledTaskAction `
-Execute $powerShell `
-Argument $arguments `
-WorkingDirectory $payload
$principal = New-ScheduledTaskPrincipal `
-UserId $userId `
-LogonType Interactive `
-RunLevel Limited
$trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date).AddMinutes(30))
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit ([TimeSpan]::FromHours(3))
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Principal $principal `
-Trigger $trigger `
-Settings $settings `
-Description "One-shot M49 T3 pinned TRAVEL upstream qualification." `
-Force | Out-Null
Start-ScheduledTask -TaskName $taskName
[pscustomobject]@{
task_name = $taskName
run_id = $RunId
release_root = $release
state = (Get-ScheduledTask -TaskName $taskName).State.ToString()
} | ConvertTo-Json -Compress
@@ -0,0 +1,205 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ReleaseRoot,
[Parameter(Mandatory = $true)]
[ValidatePattern("^[A-Za-z0-9._-]{1,96}$")]
[string]$RunId,
[string]$SourcePackPath = "D:\NDC_MISSIONCORE\runtime\derived\e10-lidar-pack-576c994a6c814e2592dd6240ace3902a5db94843312c759a73ba0c9166157d2b\lidar-pack.npz",
[string]$OutputRoot = "D:\NDC_MISSIONCORE\runtime\results\m49-t3-travel-ravnoves"
)
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
$TravelImageTag = "ndc/mission-core-m49-t3-travel:20260826"
$TravelImageId = "sha256:7b412020f4d8392d1d1ed1b33beadc44140f0ea8f781e62dd69796042334300f"
$ParityImageTag = "ndc-mission-core-m48t-upstream-parity:1.9.4-cu130"
$ParityImageId = "sha256:ceb13548617e4bd3f619766bfdff00af3fa5160946b367828da6d2233dcdcba0"
function Assert-LastExitCode([string]$Operation) {
if ($LASTEXITCODE -ne 0) { throw "$Operation failed with exit code $LASTEXITCODE" }
}
function Resolve-DDirectory([string]$Path, [string]$Label, [bool]$Create) {
if ($Create -and -not (Test-Path -LiteralPath $Path)) {
$null = New-Item -ItemType Directory -Path $Path
}
$item = Get-Item -LiteralPath (Resolve-Path -LiteralPath $Path).Path -Force
if (
-not $item.PSIsContainer -or
($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
[IO.Path]::GetPathRoot($item.FullName).TrimEnd("\") -ine "D:"
) {
throw "$Label must be a real D: directory"
}
return $item.FullName
}
function Resolve-DFile([string]$Path, [string]$Label) {
$item = Get-Item -LiteralPath (Resolve-Path -LiteralPath $Path).Path -Force
if (
$item.PSIsContainer -or
($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
[IO.Path]::GetPathRoot($item.FullName).TrimEnd("\") -ine "D:"
) {
throw "$Label must be a real D: file"
}
return $item.FullName
}
function Convert-ToDockerPath([string]$Path) {
return ($Path -replace "\\", "/")
}
function Get-Container([string]$Name) {
$rows = @(((& docker inspect $Name) | ConvertFrom-Json))
Assert-LastExitCode "Docker inspection for $Name"
if ($rows.Count -ne 1) { throw "Container identity for $Name is not unique" }
return $rows[0]
}
function Assert-Image([string]$Tag, [string]$ExpectedId) {
$rows = @(((& docker image inspect $Tag) | ConvertFrom-Json))
Assert-LastExitCode "Docker image inspection for $Tag"
if ($rows.Count -ne 1 -or [string]$rows[0].Id -cne $ExpectedId) {
throw "Pinned image identity changed for $Tag"
}
}
function Remove-ExactContainer([string]$Name) {
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$Name$") {
& docker rm --force $Name *> $null
}
}
if ($env:COMPUTERNAME -cne "DESKTOP-OPJ8J04") {
throw "M49 T3 RAVNOVES probe is pinned to Worker 006"
}
$release = Resolve-DDirectory $ReleaseRoot "M49 T3 probe release" $false
$payload = Resolve-DDirectory (Join-Path $release "payload") "M49 T3 probe payload" $false
$sourcePack = Resolve-DFile $SourcePackPath "RAVNOVES00 source pack"
$output = Resolve-DDirectory $OutputRoot "M49 T3 probe output root" $true
$runOutputCandidate = Join-Path $output $RunId
if (Test-Path -LiteralPath $runOutputCandidate) { throw "M49 T3 probe output already exists" }
$null = New-Item -ItemType Directory -Path $runOutputCandidate
$runOutput = Resolve-DDirectory $runOutputCandidate "M49 T3 probe run output" $false
$releaseDocument = Get-Content -LiteralPath (Join-Path $payload "release.json") -Raw | ConvertFrom-Json
if (
$releaseDocument.schema_version -cne "missioncore.m49-t3-ravnoves-worker-release/v1" -or
$releaseDocument.worker_id -cne "worker-006" -or
$releaseDocument.candidate_id -cne "travel"
) {
throw "M49 T3 probe release contract changed"
}
foreach ($property in $releaseDocument.files.PSObject.Properties) {
$path = Join-Path $payload $property.Name
$actual = (Get-FileHash -Algorithm SHA256 -LiteralPath $path).Hash.ToLowerInvariant()
if ($actual -cne [string]$property.Value.sha256) {
throw "M49 T3 probe payload digest changed: $($property.Name)"
}
}
$sourcePackSha = (Get-FileHash -Algorithm SHA256 -LiteralPath $sourcePack).Hash.ToLowerInvariant()
if ($sourcePackSha -cne "0685d24219d8236caf8b7f1685e93f6d6b59e7fd015a768d88a92bbe8b154944") {
throw "RAVNOVES00 source pack digest changed"
}
$os = Get-CimInstance Win32_OperatingSystem
$freeMemoryGiB = [double]$os.FreePhysicalMemory / 1MB
if ($freeMemoryGiB -lt 16.0) {
throw ("M49 T3 probe requires 16 GiB free memory; observed {0:N2} GiB" -f $freeMemoryGiB)
}
$canonicalTritonBefore = Get-Container "ndc-mission-core-triton"
if (
-not $canonicalTritonBefore.State.Running -or
$canonicalTritonBefore.State.Health.Status -cne "healthy"
) {
throw "Canonical Mission Core Triton must remain healthy during M49 T3 probe"
}
Assert-Image $TravelImageTag $TravelImageId
Assert-Image $ParityImageTag $ParityImageId
$prepareName = "ndc-mission-core-m49-t3-prepare-$RunId"
$travelName = "ndc-mission-core-m49-t3-probe-$RunId"
$analyzeName = "ndc-mission-core-m49-t3-analyze-$RunId"
foreach ($name in @($prepareName, $travelName, $analyzeName)) {
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$name$") {
throw "M49 T3 probe container name already exists: $name"
}
}
$started = [DateTimeOffset]::UtcNow
try {
& docker run --rm --name $prepareName --network none --cpus 8 --memory 16g `
--entrypoint python3 `
--volume ((Convert-ToDockerPath $sourcePack) + ":/source/lidar-pack.npz:ro") `
--volume ((Convert-ToDockerPath $payload) + ":/release:ro") `
--volume ((Convert-ToDockerPath $runOutput) + ":/probe") `
$ParityImageTag /release/prepare_ravnoves_probe.py `
--source-pack /source/lidar-pack.npz `
--config /release/m49-travel-ravnoves-compatibility-v1.json `
--output-root /probe/inputs
Assert-LastExitCode "M49 T3 RAVNOVES input preparation"
& docker run --rm --name $travelName --network none --cpus 16 --memory 24g `
--entrypoint /bin/bash `
--volume ((Convert-ToDockerPath $payload) + ":/release:ro") `
--volume ((Convert-ToDockerPath $runOutput) + ":/probe") `
$TravelImageTag /release/run_ravnoves_probe.sh
Assert-LastExitCode "M49 T3 RAVNOVES TRAVEL probe"
& docker run --rm --name $analyzeName --network none --cpus 8 --memory 16g `
--entrypoint python3 `
--volume ((Convert-ToDockerPath $payload) + ":/release:ro") `
--volume ((Convert-ToDockerPath $runOutput) + ":/probe") `
$ParityImageTag /release/analyze_ravnoves_probe.py `
--probe-root /probe `
--config /release/m49-travel-ravnoves-compatibility-v1.json
Assert-LastExitCode "M49 T3 RAVNOVES result analysis"
} finally {
foreach ($name in @($prepareName, $travelName, $analyzeName)) {
Remove-ExactContainer $name
}
}
$completed = [DateTimeOffset]::UtcNow
$resultPath = Join-Path $runOutput "result.json"
if (-not (Test-Path -LiteralPath $resultPath -PathType Leaf)) {
throw "M49 T3 RAVNOVES result.json is missing"
}
$result = Get-Content -LiteralPath $resultPath -Raw | ConvertFrom-Json
if ($result.status -cne "passed") { throw "M49 T3 RAVNOVES probe did not complete" }
$canonicalTritonAfter = Get-Container "ndc-mission-core-triton"
if (
-not $canonicalTritonAfter.State.Running -or
$canonicalTritonAfter.State.Health.Status -cne "healthy" -or
[string]$canonicalTritonAfter.Id -cne [string]$canonicalTritonBefore.Id
) {
throw "Canonical Mission Core Triton changed during M49 T3 probe"
}
$summary = [ordered]@{
schema_version = "missioncore.m49-t3-ravnoves-worker-summary/v1"
worker_id = "worker-006"
run_id = $RunId
code_revision = [string]$releaseDocument.code_revision
travel_image_id = $TravelImageId
parity_image_id = $ParityImageId
source_pack_sha256 = $sourcePackSha
started_utc = $started.ToString("o")
wall_seconds = [math]::Round(($completed - $started).TotalSeconds, 6)
free_memory_gib_before = [math]::Round($freeMemoryGiB, 6)
canonical_triton_id = [string]$canonicalTritonAfter.Id
canonical_triton_health = [string]$canonicalTritonAfter.State.Health.Status
candidate_run_count = [int]$result.candidate_run_count
direct_upstream_profile_compatible = [bool]$result.summary.direct_upstream_profile_compatible
ravnoves00_visual_quality_accepted = $false
realtime_accepted = $false
navigation_or_actuation_allowed = $false
}
$summary | ConvertTo-Json -Depth 3 | Set-Content -LiteralPath (
Join-Path $runOutput "worker-summary.json"
) -Encoding utf8
$summary | ConvertTo-Json -Depth 3
@@ -0,0 +1,61 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ReleaseRoot,
[Parameter(Mandatory = $true)]
[ValidatePattern("^[A-Za-z0-9._-]{1,96}$")]
[string]$RunId
)
$ErrorActionPreference = "Stop"
$taskName = "MissionCore-M49T3TravelRavnoves"
$release = (Resolve-Path -LiteralPath $ReleaseRoot).Path
$runner = Join-Path $release "payload\Invoke-M49T3TravelRavnovesProbe.ps1"
if (-not (Test-Path -LiteralPath $runner -PathType Leaf)) {
throw "M49 T3 RAVNOVES runner is missing"
}
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($existing -and $existing.State -eq "Running") {
throw "$taskName is already running"
}
$powerShell = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
$arguments = @(
"-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass",
"-File", "`"$runner`"",
"-ReleaseRoot", "`"$release`"",
"-RunId", "`"$RunId`""
) -join " "
$userId = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$action = New-ScheduledTaskAction `
-Execute $powerShell `
-Argument $arguments `
-WorkingDirectory $release
$principal = New-ScheduledTaskPrincipal `
-UserId $userId `
-LogonType Interactive `
-RunLevel Limited
$trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date).AddMinutes(30))
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit ([TimeSpan]::FromHours(2))
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Principal $principal `
-Trigger $trigger `
-Settings $settings `
-Description "One-shot CPU-only M49 T3 TRAVEL compatibility probe on RAVNOVES00." `
-Force | Out-Null
Start-ScheduledTask -TaskName $taskName
[pscustomobject]@{
task_name = $taskName
run_id = $RunId
release_root = $release
state = (Get-ScheduledTask -TaskName $taskName).State.ToString()
} | ConvertTo-Json -Compress
@@ -0,0 +1,205 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ReleaseRoot,
[Parameter(Mandatory = $true)]
[ValidatePattern("^[A-Za-z0-9._-]{1,96}$")]
[string]$RunId,
[string]$SourcePackPath = "D:\NDC_MISSIONCORE\runtime\derived\e10-lidar-pack-576c994a6c814e2592dd6240ace3902a5db94843312c759a73ba0c9166157d2b\lidar-pack.npz",
[string]$OutputRoot = "D:\NDC_MISSIONCORE\runtime\results\m49-tgs-fail-closed"
)
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
$TravelImageTag = "ndc/mission-core-m49-t3-travel:20260826"
$TravelImageId = "sha256:7b412020f4d8392d1d1ed1b33beadc44140f0ea8f781e62dd69796042334300f"
$ParityImageTag = "ndc-mission-core-m48t-upstream-parity:1.9.4-cu130"
$ParityImageId = "sha256:ceb13548617e4bd3f619766bfdff00af3fa5160946b367828da6d2233dcdcba0"
function Assert-LastExitCode([string]$Operation) {
if ($LASTEXITCODE -ne 0) { throw "$Operation failed with exit code $LASTEXITCODE" }
}
function Resolve-DDirectory([string]$Path, [string]$Label, [bool]$Create) {
if ($Create -and -not (Test-Path -LiteralPath $Path)) {
$null = New-Item -ItemType Directory -Path $Path
}
$item = Get-Item -LiteralPath (Resolve-Path -LiteralPath $Path).Path -Force
if (
-not $item.PSIsContainer -or
($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
[IO.Path]::GetPathRoot($item.FullName).TrimEnd("\") -ine "D:"
) {
throw "$Label must be a real D: directory"
}
return $item.FullName
}
function Resolve-DFile([string]$Path, [string]$Label) {
$item = Get-Item -LiteralPath (Resolve-Path -LiteralPath $Path).Path -Force
if (
$item.PSIsContainer -or
($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
[IO.Path]::GetPathRoot($item.FullName).TrimEnd("\") -ine "D:"
) {
throw "$Label must be a real D: file"
}
return $item.FullName
}
function Convert-ToDockerPath([string]$Path) {
return ($Path -replace "\\", "/")
}
function Get-Container([string]$Name) {
$rows = @(((& docker inspect $Name) | ConvertFrom-Json))
Assert-LastExitCode "Docker inspection for $Name"
if ($rows.Count -ne 1) { throw "Container identity for $Name is not unique" }
return $rows[0]
}
function Assert-Image([string]$Tag, [string]$ExpectedId) {
$rows = @(((& docker image inspect $Tag) | ConvertFrom-Json))
Assert-LastExitCode "Docker image inspection for $Tag"
if ($rows.Count -ne 1 -or [string]$rows[0].Id -cne $ExpectedId) {
throw "Pinned image identity changed for $Tag"
}
}
function Remove-ExactContainer([string]$Name) {
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$Name$") {
& docker rm --force $Name *> $null
}
}
if ($env:COMPUTERNAME -cne "DESKTOP-OPJ8J04") {
throw "M49 TGS fail-closed evidence is pinned to Worker 006"
}
$release = Resolve-DDirectory $ReleaseRoot "M49 TGS release" $false
$payload = Resolve-DDirectory (Join-Path $release "payload") "M49 TGS payload" $false
$sourcePack = Resolve-DFile $SourcePackPath "RAVNOVES00 source pack"
$output = Resolve-DDirectory $OutputRoot "M49 TGS output root" $true
$runCandidate = Join-Path $output $RunId
if (Test-Path -LiteralPath $runCandidate) { throw "M49 TGS output already exists" }
$null = New-Item -ItemType Directory -Path $runCandidate
$runOutput = Resolve-DDirectory $runCandidate "M49 TGS run output" $false
$releaseDocument = Get-Content -LiteralPath (Join-Path $payload "release.json") -Raw | ConvertFrom-Json
if (
$releaseDocument.schema_version -cne "missioncore.m49-tgs-worker-release/v1" -or
$releaseDocument.worker_id -cne "worker-006" -or
$releaseDocument.candidate_id -cne "travel-tgs-only"
) {
throw "M49 TGS release contract changed"
}
foreach ($property in $releaseDocument.files.PSObject.Properties) {
$path = Join-Path $payload $property.Name
$actual = (Get-FileHash -Algorithm SHA256 -LiteralPath $path).Hash.ToLowerInvariant()
if ($actual -cne [string]$property.Value.sha256) {
throw "M49 TGS payload digest changed: $($property.Name)"
}
}
$sourcePackSha = (Get-FileHash -Algorithm SHA256 -LiteralPath $sourcePack).Hash.ToLowerInvariant()
if ($sourcePackSha -cne "0685d24219d8236caf8b7f1685e93f6d6b59e7fd015a768d88a92bbe8b154944") {
throw "RAVNOVES00 source pack digest changed"
}
$os = Get-CimInstance Win32_OperatingSystem
$freeMemoryGiB = [double]$os.FreePhysicalMemory / 1MB
if ($freeMemoryGiB -lt 16.0) {
throw ("M49 TGS requires 16 GiB free memory; observed {0:N2} GiB" -f $freeMemoryGiB)
}
$tritonBefore = Get-Container "ndc-mission-core-triton"
if (-not $tritonBefore.State.Running -or $tritonBefore.State.Health.Status -cne "healthy") {
throw "Canonical Mission Core Triton must remain healthy during M49 TGS"
}
Assert-Image $TravelImageTag $TravelImageId
Assert-Image $ParityImageTag $ParityImageId
$prepareName = "ndc-mission-core-m49-tgs-prepare-$RunId"
$runName = "ndc-mission-core-m49-tgs-run-$RunId"
$analyzeName = "ndc-mission-core-m49-tgs-analyze-$RunId"
foreach ($name in @($prepareName, $runName, $analyzeName)) {
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$name$") {
throw "M49 TGS container name already exists: $name"
}
}
$started = [DateTimeOffset]::UtcNow
try {
& docker run --rm --name $prepareName --network none --cpus 8 --memory 16g `
--entrypoint python3 `
--volume ((Convert-ToDockerPath $sourcePack) + ":/source/lidar-pack.npz:ro") `
--volume ((Convert-ToDockerPath $payload) + ":/release:ro") `
--volume ((Convert-ToDockerPath $runOutput) + ":/tgs") `
$ParityImageTag /release/prepare_tgs_fail_closed_inputs.py `
--source-pack /source/lidar-pack.npz `
--config /release/m49-tgs-fail-closed-evidence-v1.json `
--output-root /tgs/inputs
Assert-LastExitCode "M49 TGS input preparation"
& docker run --rm --name $runName --network none --cpus 16 --memory 24g `
--entrypoint /bin/bash `
--volume ((Convert-ToDockerPath $payload) + ":/release:ro") `
--volume ((Convert-ToDockerPath $runOutput) + ":/tgs") `
$TravelImageTag /release/run_tgs_fail_closed.sh
Assert-LastExitCode "M49 TGS-only run"
& docker run --rm --name $analyzeName --network none --cpus 8 --memory 16g `
--entrypoint python3 `
--volume ((Convert-ToDockerPath $payload) + ":/release:ro") `
--volume ((Convert-ToDockerPath $runOutput) + ":/tgs") `
$ParityImageTag /release/build_tgs_fail_closed_evidence.py `
--run-root /tgs `
--config /release/m49-tgs-fail-closed-evidence-v1.json `
--output-root /tgs/evidence
Assert-LastExitCode "M49 TGS fail-closed evidence analysis"
} finally {
foreach ($name in @($prepareName, $runName, $analyzeName)) {
Remove-ExactContainer $name
}
}
$completed = [DateTimeOffset]::UtcNow
$resultPath = Join-Path $runOutput "evidence\result.json"
if (-not (Test-Path -LiteralPath $resultPath -PathType Leaf)) {
throw "M49 TGS evidence result is missing"
}
$result = Get-Content -LiteralPath $resultPath -Raw | ConvertFrom-Json
if (
$result.status -cne "passed" -or
-not [bool]$result.summary.all_eligible_points_accounted -or
[bool]$result.summary.aos_used
) {
throw "M49 TGS fail-closed acceptance failed"
}
$tritonAfter = Get-Container "ndc-mission-core-triton"
if (
-not $tritonAfter.State.Running -or
$tritonAfter.State.Health.Status -cne "healthy" -or
[string]$tritonAfter.Id -cne [string]$tritonBefore.Id
) {
throw "Canonical Mission Core Triton changed during M49 TGS"
}
$summary = [ordered]@{
schema_version = "missioncore.m49-tgs-worker-summary/v1"
worker_id = "worker-006"
run_id = $RunId
code_revision = [string]$releaseDocument.code_revision
source_pack_sha256 = $sourcePackSha
travel_image_id = $TravelImageId
parity_image_id = $ParityImageId
started_utc = $started.ToString("o")
wall_seconds = [math]::Round(($completed - $started).TotalSeconds, 6)
free_memory_gib_before = [math]::Round($freeMemoryGiB, 6)
canonical_triton_id = [string]$tritonAfter.Id
canonical_triton_health = [string]$tritonAfter.State.Health.Status
all_eligible_points_accounted = [bool]$result.summary.all_eligible_points_accounted
aos_used = [bool]$result.summary.aos_used
visual_quality_accepted = $false
realtime_accepted = $false
navigation_or_actuation_allowed = $false
}
$summary | ConvertTo-Json -Depth 3 | Set-Content -LiteralPath (
Join-Path $runOutput "worker-summary.json"
) -Encoding utf8
$summary | ConvertTo-Json -Depth 3
@@ -0,0 +1,51 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ReleaseRoot,
[Parameter(Mandatory = $true)]
[ValidatePattern("^[A-Za-z0-9._-]{1,96}$")]
[string]$RunId
)
$ErrorActionPreference = "Stop"
$taskName = "MissionCore-M49TgsFailClosed"
$release = (Resolve-Path -LiteralPath $ReleaseRoot).Path
$runner = Join-Path $release "payload\Invoke-M49TgsFailClosedEvidence.ps1"
if (-not (Test-Path -LiteralPath $runner -PathType Leaf)) {
throw "M49 TGS runner is missing"
}
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($existing -and $existing.State -eq "Running") {
throw "$taskName is already running"
}
$powerShell = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
$arguments = @(
"-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass",
"-File", "`"$runner`"",
"-ReleaseRoot", "`"$release`"",
"-RunId", "`"$RunId`""
) -join " "
$userId = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$action = New-ScheduledTaskAction -Execute $powerShell -Argument $arguments -WorkingDirectory $release
$principal = New-ScheduledTaskPrincipal -UserId $userId -LogonType Interactive -RunLevel Limited
$trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date).AddMinutes(30))
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit ([TimeSpan]::FromHours(2))
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Principal $principal `
-Trigger $trigger `
-Settings $settings `
-Description "One-shot CPU-only gravity-aligned TGS fail-closed evidence run." `
-Force | Out-Null
Start-ScheduledTask -TaskName $taskName
[pscustomobject]@{
task_name = $taskName
run_id = $RunId
release_root = $release
state = (Get-ScheduledTask -TaskName $taskName).State.ToString()
} | ConvertTo-Json -Compress
@@ -0,0 +1,187 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ReleaseRoot,
[Parameter(Mandatory = $true)]
[ValidatePattern("^[A-Za-z0-9._-]{1,96}$")]
[string]$RunId,
[string]$SourcePackPath = "D:\NDC_MISSIONCORE\runtime\derived\e10-lidar-pack-576c994a6c814e2592dd6240ace3902a5db94843312c759a73ba0c9166157d2b\lidar-pack.npz",
[string]$OutputRoot = "D:\NDC_MISSIONCORE\runtime\results\m49-tgs-full-shadow"
)
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
$TravelImageTag = "ndc/mission-core-m49-t3-travel:20260826"
$TravelImageId = "sha256:7b412020f4d8392d1d1ed1b33beadc44140f0ea8f781e62dd69796042334300f"
$ParityImageTag = "ndc-mission-core-m48t-upstream-parity:1.9.4-cu130"
$ParityImageId = "sha256:ceb13548617e4bd3f619766bfdff00af3fa5160946b367828da6d2233dcdcba0"
function Assert-LastExitCode([string]$Operation) {
if ($LASTEXITCODE -ne 0) { throw "$Operation failed with exit code $LASTEXITCODE" }
}
function Resolve-DDirectory([string]$Path, [string]$Label, [bool]$Create) {
if ($Create -and -not (Test-Path -LiteralPath $Path)) {
$null = New-Item -ItemType Directory -Path $Path
}
$item = Get-Item -LiteralPath (Resolve-Path -LiteralPath $Path).Path -Force
if (
-not $item.PSIsContainer -or
($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
[IO.Path]::GetPathRoot($item.FullName).TrimEnd("\") -ine "D:"
) { throw "$Label must be a real D: directory" }
return $item.FullName
}
function Resolve-DFile([string]$Path, [string]$Label) {
$item = Get-Item -LiteralPath (Resolve-Path -LiteralPath $Path).Path -Force
if (
$item.PSIsContainer -or
($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
[IO.Path]::GetPathRoot($item.FullName).TrimEnd("\") -ine "D:"
) { throw "$Label must be a real D: file" }
return $item.FullName
}
function Convert-ToDockerPath([string]$Path) { return ($Path -replace "\\", "/") }
function Get-Container([string]$Name) {
$rows = @(((& docker inspect $Name) | ConvertFrom-Json))
Assert-LastExitCode "Docker inspection for $Name"
if ($rows.Count -ne 1) { throw "Container identity for $Name is not unique" }
return $rows[0]
}
function Assert-Image([string]$Tag, [string]$ExpectedId) {
$rows = @(((& docker image inspect $Tag) | ConvertFrom-Json))
Assert-LastExitCode "Docker image inspection for $Tag"
if ($rows.Count -ne 1 -or [string]$rows[0].Id -cne $ExpectedId) {
throw "Pinned image identity changed for $Tag"
}
}
function Remove-ExactContainer([string]$Name) {
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$Name$") {
& docker rm --force $Name *> $null
}
}
if ($env:COMPUTERNAME -cne "DESKTOP-OPJ8J04") { throw "M49 TGS full shadow is pinned to Worker 006" }
$release = Resolve-DDirectory $ReleaseRoot "M49 TGS full-shadow release" $false
$payload = Resolve-DDirectory (Join-Path $release "payload") "M49 TGS full-shadow payload" $false
$sourcePack = Resolve-DFile $SourcePackPath "RAVNOVES00 source pack"
$output = Resolve-DDirectory $OutputRoot "M49 TGS full-shadow output root" $true
$runCandidate = Join-Path $output $RunId
if (Test-Path -LiteralPath $runCandidate) { throw "M49 TGS full-shadow output already exists" }
$null = New-Item -ItemType Directory -Path $runCandidate
$runOutput = Resolve-DDirectory $runCandidate "M49 TGS full-shadow run output" $false
$releaseDocument = Get-Content -LiteralPath (Join-Path $payload "release.json") -Raw | ConvertFrom-Json
if (
$releaseDocument.schema_version -cne "missioncore.m49-tgs-full-shadow-worker-release/v1" -or
$releaseDocument.worker_id -cne "worker-006" -or
$releaseDocument.candidate_id -cne "travel-tgs-full-shadow"
) { throw "M49 TGS full-shadow release contract changed" }
foreach ($property in $releaseDocument.files.PSObject.Properties) {
$path = Join-Path $payload $property.Name
$actual = (Get-FileHash -Algorithm SHA256 -LiteralPath $path).Hash.ToLowerInvariant()
if ($actual -cne [string]$property.Value.sha256) {
throw "M49 TGS full-shadow payload digest changed: $($property.Name)"
}
}
$sourcePackSha = (Get-FileHash -Algorithm SHA256 -LiteralPath $sourcePack).Hash.ToLowerInvariant()
if ($sourcePackSha -cne "0685d24219d8236caf8b7f1685e93f6d6b59e7fd015a768d88a92bbe8b154944") {
throw "RAVNOVES00 source pack digest changed"
}
$os = Get-CimInstance Win32_OperatingSystem
$freeMemoryGiB = [double]$os.FreePhysicalMemory / 1MB
if ($freeMemoryGiB -lt 24.0) {
throw ("M49 TGS full shadow requires 24 GiB free memory; observed {0:N2} GiB" -f $freeMemoryGiB)
}
$tritonBefore = Get-Container "ndc-mission-core-triton"
if (-not $tritonBefore.State.Running -or $tritonBefore.State.Health.Status -cne "healthy") {
throw "Canonical Mission Core Triton must remain healthy during M49 TGS full shadow"
}
Assert-Image $TravelImageTag $TravelImageId
Assert-Image $ParityImageTag $ParityImageId
$prepareName = "ndc-mission-core-m49-tgs-full-prepare-$RunId"
$runName = "ndc-mission-core-m49-tgs-full-run-$RunId"
$analyzeName = "ndc-mission-core-m49-tgs-full-analyze-$RunId"
foreach ($name in @($prepareName, $runName, $analyzeName)) {
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$name$") {
throw "M49 TGS full-shadow container name already exists: $name"
}
}
$started = [DateTimeOffset]::UtcNow
try {
& docker run --rm --name $prepareName --network none --cpus 8 --memory 16g `
--entrypoint python3 `
--volume ((Convert-ToDockerPath $sourcePack) + ":/source/lidar-pack.npz:ro") `
--volume ((Convert-ToDockerPath $payload) + ":/release:ro") `
--volume ((Convert-ToDockerPath $runOutput) + ":/tgs") `
$ParityImageTag /release/prepare_tgs_full_shadow_inputs.py `
--source-pack /source/lidar-pack.npz `
--config /release/m49-tgs-full-shadow-v1.json `
--output-root /tgs/inputs
Assert-LastExitCode "M49 TGS full-shadow input preparation"
& docker run --rm --name $runName --network none --cpus 16 --memory 24g `
--entrypoint /bin/bash `
--volume ((Convert-ToDockerPath $payload) + ":/release:ro") `
--volume ((Convert-ToDockerPath $runOutput) + ":/tgs") `
$TravelImageTag /release/run_tgs_full_shadow.sh
Assert-LastExitCode "M49 source-paced TGS full-shadow run"
& docker run --rm --name $analyzeName --network none --cpus 8 --memory 16g `
--entrypoint python3 `
--volume ((Convert-ToDockerPath $payload) + ":/release:ro") `
--volume ((Convert-ToDockerPath $runOutput) + ":/tgs") `
$ParityImageTag /release/build_tgs_full_shadow_evidence.py `
--run-root /tgs `
--config /release/m49-tgs-full-shadow-v1.json `
--output-root /tgs/evidence
Assert-LastExitCode "M49 TGS full-shadow evidence analysis"
} finally {
foreach ($name in @($prepareName, $runName, $analyzeName)) { Remove-ExactContainer $name }
}
$completed = [DateTimeOffset]::UtcNow
$resultPath = Join-Path $runOutput "evidence\result.json"
if (-not (Test-Path -LiteralPath $resultPath -PathType Leaf)) { throw "M49 TGS full-shadow result is missing" }
$result = Get-Content -LiteralPath $resultPath -Raw | ConvertFrom-Json
if (
$result.timeline.frame_count -ne 4489 -or
$result.timeline.available_lidar_frame_count -ne 3928 -or
$result.timeline.missing_lidar_frame_count -ne 561 -or
$result.point_accounting.unaccounted -ne 0
) { throw "M49 TGS full-shadow structural acceptance failed" }
$tritonAfter = Get-Container "ndc-mission-core-triton"
if (
-not $tritonAfter.State.Running -or
$tritonAfter.State.Health.Status -cne "healthy" -or
[string]$tritonAfter.Id -cne [string]$tritonBefore.Id
) { throw "Canonical Mission Core Triton changed during M49 TGS full shadow" }
$summary = [ordered]@{
schema_version = "missioncore.m49-tgs-full-shadow-worker-summary/v1"
worker_id = "worker-006"
run_id = $RunId
code_revision = [string]$releaseDocument.code_revision
source_pack_sha256 = $sourcePackSha
travel_image_id = $TravelImageId
parity_image_id = $ParityImageId
started_utc = $started.ToString("o")
wall_seconds = [math]::Round(($completed - $started).TotalSeconds, 6)
free_memory_gib_before = [math]::Round($freeMemoryGiB, 6)
canonical_triton_id = [string]$tritonAfter.Id
canonical_triton_health = [string]$tritonAfter.State.Health.Status
result_status = [string]$result.status
all_timeline_frames_accounted = $true
all_eligible_points_accounted = $true
aos_used = $false
gpu_requested = $false
integrated_graph_performance_accepted = $false
navigation_or_actuation_allowed = $false
}
$summary | ConvertTo-Json -Depth 3 | Set-Content -LiteralPath (Join-Path $runOutput "worker-summary.json") -Encoding utf8
$summary | ConvertTo-Json -Depth 3
@@ -0,0 +1,45 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ReleaseRoot,
[Parameter(Mandatory = $true)]
[ValidatePattern("^[A-Za-z0-9._-]{1,96}$")]
[string]$RunId
)
$ErrorActionPreference = "Stop"
$taskName = "MissionCore-M49TgsFullShadow"
$release = (Resolve-Path -LiteralPath $ReleaseRoot).Path
$runner = Join-Path $release "payload\Invoke-M49TgsFullShadow.ps1"
if (-not (Test-Path -LiteralPath $runner -PathType Leaf)) { throw "M49 TGS full-shadow runner is missing" }
$existing = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($existing -and $existing.State -eq "Running") { throw "$taskName is already running" }
$powerShell = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
$arguments = @(
"-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass",
"-File", "`"$runner`"", "-ReleaseRoot", "`"$release`"", "-RunId", "`"$RunId`""
) -join " "
$userId = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
$action = New-ScheduledTaskAction -Execute $powerShell -Argument $arguments -WorkingDirectory $release
$principal = New-ScheduledTaskPrincipal -UserId $userId -LogonType Interactive -RunLevel Limited
$trigger = New-ScheduledTaskTrigger -Once -At ((Get-Date).AddMinutes(30))
$settings = New-ScheduledTaskSettingsSet `
-AllowStartIfOnBatteries `
-DontStopIfGoingOnBatteries `
-StartWhenAvailable `
-ExecutionTimeLimit ([TimeSpan]::FromHours(2))
Register-ScheduledTask `
-TaskName $taskName `
-Action $action `
-Principal $principal `
-Trigger $trigger `
-Settings $settings `
-Description "One-shot CPU-only source-paced TGS full shadow." `
-Force | Out-Null
Start-ScheduledTask -TaskName $taskName
[pscustomobject]@{
task_name = $taskName
run_id = $RunId
release_root = $release
state = (Get-ScheduledTask -TaskName $taskName).State.ToString()
} | ConvertTo-Json -Compress
@@ -0,0 +1,455 @@
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$ReleaseRoot,
[Parameter(Mandatory = $true)]
[string]$CandidateRoot,
[Parameter(Mandatory = $true)]
[ValidatePattern("^[a-f0-9]{64}$")]
[string]$ExpectedArtifactSha256,
[Parameter(Mandatory = $true)]
[ValidatePattern("^[A-Za-z0-9._-]{1,96}$")]
[string]$RunId,
[ValidateRange(1.0, 120.0)]
[double]$SourceRateHz = 12.0,
[string]$OutputRoot = (
"D:\NDC_MISSIONCORE\runtime\results\m49-tgs-integrated-graph-shadow"
)
)
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
$TravelImageTag = "ndc/mission-core-m49-t3-travel:20260826"
$TravelImageId = "sha256:7b412020f4d8392d1d1ed1b33beadc44140f0ea8f781e62dd69796042334300f"
$ParityImageTag = "ndc-mission-core-m48t-upstream-parity:1.9.4-cu130"
$ParityImageId = "sha256:ceb13548617e4bd3f619766bfdff00af3fa5160946b367828da6d2233dcdcba0"
$RuntimeImage = (
"nvcr.io/nvidia/tritonserver:26.06-py3@" +
"sha256:58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794"
)
function Assert-LastExitCode([string]$Operation) {
if ($LASTEXITCODE -ne 0) { throw "$Operation failed with exit code $LASTEXITCODE" }
}
function Get-Sha256([string]$Path) {
return (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
}
function Resolve-DDirectory([string]$Path, [string]$Label, [bool]$Create) {
if ($Create -and -not (Test-Path -LiteralPath $Path)) {
$null = New-Item -ItemType Directory -Path $Path
}
$item = Get-Item -LiteralPath (Resolve-Path -LiteralPath $Path).Path -Force
if (
-not $item.PSIsContainer -or
($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
[IO.Path]::GetPathRoot($item.FullName).TrimEnd("\") -ine "D:"
) { throw "$Label must be a real D: directory" }
return $item.FullName
}
function Resolve-DFile([string]$Path, [string]$Label) {
$item = Get-Item -LiteralPath (Resolve-Path -LiteralPath $Path).Path -Force
if (
$item.PSIsContainer -or
($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
[IO.Path]::GetPathRoot($item.FullName).TrimEnd("\") -ine "D:"
) { throw "$Label must be a real D: file" }
return $item.FullName
}
function Convert-ToDockerPath([string]$Path) { return ($Path -replace "\\", "/") }
function Get-Container([string]$Name) {
$rows = @(((& docker inspect $Name) | ConvertFrom-Json))
Assert-LastExitCode "Docker inspection for $Name"
if ($rows.Count -ne 1) { throw "Container identity for $Name is not unique" }
return $rows[0]
}
function Assert-Image([string]$Tag, [string]$ExpectedId) {
$rows = @(((& docker image inspect $Tag) | ConvertFrom-Json))
Assert-LastExitCode "Docker image inspection for $Tag"
if ($rows.Count -ne 1 -or [string]$rows[0].Id -cne $ExpectedId) {
throw "Pinned image identity changed for $Tag"
}
}
function Remove-ExactContainer([string]$Name) {
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$Name$") {
& docker rm --force $Name *> $null
}
}
function Wait-Healthy([string]$Name) {
foreach ($attempt in 1..60) {
Start-Sleep -Seconds 2
$container = Get-Container $Name
if (-not $container.State.Running) {
& docker logs $Name
throw "$Name stopped during startup"
}
if ($container.State.Health.Status -ceq "healthy") { return }
}
throw "$Name did not become healthy"
}
function Wait-SharedReady(
[string]$GraphReady,
[string]$TgsReady,
[string]$GraphName,
[string]$TgsName
) {
$deadline = [DateTimeOffset]::UtcNow.AddMinutes(10)
while (-not ((Test-Path -LiteralPath $GraphReady) -and (Test-Path -LiteralPath $TgsReady))) {
foreach ($name in @($GraphName, $TgsName)) {
$container = Get-Container $name
if (-not $container.State.Running) {
& docker logs $name
throw "$name stopped before shared-start readiness"
}
}
if ([DateTimeOffset]::UtcNow -ge $deadline) {
throw "M49 integrated shared-start readiness timed out"
}
Start-Sleep -Milliseconds 250
}
}
if ($env:COMPUTERNAME -cne "DESKTOP-OPJ8J04") {
throw "M49 integrated TGS graph shadow is pinned to Worker 006"
}
$release = Resolve-DDirectory $ReleaseRoot "M49 integrated release root" $false
$payload = Resolve-DDirectory (Join-Path $release "payload") "M49 integrated payload" $false
$candidate = Resolve-DDirectory $CandidateRoot "M49 native candidate root" $false
$output = Resolve-DDirectory $OutputRoot "M49 integrated output root" $true
$runCandidate = Join-Path $output $RunId
if (Test-Path -LiteralPath $runCandidate) { throw "M49 integrated output already exists" }
$null = New-Item -ItemType Directory -Path $runCandidate
$runOutput = Resolve-DDirectory $runCandidate "M49 integrated run output" $false
foreach ($directory in @("bin", "control", "graph", "tgs")) {
$null = New-Item -ItemType Directory -Path (Join-Path $runOutput $directory)
}
$releaseDocument = Get-Content -LiteralPath (Join-Path $payload "release.json") -Raw | ConvertFrom-Json
if (
$releaseDocument.schema_version -cne "missioncore.m49-tgs-integrated-graph-worker-release/v1" -or
$releaseDocument.worker_id -cne "worker-006" -or
$releaseDocument.transition -cne "m49-tgs-native-risk-integrated-shadow/v1"
) { throw "M49 integrated release contract changed" }
foreach ($property in $releaseDocument.files.PSObject.Properties) {
$path = Join-Path $payload $property.Name
if ((Get-Sha256 $path) -cne [string]$property.Value.sha256) {
throw "M49 integrated payload digest changed: $($property.Name)"
}
}
$wheelSha256 = [string]$releaseDocument.files."nodedc_mission_core-0.1.0-py3-none-any.whl".sha256
$runnerSha256 = [string]$releaseDocument.files."run_m48s_reference_graph_shadow_worker.py".sha256
$source = [ordered]@{
CameraIndex = (
"D:\NDC_MISSIONCORE\runtime\jobs\recorded-camera-602ac89026ed12978619801d" +
"\input\camera\sensor.camera.right\epoch-1\index.jsonl"
)
SourcePack = (
"D:\NDC_MISSIONCORE\runtime\derived" +
"\e10-lidar-pack-576c994a6c814e2592dd6240ace3902a5db94843312c759a73ba0c9166157d2b" +
"\lidar-pack.npz"
)
LocalSurface = (
"D:\NDC_MISSIONCORE\runtime\derived" +
"\k1-local-surface-23762244c8bdb97de26fb721ac957d7a00bc9a63571ac4cfa4be19c4effc7d55" +
"\local-surface.npz"
)
Video = (
"D:\NDC_MISSIONCORE\runtime\experiments\e46e\inputs" +
"\right-cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8.mp4"
)
Mask = (
"D:\NDC_MISSIONCORE\runtime\inputs\e2" +
"\valid-fov-mask-b4dd8ddf2b87c1d520ee8a0868c4fea062d7c14d1bae73ccabd3abe1f3acbac2" +
"\mask.png"
)
}
foreach ($entry in $source.GetEnumerator()) {
$null = Resolve-DFile $entry.Value "M49 source $($entry.Key)"
}
if ((Get-Sha256 $source.SourcePack) -cne [string]$releaseDocument.source_pack_sha256) {
throw "RAVNOVES00 source pack digest changed"
}
$nativeConfig = Resolve-DFile (
(Join-Path $payload "rf_detr_large_native_kb4_config.pbtxt")
) "native RF-DETR config"
$nativeEngine = Resolve-DFile (
(Join-Path $candidate "rf-detr-native-uint8.plan")
) "native RF-DETR engine"
if ((Get-Sha256 $nativeEngine) -cne "b8a40b3580edff001ec9680de68707242294ff590ab296000fae371f1083f695") {
throw "native RF-DETR engine SHA-256 changed"
}
$modelRoot = Join-Path $runOutput "triton-models"
$modelDirectory = Join-Path $modelRoot "rf_detr_large_native_kb4"
$modelVersionDirectory = Join-Path $modelDirectory "1"
$null = New-Item -ItemType Directory -Path $modelVersionDirectory
Copy-Item -LiteralPath $nativeConfig -Destination (Join-Path $modelDirectory "config.pbtxt")
Copy-Item -LiteralPath $nativeEngine -Destination (Join-Path $modelVersionDirectory "model.plan")
$media = Resolve-DDirectory (
"D:\NDC_MISSIONCORE\runtime\derived\perception-e15-media-pyav180-lz445-v1"
) "PyAV dependency" $false
$opencv = Resolve-DDirectory (
"D:\NDC_MISSIONCORE\runtime\derived\perception-e3-opencv413092-v1\packages"
) "OpenCV dependency" $false
$pillow = Resolve-DDirectory (
"D:\NDC_MISSIONCORE\runtime\derived\perception-p0-env-v1"
) "Pillow dependency" $false
Assert-Image $TravelImageTag $TravelImageId
Assert-Image $ParityImageTag $ParityImageId
& docker image inspect $RuntimeImage *> $null
Assert-LastExitCode "pinned runtime image inspection"
$os = Get-CimInstance Win32_OperatingSystem
$freeMemoryGiB = [double]$os.FreePhysicalMemory / 1MB
if ($freeMemoryGiB -lt 24.0) {
throw ("M49 integrated shadow requires 24 GiB free memory; observed {0:N2} GiB" -f $freeMemoryGiB)
}
$canonicalBefore = Get-Container "ndc-mission-core-triton"
if (-not $canonicalBefore.State.Running -or $canonicalBefore.State.Health.Status -cne "healthy") {
throw "Canonical Mission Core Triton must remain healthy"
}
$canonicalId = [string]$canonicalBefore.Id
$prepareName = "ndc-mission-core-m49-integrated-prepare-$RunId"
$compileName = "ndc-mission-core-m49-integrated-compile-$RunId"
$tritonName = "ndc-mission-core-m49-integrated-triton-$RunId"
$graphName = "ndc-mission-core-m49-integrated-graph-$RunId"
$tgsName = "ndc-mission-core-m49-integrated-tgs-$RunId"
$analyzeName = "ndc-mission-core-m49-integrated-analyze-$RunId"
$evidenceName = "ndc-mission-core-m49-integrated-evidence-$RunId"
$containers = @($prepareName, $compileName, $tritonName, $graphName, $tgsName, $analyzeName, $evidenceName)
foreach ($name in $containers) {
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$name$") {
throw "M49 integrated container name already exists: $name"
}
}
$started = [DateTimeOffset]::UtcNow
try {
& docker run --rm --name $prepareName --network none --cpus 8 --memory 16g `
--entrypoint python3 `
--volume ((Convert-ToDockerPath $source.SourcePack) + ":/source/lidar-pack.npz:ro") `
--volume ((Convert-ToDockerPath $payload) + ":/release:ro") `
--volume ((Convert-ToDockerPath (Join-Path $runOutput "tgs")) + ":/tgs") `
$ParityImageTag /release/prepare_tgs_full_shadow_inputs.py `
--source-pack /source/lidar-pack.npz `
--config /release/m49-tgs-full-shadow-v1.json `
--output-root /tgs/inputs
Assert-LastExitCode "M49 integrated TGS input preparation"
& docker run --rm --name $compileName --network none --cpus 8 --memory 8g `
--entrypoint /bin/bash `
--volume ((Convert-ToDockerPath $payload) + ":/release:ro") `
--volume ((Convert-ToDockerPath (Join-Path $runOutput "bin")) + ":/out") `
$TravelImageTag /release/build_tgs_full_shadow_binary.sh /out/run_tgs_full_shadow
Assert-LastExitCode "M49 integrated TGS binary build"
& docker create --name $tritonName `
--read-only --security-opt "no-new-privileges:true" --cap-drop ALL `
--pids-limit 512 --shm-size 1g --gpus all `
--tmpfs "/tmp:rw,noexec,nosuid,size=2g" `
--health-cmd "curl --fail --silent http://127.0.0.1:8000/v2/health/ready" `
--health-interval 5s --health-timeout 3s --health-start-period 20s --health-retries 24 `
-v ((Convert-ToDockerPath $modelRoot) + ":/models:ro") `
$RuntimeImage tritonserver --model-repository=/models `
--model-control-mode=explicit --load-model=rf_detr_large_native_kb4 `
--disable-auto-complete-config --strict-readiness=true --exit-on-error=true `
--allow-http=true --allow-grpc=false --allow-metrics=false *> $null
Assert-LastExitCode "M49 integrated Triton creation"
& docker start $tritonName *> $null
Assert-LastExitCode "M49 integrated Triton start"
Wait-Healthy $tritonName
$dockerRelease = Convert-ToDockerPath $payload
$dockerRun = Convert-ToDockerPath $runOutput
$rate = [string]::Format([Globalization.CultureInfo]::InvariantCulture, "{0:R}", $SourceRateHz)
$graphArguments = @(
"create", "--name", $graphName,
"--network", ("container:{0}" -f $tritonName),
"--read-only", "--security-opt", "no-new-privileges:true", "--cap-drop", "ALL",
"--pids-limit", "256", "--gpus", "all",
"--tmpfs", "/tmp:rw,noexec,nosuid,size=2g",
"-e", "PYTHONDONTWRITEBYTECODE=1",
"-e", "PYTHONPATH=/release/nodedc_mission_core-0.1.0-py3-none-any.whl:/opt/media:/opt/opencv:/opt/pillow",
"-v", ("{0}:/release:ro" -f $dockerRelease),
"-v", ("{0}:/shared:rw" -f $dockerRun),
"-v", ((Convert-ToDockerPath $media) + ":/opt/media:ro"),
"-v", ((Convert-ToDockerPath $opencv) + ":/opt/opencv:ro"),
"-v", ((Convert-ToDockerPath $pillow) + ":/opt/pillow:ro"),
"-v", ((Convert-ToDockerPath $source.CameraIndex) + ":/source/camera-index.jsonl:ro"),
"-v", ((Convert-ToDockerPath $source.SourcePack) + ":/source/source-pack.npz:ro"),
"-v", ((Convert-ToDockerPath $source.LocalSurface) + ":/source/local-surface.npz:ro"),
"-v", ((Convert-ToDockerPath $source.Video) + ":/source/right.mp4:ro"),
"-v", ((Convert-ToDockerPath $source.Mask) + ":/source/mask.png:ro"),
"--entrypoint", "python3", $RuntimeImage,
"/release/run_m48s_reference_graph_shadow_worker.py",
"--graph-config", "/release/m48n-rf-detr-native-reference-graph-shadow-v0.json",
"--baseline-profile", "/release/m4-recorded-realtime-baseline-v1.json",
"--detector-profile", "/release/rf-detr-large-native-kb4-risk-shadow-v0.json",
"--geometry-profile", "/release/m4-geometry-association-v1.json",
"--temporal-motion-profile", "/release/m4-temporal-motion-v1.json",
"--rolling-map-profile", "/release/m4-rolling-local-map-v1.json",
"--threat-profile", "/release/m4-replay-threat-v3.json",
"--camera-index", "/source/camera-index.jsonl",
"--source-pack", "/source/source-pack.npz",
"--local-surface", "/source/local-surface.npz",
"--video", "/source/right.mp4",
"--valid-fov-mask", "/source/mask.png",
"--triton-origin", "http://127.0.0.1:8000",
"--loops", "1", "--maximum-frames", "4489", "--source-rate-hz", $rate,
"--minimum-delivery-ratio", "1.0",
"--minimum-effective-world-state-fps", "11.209069",
"--maximum-world-state-completion-p95-ms", "125.0",
"--load-purpose", "reserve-gate",
"--runtime-artifact-sha256", $wheelSha256,
"--runner-sha256", $runnerSha256,
"--shared-start-ready-file", "/shared/control/graph.ready",
"--shared-start-file", "/shared/control/start.signal",
"--output", "/shared/graph/result.json",
"--progress", "/shared/graph/progress.jsonl",
"--frame-ledger", "/shared/graph/frames.jsonl"
)
& docker @graphArguments *> $null
Assert-LastExitCode "M49 integrated graph creation"
& docker create --name $tgsName --network none --cpus 16 --memory 24g `
--read-only --security-opt "no-new-privileges:true" --cap-drop ALL `
--pids-limit 256 --tmpfs "/tmp:rw,noexec,nosuid,size=1g" `
-e ("M49_SOURCE_RATE_HZ={0}" -f $rate) `
--entrypoint /bin/bash `
--volume ($dockerRelease + ":/release:ro") `
--volume ($dockerRun + ":/shared:rw") `
$TravelImageTag /release/run_tgs_integrated_shadow.sh *> $null
Assert-LastExitCode "M49 integrated TGS creation"
& docker start $graphName *> $null
Assert-LastExitCode "M49 integrated graph start"
& docker start $tgsName *> $null
Assert-LastExitCode "M49 integrated TGS start"
$graphReady = Join-Path $runOutput "control\graph.ready"
$tgsReady = Join-Path $runOutput "control\tgs.ready"
Wait-SharedReady $graphReady $tgsReady $graphName $tgsName
[DateTimeOffset]::UtcNow.ToString("o") | Set-Content -LiteralPath (
Join-Path $runOutput "control\start.signal"
) -Encoding utf8
$telemetryPath = Join-Path $runOutput "container-telemetry.jsonl"
while ($true) {
$graphState = Get-Container $graphName
$tgsState = Get-Container $tgsName
$running = @()
if ($graphState.State.Running) { $running += $graphName }
if ($tgsState.State.Running) { $running += $tgsName }
if ((Get-Container $tritonName).State.Running) { $running += $tritonName }
if ($running.Count -gt 0) {
$stats = @((& docker stats --no-stream --format "{{json .}}" @running))
Assert-LastExitCode "M49 integrated container telemetry"
foreach ($line in $stats) {
$value = $line | ConvertFrom-Json
$role = if ($value.Name -ceq $graphName) {
"graph"
} elseif ($value.Name -ceq $tgsName) {
"tgs"
} elseif ($value.Name -ceq $tritonName) {
"triton"
} else {
throw "Unknown M49 telemetry container"
}
[ordered]@{
observed_utc = [DateTimeOffset]::UtcNow.ToString("o")
role = $role
name = [string]$value.Name
cpu_percent = [string]$value.CPUPerc
memory_usage = [string]$value.MemUsage
memory_percent = [string]$value.MemPerc
pids = [string]$value.PIDs
} | ConvertTo-Json -Compress | Out-File -LiteralPath $telemetryPath -Encoding utf8 -Append
}
}
if (-not $graphState.State.Running -and -not $tgsState.State.Running) { break }
Start-Sleep -Seconds 1
}
$graphExit = [int](Get-Container $graphName).State.ExitCode
$tgsExit = [int](Get-Container $tgsName).State.ExitCode
$previousErrorAction = $ErrorActionPreference
$ErrorActionPreference = "Continue"
$graphLogs = & docker logs $graphName 2>&1
$tgsLogs = & docker logs $tgsName 2>&1
$ErrorActionPreference = $previousErrorAction
$graphLogs | Set-Content -LiteralPath (Join-Path $runOutput "graph.log") -Encoding utf8
$tgsLogs | Set-Content -LiteralPath (Join-Path $runOutput "tgs.log") -Encoding utf8
if ($graphExit -ne 0) { throw "M49 integrated graph failed with exit code $graphExit" }
if ($tgsExit -ne 0) { throw "M49 integrated TGS failed with exit code $tgsExit" }
& docker run --rm --name $analyzeName --network none --cpus 8 --memory 16g `
--entrypoint python3 `
--volume ($dockerRelease + ":/release:ro") `
--volume ($dockerRun + ":/shared:rw") `
$ParityImageTag /release/build_tgs_full_shadow_evidence.py `
--run-root /shared/tgs `
--config /release/m49-tgs-full-shadow-v1.json `
--output-root /shared/tgs/evidence
Assert-LastExitCode "M49 integrated TGS evidence analysis"
& docker run --rm --name $evidenceName --network none --cpus 4 --memory 8g `
--entrypoint python3 `
--volume ($dockerRelease + ":/release:ro") `
--volume ($dockerRun + ":/shared:rw") `
$ParityImageTag /release/build_tgs_integrated_graph_evidence.py `
--profile /release/m49-tgs-integrated-graph-shadow-v1.json `
--graph-result /shared/graph/result.json `
--graph-frames /shared/graph/frames.jsonl `
--tgs-result /shared/tgs/evidence/result.json `
--tgs-timing /shared/tgs/tgs-full-timing.tsv `
--telemetry /shared/container-telemetry.jsonl `
--output /shared/result.json `
--release-sha256 $ExpectedArtifactSha256
Assert-LastExitCode "M49 integrated evidence gate"
} finally {
foreach ($name in $containers) { Remove-ExactContainer $name }
$canonicalAfter = Get-Container "ndc-mission-core-triton"
if (
[string]$canonicalAfter.Id -cne $canonicalId -or
-not $canonicalAfter.State.Running -or
$canonicalAfter.State.Health.Status -cne "healthy"
) { throw "Canonical Mission Core Triton changed during M49 integrated shadow" }
}
$completed = [DateTimeOffset]::UtcNow
$resultPath = Join-Path $runOutput "result.json"
if (-not (Test-Path -LiteralPath $resultPath -PathType Leaf)) {
throw "M49 integrated result is missing"
}
$result = Get-Content -LiteralPath $resultPath -Raw | ConvertFrom-Json
$summary = [ordered]@{
schema_version = "missioncore.m49-tgs-integrated-graph-worker-summary/v1"
worker_id = "worker-006"
run_id = $RunId
code_revision = [string]$releaseDocument.code_revision
source_rate_hz = $SourceRateHz
started_utc = $started.ToString("o")
completed_utc = $completed.ToString("o")
wall_seconds = [math]::Round(($completed - $started).TotalSeconds, 6)
free_memory_gib_before = [math]::Round($freeMemoryGiB, 6)
result_id = [string]$result.result_id
result_status = [string]$result.status
canonical_triton_id = $canonicalId
canonical_triton_health = "healthy"
gauss_or_playcanvas_action = "none"
durable_worker_action = "none"
navigation_or_actuation_allowed = $false
}
$summary | ConvertTo-Json -Depth 3 | Set-Content -LiteralPath (
Join-Path $runOutput "worker-summary.json"
) -Encoding utf8
$summary | ConvertTo-Json -Depth 3
@@ -0,0 +1,49 @@
FROM nvidia/cuda:12.8.1-cudnn-devel-ubuntu22.04@sha256:ad6d59a3bbf3e82c1c849c9ac09cfc2a3e0bbb8655042fd899be6681b3fe2a85
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
ENV DEBIAN_FRONTEND=noninteractive \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PATH=/opt/conda/bin:$PATH
ARG MINICONDA_INSTALLER=Miniconda3-py39_24.11.1-0-Linux-x86_64.sh
ARG MINICONDA_SHA256=3ea8373098d72140e08aac9217822b047ec094eb457e7f73945af7c6f68bf6f5
RUN apt-get update \
&& apt-get install --yes --no-install-recommends \
build-essential \
ca-certificates \
curl \
git \
libglib2.0-0 \
libgl1 \
&& curl --fail --location --retry 5 \
--output /tmp/miniconda.sh \
"https://repo.anaconda.com/miniconda/${MINICONDA_INSTALLER}" \
&& echo "${MINICONDA_SHA256} /tmp/miniconda.sh" | sha256sum --check --strict \
&& bash /tmp/miniconda.sh -b -p /opt/conda \
&& rm -f /tmp/miniconda.sh \
&& rm -rf /var/lib/apt/lists/*
RUN conda create --yes --name goose python=3.9 pip \
&& conda install --yes --name goose --channel pytorch --channel nvidia \
pytorch=1.13.1 torchvision=0.14.1 pytorch-cuda=11.7
RUN conda run --name goose python -m pip install --no-cache-dir \
cmake==3.31.6 \
numpy==1.23.0 \
onnxsim==0.4.36 \
opencv-python==4.8.1.78 \
protobuf==3.20.3 \
pyparsing==2.4.5
RUN conda run --name goose python -m pip install --no-cache-dir \
super-gradients==3.2.0 \
torchmetrics==0.8.0
RUN conda clean --all --yes
WORKDIR /opt/mission-core/lab-v1
COPY run_goose_vegetation_benchmark.py /opt/mission-core/lab-v1/runner.py
ENTRYPOINT ["conda", "run", "--no-capture-output", "--name", "goose", "python", "/opt/mission-core/lab-v1/runner.py"]
@@ -0,0 +1,777 @@
"""Run isolated GOOSE vegetation qualification and RAVNOVES shadow inference."""
from __future__ import annotations
import argparse
import csv
import hashlib
import json
import math
import os
import platform
import statistics
import time
import zipfile
from pathlib import Path
from typing import Any
import numpy as np
import torch
from PIL import Image
from super_gradients.training import models
SCHEMA = "missioncore.lab-v1-goose-vegetation-run/v1"
VISUAL_SCHEMA = "missioncore.lab-v1-goose-vegetation-visual-case/v1"
CLASS_COUNT = 64
MAX_CONFIG_BYTES = 1024 * 1024
MODEL_NAMES = {
"ddrnet": ("ddrnet_39",),
"ppliteseg": (
"pp_lite_t_seg",
"pp_lite_t_seg50",
"pp_lite_t_seg75",
"pp_lite_b_seg",
"pp_lite_b_seg50",
"pp_lite_b_seg75",
),
}
RESAMPLE_NEAREST = getattr(Image, "Resampling", Image).NEAREST
class RunnerError(RuntimeError):
"""The bounded runner input or output contract is invalid."""
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser()
parser.add_argument(
"--mode",
choices=("goose", "ravnoves", "ravnoves-video"),
required=True,
)
parser.add_argument("--candidate", choices=tuple(MODEL_NAMES), required=True)
parser.add_argument("--config", type=Path, required=True)
parser.add_argument("--policy", type=Path, required=True)
parser.add_argument("--provider-map", type=Path, required=True)
parser.add_argument("--checkpoint", type=Path, required=True)
parser.add_argument("--dataset-root", type=Path)
parser.add_argument("--frames-root", type=Path)
parser.add_argument("--output", type=Path, required=True)
parser.add_argument("--limit", type=int, default=0)
parser.add_argument("--visual-count", type=int, default=12)
return parser.parse_args()
def read_json(path: Path, label: str) -> dict[str, Any]:
if path.is_symlink() or not path.is_file() or path.stat().st_size > MAX_CONFIG_BYTES:
raise RunnerError(f"{label} is unavailable")
value = json.loads(path.read_text(encoding="utf-8-sig"))
if not isinstance(value, dict):
raise RunnerError(f"{label} must be an object")
return value
def sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for block in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
def stable_digest(value: object) -> str:
encoded = json.dumps(value, sort_keys=True, separators=(",", ":")).encode("utf-8")
return hashlib.sha256(encoded).hexdigest()
def validate_contracts(
config: dict[str, Any], policy: dict[str, Any], provider_map: dict[str, Any], candidate: str
) -> dict[str, Any]:
if config.get("schema_version") != "missioncore.lab-v1-goose-vegetation-benchmark/v1":
raise RunnerError("benchmark configuration identity changed")
if policy.get("schema_version") != "missioncore.vegetation-mission-policy/v1":
raise RunnerError("mission policy identity changed")
if provider_map.get("schema_version") != "missioncore.vegetation-provider-label-map/v1":
raise RunnerError("provider map identity changed")
invariants = config.get("invariants")
true_invariants = {"one_heavy_candidate_at_a_time", "raw_fisheye_is_immutable"}
if not isinstance(invariants, dict) or any(
value is not False for key, value in invariants.items() if key not in true_invariants
):
raise RunnerError("benchmark fail-closed invariants changed")
if (
invariants.get("one_heavy_candidate_at_a_time") is not True
or invariants.get("raw_fisheye_is_immutable") is not True
):
raise RunnerError("benchmark isolation invariants changed")
candidates = config.get("candidates")
if not isinstance(candidates, dict) or not isinstance(candidates.get(candidate), dict):
raise RunnerError("candidate is not configured")
expected_models = candidates[candidate].get("model_names")
if expected_models != list(MODEL_NAMES[candidate]):
raise RunnerError("candidate architecture probe order changed")
return candidates[candidate]
def load_mapping(path: Path, expected_sha256: str) -> tuple[dict[int, str], np.ndarray]:
if sha256(path) != expected_sha256:
raise RunnerError("GOOSE label mapping digest changed")
names: dict[int, str] = {}
palette = np.zeros((CLASS_COUNT, 4), dtype=np.uint8)
with path.open(newline="", encoding="utf-8-sig") as stream:
for row in csv.DictReader(stream):
label_id = int(row["label_key"])
if label_id < 0 or label_id >= CLASS_COUNT:
raise RunnerError("GOOSE label id is outside the 64-class contract")
color = row["hex"].lstrip("#")
names[label_id] = row["class_name"]
palette[label_id] = (*bytes.fromhex(color), 190)
if set(names) != set(range(CLASS_COUNT)):
raise RunnerError("GOOSE mapping does not cover exactly 64 classes")
palette[0, 3] = 0
return names, palette
def center_crop(image: Image.Image) -> tuple[Image.Image, tuple[int, int, int, int]]:
side = min(image.width, image.height)
left = (image.width - side) // 2
top = (image.height - side) // 2
box = (left, top, left + side, top + side)
return image.crop(box), box
def preprocess(image: Image.Image) -> tuple[torch.Tensor, tuple[int, int, int, int]]:
cropped, crop_box = center_crop(image.convert("RGB"))
resized = cropped.resize((512, 512), resample=RESAMPLE_NEAREST)
array = np.asarray(resized, dtype=np.float32) / 255.0
tensor = torch.from_numpy(np.transpose(array, (2, 0, 1))).unsqueeze(0)
return tensor, crop_box
def preprocess_label(image: Image.Image) -> np.ndarray:
cropped, _ = center_crop(image.convert("L"))
return np.asarray(cropped.resize((512, 512), resample=RESAMPLE_NEAREST), dtype=np.uint8)
def find_goose_pairs(root: Path) -> list[tuple[Path, Path]]:
pairs: list[tuple[Path, Path]] = []
image_root = root / "images" / "val"
label_root = root / "labels" / "val"
for image_path in sorted(image_root.rglob("*_windshield_vis.png")):
stem = image_path.name.removesuffix("_windshield_vis.png")
relative_parent = image_path.parent.relative_to(image_root)
label_path = label_root / relative_parent / f"{stem}_labelids.png"
if label_path.is_file() and not label_path.is_symlink():
pairs.append((image_path, label_path))
return pairs
def visual_indices(count: int, visual_count: int) -> dict[int, dict[str, Any]]:
if count <= 0 or visual_count <= 0:
return {}
selected_count = min(count, visual_count)
if selected_count == 1:
return {0: {}}
return {
round(index * (count - 1) / (selected_count - 1)): {}
for index in range(selected_count)
}
def truth_focused_visuals(
items: list[tuple[str, Path, Path | None]],
names: dict[int, str],
contract: dict[str, Any],
) -> dict[int, dict[str, Any]]:
if contract.get("selection_basis") != "ground-truth-class-support-only":
raise RunnerError("visual selection basis changed")
case_count = contract.get("case_count")
minimum_pixels = contract.get("minimum_focus_pixels")
strata = contract.get("strata")
if (
not isinstance(case_count, int)
or case_count <= 0
or not isinstance(minimum_pixels, int)
or minimum_pixels <= 0
or not isinstance(strata, list)
or sum(row.get("count", 0) for row in strata if isinstance(row, dict)) != case_count
):
raise RunnerError("visual case contract is invalid")
ids_by_name = {class_name: label_id for label_id, class_name in names.items()}
supports: list[dict[int, int]] = []
for _, _, label_path in items:
if label_path is None:
raise RunnerError("truth-focused selection requires labels")
truth = preprocess_label(Image.open(label_path))
values, counts = np.unique(truth, return_counts=True)
supports.append({int(value): int(count) for value, count in zip(values, counts)})
selected: dict[int, dict[str, Any]] = {}
for raw in strata:
if not isinstance(raw, dict):
raise RunnerError("visual stratum is invalid")
class_name = raw.get("class_name")
count = raw.get("count")
if class_name not in ids_by_name or not isinstance(count, int) or count <= 0:
raise RunnerError("visual stratum identity changed")
label_id = ids_by_name[class_name]
ranked = sorted(
(
(support.get(label_id, 0), items[index][0], index)
for index, support in enumerate(supports)
if index not in selected and support.get(label_id, 0) > 0
),
key=lambda row: (-row[0], row[1]),
)
admitted = [row for row in ranked if row[0] >= minimum_pixels]
if len(admitted) < count:
admitted = ranked
if len(admitted) < count:
raise RunnerError(f"visual stratum {class_name} has fewer than {count} cases")
for rank, (truth_pixels, _, index) in enumerate(admitted[:count], start=1):
selected[index] = {
"class_name": class_name,
"label_id": label_id,
"truth_pixels": truth_pixels,
"truth_fraction": round(truth_pixels / float(512 * 512), 8),
"stratum_rank": rank,
}
if len(selected) != case_count:
raise RunnerError("truth-focused visual selection did not produce the frozen case count")
return selected
def load_model(candidate: str, checkpoint: Path) -> tuple[torch.nn.Module, str, list[str]]:
failures: list[str] = []
for model_name in MODEL_NAMES[candidate]:
try:
model = models.get(
model_name=model_name,
num_classes=CLASS_COUNT,
checkpoint_path=str(checkpoint),
)
model.eval()
model.cuda()
return model, model_name, failures
except Exception as error: # noqa: BLE001 - each upstream architecture is a probe
failures.append(f"{model_name}: {type(error).__name__}: {str(error)[:240]}")
if torch.cuda.is_available():
torch.cuda.empty_cache()
raise RunnerError("checkpoint did not load: " + " | ".join(failures))
def logits_from_output(value: object) -> torch.Tensor:
if isinstance(value, torch.Tensor) and value.ndim == 4 and value.shape[1] == CLASS_COUNT:
return value
if isinstance(value, (list, tuple)):
for item in value:
try:
return logits_from_output(item)
except RunnerError:
continue
raise RunnerError("model output does not contain a 64-class raster")
def infer(model: torch.nn.Module, tensor: torch.Tensor) -> tuple[np.ndarray, float]:
tensor = tensor.cuda(non_blocking=True)
torch.cuda.synchronize()
started = time.perf_counter_ns()
with torch.inference_mode():
logits = logits_from_output(model(tensor))
prediction = torch.argmax(torch.sigmoid(logits), dim=1)
torch.cuda.synchronize()
elapsed_ms = (time.perf_counter_ns() - started) / 1_000_000.0
return prediction[0].to(device="cpu", dtype=torch.uint8).numpy(), elapsed_ms
def update_confusion(confusion: np.ndarray, truth: np.ndarray, prediction: np.ndarray) -> None:
valid = (truth >= 0) & (truth < CLASS_COUNT)
indices = CLASS_COUNT * truth[valid].astype(np.int64) + prediction[valid].astype(np.int64)
confusion += np.bincount(indices, minlength=CLASS_COUNT**2).reshape(CLASS_COUNT, CLASS_COUNT)
def class_metrics(confusion: np.ndarray, names: dict[int, str]) -> list[dict[str, Any]]:
truth = confusion.sum(axis=1)
predicted = confusion.sum(axis=0)
intersection = np.diag(confusion)
union = truth + predicted - intersection
rows: list[dict[str, Any]] = []
for label_id in range(CLASS_COUNT):
rows.append(
{
"label_id": label_id,
"class_name": names[label_id],
"support_pixels": int(truth[label_id]),
"predicted_pixels": int(predicted[label_id]),
"intersection_pixels": int(intersection[label_id]),
"union_pixels": int(union[label_id]),
"iou": round(float(intersection[label_id] / union[label_id]), 8)
if union[label_id]
else None,
}
)
return rows
def hex_rgb(value: str) -> tuple[int, int, int]:
raw = bytes.fromhex(value.removeprefix("#"))
if len(raw) != 3:
raise RunnerError("policy action color must be RGB")
return raw[0], raw[1], raw[2]
def policy_palette(
names: dict[int, str], policy: dict[str, Any], provider_map: dict[str, Any], preset: str,
action_colors: dict[str, str]
) -> np.ndarray:
palette = np.zeros((CLASS_COUNT, 4), dtype=np.uint8)
labels = provider_map["providers"]["goose-fine-64"]["labels"]
rules = policy["presets"][preset]
for label_id, class_name in names.items():
material = labels.get(class_name)
if material is None:
continue
action = rules[material]
palette[label_id] = (*hex_rgb(action_colors[action]), 190)
return palette
def save_image(path: Path, value: Image.Image | np.ndarray, mode: str | None = None) -> str:
path.parent.mkdir(parents=True, exist_ok=True)
image = value if isinstance(value, Image.Image) else Image.fromarray(value, mode=mode)
image.save(path, format="PNG", optimize=True)
return sha256(path)
def write_mask_archive(
output: Path,
masks_root: Path,
frame_count: int,
) -> dict[str, Any]:
archive_path = output / "semantic-masks.zip"
expected = [f"frame-{sequence + 1:06d}.png" for sequence in range(frame_count)]
actual = sorted(path.name for path in masks_root.glob("frame-*.png"))
if actual != expected:
raise RunnerError("RAVNOVES video mask sequence is incomplete")
with zipfile.ZipFile(
archive_path,
mode="x",
compression=zipfile.ZIP_STORED,
allowZip64=True,
) as archive:
for name in expected:
archive.write(masks_root / name, arcname=f"masks/{name}")
for path in masks_root.iterdir():
path.unlink()
masks_root.rmdir()
return {
"path": archive_path.name,
"sha256": sha256(archive_path),
"byte_length": archive_path.stat().st_size,
"media_type": "application/zip",
"frame_count": frame_count,
"width": 800,
"height": 600,
"encoding": "uint8-class-id-png",
"sequence_binding": "sequence-0-to-masks/frame-000001.png",
}
def expand_mask(
mask: np.ndarray,
original_size: tuple[int, int],
crop_box: tuple[int, int, int, int],
) -> np.ndarray:
left, top, right, bottom = crop_box
side = right - left
resized = Image.fromarray(mask, mode="L").resize((side, side), resample=RESAMPLE_NEAREST)
canvas = np.zeros((original_size[1], original_size[0]), dtype=np.uint8)
canvas[top:bottom, left:right] = np.asarray(resized, dtype=np.uint8)
return canvas
def write_visual_case(
output: Path,
case_id: str,
source: Image.Image,
prediction: np.ndarray,
semantic_palette: np.ndarray,
policy_palettes: dict[str, np.ndarray],
crop_box: tuple[int, int, int, int],
truth: np.ndarray | None = None,
focus: dict[str, Any] | None = None,
material_codes: np.ndarray | None = None,
error_colors: dict[str, list[int]] | None = None,
preserve_source_size: bool = False,
) -> dict[str, Any]:
case_root = output / "cases" / case_id
if preserve_source_size:
source_image = source.convert("RGB")
prediction_image = expand_mask(prediction, source_image.size, crop_box)
truth_image = expand_mask(truth, source_image.size, crop_box) if truth is not None else None
else:
cropped, _ = center_crop(source.convert("RGB"))
source_image = cropped.resize((512, 512), resample=RESAMPLE_NEAREST)
prediction_image = prediction
truth_image = truth
files: dict[str, dict[str, str]] = {}
source_path = case_root / "source.png"
files["source"] = {
"relative_path": source_path.relative_to(output).as_posix(),
"sha256": save_image(source_path, source_image),
}
prediction_path = case_root / "prediction-labelids.png"
files["prediction_labelids"] = {
"relative_path": prediction_path.relative_to(output).as_posix(),
"sha256": save_image(prediction_path, prediction_image, "L"),
}
semantic_path = case_root / "prediction-semantic.png"
files["prediction_semantic"] = {
"relative_path": semantic_path.relative_to(output).as_posix(),
"sha256": save_image(semantic_path, semantic_palette[prediction_image], "RGBA"),
}
for preset, palette in policy_palettes.items():
policy_path = case_root / f"policy-{preset}.png"
files[f"policy_{preset}"] = {
"relative_path": policy_path.relative_to(output).as_posix(),
"sha256": save_image(policy_path, palette[prediction_image], "RGBA"),
}
if truth_image is not None:
truth_path = case_root / "truth-labelids.png"
files["truth_labelids"] = {
"relative_path": truth_path.relative_to(output).as_posix(),
"sha256": save_image(truth_path, truth_image, "L"),
}
truth_semantic_path = case_root / "truth-semantic.png"
files["truth_semantic"] = {
"relative_path": truth_semantic_path.relative_to(output).as_posix(),
"sha256": save_image(truth_semantic_path, semantic_palette[truth_image], "RGBA"),
}
if material_codes is None or error_colors is None:
raise RunnerError("truth visual case requires the material-error contract")
truth_material = material_codes[truth_image]
predicted_material = material_codes[prediction_image]
truth_vegetation = truth_material > 0
predicted_vegetation = predicted_material > 0
error_overlay = np.zeros((*truth_image.shape, 4), dtype=np.uint8)
correct = truth_vegetation & (truth_material == predicted_material)
missed = truth_vegetation & ~predicted_vegetation
false_positive = ~truth_vegetation & predicted_vegetation
wrong_material = truth_vegetation & predicted_vegetation & (truth_material != predicted_material)
error_overlay[correct] = error_colors["correct_material_rgba"]
error_overlay[missed] = error_colors["missed_vegetation_rgba"]
error_overlay[false_positive] = error_colors["false_vegetation_rgba"]
error_overlay[wrong_material] = error_colors["wrong_vegetation_material_rgba"]
error_path = case_root / "vegetation-material-error.png"
files["vegetation_material_error"] = {
"relative_path": error_path.relative_to(output).as_posix(),
"sha256": save_image(error_path, error_overlay, "RGBA"),
}
return {
"schema_version": VISUAL_SCHEMA,
"case_id": case_id,
"source_width": source_image.width,
"source_height": source_image.height,
"center_crop_xyxy": list(crop_box),
"outside_crop_state": "undefined" if preserve_source_size else "not-applicable",
"focus": focus,
"files": files,
}
def percentile(values: list[float], fraction: float) -> float:
if not values:
return 0.0
ordered = sorted(values)
index = (len(ordered) - 1) * fraction
lower = math.floor(index)
upper = math.ceil(index)
if lower == upper:
return ordered[lower]
return ordered[lower] * (upper - index) + ordered[upper] * (index - lower)
def run() -> None:
args = parse_args()
if not torch.cuda.is_available():
raise RunnerError("CUDA is required for Worker 006 qualification")
if args.limit < 0 or args.visual_count < 0:
raise RunnerError("limit and visual-count must be non-negative")
config = read_json(args.config, "benchmark config")
policy = read_json(args.policy, "mission policy")
provider_map = read_json(args.provider_map, "provider map")
candidate_config = validate_contracts(config, policy, provider_map, args.candidate)
if args.checkpoint.is_symlink() or not args.checkpoint.is_file():
raise RunnerError("checkpoint is unavailable")
if args.checkpoint.stat().st_size != candidate_config["checkpoint_size_bytes"]:
raise RunnerError("checkpoint size changed")
checkpoint_sha256 = sha256(args.checkpoint)
if checkpoint_sha256 != candidate_config["checkpoint_sha256"]:
raise RunnerError("checkpoint digest changed")
dataset_config = config["dataset"]
mapping_root = args.dataset_root
if mapping_root is None:
raise RunnerError("dataset-root is required for the immutable mapping")
mapping_path = mapping_root / dataset_config["mapping_relative_path"]
names, semantic_palette = load_mapping(mapping_path, dataset_config["mapping_sha256"])
policy_palettes = {
preset: policy_palette(
names,
policy,
provider_map,
preset,
config["policy_action_colors"],
)
for preset in ("urban", "rural", "offroad")
}
provider_labels = provider_map["providers"]["goose-fine-64"]["labels"]
vegetation_materials = sorted(
{
material
for material in provider_labels.values()
if material in {
"grass",
"herbaceous_vegetation",
"cultivated_vegetation",
"woody_shrub",
"tree_or_trunk",
"vegetation_unknown",
}
}
)
material_code_by_name = {
material: index for index, material in enumerate(vegetation_materials, start=1)
}
material_codes = np.zeros(CLASS_COUNT, dtype=np.uint8)
for label_id, class_name in names.items():
material_codes[label_id] = material_code_by_name.get(provider_labels.get(class_name), 0)
if args.mode == "goose":
pairs = find_goose_pairs(mapping_root)
if len(pairs) != dataset_config["expected_pair_count"]:
raise RunnerError(f"GOOSE pair count changed: {len(pairs)}")
items: list[tuple[str, Path, Path | None]] = [
(image.stem.removesuffix("_windshield_vis"), image, label)
for image, label in pairs
]
elif args.mode == "ravnoves":
if args.frames_root is None or not args.frames_root.is_dir():
raise RunnerError("frames-root is required for RAVNOVES mode")
frames = sorted(args.frames_root.glob("frame-*.png"))
expected = {f"frame-{index:06d}" for index in config["ravnoves"]["frame_indices"]}
if {frame.stem for frame in frames} != expected:
raise RunnerError("RAVNOVES frame island identity changed")
items = [(frame.stem, frame, None) for frame in frames]
else:
if args.frames_root is None or not args.frames_root.is_dir():
raise RunnerError("frames-root is required for RAVNOVES video mode")
frames = sorted(args.frames_root.glob("frame-*.png"))
expected_count = config["ravnoves"]["expected_frame_count"]
expected_names = [f"frame-{sequence + 1:06d}.png" for sequence in range(expected_count)]
if len(frames) != expected_count or [frame.name for frame in frames] != expected_names:
raise RunnerError("RAVNOVES full-video frame sequence changed")
items = [(frame.stem, frame, None) for frame in frames]
if args.limit:
items = items[: args.limit]
if not items:
raise RunnerError("no inputs were selected")
visual_contract = config.get("visual_case_contract")
if not isinstance(visual_contract, dict):
raise RunnerError("visual case contract is unavailable")
configured_visual_count = visual_contract.get("case_count")
if args.mode == "goose":
if args.visual_count != configured_visual_count:
raise RunnerError("GOOSE visual count differs from the truth-focused contract")
selected_visuals = truth_focused_visuals(items, names, visual_contract)
elif args.mode == "ravnoves":
selected_visuals = visual_indices(len(items), args.visual_count)
else:
if args.visual_count != 0 or args.limit:
raise RunnerError("RAVNOVES video mode requires the complete frame sequence")
selected_visuals = {}
args.output.mkdir(parents=True, exist_ok=False)
torch.cuda.empty_cache()
model, model_name, architecture_failures = load_model(args.candidate, args.checkpoint)
warmup_source = Image.open(items[0][1]).convert("RGB")
warmup_tensor, _ = preprocess(warmup_source)
warmup_latencies_ms = [infer(model, warmup_tensor)[1] for _ in range(3)]
torch.cuda.reset_peak_memory_stats()
confusion = np.zeros((CLASS_COUNT, CLASS_COUNT), dtype=np.int64)
latencies_ms: list[float] = []
visuals: list[dict[str, Any]] = []
mask_root = args.output / "masks" if args.mode == "ravnoves-video" else None
if mask_root is not None:
mask_root.mkdir()
aggregate_prediction_pixels = np.zeros(CLASS_COUNT, dtype=np.int64)
for index, (case_id, source_path, label_path) in enumerate(items):
source = Image.open(source_path).convert("RGB")
if args.mode == "ravnoves-video" and source.size != (
config["ravnoves"]["expected_width"],
config["ravnoves"]["expected_height"],
):
raise RunnerError("RAVNOVES video frame dimensions changed")
tensor, crop_box = preprocess(source)
prediction, latency_ms = infer(model, tensor)
latencies_ms.append(latency_ms)
if mask_root is not None:
expanded_prediction = expand_mask(prediction, source.size, crop_box)
save_image(mask_root / f"frame-{index + 1:06d}.png", expanded_prediction, "L")
aggregate_prediction_pixels += np.bincount(
expanded_prediction.reshape(-1),
minlength=CLASS_COUNT,
)
truth = preprocess_label(Image.open(label_path)) if label_path is not None else None
if truth is not None:
update_confusion(confusion, truth, prediction)
if index in selected_visuals:
visuals.append(
write_visual_case(
args.output,
case_id,
source,
prediction,
semantic_palette,
policy_palettes,
crop_box,
truth=truth,
focus=selected_visuals[index] or None,
material_codes=material_codes,
error_colors=visual_contract["error_overlay"],
preserve_source_size=args.mode == "ravnoves",
)
)
mask_archive = (
write_mask_archive(args.output, mask_root, len(items))
if mask_root is not None
else None
)
taxonomy = {
"schema_version": "missioncore.lab-v1-vegetation-taxonomy/v1",
"classes": [
{
"class_id": label_id,
"label": names[label_id],
"color_rgb": semantic_palette[label_id, :3].astype(int).tolist(),
"disposition": "undefined" if label_id == 0 else "prediction",
}
for label_id in range(CLASS_COUNT)
],
}
rows = class_metrics(confusion, names) if args.mode == "goose" else []
valid_ious = [row["iou"] for row in rows if row["iou"] is not None]
vegetation_names = set(config["vegetation_class_names"])
vegetation_rows = [row for row in rows if row["class_name"] in vegetation_names]
vegetation_ious = [row["iou"] for row in vegetation_rows if row["iou"] is not None]
timing = {
"prewarm_inference_count": len(warmup_latencies_ms),
"prewarm_latency_ms": round(warmup_latencies_ms[0], 4),
"prewarm_latency_ms_last": round(warmup_latencies_ms[-1], 4),
"sample_count": len(latencies_ms),
"latency_ms_p50": round(percentile(latencies_ms, 0.50), 4),
"latency_ms_p95": round(percentile(latencies_ms, 0.95), 4),
"latency_ms_mean": round(statistics.fmean(latencies_ms), 4),
"throughput_fps_from_mean_inference": round(1000.0 / statistics.fmean(latencies_ms), 4),
}
result: dict[str, Any] = {
"schema_version": SCHEMA,
"lab_id": config["lab_id"],
"worker_id": config["worker_id"],
"mode": args.mode,
"candidate": {
"candidate_id": candidate_config["candidate_id"],
"candidate_key": args.candidate,
"loaded_model_name": model_name,
"architecture_probe_failures": architecture_failures,
"checkpoint_size_bytes": args.checkpoint.stat().st_size,
"checkpoint_sha256": checkpoint_sha256,
},
"source": {
"source_id": dataset_config["dataset_id"]
if args.mode == "goose"
else config["ravnoves"]["source_id"],
"input_count": len(items),
"ground_truth_available": args.mode == "goose",
"mapping_sha256": dataset_config["mapping_sha256"],
},
"video_semantics": {
"base_m4_result_id": config["ravnoves"].get("base_m4_result_id"),
"mask_archive": mask_archive,
"taxonomy": taxonomy,
"aggregate_prediction_pixels": aggregate_prediction_pixels.tolist()
if mask_archive is not None
else None,
"center_crop_xyxy": [100, 0, 700, 600]
if mask_archive is not None
else None,
"outside_crop_state": "undefined" if mask_archive is not None else None,
}
if args.mode == "ravnoves-video"
else None,
"preprocessing": dataset_config["preprocessing"],
"metrics": {
"mean_iou": round(statistics.fmean(valid_ious), 8) if valid_ious else None,
"mean_iou_percent": round(statistics.fmean(valid_ious) * 100.0, 4)
if valid_ious
else None,
"published_mean_iou_percent": candidate_config["published_validation_miou_percent"],
"vegetation_mean_iou": round(statistics.fmean(vegetation_ious), 8)
if vegetation_ious
else None,
"vegetation_classes": vegetation_rows,
"all_classes": rows,
},
"timing": timing,
"resource": {
"gpu_name": torch.cuda.get_device_name(0),
"peak_allocated_vram_bytes": int(torch.cuda.max_memory_allocated()),
"peak_reserved_vram_bytes": int(torch.cuda.max_memory_reserved()),
"torch_version": torch.__version__,
"cuda_runtime_version": torch.version.cuda,
"python_version": platform.python_version(),
"super_gradients_version": "3.2.0",
},
"visual_cases": visuals,
"authority": {
"navigation_accepted": False,
"safety_accepted": False,
"actuation_accepted": False,
"camera_semantics_can_clear_rigid_geometry": False,
},
}
identity_value = {
"schema_version": result["schema_version"],
"candidate": result["candidate"],
"source": result["source"],
"video_semantics": result["video_semantics"],
"preprocessing": result["preprocessing"],
"metrics": result["metrics"],
"timing": result["timing"],
"resource": result["resource"],
"visual_cases": result["visual_cases"],
"authority": result["authority"],
"config_sha256": sha256(args.config),
"policy_sha256": sha256(args.policy),
"provider_map_sha256": sha256(args.provider_map),
}
result["result_id"] = f"lab-v1-{args.mode}-{args.candidate}-{stable_digest(identity_value)}"
result["provenance"] = {
"config_sha256": identity_value["config_sha256"],
"policy_sha256": identity_value["policy_sha256"],
"provider_map_sha256": identity_value["provider_map_sha256"],
"hostname": platform.node(),
"pid": os.getpid(),
}
result_path = args.output / "result.json"
result_path.write_text(json.dumps(result, indent=2, sort_keys=True) + "\n", encoding="utf-8")
print(json.dumps({"result_id": result["result_id"], "result_path": str(result_path)}))
if __name__ == "__main__":
run()
@@ -0,0 +1,65 @@
FROM ros:jazzy-ros-base@sha256:2589a8fba5257307857890173c069852c2abf913a0be7970f172478baecb09e4
ARG GSEG3D_CORE_REVISION=116f510485ea5b4707e2b476958d34a5a067f647
ARG GSEG3D_ROS2_REVISION=5c5ba6f5ca0d682db2d59b2ad2f6a317ee938b95
ARG GROUND_CONSISTENCY_REVISION=41cec620efba6c370dccfc59a6ec1134775ff48a
ARG BUILD_JOBS=12
ENV DEBIAN_FRONTEND=noninteractive
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
RUN apt-get update \
&& apt-get install --yes --no-install-recommends \
build-essential \
cmake \
git \
jq \
libeigen3-dev \
libgtest-dev \
libnanoflann-dev \
libpcl-dev \
ninja-build \
ros-jazzy-message-filters \
ros-jazzy-nav2-costmap-2d \
ros-jazzy-pcl-conversions \
ros-jazzy-rclcpp-lifecycle \
ros-jazzy-tf2-eigen \
ros-jazzy-tf2-geometry-msgs \
time \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /opt/m49/src
RUN git clone --filter=blob:none https://github.com/dfki-ric/ground_segmentation.git \
&& git -C ground_segmentation checkout --detach "${GSEG3D_CORE_REVISION}" \
&& git clone --filter=blob:none https://github.com/dfki-ric/ground_segmentation_ros2.git \
&& git -C ground_segmentation_ros2 checkout --detach "${GSEG3D_ROS2_REVISION}" \
&& git clone --filter=blob:none https://github.com/dfki-ric/nav2_ground_consistency_costmap_plugin.git \
&& git -C nav2_ground_consistency_costmap_plugin checkout --detach "${GROUND_CONSISTENCY_REVISION}"
RUN cmake -S /opt/m49/src/ground_segmentation \
-B /opt/m49/core-build \
-G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/opt/m49/core-install \
&& cmake --build /opt/m49/core-build --parallel "${BUILD_JOBS}" \
&& ctest --test-dir /opt/m49/core-build --output-on-failure \
&& cmake --install /opt/m49/core-build
RUN source /opt/ros/jazzy/setup.bash \
&& export CMAKE_PREFIX_PATH="/opt/m49/core-install:${CMAKE_PREFIX_PATH:-}" \
&& colcon build \
--base-paths \
/opt/m49/src/ground_segmentation_ros2 \
/opt/m49/src/nav2_ground_consistency_costmap_plugin \
--build-base /opt/m49/ros-build \
--install-base /opt/m49/ros-install \
--merge-install \
--cmake-args -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON \
--executor sequential \
--event-handlers console_direct+
COPY qualify.sh /usr/local/bin/m49-gseg3d-qualify
RUN chmod 0755 /usr/local/bin/m49-gseg3d-qualify
ENTRYPOINT ["/usr/local/bin/m49-gseg3d-qualify"]
@@ -0,0 +1,100 @@
#!/usr/bin/env bash
set -euo pipefail
readonly EVIDENCE_ROOT=/evidence
readonly RESULT_PATH="${EVIDENCE_ROOT}/result.json"
readonly LOG_PATH="${EVIDENCE_ROOT}/qualification.log"
readonly RESOURCE_PATH="${EVIDENCE_ROOT}/resource.txt"
readonly STARTED_UTC="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
readonly STARTED_NS="$(date +%s%N)"
mkdir -p "${EVIDENCE_ROOT}"
if [[ -e "${RESULT_PATH}" || -e "${LOG_PATH}" || -e "${RESOURCE_PATH}" ]]; then
echo "evidence output already exists" >&2
exit 2
fi
status=failed
failure_stage=bootstrap
finalize() {
local exit_code=$?
local completed_ns
local elapsed_ms
completed_ns="$(date +%s%N)"
elapsed_ms="$(( (completed_ns - STARTED_NS) / 1000000 ))"
python3 - "${RESULT_PATH}" "${status}" "${failure_stage}" "${exit_code}" \
"${STARTED_UTC}" "${elapsed_ms}" <<'PY'
import json
import pathlib
import sys
target, status, stage, exit_code, started_utc, elapsed_ms = sys.argv[1:]
document = {
"schema_version": "missioncore.m49-t1-gseg3d-upstream-qualification/v1",
"status": status,
"failure_stage": None if status == "passed" else stage,
"exit_code": int(exit_code),
"started_utc": started_utc,
"elapsed_ms": int(elapsed_ms),
"authority": {
"candidate_build_qualified": status == "passed",
"ravnoves00_quality_accepted": False,
"realtime_accepted": False,
"navigation_or_actuation_allowed": False,
},
"revisions": {
"ground_segmentation": "116f510485ea5b4707e2b476958d34a5a067f647",
"ground_segmentation_ros2": "5c5ba6f5ca0d682db2d59b2ad2f6a317ee938b95",
"ground_consistency": "41cec620efba6c370dccfc59a6ec1134775ff48a",
},
}
pathlib.Path(target).write_text(
json.dumps(document, indent=2, sort_keys=True) + "\n", encoding="utf-8"
)
PY
}
trap finalize EXIT
exec > >(tee "${LOG_PATH}") 2>&1
failure_stage=source-integrity
test "$(git -C /opt/m49/src/ground_segmentation rev-parse HEAD)" = \
"116f510485ea5b4707e2b476958d34a5a067f647"
test "$(git -C /opt/m49/src/ground_segmentation_ros2 rev-parse HEAD)" = \
"5c5ba6f5ca0d682db2d59b2ad2f6a317ee938b95"
test "$(git -C /opt/m49/src/nav2_ground_consistency_costmap_plugin rev-parse HEAD)" = \
"41cec620efba6c370dccfc59a6ec1134775ff48a"
test -z "$(git -C /opt/m49/src/ground_segmentation status --porcelain)"
test -z "$(git -C /opt/m49/src/ground_segmentation_ros2 status --porcelain)"
test -z "$(git -C /opt/m49/src/nav2_ground_consistency_costmap_plugin status --porcelain)"
failure_stage=core-upstream-tests
/usr/bin/time -v -o "${RESOURCE_PATH}" \
ctest --test-dir /opt/m49/core-build --output-on-failure
failure_stage=ros2-plugin-tests
# ROS 2 environment hooks legitimately probe unset variables. Keep strict mode
# for the qualification itself, but do not let nounset turn a standard setup
# hook into a false candidate failure.
set +u
source /opt/ros/jazzy/setup.bash
source /opt/m49/ros-install/setup.bash
set -u
colcon test \
--build-base /opt/m49/ros-build \
--install-base /opt/m49/ros-install \
--test-result-base /opt/m49/test-results \
--merge-install \
--packages-select nav2_ground_consistency_costmap_plugin \
--event-handlers console_direct+
colcon test-result --test-result-base /opt/m49/test-results --verbose
failure_stage=ros2-discovery
ros2 pkg prefix ground_segmentation_ros2
ros2 pkg prefix nav2_ground_consistency_costmap_plugin
ros2 pkg executables ground_segmentation_ros2 | \
grep -F "ground_segmentation_ros2_node"
failure_stage=complete
status=passed
@@ -0,0 +1,72 @@
FROM ros:jazzy-ros-base@sha256:2589a8fba5257307857890173c069852c2abf913a0be7970f172478baecb09e4
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
ARG TRAVEL_REVISION=95dc2fbd66a343efd9060c45a5711b6307a950a4
ARG KITTI_FIXTURE_SHA256=bf272996d5b6d25cc5589e1089137cb20a98b63bd4823a7fea5631b359f6d68c
ARG KITTI_GOLD_SHA256=8aaacaa57d17a8a2f043c3ea0a0c134d36a3c50da2385f2364ca4eff53ea9b9f
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
cmake \
curl \
git \
libboost-filesystem-dev \
libboost-system-dev \
libeigen3-dev \
libpcl-dev \
mpi-default-dev \
python3-colcon-common-extensions \
ros-jazzy-pcl-conversions \
ros-jazzy-rclcpp \
ros-jazzy-sensor-msgs \
ros-jazzy-std-msgs \
time \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir -p /opt/travel/src/TRAVEL \
&& git -C /opt/travel/src/TRAVEL init \
&& git -C /opt/travel/src/TRAVEL remote add origin https://github.com/url-kaist/TRAVEL.git \
&& git -C /opt/travel/src/TRAVEL fetch --depth 1 origin "${TRAVEL_REVISION}" \
&& git -C /opt/travel/src/TRAVEL checkout --detach FETCH_HEAD \
&& test "$(git -C /opt/travel/src/TRAVEL rev-parse HEAD)" = "${TRAVEL_REVISION}" \
&& test -z "$(git -C /opt/travel/src/TRAVEL status --porcelain)"
RUN mkdir -p /opt/travel/fixture/00/velodyne \
&& curl -L --fail --retry 3 \
-o /opt/travel/fixture/00/velodyne/000000.bin \
https://github.com/url-kaist/TRAVEL/releases/download/test-data-v1/kitti00_000000.bin \
&& test "$(stat -c%s /opt/travel/fixture/00/velodyne/000000.bin)" = "1994688" \
&& echo "${KITTI_FIXTURE_SHA256} /opt/travel/fixture/00/velodyne/000000.bin" | sha256sum -c - \
&& echo "${KITTI_GOLD_SHA256} /opt/travel/src/TRAVEL/cpp/tests/data/kitti00_000000_gold.bin" | sha256sum -c -
RUN cmake \
-S /opt/travel/src/TRAVEL/cpp/travel \
-B /opt/travel/core-build \
-DTRAVEL_BUILD_EXAMPLES=ON \
-DTRAVEL_BUILD_TESTS=ON \
-DCMAKE_BUILD_TYPE=Release \
&& cmake --build /opt/travel/core-build \
--target regression_kitti run_travel_kitti \
--parallel 16
RUN set +u \
&& source /opt/ros/jazzy/setup.bash \
&& set -u \
&& colcon build \
--base-paths /opt/travel/src/TRAVEL \
--build-base /opt/travel/ros-build \
--install-base /opt/travel/ros-install \
--merge-install \
--packages-select travel_ros \
--cmake-args -DCMAKE_BUILD_TYPE=Release \
&& test -x /opt/travel/ros-install/lib/travel_ros/travel_node
COPY qualify.sh /usr/local/bin/m49-t3-travel-qualify
RUN chmod 0755 /usr/local/bin/m49-t3-travel-qualify
ENTRYPOINT ["/usr/local/bin/m49-t3-travel-qualify"]
@@ -0,0 +1,214 @@
#!/usr/bin/env python3
"""Analyze TRAVEL partitions without assigning navigation or visual-quality authority."""
from __future__ import annotations
import argparse
import csv
import hashlib
import json
from collections import Counter
from pathlib import Path
import numpy as np
class ProbeAnalysisError(RuntimeError):
"""The candidate output does not satisfy the evidence contract."""
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def _load(path: Path, columns: int) -> np.ndarray:
values = np.fromfile(path, dtype=np.float32)
if values.size % columns:
raise ProbeAnalysisError(f"malformed float32 output: {path}")
result = values.reshape(-1, columns)
if not np.isfinite(result).all():
raise ProbeAnalysisError(f"non-finite output: {path}")
return result
def _point_multiset(points: np.ndarray) -> Counter[bytes]:
contiguous = np.ascontiguousarray(points[:, :3], dtype=np.float32)
return Counter(row.tobytes() for row in contiguous)
def multiset_jaccard(left: np.ndarray, right: np.ndarray) -> float:
left_counts = _point_multiset(left)
right_counts = _point_multiset(right)
keys = left_counts.keys() | right_counts.keys()
intersection = sum(min(left_counts[key], right_counts[key]) for key in keys)
union = sum(max(left_counts[key], right_counts[key]) for key in keys)
return 1.0 if union == 0 else intersection / union
def _timings(path: Path) -> dict[tuple[str, str, int], dict[str, float | int]]:
result: dict[tuple[str, str, int], dict[str, float | int]] = {}
with path.open(newline="", encoding="utf-8") as stream:
for row in csv.DictReader(stream, delimiter="\t"):
key = (row["profile"], row["variant"], int(row["slot"]))
result[key] = {
"wall_seconds": float(row["wall_seconds"]),
"max_rss_kib": int(row["max_rss_kib"]),
}
if len(result) != 40:
raise ProbeAnalysisError("timing row count changed")
return result
def analyze(probe_root: Path, config_path: Path) -> dict[str, object]:
inputs = probe_root / "inputs"
outputs = probe_root / "outputs"
manifest = json.loads((inputs / "input-manifest.json").read_text(encoding="utf-8"))
config = json.loads(config_path.read_text(encoding="utf-8"))
if (
manifest.get("schema_version") != "missioncore.m49-travel-ravnoves-probe-input/v1"
or manifest.get("config_sha256") != sha256_file(config_path)
or len(manifest.get("records", [])) != 20
):
raise ProbeAnalysisError("probe input manifest changed")
timings = _timings(probe_root / "travel-timing.tsv")
rows: list[dict[str, object]] = []
variants: dict[tuple[str, int, str], np.ndarray] = {}
for record in manifest["records"]:
profile = str(record["profile_id"])
slot = int(record["slot"])
for variant in ("source", "reversed"):
input_path = inputs / record["variants"][variant]["relative_path"]
if sha256_file(input_path) != record["variants"][variant]["sha256"]:
raise ProbeAnalysisError("probe input changed after preparation")
native = _load(input_path, 4)
xy_range = np.linalg.norm(native[:, :2].astype(np.float64), axis=1)
eligible_input_points = int(np.count_nonzero((xy_range > 1.0) & (xy_range < 80.0)))
base = outputs / profile / variant / str(slot)
ground = _load(base.with_name(base.name + "_ground.bin"), 4)
nonground = _load(base.with_name(base.name + "_nonground.bin"), 4)
labeled = _load(base.with_name(base.name + "_labeled.bin"), 5)
variants[(profile, slot, variant + "-ground")] = ground
variants[(profile, slot, variant + "-nonground")] = nonground
variants[(profile, slot, variant + "-labeled")] = labeled
cluster_ids = np.unique(labeled[:, 4].astype(np.int64))
cluster_ids = cluster_ids[cluster_ids > 0]
rows.append(
{
"profile_id": profile,
"slot": slot,
"anchor_frame_index": int(record["anchor_frame_index"]),
"variant": variant,
"input_points": int(native.shape[0]),
"eligible_input_points": eligible_input_points,
"ground_points": int(ground.shape[0]),
"nonground_points": int(nonground.shape[0]),
"labeled_points": int(labeled.shape[0]),
"cluster_count": int(cluster_ids.size),
"partition_accounted_for_all_eligible_input_points": bool(
ground.shape[0] + nonground.shape[0] == eligible_input_points
),
"nonground_labeled_fraction": (
1.0
if nonground.shape[0] == 0
else float(labeled.shape[0] / nonground.shape[0])
),
**timings[(profile, variant, slot)],
}
)
threshold = float(config["compatibility_acceptance"]["minimum_partition_multiset_jaccard"])
labeled_threshold = float(
config["compatibility_acceptance"]["minimum_labeled_multiset_jaccard"]
)
comparisons: list[dict[str, object]] = []
for profile in ("current_increment", "causal_rolling_1s"):
for slot, anchor in enumerate(config["anchors"]):
ground_jaccard = multiset_jaccard(
variants[(profile, slot, "source-ground")],
variants[(profile, slot, "reversed-ground")],
)
nonground_jaccard = multiset_jaccard(
variants[(profile, slot, "source-nonground")],
variants[(profile, slot, "reversed-nonground")],
)
labeled_jaccard = multiset_jaccard(
variants[(profile, slot, "source-labeled")],
variants[(profile, slot, "reversed-labeled")],
)
comparisons.append(
{
"profile_id": profile,
"slot": slot,
"anchor_frame_index": int(anchor),
"ground_multiset_jaccard": ground_jaccard,
"nonground_multiset_jaccard": nonground_jaccard,
"labeled_multiset_jaccard": labeled_jaccard,
"partition_order_accepted": bool(
ground_jaccard >= threshold and nonground_jaccard >= threshold
),
"aos_order_accepted": bool(labeled_jaccard >= labeled_threshold),
}
)
partition_complete = all(
bool(row["partition_accounted_for_all_eligible_input_points"]) for row in rows
)
partition_order = all(bool(row["partition_order_accepted"]) for row in comparisons)
aos_order = all(bool(row["aos_order_accepted"]) for row in comparisons)
wall = np.asarray([float(row["wall_seconds"]) for row in rows], dtype=np.float64)
rss = np.asarray([int(row["max_rss_kib"]) for row in rows], dtype=np.int64)
result = {
"schema_version": "missioncore.m49-travel-ravnoves-compatibility-result/v1",
"status": "passed",
"source_pack_sha256": manifest["source_pack_sha256"],
"config_sha256": manifest["config_sha256"],
"input_record_count": len(manifest["records"]),
"candidate_run_count": len(rows),
"rows": rows,
"order_comparisons": comparisons,
"summary": {
"partition_complete": partition_complete,
"partition_order_accepted": partition_order,
"aos_order_accepted": aos_order,
"direct_upstream_profile_compatible": bool(
partition_complete and partition_order and aos_order
),
"wall_seconds_p50": float(np.median(wall)),
"wall_seconds_p95": float(np.percentile(wall, 95)),
"max_rss_kib": int(rss.max()),
"minimum_ground_multiset_jaccard": min(
float(row["ground_multiset_jaccard"]) for row in comparisons
),
"minimum_nonground_multiset_jaccard": min(
float(row["nonground_multiset_jaccard"]) for row in comparisons
),
"minimum_labeled_multiset_jaccard": min(
float(row["labeled_multiset_jaccard"]) for row in comparisons
),
},
"authority": {
"ravnoves00_visual_quality_accepted": False,
"realtime_accepted": False,
"navigation_or_actuation_allowed": False,
},
}
(probe_root / "result.json").write_text(
json.dumps(result, indent=2, sort_keys=True) + "\n", encoding="utf-8"
)
return result
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--probe-root", type=Path, required=True)
parser.add_argument("--config", type=Path, required=True)
arguments = parser.parse_args()
result = analyze(arguments.probe_root, arguments.config)
print(json.dumps(result["summary"], indent=2, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,297 @@
#!/usr/bin/env python3
"""Build a fail-closed TGS evidence pack from the sealed compatibility run."""
from __future__ import annotations
import argparse
import hashlib
import io
import json
import math
import zipfile
from collections import Counter
from pathlib import Path
import numpy as np
class TgsEvidenceError(RuntimeError):
"""The sealed compatibility evidence or fail-closed contract is invalid."""
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def _load_float32(path: Path, columns: int) -> np.ndarray:
if path.is_symlink() or not path.is_file():
raise TgsEvidenceError(f"sealed input is unavailable: {path.name}")
values = np.fromfile(path, dtype=np.float32)
if values.size % columns:
raise TgsEvidenceError(f"sealed input shape changed: {path.name}")
result = values.reshape(-1, columns)
if not np.isfinite(result).all():
raise TgsEvidenceError(f"sealed input is non-finite: {path.name}")
return result
def classify_exact_input(
native: np.ndarray,
ground: np.ndarray,
nonground: np.ndarray,
*,
min_range_m: float = 1.0,
max_range_m: float = 80.0,
) -> tuple[np.ndarray, np.ndarray]:
points = np.asarray(native, dtype=np.float32)
if points.ndim != 2 or points.shape[1:] != (4,):
raise TgsEvidenceError("native XYZI input shape changed")
ranges = np.linalg.norm(points[:, :2].astype(np.float64), axis=1)
eligible = points[(ranges > min_range_m) & (ranges < max_range_m)]
ground_counts = Counter(row[:3].tobytes() for row in ground)
nonground_counts = Counter(row[:3].tobytes() for row in nonground)
states = np.empty(eligible.shape[0], dtype=np.uint8)
for index, row in enumerate(eligible):
key = row[:3].tobytes()
if ground_counts[key] > 0:
states[index] = 1
ground_counts[key] -= 1
elif nonground_counts[key] > 0:
states[index] = 2
nonground_counts[key] -= 1
else:
states[index] = 3
if any(value for value in ground_counts.values()) or any(
value for value in nonground_counts.values()
):
raise TgsEvidenceError("TGS output is not a multiset subset of its exact input")
if not np.isin(states, np.asarray([1, 2, 3], dtype=np.uint8)).all():
raise TgsEvidenceError("point state reconstruction failed")
return eligible[:, :3].copy(), states
def costmap_grid(radius_m: float, cell_size_m: float) -> np.ndarray:
if not math.isfinite(radius_m) or not math.isfinite(cell_size_m):
raise TgsEvidenceError("costmap bounds are non-finite")
if radius_m <= 0 or cell_size_m <= 0 or cell_size_m > radius_m:
raise TgsEvidenceError("costmap bounds are invalid")
minimum = math.floor(-radius_m / cell_size_m)
maximum = math.ceil(radius_m / cell_size_m)
cells = []
for ix in range(minimum, maximum):
for iy in range(minimum, maximum):
center_x = (ix + 0.5) * cell_size_m
center_y = (iy + 0.5) * cell_size_m
if math.hypot(center_x, center_y) <= radius_m:
cells.append((ix, iy, center_x, center_y))
if not cells:
raise TgsEvidenceError("costmap grid is empty")
return np.asarray(cells, dtype=np.float64)
def rasterize_costmap(
points_xyz: np.ndarray,
point_states: np.ndarray,
grid: np.ndarray,
*,
cell_size_m: float,
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
lookup = {(int(row[0]), int(row[1])): index for index, row in enumerate(grid)}
counts = np.zeros((grid.shape[0], 3), dtype=np.int32)
minimum_z = np.full(grid.shape[0], np.nan, dtype=np.float32)
maximum_z = np.full(grid.shape[0], np.nan, dtype=np.float32)
for point, state in zip(points_xyz, point_states, strict=True):
cell = (
math.floor(float(point[0]) / cell_size_m),
math.floor(float(point[1]) / cell_size_m),
)
cell_index = lookup.get(cell)
if cell_index is None:
continue
counts[cell_index, int(state) - 1] += 1
z = np.float32(point[2])
if np.isnan(minimum_z[cell_index]) or z < minimum_z[cell_index]:
minimum_z[cell_index] = z
if np.isnan(maximum_z[cell_index]) or z > maximum_z[cell_index]:
maximum_z[cell_index] = z
states = np.zeros(grid.shape[0], dtype=np.uint8)
states[counts[:, 0] > 0] = 1
states[counts[:, 2] > 0] = 3
states[counts[:, 1] > 0] = 2
return states, counts[:, 0], counts[:, 1], counts[:, 2], np.column_stack((minimum_z, maximum_z))
def _array_bytes(array: np.ndarray) -> bytes:
stream = io.BytesIO()
np.lib.format.write_array(stream, np.ascontiguousarray(array), allow_pickle=False)
return stream.getvalue()
def write_deterministic_npz(path: Path, arrays: dict[str, np.ndarray]) -> None:
with zipfile.ZipFile(
path, mode="w", compression=zipfile.ZIP_DEFLATED, compresslevel=6
) as archive:
for name in sorted(arrays):
info = zipfile.ZipInfo(f"{name}.npy", date_time=(1980, 1, 1, 0, 0, 0))
info.compress_type = zipfile.ZIP_DEFLATED
info.external_attr = 0o100644 << 16
archive.writestr(info, _array_bytes(arrays[name]))
def build(run_root: Path, config_path: Path, output_root: Path) -> dict[str, object]:
if output_root.exists():
raise TgsEvidenceError("TGS evidence output already exists")
config = json.loads(config_path.read_text(encoding="utf-8"))
if (
config.get("schema_version") != "missioncore.m49-tgs-fail-closed-evidence-profile/v1"
or config.get("invariants", {}).get("aos_allowed") is not False
or config.get("invariants", {}).get("missing_support_means_free") is not False
or config.get("state_codes")
!= {
"UNOBSERVED": 0,
"GROUND_SUPPORT": 1,
"NONGROUND_OCCUPIED": 2,
"UNKNOWN_REJECTED": 3,
}
):
raise TgsEvidenceError("TGS fail-closed profile changed")
input_manifest_path = run_root / "inputs" / "input-manifest.json"
input_manifest = json.loads(input_manifest_path.read_text(encoding="utf-8"))
if (
input_manifest.get("schema_version") != "missioncore.m49-tgs-fail-closed-input/v1"
or input_manifest.get("source_pack_sha256") != config["source"]["source_pack_sha256"]
or input_manifest.get("config_sha256") != sha256_file(config_path)
or input_manifest.get("coordinate_frame") != "map-gravity-local"
or input_manifest.get("future_frames_used") is not False
or len(input_manifest.get("records", [])) != 20
):
raise TgsEvidenceError("sealed TGS input manifest changed")
records = {(str(row["profile_id"]), int(row["slot"])): row for row in input_manifest["records"]}
cell_size = float(config["costmap"]["cell_size_m"])
radius = float(config["costmap"]["radius_m"])
grid = costmap_grid(radius, cell_size)
arrays: dict[str, np.ndarray] = {
"costmap_cell_indices_xy": grid[:, :2].astype(np.int32),
"costmap_cell_centers_xy_m": grid[:, 2:].astype(np.float32),
}
summaries: list[dict[str, object]] = []
for profile in ("current_increment", "causal_rolling_1s"):
all_points: list[np.ndarray] = []
all_states: list[np.ndarray] = []
offsets = [0]
profile_grid_states: list[np.ndarray] = []
profile_ground_counts: list[np.ndarray] = []
profile_nonground_counts: list[np.ndarray] = []
profile_rejected_counts: list[np.ndarray] = []
profile_z_bounds: list[np.ndarray] = []
for slot in range(10):
record = records[(profile, slot)]
native_path = run_root / "inputs" / record["relative_path"]
if sha256_file(native_path) != record["sha256"]:
raise TgsEvidenceError("sealed gravity-aligned input changed")
output = run_root / "outputs" / profile
points, states = classify_exact_input(
_load_float32(native_path, 4),
_load_float32(output / f"{slot}_ground.bin", 4),
_load_float32(output / f"{slot}_nonground.bin", 4),
)
grid_state, ground_count, nonground_count, rejected_count, z_bounds = rasterize_costmap(
points, states, grid, cell_size_m=cell_size
)
all_points.append(points.astype(np.float32, copy=False))
all_states.append(states)
offsets.append(offsets[-1] + points.shape[0])
profile_grid_states.append(grid_state)
profile_ground_counts.append(ground_count)
profile_nonground_counts.append(nonground_count)
profile_rejected_counts.append(rejected_count)
profile_z_bounds.append(z_bounds)
summaries.append(
{
"profile_id": profile,
"slot": slot,
"anchor_frame_index": int(record["anchor_frame_index"]),
"point_count": int(points.shape[0]),
"ground_point_count": int(np.count_nonzero(states == 1)),
"nonground_point_count": int(np.count_nonzero(states == 2)),
"rejected_point_count": int(np.count_nonzero(states == 3)),
"ground_cell_count": int(np.count_nonzero(grid_state == 1)),
"nonground_cell_count": int(np.count_nonzero(grid_state == 2)),
"rejected_cell_count": int(np.count_nonzero(grid_state == 3)),
"unobserved_cell_count": int(np.count_nonzero(grid_state == 0)),
"all_points_accounted": bool(
np.count_nonzero(states == 1)
+ np.count_nonzero(states == 2)
+ np.count_nonzero(states == 3)
== points.shape[0]
),
}
)
arrays[f"{profile}_points_xyz_m"] = np.concatenate(all_points, axis=0)
arrays[f"{profile}_point_states"] = np.concatenate(all_states, axis=0)
arrays[f"{profile}_point_offsets"] = np.asarray(offsets, dtype=np.int64)
arrays[f"{profile}_costmap_states"] = np.stack(profile_grid_states)
arrays[f"{profile}_costmap_ground_point_counts"] = np.stack(profile_ground_counts)
arrays[f"{profile}_costmap_nonground_point_counts"] = np.stack(profile_nonground_counts)
arrays[f"{profile}_costmap_rejected_point_counts"] = np.stack(profile_rejected_counts)
arrays[f"{profile}_costmap_z_bounds_m"] = np.stack(profile_z_bounds)
if not all(bool(row["all_points_accounted"]) for row in summaries):
raise TgsEvidenceError("fail-closed evidence lost an eligible input point")
output_root.mkdir(parents=True)
evidence_path = output_root / "evidence.npz"
write_deterministic_npz(evidence_path, arrays)
result = {
"schema_version": "missioncore.m49-tgs-fail-closed-evidence-result/v1",
"status": "passed",
"config_sha256": sha256_file(config_path),
"source_pack_sha256": input_manifest["source_pack_sha256"],
"input_manifest_sha256": sha256_file(input_manifest_path),
"evidence": {
"path": "evidence.npz",
"bytes": evidence_path.stat().st_size,
"sha256": sha256_file(evidence_path),
},
"costmap": {
"coordinate_frame": "map-gravity-local",
"cell_size_m": cell_size,
"radius_m": radius,
"cell_count": int(grid.shape[0]),
},
"anchors": summaries,
"summary": {
"anchor_profile_count": len(summaries),
"all_eligible_points_accounted": True,
"aos_used": False,
"primary_profile": "causal_rolling_1s",
},
"authority": {
"visual_quality_accepted": False,
"traversability_accepted": False,
"realtime_accepted": False,
"navigation_or_actuation_allowed": False,
},
}
result_path_out = output_root / "result.json"
result_path_out.write_text(
json.dumps(result, indent=2, sort_keys=True) + "\n", encoding="utf-8"
)
return result
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--run-root", type=Path, required=True)
parser.add_argument("--config", type=Path, required=True)
parser.add_argument("--output-root", type=Path, required=True)
arguments = parser.parse_args()
result = build(arguments.run_root, arguments.config, arguments.output_root)
print(json.dumps(result["summary"], indent=2, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -euo pipefail
readonly TARGET=${1:?target binary path is required}
test -f /release/run_tgs_full_shadow.cpp
test ! -e "${TARGET}"
mkdir -p "$(dirname "${TARGET}")"
g++ -std=c++17 -O3 -DNDEBUG -pthread \
-I/opt/travel/src/TRAVEL/cpp/travel/core \
-I/usr/include/eigen3 \
/release/run_tgs_full_shadow.cpp \
-o "${TARGET}"
chmod 0755 "${TARGET}"
sha256sum "${TARGET}"
@@ -0,0 +1,332 @@
#!/usr/bin/env python3
"""Build mmap-friendly evidence for the complete source-paced TGS shadow."""
from __future__ import annotations
import argparse
import csv
import hashlib
import json
import math
from pathlib import Path
import numpy as np
from build_tgs_fail_closed_evidence import costmap_grid
class FullShadowError(RuntimeError):
"""The complete TGS shadow or its fail-closed contract is invalid."""
def sha256_file(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()
def load_xyzi(path: Path) -> np.ndarray:
if path.is_symlink() or not path.is_file():
raise FullShadowError(f"sealed input is unavailable: {path.name}")
values = np.fromfile(path, dtype=np.float32)
if values.size % 4:
raise FullShadowError(f"sealed XYZI shape changed: {path.name}")
result = values.reshape(-1, 4)
if not np.isfinite(result).all():
raise FullShadowError(f"sealed XYZI is non-finite: {path.name}")
return result
def classify_exact_input(
native: np.ndarray, ground: np.ndarray, nonground: np.ndarray
) -> tuple[np.ndarray, np.ndarray]:
ranges = np.linalg.norm(native[:, :2].astype(np.float64), axis=1)
points = np.ascontiguousarray(native[(ranges > 1.0) & (ranges < 80.0), :3])
output = np.ascontiguousarray(np.concatenate((ground[:, :3], nonground[:, :3]), axis=0))
output_states = np.concatenate(
(np.ones(ground.shape[0], dtype=np.uint8), np.full(nonground.shape[0], 2, dtype=np.uint8))
)
key_dtype = np.dtype((np.void, 12))
input_keys = points.view(key_dtype).reshape(-1)
output_keys = output.view(key_dtype).reshape(-1)
input_order = np.argsort(input_keys, kind="stable")
output_order = np.argsort(output_keys, kind="stable")
sorted_input = input_keys[input_order]
sorted_output = output_keys[output_order]
positions = np.searchsorted(sorted_input, sorted_output, side="left")
if sorted_output.size:
group_starts = np.r_[0, np.flatnonzero(sorted_output[1:] != sorted_output[:-1]) + 1]
group_lengths = np.diff(np.r_[group_starts, sorted_output.size])
occurrence = np.arange(sorted_output.size) - np.repeat(group_starts, group_lengths)
targets = positions + occurrence
if (
np.any(targets >= sorted_input.size)
or np.any(sorted_input[targets] != sorted_output)
or np.unique(targets).size != targets.size
):
raise FullShadowError("TGS output is not a multiset subset of its exact input")
else:
targets = np.empty(0, dtype=np.int64)
sorted_states = np.full(points.shape[0], 3, dtype=np.uint8)
sorted_states[targets] = output_states[output_order]
states = np.empty_like(sorted_states)
states[input_order] = sorted_states
return points, states
def rasterize(
points: np.ndarray,
states: np.ndarray,
grid: np.ndarray,
cell_size_m: float,
) -> tuple[np.ndarray, np.ndarray]:
minimum_ix = int(np.min(grid[:, 0]))
maximum_ix = int(np.max(grid[:, 0]))
minimum_iy = int(np.min(grid[:, 1]))
maximum_iy = int(np.max(grid[:, 1]))
lookup = np.full(
(maximum_ix - minimum_ix + 1, maximum_iy - minimum_iy + 1), -1, dtype=np.int32
)
lookup[
grid[:, 0].astype(np.int32) - minimum_ix,
grid[:, 1].astype(np.int32) - minimum_iy,
] = np.arange(grid.shape[0], dtype=np.int32)
cell_xy = np.floor(points[:, :2] / cell_size_m).astype(np.int32)
inside = (
(cell_xy[:, 0] >= minimum_ix)
& (cell_xy[:, 0] <= maximum_ix)
& (cell_xy[:, 1] >= minimum_iy)
& (cell_xy[:, 1] <= maximum_iy)
)
point_indices = np.flatnonzero(inside)
cell_indices = lookup[
cell_xy[inside, 0] - minimum_ix, cell_xy[inside, 1] - minimum_iy
]
valid = cell_indices >= 0
point_indices = point_indices[valid]
cell_indices = cell_indices[valid]
cell_states = np.zeros(grid.shape[0], dtype=np.uint8)
selected_states = states[point_indices]
ground_cells = np.zeros(grid.shape[0], dtype=np.uint8)
rejected_cells = np.zeros(grid.shape[0], dtype=np.uint8)
nonground_cells = np.zeros(grid.shape[0], dtype=np.uint8)
np.maximum.at(ground_cells, cell_indices, (selected_states == 1).astype(np.uint8))
np.maximum.at(rejected_cells, cell_indices, (selected_states == 3).astype(np.uint8))
np.maximum.at(nonground_cells, cell_indices, (selected_states == 2).astype(np.uint8))
cell_states[ground_cells > 0] = 1
cell_states[rejected_cells > 0] = 3
cell_states[nonground_cells > 0] = 2
minimum_z = np.full(grid.shape[0], np.inf, dtype=np.float32)
maximum_z = np.full(grid.shape[0], -np.inf, dtype=np.float32)
np.minimum.at(minimum_z, cell_indices, points[point_indices, 2])
np.maximum.at(maximum_z, cell_indices, points[point_indices, 2])
z_bounds = np.column_stack((minimum_z, maximum_z)).astype(np.float32, copy=False)
z_bounds[~np.isfinite(z_bounds)] = np.nan
return cell_states, z_bounds
def percentile(values: np.ndarray, value: float) -> float:
return float(np.percentile(values.astype(np.float64), value)) if values.size else 0.0
def build(run_root: Path, config_path: Path, output_root: Path) -> dict[str, object]:
if output_root.exists():
raise FullShadowError("full-shadow evidence output already exists")
config = json.loads(config_path.read_text(encoding="utf-8"))
if (
config.get("schema_version") != "missioncore.m49-tgs-full-shadow-profile/v1"
or config.get("invariants", {}).get("aos_allowed") is not False
or config.get("invariants", {}).get("gpu_allowed") is not False
or config.get("invariants", {}).get("missing_lidar_means_unobserved") is not True
):
raise FullShadowError("full-shadow profile changed")
manifest_path = run_root / "inputs" / "input-manifest.json"
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
records = manifest.get("records", [])
if (
manifest.get("schema_version") != "missioncore.m49-tgs-full-shadow-input/v1"
or manifest.get("source_pack_sha256") != config["source"]["source_pack_sha256"]
or manifest.get("config_sha256") != sha256_file(config_path)
or manifest.get("future_frames_used") is not False
or len(records) != 4489
or sum(bool(row["sample_available"]) for row in records) != 3928
):
raise FullShadowError("full-shadow input manifest changed")
with (run_root / "tgs-full-timing.tsv").open("r", encoding="utf-8", newline="") as stream:
timing_rows = list(csv.DictReader(stream, delimiter="\t"))
if len(timing_rows) != 4489:
raise FullShadowError("full-shadow timing frame accounting changed")
cell_size = float(config["costmap"]["cell_size_m"])
radius = float(config["costmap"]["radius_m"])
grid = costmap_grid(radius, cell_size)
output_root.mkdir(parents=True)
np.save(output_root / "costmap-cell-indices-xy.npy", grid[:, :2].astype(np.int32))
np.save(output_root / "costmap-cell-centers-xy-m.npy", grid[:, 2:].astype(np.float32))
states_out = np.lib.format.open_memmap(
output_root / "costmap-states.npy", mode="w+", dtype=np.uint8, shape=(4489, grid.shape[0])
)
z_out = np.lib.format.open_memmap(
output_root / "costmap-z-bounds-m.npy",
mode="w+",
dtype=np.float32,
shape=(4489, grid.shape[0], 2),
)
z_out[:] = np.nan
summaries: list[dict[str, object]] = []
eligible_total = ground_total = nonground_total = rejected_total = 0
available_seen = 0
for frame_index, (record, timing) in enumerate(zip(records, timing_rows, strict=True)):
if int(timing["timeline_frame_index"]) != frame_index:
raise FullShadowError("full-shadow timing order changed")
available = bool(record["sample_available"])
if not available:
if int(timing["sample_available"]) != 0:
raise FullShadowError("missing LiDAR frame was processed")
states_out[frame_index] = 0
summaries.append(
{
"timeline_frame_index": frame_index,
"source_frame_index": int(record["source_frame_index"]),
"session_seconds": float(record["session_seconds"]),
"sample_available": False,
"eligible_point_count": 0,
"ground_point_count": 0,
"nonground_point_count": 0,
"rejected_point_count": 0,
"occupied_cell_count": 0,
}
)
continue
available_seen += 1
native_path = run_root / "inputs" / str(record["relative_path"])
if sha256_file(native_path) != record["sha256"]:
raise FullShadowError("sealed gravity-aligned full-shadow input changed")
output = run_root / "outputs" / "causal_rolling_1s"
ground = load_xyzi(output / f"{frame_index}_ground.bin")
nonground = load_xyzi(output / f"{frame_index}_nonground.bin")
points, point_states = classify_exact_input(load_xyzi(native_path), ground, nonground)
cell_states, z_bounds = rasterize(points, point_states, grid, cell_size)
states_out[frame_index] = cell_states
z_out[frame_index] = z_bounds
ground_count = int(np.count_nonzero(point_states == 1))
nonground_count = int(np.count_nonzero(point_states == 2))
rejected_count = int(np.count_nonzero(point_states == 3))
eligible_total += int(points.shape[0])
ground_total += ground_count
nonground_total += nonground_count
rejected_total += rejected_count
summaries.append(
{
"timeline_frame_index": frame_index,
"source_frame_index": int(record["source_frame_index"]),
"session_seconds": float(record["session_seconds"]),
"sample_available": True,
"eligible_point_count": int(points.shape[0]),
"ground_point_count": ground_count,
"nonground_point_count": nonground_count,
"rejected_point_count": rejected_count,
"occupied_cell_count": int(np.count_nonzero(cell_states == 2)),
}
)
states_out.flush()
z_out.flush()
if available_seen != 3928 or eligible_total != ground_total + nonground_total + rejected_total:
raise FullShadowError("full-shadow eligible point accounting failed")
frames_path = output_root / "frames.ndjson"
frames_path.write_text(
"".join(json.dumps(row, sort_keys=True) + "\n" for row in summaries), encoding="utf-8"
)
available_timings = [row for row in timing_rows if int(row["sample_available"]) == 1]
tgs_ms = np.asarray([float(row["tgs_ms"]) for row in available_timings])
completion_ms = np.asarray([float(row["completion_age_ms"]) for row in timing_rows])
capacity_drops = sum(int(row["capacity_drop"]) for row in timing_rows)
duration = float(records[-1]["session_seconds"]) - float(records[0]["session_seconds"])
effective_fps = (len(records) - 1) / duration
thresholds = config["acceptance"]
acceptance = {
"minimum_effective_timeline_fps": effective_fps >= float(thresholds["minimum_effective_timeline_fps"]),
"candidate_stage_p95_ms": percentile(tgs_ms, 95) <= float(thresholds["candidate_stage_p95_ms_max"]),
"candidate_stage_p99_ms": percentile(tgs_ms, 99) <= float(thresholds["candidate_stage_p99_ms_max"]),
"completion_age_p99_ms": percentile(completion_ms, 99) <= float(thresholds["completion_age_p99_ms_max"]),
"capacity_drop_count": capacity_drops <= int(thresholds["capacity_drop_count_max"]),
"all_frames_accounted": len(summaries) == 4489 and available_seen == 3928,
"all_eligible_points_accounted": eligible_total == ground_total + nonground_total + rejected_total,
}
files = {}
for path in sorted(output_root.iterdir()):
if path.is_file() and path.name != "result.json":
files[path.name] = {"bytes": path.stat().st_size, "sha256": sha256_file(path)}
result = {
"schema_version": "missioncore.m49-tgs-full-shadow-result/v1",
"status": "passed" if all(acceptance.values()) else "failed",
"config_sha256": sha256_file(config_path),
"source_pack_sha256": manifest["source_pack_sha256"],
"input_manifest_sha256": sha256_file(manifest_path),
"timeline": {
"frame_count": 4489,
"available_lidar_frame_count": 3928,
"missing_lidar_frame_count": 561,
"duration_seconds": duration,
"effective_fps": effective_fps,
},
"costmap": {
"coordinate_frame": "map-gravity-local",
"cell_size_m": cell_size,
"radius_m": radius,
"cell_count": int(grid.shape[0]),
},
"point_accounting": {
"eligible": eligible_total,
"ground": ground_total,
"nonground": nonground_total,
"rejected": rejected_total,
"unaccounted": eligible_total - ground_total - nonground_total - rejected_total,
},
"performance": {
"candidate_tgs_ms": {
"p50": percentile(tgs_ms, 50),
"p95": percentile(tgs_ms, 95),
"p99": percentile(tgs_ms, 99),
"max": float(np.max(tgs_ms)),
},
"completion_age_ms": {
"p50": percentile(completion_ms, 50),
"p95": percentile(completion_ms, 95),
"p99": percentile(completion_ms, 99),
"max": float(np.max(completion_ms)),
},
"capacity_drop_count": capacity_drops,
},
"acceptance": acceptance,
"files": files,
"authority": {
"visual_quality_accepted": False,
"traversability_accepted": False,
"realtime_accepted": bool(all(acceptance.values())),
"integrated_graph_performance_accepted": False,
"navigation_or_actuation_allowed": False,
},
}
(output_root / "result.json").write_text(
json.dumps(result, indent=2, sort_keys=True) + "\n", encoding="utf-8"
)
return result
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--run-root", type=Path, required=True)
parser.add_argument("--config", type=Path, required=True)
parser.add_argument("--output-root", type=Path, required=True)
arguments = parser.parse_args()
result = build(arguments.run_root, arguments.config, arguments.output_root)
print(json.dumps({"status": result["status"], **result["performance"]}, sort_keys=True))
return 0
if __name__ == "__main__":
raise SystemExit(main())

Some files were not shown because too many files have changed in this diff Show More