Compare commits
32
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e44e27967 | ||
|
|
2ccf172319 | ||
|
|
30080c51aa | ||
|
|
67e6ae98e2 | ||
|
|
57204b3b0b | ||
|
|
7594c71dd1 | ||
|
|
92aa796c9d | ||
|
|
7c0fd81b2f | ||
|
|
95a1ef5057 | ||
|
|
c8593207d3 | ||
|
|
7aeea4ed81 | ||
|
|
bf76304e00 | ||
|
|
b70dc4298a | ||
|
|
f153d671d3 | ||
|
|
7720594790 | ||
|
|
296cf610cd | ||
|
|
46cef8df17 | ||
|
|
d722b0f82b | ||
|
|
40c850b167 | ||
|
|
6544d9e918 | ||
|
|
67d5d6fa05 | ||
|
|
941fb89616 | ||
|
|
989a3ce28c | ||
|
|
28d5297237 | ||
|
|
b0738438d4 | ||
|
|
67578f4ce1 | ||
|
|
83ef98e9e5 | ||
|
|
316f0d7f85 | ||
|
|
2e7abbc56b | ||
|
|
b445745d27 | ||
|
|
1cc87e1836 | ||
|
|
aed59030b4 |
Generated
+1
-1
@@ -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": {
|
||||
|
||||
@@ -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;
|
||||
Binary file not shown.
@@ -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}
|
||||
|
||||
+8
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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": [
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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"]
|
||||
+777
@@ -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
Reference in New Issue
Block a user