From a65ae9c285578f0c670e075baad32151e7dcdd47 Mon Sep 17 00:00:00 2001 From: DCCONSTRUCTIONS Date: Wed, 26 Aug 2026 15:18:34 +0300 Subject: [PATCH] feat(simulation): optimize and persist collision viewing --- .../playcanvas/draco/1.5.7/BUILD_INFO.md | 8 + .../draco/1.5.7/draco_decoder_gltf.wasm | Bin 0 -> 192593 bytes .../draco/1.5.7/draco_wasm_wrapper_gltf.js | 116 +++++++++ .../simulation/PlayCanvasRuntime.ts | 51 +++- .../simulation/SimulationViewport.tsx | 230 ++++++++++++++---- .../src/core/simulation/projects.ts | 84 ++++++- .../control-station/src/styles/simulation.css | 19 ++ .../simulation/SimulationWorkspace.tsx | 11 +- .../test/simulationWorkspace.test.mjs | 10 +- src/k1link/service_watchdog.py | 14 +- src/k1link/simulation/projects.py | 79 +++++- src/k1link/web/simulation_projects_api.py | 41 ++++ tests/test_simulation_projects.py | 64 ++++- 13 files changed, 662 insertions(+), 65 deletions(-) create mode 100644 apps/control-station/public/vendor/playcanvas/draco/1.5.7/BUILD_INFO.md create mode 100644 apps/control-station/public/vendor/playcanvas/draco/1.5.7/draco_decoder_gltf.wasm create mode 100644 apps/control-station/public/vendor/playcanvas/draco/1.5.7/draco_wasm_wrapper_gltf.js diff --git a/apps/control-station/public/vendor/playcanvas/draco/1.5.7/BUILD_INFO.md b/apps/control-station/public/vendor/playcanvas/draco/1.5.7/BUILD_INFO.md new file mode 100644 index 0000000..e837ac8 --- /dev/null +++ b/apps/control-station/public/vendor/playcanvas/draco/1.5.7/BUILD_INFO.md @@ -0,0 +1,8 @@ +# Draco glTF decoder 1.5.7 + +Vendored from Google's immutable versioned Draco decoder distribution for the +PlayCanvas `DracoDecoderModule`. Runtime loading remains local to Mission Core. + +- `draco_wasm_wrapper_gltf.js`: `8bb2952d2ba7d67e1414f8df819410cb0434a666be53f671fff75f68843d76f6` +- `draco_decoder_gltf.wasm`: `712db3449ae2041d6e8a224c395bda6cedb49e51322fae38b7db9beb8b381889` +- Upstream base: `https://www.gstatic.com/draco/versioned/decoders/1.5.7/` diff --git a/apps/control-station/public/vendor/playcanvas/draco/1.5.7/draco_decoder_gltf.wasm b/apps/control-station/public/vendor/playcanvas/draco/1.5.7/draco_decoder_gltf.wasm new file mode 100644 index 0000000000000000000000000000000000000000..26aa5d3c9301490e6f88ba5d3283603bcd072b57 GIT binary patch literal 192593 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 literal 0 HcmV?d00001 diff --git a/apps/control-station/public/vendor/playcanvas/draco/1.5.7/draco_wasm_wrapper_gltf.js b/apps/control-station/public/vendor/playcanvas/draco/1.5.7/draco_wasm_wrapper_gltf.js new file mode 100644 index 0000000..43f1556 --- /dev/null +++ b/apps/control-station/public/vendor/playcanvas/draco/1.5.7/draco_wasm_wrapper_gltf.js @@ -0,0 +1,116 @@ +var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(h){var n=0;return function(){return n>>0,$jscomp.propertyToPolyfillSymbol[l]=$jscomp.IS_SYMBOL_NATIVE? +$jscomp.global.Symbol(l):$jscomp.POLYFILL_PREFIX+k+"$"+l),$jscomp.defineProperty(p,$jscomp.propertyToPolyfillSymbol[l],{configurable:!0,writable:!0,value:n})))}; +$jscomp.polyfill("Promise",function(h){function n(){this.batch_=null}function k(f){return f instanceof l?f:new l(function(q,u){q(f)})}if(h&&(!($jscomp.FORCE_POLYFILL_PROMISE||$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION&&"undefined"===typeof $jscomp.global.PromiseRejectionEvent)||!$jscomp.global.Promise||-1===$jscomp.global.Promise.toString().indexOf("[native code]")))return h;n.prototype.asyncExecute=function(f){if(null==this.batch_){this.batch_=[];var q=this;this.asyncExecuteFunction(function(){q.executeBatch_()})}this.batch_.push(f)}; +var p=$jscomp.global.setTimeout;n.prototype.asyncExecuteFunction=function(f){p(f,0)};n.prototype.executeBatch_=function(){for(;this.batch_&&this.batch_.length;){var f=this.batch_;this.batch_=[];for(var q=0;q=y}},"es6","es3"); +$jscomp.polyfill("Array.prototype.copyWithin",function(h){function n(k){k=Number(k);return Infinity===k||-Infinity===k?k:k|0}return h?h:function(k,p,l){var y=this.length;k=n(k);p=n(p);l=void 0===l?y:n(l);k=0>k?Math.max(y+k,0):Math.min(k,y);p=0>p?Math.max(y+p,0):Math.min(p,y);l=0>l?Math.max(y+l,0):Math.min(l,y);if(kp;)--l in this?this[--k]=this[l]:delete this[--k];return this}},"es6","es3"); +$jscomp.typedArrayCopyWithin=function(h){return h?h:Array.prototype.copyWithin};$jscomp.polyfill("Int8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8ClampedArray.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5"); +$jscomp.polyfill("Uint16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float64Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5"); +var DracoDecoderModule=function(){var h="undefined"!==typeof document&&document.currentScript?document.currentScript.src:void 0;"undefined"!==typeof __filename&&(h=h||__filename);return function(n){function k(e){return a.locateFile?a.locateFile(e,U):U+e}function p(e,b){if(e){var c=ia;var d=e+b;for(b=e;c[b]&&!(b>=d);)++b;if(16g?d+=String.fromCharCode(g):(g-=65536,d+=String.fromCharCode(55296|g>>10,56320|g&1023))}}else d+=String.fromCharCode(g)}c=d}}else c="";return c}function l(){var e=ja.buffer;a.HEAP8=W=new Int8Array(e);a.HEAP16=new Int16Array(e);a.HEAP32=ca=new Int32Array(e);a.HEAPU8=ia=new Uint8Array(e);a.HEAPU16=new Uint16Array(e);a.HEAPU32=Y=new Uint32Array(e);a.HEAPF32=new Float32Array(e);a.HEAPF64=new Float64Array(e)}function y(e){if(a.onAbort)a.onAbort(e); +e="Aborted("+e+")";da(e);sa=!0;e=new WebAssembly.RuntimeError(e+". Build with -sASSERTIONS for more info.");ka(e);throw e;}function f(e){try{if(e==P&&ea)return new Uint8Array(ea);if(ma)return ma(e);throw"both async and sync fetching of the wasm failed";}catch(b){y(b)}}function q(){if(!ea&&(ta||fa)){if("function"==typeof fetch&&!P.startsWith("file://"))return fetch(P,{credentials:"same-origin"}).then(function(e){if(!e.ok)throw"failed to load wasm binary file at '"+P+"'";return e.arrayBuffer()}).catch(function(){return f(P)}); +if(na)return new Promise(function(e,b){na(P,function(c){e(new Uint8Array(c))},b)})}return Promise.resolve().then(function(){return f(P)})}function u(e){for(;0>2]=b};this.get_type=function(){return Y[this.ptr+4>>2]};this.set_destructor=function(b){Y[this.ptr+8>>2]=b};this.get_destructor=function(){return Y[this.ptr+8>>2]};this.set_refcount=function(b){ca[this.ptr>>2]=b};this.set_caught=function(b){W[this.ptr+ +12>>0]=b?1:0};this.get_caught=function(){return 0!=W[this.ptr+12>>0]};this.set_rethrown=function(b){W[this.ptr+13>>0]=b?1:0};this.get_rethrown=function(){return 0!=W[this.ptr+13>>0]};this.init=function(b,c){this.set_adjusted_ptr(0);this.set_type(b);this.set_destructor(c);this.set_refcount(0);this.set_caught(!1);this.set_rethrown(!1)};this.add_ref=function(){ca[this.ptr>>2]+=1};this.release_ref=function(){var b=ca[this.ptr>>2];ca[this.ptr>>2]=b-1;return 1===b};this.set_adjusted_ptr=function(b){Y[this.ptr+ +16>>2]=b};this.get_adjusted_ptr=function(){return Y[this.ptr+16>>2]};this.get_exception_ptr=function(){if(ua(this.get_type()))return Y[this.excPtr>>2];var b=this.get_adjusted_ptr();return 0!==b?b:this.excPtr}}function F(){function e(){if(!la&&(la=!0,a.calledRun=!0,!sa)){va=!0;u(oa);wa(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;)xa.unshift(a.postRun.shift());u(xa)}}if(!(0=d?b++:2047>=d?b+=2:55296<=d&&57343>= +d?(b+=4,++c):b+=3}b=Array(b+1);c=0;d=b.length;if(0=t){var aa=e.charCodeAt(++g);t=65536+((t&1023)<<10)|aa&1023}if(127>=t){if(c>=d)break;b[c++]=t}else{if(2047>=t){if(c+1>=d)break;b[c++]=192|t>>6}else{if(65535>=t){if(c+2>=d)break;b[c++]=224|t>>12}else{if(c+3>=d)break;b[c++]=240|t>>18;b[c++]=128|t>>12&63}b[c++]=128|t>>6&63}b[c++]=128|t&63}}b[c]=0}e=r.alloc(b,W);r.copy(b,W,e);return e}return e}function Z(e){if("object"=== +typeof e){var b=r.alloc(e,W);r.copy(e,W,b);return b}return e}function X(){throw"cannot construct a VoidPtr, no constructor in IDL";}function S(){this.ptr=za();w(S)[this.ptr]=this}function Q(){this.ptr=Aa();w(Q)[this.ptr]=this}function V(){this.ptr=Ba();w(V)[this.ptr]=this}function x(){this.ptr=Ca();w(x)[this.ptr]=this}function D(){this.ptr=Da();w(D)[this.ptr]=this}function G(){this.ptr=Ea();w(G)[this.ptr]=this}function H(){this.ptr=Fa();w(H)[this.ptr]=this}function E(){this.ptr=Ga();w(E)[this.ptr]= +this}function T(){this.ptr=Ha();w(T)[this.ptr]=this}function C(){throw"cannot construct a Status, no constructor in IDL";}function I(){this.ptr=Ia();w(I)[this.ptr]=this}function J(){this.ptr=Ja();w(J)[this.ptr]=this}function K(){this.ptr=Ka();w(K)[this.ptr]=this}function L(){this.ptr=La();w(L)[this.ptr]=this}function M(){this.ptr=Ma();w(M)[this.ptr]=this}function N(){this.ptr=Na();w(N)[this.ptr]=this}function O(){this.ptr=Oa();w(O)[this.ptr]=this}function z(){this.ptr=Pa();w(z)[this.ptr]=this}function m(){this.ptr= +Qa();w(m)[this.ptr]=this}n=void 0===n?{}:n;var a="undefined"!=typeof n?n:{},wa,ka;a.ready=new Promise(function(e,b){wa=e;ka=b});var Ra=!1,Sa=!1;a.onRuntimeInitialized=function(){Ra=!0;if(Sa&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.onModuleParsed=function(){Sa=!0;if(Ra&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.isVersionSupported=function(e){if("string"!==typeof e)return!1;e=e.split(".");return 2>e.length||3=e[1]?!0:0!=e[0]||10< +e[1]?!1:!0};var Ta=Object.assign({},a),ta="object"==typeof window,fa="function"==typeof importScripts,Ua="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,U="";if(Ua){var Va=require("fs"),pa=require("path");U=fa?pa.dirname(U)+"/":__dirname+"/";var Wa=function(e,b){e=e.startsWith("file://")?new URL(e):pa.normalize(e);return Va.readFileSync(e,b?void 0:"utf8")};var ma=function(e){e=Wa(e,!0);e.buffer||(e=new Uint8Array(e));return e};var na=function(e, +b,c){e=e.startsWith("file://")?new URL(e):pa.normalize(e);Va.readFile(e,function(d,g){d?c(d):b(g.buffer)})};1>>=0;if(2147483648=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,e+100663296);var g=Math;d=Math.max(e,d);g=g.min.call(g,2147483648,d+(65536-d%65536)%65536);a:{d=ja.buffer;try{ja.grow(g-d.byteLength+65535>>>16);l();var t=1;break a}catch(aa){}t=void 0}if(t)return!0}return!1}};(function(){function e(g,t){a.asm=g.exports;ja=a.asm.e;l();oa.unshift(a.asm.f);ba--;a.monitorRunDependencies&&a.monitorRunDependencies(ba);0==ba&&(null!==qa&&(clearInterval(qa),qa=null),ha&&(g=ha,ha=null,g()))}function b(g){e(g.instance)} +function c(g){return q().then(function(t){return WebAssembly.instantiate(t,d)}).then(function(t){return t}).then(g,function(t){da("failed to asynchronously prepare wasm: "+t);y(t)})}var d={a:qd};ba++;a.monitorRunDependencies&&a.monitorRunDependencies(ba);if(a.instantiateWasm)try{return a.instantiateWasm(d,e)}catch(g){da("Module.instantiateWasm callback failed with error: "+g),ka(g)}(function(){return ea||"function"!=typeof WebAssembly.instantiateStreaming||P.startsWith("data:application/octet-stream;base64,")|| +P.startsWith("file://")||Ua||"function"!=typeof fetch?c(b):fetch(P,{credentials:"same-origin"}).then(function(g){return WebAssembly.instantiateStreaming(g,d).then(b,function(t){da("wasm streaming compile failed: "+t);da("falling back to ArrayBuffer instantiation");return c(b)})})})().catch(ka);return{}})();var Xa=a._emscripten_bind_VoidPtr___destroy___0=function(){return(Xa=a._emscripten_bind_VoidPtr___destroy___0=a.asm.h).apply(null,arguments)},za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0= +function(){return(za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=a.asm.i).apply(null,arguments)},Ya=a._emscripten_bind_DecoderBuffer_Init_2=function(){return(Ya=a._emscripten_bind_DecoderBuffer_Init_2=a.asm.j).apply(null,arguments)},Za=a._emscripten_bind_DecoderBuffer___destroy___0=function(){return(Za=a._emscripten_bind_DecoderBuffer___destroy___0=a.asm.k).apply(null,arguments)},Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=function(){return(Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0= +a.asm.l).apply(null,arguments)},$a=a._emscripten_bind_AttributeTransformData_transform_type_0=function(){return($a=a._emscripten_bind_AttributeTransformData_transform_type_0=a.asm.m).apply(null,arguments)},ab=a._emscripten_bind_AttributeTransformData___destroy___0=function(){return(ab=a._emscripten_bind_AttributeTransformData___destroy___0=a.asm.n).apply(null,arguments)},Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return(Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0= +a.asm.o).apply(null,arguments)},bb=a._emscripten_bind_GeometryAttribute___destroy___0=function(){return(bb=a._emscripten_bind_GeometryAttribute___destroy___0=a.asm.p).apply(null,arguments)},Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return(Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=a.asm.q).apply(null,arguments)},cb=a._emscripten_bind_PointAttribute_size_0=function(){return(cb=a._emscripten_bind_PointAttribute_size_0=a.asm.r).apply(null,arguments)},db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0= +function(){return(db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=a.asm.s).apply(null,arguments)},eb=a._emscripten_bind_PointAttribute_attribute_type_0=function(){return(eb=a._emscripten_bind_PointAttribute_attribute_type_0=a.asm.t).apply(null,arguments)},fb=a._emscripten_bind_PointAttribute_data_type_0=function(){return(fb=a._emscripten_bind_PointAttribute_data_type_0=a.asm.u).apply(null,arguments)},gb=a._emscripten_bind_PointAttribute_num_components_0=function(){return(gb=a._emscripten_bind_PointAttribute_num_components_0= +a.asm.v).apply(null,arguments)},hb=a._emscripten_bind_PointAttribute_normalized_0=function(){return(hb=a._emscripten_bind_PointAttribute_normalized_0=a.asm.w).apply(null,arguments)},ib=a._emscripten_bind_PointAttribute_byte_stride_0=function(){return(ib=a._emscripten_bind_PointAttribute_byte_stride_0=a.asm.x).apply(null,arguments)},jb=a._emscripten_bind_PointAttribute_byte_offset_0=function(){return(jb=a._emscripten_bind_PointAttribute_byte_offset_0=a.asm.y).apply(null,arguments)},kb=a._emscripten_bind_PointAttribute_unique_id_0= +function(){return(kb=a._emscripten_bind_PointAttribute_unique_id_0=a.asm.z).apply(null,arguments)},lb=a._emscripten_bind_PointAttribute___destroy___0=function(){return(lb=a._emscripten_bind_PointAttribute___destroy___0=a.asm.A).apply(null,arguments)},Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=function(){return(Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=a.asm.B).apply(null,arguments)},mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1= +function(){return(mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=a.asm.C).apply(null,arguments)},nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=function(){return(nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=a.asm.D).apply(null,arguments)},ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=function(){return(ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=a.asm.E).apply(null,arguments)},pb= +a._emscripten_bind_AttributeQuantizationTransform_range_0=function(){return(pb=a._emscripten_bind_AttributeQuantizationTransform_range_0=a.asm.F).apply(null,arguments)},qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=function(){return(qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=a.asm.G).apply(null,arguments)},Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=function(){return(Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0= +a.asm.H).apply(null,arguments)},rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=function(){return(rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=a.asm.I).apply(null,arguments)},sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=function(){return(sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=a.asm.J).apply(null,arguments)},tb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=function(){return(tb= +a._emscripten_bind_AttributeOctahedronTransform___destroy___0=a.asm.K).apply(null,arguments)},Fa=a._emscripten_bind_PointCloud_PointCloud_0=function(){return(Fa=a._emscripten_bind_PointCloud_PointCloud_0=a.asm.L).apply(null,arguments)},ub=a._emscripten_bind_PointCloud_num_attributes_0=function(){return(ub=a._emscripten_bind_PointCloud_num_attributes_0=a.asm.M).apply(null,arguments)},vb=a._emscripten_bind_PointCloud_num_points_0=function(){return(vb=a._emscripten_bind_PointCloud_num_points_0=a.asm.N).apply(null, +arguments)},wb=a._emscripten_bind_PointCloud___destroy___0=function(){return(wb=a._emscripten_bind_PointCloud___destroy___0=a.asm.O).apply(null,arguments)},Ga=a._emscripten_bind_Mesh_Mesh_0=function(){return(Ga=a._emscripten_bind_Mesh_Mesh_0=a.asm.P).apply(null,arguments)},xb=a._emscripten_bind_Mesh_num_faces_0=function(){return(xb=a._emscripten_bind_Mesh_num_faces_0=a.asm.Q).apply(null,arguments)},yb=a._emscripten_bind_Mesh_num_attributes_0=function(){return(yb=a._emscripten_bind_Mesh_num_attributes_0= +a.asm.R).apply(null,arguments)},zb=a._emscripten_bind_Mesh_num_points_0=function(){return(zb=a._emscripten_bind_Mesh_num_points_0=a.asm.S).apply(null,arguments)},Ab=a._emscripten_bind_Mesh___destroy___0=function(){return(Ab=a._emscripten_bind_Mesh___destroy___0=a.asm.T).apply(null,arguments)},Ha=a._emscripten_bind_Metadata_Metadata_0=function(){return(Ha=a._emscripten_bind_Metadata_Metadata_0=a.asm.U).apply(null,arguments)},Bb=a._emscripten_bind_Metadata___destroy___0=function(){return(Bb=a._emscripten_bind_Metadata___destroy___0= +a.asm.V).apply(null,arguments)},Cb=a._emscripten_bind_Status_code_0=function(){return(Cb=a._emscripten_bind_Status_code_0=a.asm.W).apply(null,arguments)},Db=a._emscripten_bind_Status_ok_0=function(){return(Db=a._emscripten_bind_Status_ok_0=a.asm.X).apply(null,arguments)},Eb=a._emscripten_bind_Status_error_msg_0=function(){return(Eb=a._emscripten_bind_Status_error_msg_0=a.asm.Y).apply(null,arguments)},Fb=a._emscripten_bind_Status___destroy___0=function(){return(Fb=a._emscripten_bind_Status___destroy___0= +a.asm.Z).apply(null,arguments)},Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=function(){return(Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=a.asm._).apply(null,arguments)},Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=function(){return(Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=a.asm.$).apply(null,arguments)},Hb=a._emscripten_bind_DracoFloat32Array_size_0=function(){return(Hb=a._emscripten_bind_DracoFloat32Array_size_0=a.asm.aa).apply(null,arguments)},Ib= +a._emscripten_bind_DracoFloat32Array___destroy___0=function(){return(Ib=a._emscripten_bind_DracoFloat32Array___destroy___0=a.asm.ba).apply(null,arguments)},Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=function(){return(Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=a.asm.ca).apply(null,arguments)},Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=function(){return(Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=a.asm.da).apply(null,arguments)},Kb=a._emscripten_bind_DracoInt8Array_size_0= +function(){return(Kb=a._emscripten_bind_DracoInt8Array_size_0=a.asm.ea).apply(null,arguments)},Lb=a._emscripten_bind_DracoInt8Array___destroy___0=function(){return(Lb=a._emscripten_bind_DracoInt8Array___destroy___0=a.asm.fa).apply(null,arguments)},Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=function(){return(Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=a.asm.ga).apply(null,arguments)},Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=function(){return(Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1= +a.asm.ha).apply(null,arguments)},Nb=a._emscripten_bind_DracoUInt8Array_size_0=function(){return(Nb=a._emscripten_bind_DracoUInt8Array_size_0=a.asm.ia).apply(null,arguments)},Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=function(){return(Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=a.asm.ja).apply(null,arguments)},La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=function(){return(La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=a.asm.ka).apply(null,arguments)},Pb=a._emscripten_bind_DracoInt16Array_GetValue_1= +function(){return(Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=a.asm.la).apply(null,arguments)},Qb=a._emscripten_bind_DracoInt16Array_size_0=function(){return(Qb=a._emscripten_bind_DracoInt16Array_size_0=a.asm.ma).apply(null,arguments)},Rb=a._emscripten_bind_DracoInt16Array___destroy___0=function(){return(Rb=a._emscripten_bind_DracoInt16Array___destroy___0=a.asm.na).apply(null,arguments)},Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=function(){return(Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0= +a.asm.oa).apply(null,arguments)},Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=function(){return(Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=a.asm.pa).apply(null,arguments)},Tb=a._emscripten_bind_DracoUInt16Array_size_0=function(){return(Tb=a._emscripten_bind_DracoUInt16Array_size_0=a.asm.qa).apply(null,arguments)},Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=function(){return(Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=a.asm.ra).apply(null,arguments)},Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0= +function(){return(Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=a.asm.sa).apply(null,arguments)},Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=function(){return(Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=a.asm.ta).apply(null,arguments)},Wb=a._emscripten_bind_DracoInt32Array_size_0=function(){return(Wb=a._emscripten_bind_DracoInt32Array_size_0=a.asm.ua).apply(null,arguments)},Xb=a._emscripten_bind_DracoInt32Array___destroy___0=function(){return(Xb=a._emscripten_bind_DracoInt32Array___destroy___0= +a.asm.va).apply(null,arguments)},Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=function(){return(Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=a.asm.wa).apply(null,arguments)},Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=function(){return(Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=a.asm.xa).apply(null,arguments)},Zb=a._emscripten_bind_DracoUInt32Array_size_0=function(){return(Zb=a._emscripten_bind_DracoUInt32Array_size_0=a.asm.ya).apply(null,arguments)},$b=a._emscripten_bind_DracoUInt32Array___destroy___0= +function(){return($b=a._emscripten_bind_DracoUInt32Array___destroy___0=a.asm.za).apply(null,arguments)},Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=function(){return(Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=a.asm.Aa).apply(null,arguments)},ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=function(){return(ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=a.asm.Ba).apply(null,arguments)},bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=function(){return(bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2= +a.asm.Ca).apply(null,arguments)},cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=function(){return(cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=a.asm.Da).apply(null,arguments)},dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=function(){return(dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=a.asm.Ea).apply(null,arguments)},ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=function(){return(ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=a.asm.Fa).apply(null, +arguments)},fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=function(){return(fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=a.asm.Ga).apply(null,arguments)},gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=function(){return(gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=a.asm.Ha).apply(null,arguments)},hc=a._emscripten_bind_MetadataQuerier___destroy___0=function(){return(hc=a._emscripten_bind_MetadataQuerier___destroy___0=a.asm.Ia).apply(null,arguments)},Qa=a._emscripten_bind_Decoder_Decoder_0= +function(){return(Qa=a._emscripten_bind_Decoder_Decoder_0=a.asm.Ja).apply(null,arguments)},ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=function(){return(ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=a.asm.Ka).apply(null,arguments)},jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=function(){return(jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=a.asm.La).apply(null,arguments)},kc=a._emscripten_bind_Decoder_GetAttributeId_2=function(){return(kc=a._emscripten_bind_Decoder_GetAttributeId_2= +a.asm.Ma).apply(null,arguments)},lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=function(){return(lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=a.asm.Na).apply(null,arguments)},mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=function(){return(mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=a.asm.Oa).apply(null,arguments)},nc=a._emscripten_bind_Decoder_GetAttribute_2=function(){return(nc=a._emscripten_bind_Decoder_GetAttribute_2=a.asm.Pa).apply(null,arguments)}, +oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=function(){return(oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=a.asm.Qa).apply(null,arguments)},pc=a._emscripten_bind_Decoder_GetMetadata_1=function(){return(pc=a._emscripten_bind_Decoder_GetMetadata_1=a.asm.Ra).apply(null,arguments)},qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=function(){return(qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=a.asm.Sa).apply(null,arguments)},rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3= +function(){return(rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=a.asm.Ta).apply(null,arguments)},sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=function(){return(sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=a.asm.Ua).apply(null,arguments)},tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=function(){return(tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=a.asm.Va).apply(null,arguments)},uc=a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=function(){return(uc= +a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=a.asm.Wa).apply(null,arguments)},vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=function(){return(vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=a.asm.Xa).apply(null,arguments)},wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=function(){return(wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=a.asm.Ya).apply(null,arguments)},xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=function(){return(xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3= +a.asm.Za).apply(null,arguments)},yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=function(){return(yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=a.asm._a).apply(null,arguments)},zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=function(){return(zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=a.asm.$a).apply(null,arguments)},Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=function(){return(Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3= +a.asm.ab).apply(null,arguments)},Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=function(){return(Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=a.asm.bb).apply(null,arguments)},Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=function(){return(Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=a.asm.cb).apply(null,arguments)},Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=function(){return(Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3= +a.asm.db).apply(null,arguments)},Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=function(){return(Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=a.asm.eb).apply(null,arguments)},Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=function(){return(Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=a.asm.fb).apply(null,arguments)},Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=function(){return(Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1= +a.asm.gb).apply(null,arguments)},Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=function(){return(Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=a.asm.hb).apply(null,arguments)},Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=function(){return(Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=a.asm.ib).apply(null,arguments)},Jc=a._emscripten_bind_Decoder___destroy___0=function(){return(Jc=a._emscripten_bind_Decoder___destroy___0=a.asm.jb).apply(null,arguments)},Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM= +function(){return(Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=a.asm.kb).apply(null,arguments)},Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=function(){return(Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=a.asm.lb).apply(null,arguments)},Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=function(){return(Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM= +a.asm.mb).apply(null,arguments)},Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=function(){return(Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=a.asm.nb).apply(null,arguments)},Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.ob).apply(null,arguments)},Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION= +a.asm.pb).apply(null,arguments)},Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return(Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=a.asm.qb).apply(null,arguments)},Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.rb).apply(null,arguments)},Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD= +a.asm.sb).apply(null,arguments)},Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return(Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=a.asm.tb).apply(null,arguments)},Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=a.asm.ub).apply(null,arguments)},Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD= +a.asm.vb).apply(null,arguments)},Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return(Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=a.asm.wb).apply(null,arguments)},Xc=a._emscripten_enum_draco_DataType_DT_INVALID=function(){return(Xc=a._emscripten_enum_draco_DataType_DT_INVALID=a.asm.xb).apply(null,arguments)},Yc=a._emscripten_enum_draco_DataType_DT_INT8=function(){return(Yc=a._emscripten_enum_draco_DataType_DT_INT8=a.asm.yb).apply(null,arguments)},Zc= +a._emscripten_enum_draco_DataType_DT_UINT8=function(){return(Zc=a._emscripten_enum_draco_DataType_DT_UINT8=a.asm.zb).apply(null,arguments)},$c=a._emscripten_enum_draco_DataType_DT_INT16=function(){return($c=a._emscripten_enum_draco_DataType_DT_INT16=a.asm.Ab).apply(null,arguments)},ad=a._emscripten_enum_draco_DataType_DT_UINT16=function(){return(ad=a._emscripten_enum_draco_DataType_DT_UINT16=a.asm.Bb).apply(null,arguments)},bd=a._emscripten_enum_draco_DataType_DT_INT32=function(){return(bd=a._emscripten_enum_draco_DataType_DT_INT32= +a.asm.Cb).apply(null,arguments)},cd=a._emscripten_enum_draco_DataType_DT_UINT32=function(){return(cd=a._emscripten_enum_draco_DataType_DT_UINT32=a.asm.Db).apply(null,arguments)},dd=a._emscripten_enum_draco_DataType_DT_INT64=function(){return(dd=a._emscripten_enum_draco_DataType_DT_INT64=a.asm.Eb).apply(null,arguments)},ed=a._emscripten_enum_draco_DataType_DT_UINT64=function(){return(ed=a._emscripten_enum_draco_DataType_DT_UINT64=a.asm.Fb).apply(null,arguments)},fd=a._emscripten_enum_draco_DataType_DT_FLOAT32= +function(){return(fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=a.asm.Gb).apply(null,arguments)},gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=function(){return(gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=a.asm.Hb).apply(null,arguments)},hd=a._emscripten_enum_draco_DataType_DT_BOOL=function(){return(hd=a._emscripten_enum_draco_DataType_DT_BOOL=a.asm.Ib).apply(null,arguments)},id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=function(){return(id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT= +a.asm.Jb).apply(null,arguments)},jd=a._emscripten_enum_draco_StatusCode_OK=function(){return(jd=a._emscripten_enum_draco_StatusCode_OK=a.asm.Kb).apply(null,arguments)},kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=function(){return(kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=a.asm.Lb).apply(null,arguments)},ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=function(){return(ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=a.asm.Mb).apply(null,arguments)},md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER= +function(){return(md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=a.asm.Nb).apply(null,arguments)},nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=function(){return(nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=a.asm.Ob).apply(null,arguments)},od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=function(){return(od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=a.asm.Pb).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Qb).apply(null,arguments)}; +a._free=function(){return(a._free=a.asm.Rb).apply(null,arguments)};var ua=function(){return(ua=a.asm.Sb).apply(null,arguments)};a.___start_em_js=11660;a.___stop_em_js=11758;var la;ha=function b(){la||F();la||(ha=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0=r.size?(0>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var g=0;gb.byteLength)return a.INVALID_GEOMETRY_TYPE;switch(b[7]){case 0:return a.POINT_CLOUD;case 1:return a.TRIANGULAR_MESH;default:return a.INVALID_GEOMETRY_TYPE}};return n.ready}}();"object"===typeof exports&&"object"===typeof module?module.exports=DracoDecoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoDecoderModule}):"object"===typeof exports&&(exports.DracoDecoderModule=DracoDecoderModule); diff --git a/apps/control-station/src/components/simulation/PlayCanvasRuntime.ts b/apps/control-station/src/components/simulation/PlayCanvasRuntime.ts index 3201b56..d9629d8 100644 --- a/apps/control-station/src/components/simulation/PlayCanvasRuntime.ts +++ b/apps/control-station/src/components/simulation/PlayCanvasRuntime.ts @@ -11,16 +11,21 @@ import { StandardMaterial, Vec2, Vec3, + WasmModule, type ContainerResource, type RenderComponent, type ScriptType, } from "playcanvas"; import { CameraControls } from "playcanvas/scripts/esm/camera-controls.mjs"; -import type { SimulationWorldManifest } from "../../core/simulation/projects"; +import type { + SimulationTransformAxis, + SimulationViewerQuality, + SimulationWorldManifest, +} from "../../core/simulation/projects"; export type SimulationViewMode = "visual" | "collision" | "combined"; -export type SimulationQuality = "low" | "medium" | "high" | "ultra" | "maximum"; +export type SimulationQuality = SimulationViewerQuality; export type SimulationLayer = "visual" | "collision"; export type SimulationCameraAxis = "horizontal" | "vertical"; @@ -38,6 +43,27 @@ export const PLAYCANVAS_X_180_TRANSFORM = [ 0, 0, 0, 1, ]; +export const PLAYCANVAS_Y_180_TRANSFORM = [ + -1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, -1, 0, + 0, 0, 0, 1, +]; + +export const PLAYCANVAS_Z_180_TRANSFORM = [ + -1, 0, 0, 0, + 0, -1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, +]; + +export function playCanvasAxisTransform(axis: SimulationTransformAxis, inverted: boolean): number[] { + if (!inverted) return PLAYCANVAS_IDENTITY_TRANSFORM; + if (axis === "x") return PLAYCANVAS_X_180_TRANSFORM; + if (axis === "y") return PLAYCANVAS_Y_180_TRANSFORM; + return PLAYCANVAS_Z_180_TRANSFORM; +} + export interface SimulationRuntime { mount(canvas: HTMLCanvasElement): Promise; loadWorld(manifest: SimulationWorldManifest): Promise; @@ -60,6 +86,7 @@ type CameraController = ScriptType & Pick & { const HOME_POSITION = new Vec3(0, 1, 0); const HOME_FOCUS = new Vec3(1, 1, 0); +const DRACO_DECODER_ROOT = "/vendor/playcanvas/draco/1.5.7"; const QUALITY_PROFILES: Record { this.viewMode = mode; this.applyViewMode(); - if (mode !== "visual" && !this.collisionEntity) { - await this.ensureCollision(); - this.applyViewMode(); + try { + if (mode !== "visual" && !this.collisionEntity) { + await this.ensureCollision(); + this.applyViewMode(); + } + } finally { + this.canvas?.focus({ preventScroll: true }); } } @@ -444,6 +476,15 @@ function preventContextMenu(event: Event): void { event.preventDefault(); } +function configureDracoDecoder(): void { + if (WasmModule.getConfig("DracoDecoderModule")) return; + WasmModule.setConfig("DracoDecoderModule", { + glueUrl: `${DRACO_DECODER_ROOT}/draco_wasm_wrapper_gltf.js`, + wasmUrl: `${DRACO_DECODER_ROOT}/draco_decoder_gltf.wasm`, + numWorkers: 2, + }); +} + function focusCanvas(event: Event): void { (event.currentTarget as HTMLCanvasElement | null)?.focus({ preventScroll: true }); } diff --git a/apps/control-station/src/components/simulation/SimulationViewport.tsx b/apps/control-station/src/components/simulation/SimulationViewport.tsx index b6629ce..32581de 100644 --- a/apps/control-station/src/components/simulation/SimulationViewport.tsx +++ b/apps/control-station/src/components/simulation/SimulationViewport.tsx @@ -10,40 +10,101 @@ import { Switch, } from "@nodedc/ui-react"; -import type { SimulationProject } from "../../core/simulation/projects"; import { - PLAYCANVAS_IDENTITY_TRANSFORM, - PLAYCANVAS_X_180_TRANSFORM, + saveSimulationViewerSettings, + type SimulationProject, + type SimulationTransformAxis, + type SimulationViewerSettings, +} from "../../core/simulation/projects"; +import { + createLatestAsyncCommitter, + type LatestAsyncCommitter, +} from "../../core/runtime/latestAsyncCommitter"; +import { PlayCanvasRuntime, + playCanvasAxisTransform, type SimulationQuality, type SimulationViewMode, } from "./PlayCanvasRuntime"; -export function SimulationViewport({ project }: { project: SimulationProject }) { +const AXIS_OPTIONS: Array<{ value: SimulationTransformAxis; label: string }> = [ + { value: "x", label: "X" }, + { value: "y", label: "Y" }, + { value: "z", label: "Z" }, +]; + +export function SimulationViewport({ + project, + onProjectChange, +}: { + project: SimulationProject; + onProjectChange?: (project: SimulationProject) => void; +}) { const canvasRef = useRef(null); const runtimeRef = useRef(null); - const cameraInversionRef = useRef({ horizontal: true, vertical: false }); + const viewerSettingsRef = useRef(project.viewerSettings); + const settingsCommitterRef = useRef | null>(null); const settingsId = useId(); const [state, setState] = useState<"mounting" | "loading" | "ready" | "failed">("mounting"); const [error, setError] = useState(null); const [viewMode, setViewMode] = useState("visual"); - const [quality, setQuality] = useState("high"); + const [quality, setQuality] = useState(project.viewerSettings.quality); const [collisionState, setCollisionState] = useState<"idle" | "loading" | "ready" | "failed">("idle"); const [settingsOpen, setSettingsOpen] = useState(false); - const [visualInverted, setVisualInverted] = useState( - () => isX180Transform(project.worldManifest?.transforms.worldFromVisual), + 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 [horizontalInverted, setHorizontalInverted] = useState( + project.viewerSettings.camera.invertHorizontal, ); - const [collisionInverted, setCollisionInverted] = useState( - () => isX180Transform(project.worldManifest?.transforms.worldFromCollision), + const [verticalInverted, setVerticalInverted] = useState( + project.viewerSettings.camera.invertVertical, ); - const [horizontalInverted, setHorizontalInverted] = useState(true); - const [verticalInverted, setVerticalInverted] = useState(false); + const [settingsError, setSettingsError] = useState(null); useEffect(() => { - setVisualInverted(isX180Transform(project.worldManifest?.transforms.worldFromVisual)); - setCollisionInverted(isX180Transform(project.worldManifest?.transforms.worldFromCollision)); + 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); + setHorizontalInverted(settings.camera.invertHorizontal); + setVerticalInverted(settings.camera.invertVertical); + setSettingsError(null); setSettingsOpen(false); - }, [project.projectId, project.worldManifest]); + }, [project.projectId]); + + useEffect(() => { + let active = true; + const committer = createLatestAsyncCommitter({ + async commit(settings) { + try { + const saved = await saveSimulationViewerSettings(project.projectId, settings); + if (active) onProjectChange?.(saved); + return true; + } catch (caught) { + if (active) { + setSettingsError( + caught instanceof Error ? caught.message : "Не удалось сохранить настройки сцены.", + ); + } + return false; + } + }, + onSettled({ applied, superseded }) { + if (active && applied && !superseded) setSettingsError(null); + }, + }); + settingsCommitterRef.current = committer; + return () => { + active = false; + committer.dispose(); + if (settingsCommitterRef.current === committer) settingsCommitterRef.current = null; + }; + }, [onProjectChange, project.projectId]); useEffect(() => { const canvas = canvasRef.current; @@ -56,10 +117,20 @@ export function SimulationViewport({ project }: { project: SimulationProject }) setError(null); void runtime.mount(canvas).then(async () => { if (cancelled) return; - runtime.setCameraInversion("horizontal", cameraInversionRef.current.horizontal); - runtime.setCameraInversion("vertical", cameraInversionRef.current.vertical); + const settings = viewerSettingsRef.current; + runtime.setQuality(settings.quality); + runtime.setCameraInversion("horizontal", settings.camera.invertHorizontal); + runtime.setCameraInversion("vertical", settings.camera.invertVertical); setState("loading"); await runtime.loadWorld(manifest); + runtime.setLayerWorldTransform( + "visual", + playCanvasAxisTransform(settings.visual.axis, settings.visual.inverted), + ); + runtime.setLayerWorldTransform( + "collision", + playCanvasAxisTransform(settings.collision.axis, settings.collision.inverted), + ); if (!cancelled) setState("ready"); }).catch((caught: unknown) => { if (cancelled) return; @@ -74,6 +145,10 @@ export function SimulationViewport({ project }: { project: SimulationProject }) }, [project.projectId, project.worldManifest]); const collisionAvailable = project.worldManifest?.collision.available ?? false; + const commitViewerSettings = (settings: SimulationViewerSettings) => { + viewerSettingsRef.current = settings; + settingsCommitterRef.current?.enqueue(settings); + }; return (
@@ -99,6 +174,7 @@ export function SimulationViewport({ project }: { project: SimulationProject }) onChange={(next) => { setQuality(next); runtimeRef.current?.setQuality(next); + commitViewerSettings({ ...viewerSettingsRef.current, quality: next }); }} options={[ { value: "maximum", label: "Максимум", description: "До 4 млн сплатов · Retina до 2× · 60 FPS" }, @@ -171,30 +247,82 @@ export function SimulationViewport({ project }: { project: SimulationProject }) Коррекция слоёв не меняет координаты камеры, навигации и будущей физики.

- { - setVisualInverted(checked); - runtimeRef.current?.setLayerWorldTransform( - "visual", - checked ? PLAYCANVAS_X_180_TRANSFORM : PLAYCANVAS_IDENTITY_TRANSFORM, - ); - }} - /> - { - setCollisionInverted(checked); - runtimeRef.current?.setLayerWorldTransform( - "collision", - checked ? PLAYCANVAS_X_180_TRANSFORM : PLAYCANVAS_IDENTITY_TRANSFORM, - ); - }} - /> +
+ { + setVisualInverted(checked); + runtimeRef.current?.setLayerWorldTransform( + "visual", + playCanvasAxisTransform(visualAxis, checked), + ); + commitViewerSettings({ + ...viewerSettingsRef.current, + visual: { ...viewerSettingsRef.current.visual, inverted: checked }, + }); + }} + /> + { + setCollisionAxis(axis); + runtimeRef.current?.setLayerWorldTransform( + "collision", + playCanvasAxisTransform(axis, collisionInverted), + ); + commitViewerSettings({ + ...viewerSettingsRef.current, + collision: { ...viewerSettingsRef.current.collision, axis }, + }); + }} + /> +
@@ -208,9 +336,12 @@ export function SimulationViewport({ project }: { project: SimulationProject }) disabled={state !== "ready"} label="Инверсия управления по горизонтали" onChange={(checked) => { - cameraInversionRef.current.horizontal = checked; setHorizontalInverted(checked); runtimeRef.current?.setCameraInversion("horizontal", checked); + commitViewerSettings({ + ...viewerSettingsRef.current, + camera: { ...viewerSettingsRef.current.camera, invertHorizontal: checked }, + }); }} /> { - cameraInversionRef.current.vertical = checked; setVerticalInverted(checked); runtimeRef.current?.setCameraInversion("vertical", checked); + commitViewerSettings({ + ...viewerSettingsRef.current, + camera: { ...viewerSettingsRef.current.camera, invertVertical: checked }, + }); }} />
+ {settingsError ? ( +

+ Настройки не сохранены: {settingsError} +

+ ) : null} ) : null} @@ -255,8 +394,3 @@ export function SimulationViewport({ project }: { project: SimulationProject }) ); } - -function isX180Transform(transform: number[] | undefined): boolean { - if (!transform || transform.length !== 16) return false; - return transform.every((value, index) => value === PLAYCANVAS_X_180_TRANSFORM[index]); -} diff --git a/apps/control-station/src/core/simulation/projects.ts b/apps/control-station/src/core/simulation/projects.ts index daa2739..99982d3 100644 --- a/apps/control-station/src/core/simulation/projects.ts +++ b/apps/control-station/src/core/simulation/projects.ts @@ -1,4 +1,6 @@ export type SimulationSceneType = "interior" | "outdoor" | "object"; +export type SimulationTransformAxis = "x" | "y" | "z"; +export type SimulationViewerQuality = "low" | "medium" | "high" | "ultra" | "maximum"; export type SimulationProjectStatus = | "uploading" | "queued" @@ -32,6 +34,14 @@ export interface SimulationWorldManifest { }; } +export interface SimulationViewerSettings { + schemaVersion: "missioncore.simulation-viewer-settings/v1"; + quality: SimulationViewerQuality; + visual: { inverted: boolean; axis: SimulationTransformAxis }; + collision: { inverted: boolean; axis: SimulationTransformAxis }; + camera: { invertHorizontal: boolean; invertVertical: boolean }; +} + export interface SimulationProject { schemaVersion: "missioncore.simulation-project/v1"; projectId: string; @@ -60,6 +70,7 @@ export interface SimulationProject { byteLength: number; }>; worldManifest: SimulationWorldManifest | null; + viewerSettings: SimulationViewerSettings; error: string | null; createdAtUtc: string; updatedAtUtc: string; @@ -223,6 +234,30 @@ export async function updateSimulationProject( return parseProject(await jsonResponse(response)); } +export async function saveSimulationViewerSettings( + projectId: string, + settings: SimulationViewerSettings, +): Promise { + const response = await fetch( + `${API_ROOT}/${encodeURIComponent(projectId)}/viewer-settings`, + { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + schema_version: settings.schemaVersion, + quality: settings.quality, + visual: settings.visual, + collision: settings.collision, + camera: { + invert_horizontal: settings.camera.invertHorizontal, + invert_vertical: settings.camera.invertVertical, + }, + }), + }, + ); + return parseProject(await jsonResponse(response)); +} + export async function retrySimulationProject(projectId: string): Promise { const response = await fetch(`${API_ROOT}/${encodeURIComponent(projectId)}/build`, { method: "POST", @@ -251,7 +286,7 @@ function parseProject(value: unknown): SimulationProject { const record = objectValue(value, "simulation project"); exactKeys(record, [ "schema_version", "project_id", "name", "scene_type", "status", "source", "provider", - "artifacts", "world_manifest", "error", "created_at_utc", "updated_at_utc", + "artifacts", "world_manifest", "viewer_settings", "error", "created_at_utc", "updated_at_utc", ], "simulation project"); if (record.schema_version !== "missioncore.simulation-project/v1") { throw new Error("Проект симуляции вернул неподдерживаемую версию."); @@ -301,12 +336,47 @@ function parseProject(value: unknown): SimulationProject { }; }), worldManifest: record.world_manifest === null ? null : parseWorldManifest(record.world_manifest), + viewerSettings: parseViewerSettings(record.viewer_settings), error: nullableString(record.error, "project error"), createdAtUtc: stringValue(record.created_at_utc, "created at"), updatedAtUtc: stringValue(record.updated_at_utc, "updated at"), }; } +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") { + 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"); + return { + schemaVersion: "missioncore.simulation-viewer-settings/v1", + quality: qualityValue(record.quality), + visual, + collision, + camera: { + invertHorizontal: booleanValue(camera.invert_horizontal, "horizontal camera inversion"), + invertVertical: booleanValue(camera.invert_vertical, "vertical camera inversion"), + }, + }; +} + +function parseLayerViewerSettings( + value: unknown, + label: string, +): { inverted: boolean; axis: SimulationTransformAxis } { + const record = objectValue(value, label); + exactKeys(record, ["inverted", "axis"], label); + return { + inverted: booleanValue(record.inverted, `${label} inversion`), + axis: axisValue(record.axis), + }; +} + function parseWorldManifest(value: unknown): SimulationWorldManifest { const record = objectValue(value, "world manifest"); const visual = objectValue(record.visual, "visual manifest"); @@ -396,6 +466,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 qualityValue(value: unknown): SimulationViewerQuality { + if (["low", "medium", "high", "ultra", "maximum"].includes(String(value))) { + return value as SimulationViewerQuality; + } + return invalid("viewer quality"); +} + function statusValue(value: unknown): SimulationProjectStatus { if (["uploading", "queued", "processing", "importing", "ready", "failed"].includes(String(value))) { return value as SimulationProjectStatus; diff --git a/apps/control-station/src/styles/simulation.css b/apps/control-station/src/styles/simulation.css index 22716d1..e32b1ab 100644 --- a/apps/control-station/src/styles/simulation.css +++ b/apps/control-station/src/styles/simulation.css @@ -440,6 +440,25 @@ gap: 0.55rem; } +.simulation-viewport__settings-layer-row { + display: grid; + grid-template-columns: minmax(0, 1fr) 4.5rem; + align-items: center; + gap: 0.55rem; +} + +.simulation-viewport__axis-select { + width: 4.5rem; + min-width: 0; +} + +.simulation-viewport__settings-error { + border-radius: 0.65rem; + background: rgb(var(--nodedc-danger-rgb) / 0.08); + color: var(--nodedc-danger) !important; + padding: 0.55rem 0.65rem; +} + .simulation-viewport__toolbar-balance { min-width: 0; } diff --git a/apps/control-station/src/workspaces/simulation/SimulationWorkspace.tsx b/apps/control-station/src/workspaces/simulation/SimulationWorkspace.tsx index 4b6f164..c5eccd1 100644 --- a/apps/control-station/src/workspaces/simulation/SimulationWorkspace.tsx +++ b/apps/control-station/src/workspaces/simulation/SimulationWorkspace.tsx @@ -78,6 +78,12 @@ export function SimulationWorkspace() { setEditing(null); }; + const acceptViewerProject = useCallback((project: SimulationProject) => { + setProjects((current) => current.map((item) => ( + item.projectId === project.projectId ? project : item + ))); + }, []); + const confirmDelete = async () => { if (!deleting) return; await deleteSimulationProject(deleting.projectId); @@ -122,7 +128,10 @@ export function SimulationWorkspace() { {selected.status === "ready" && selected.worldManifest ? ( - + ) : ( {selected.status === "failed" ? : } diff --git a/apps/control-station/test/simulationWorkspace.test.mjs b/apps/control-station/test/simulationWorkspace.test.mjs index ceb1ef0..1fb5e08 100644 --- a/apps/control-station/test/simulationWorkspace.test.mjs +++ b/apps/control-station/test/simulationWorkspace.test.mjs @@ -81,6 +81,10 @@ test("PlayCanvas owns the realtime scene graph without an iframe or React entity 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, /DracoDecoderModule/); + assert.match(runtime, /draco_decoder_gltf\.wasm/); assert.match(runtime, /ensureCollision/); assert.match(runtime, /app\.root\.addChild/); assert.match(runtime, /dispose\(\)/); @@ -88,11 +92,15 @@ test("PlayCanvas owns the realtime scene graph without an iframe or React entity assert.match(viewport, /Collision недоступен/); assert.match(viewport, /Для этой сборки collision GLB не был запрошен/); assert.match(viewport, /\("high"\)/); + assert.match(viewport, /useState\(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, /Ось инверсии collision-слоя/); + assert.match(viewport, /saveSimulationViewerSettings/); + assert.match(viewport, /onProjectChange\?\.\(saved\)/); }); diff --git a/src/k1link/service_watchdog.py b/src/k1link/service_watchdog.py index a69f7a1..a2e9cfa 100644 --- a/src/k1link/service_watchdog.py +++ b/src/k1link/service_watchdog.py @@ -28,10 +28,18 @@ class MissionCoreWatchdogError(RuntimeError): @dataclass(frozen=True, slots=True) class MissionCoreWatchdogPolicy: - startup_grace_seconds: float = 45.0 + # Cold startup on the 18 GB control laptop includes validating the durable + # recorded-evidence catalog and can exceed two minutes under storage load. + # This applies only before the first probe; steady-state failures retain the + # much tighter bounded policy below. + startup_grace_seconds: float = 300.0 probe_interval_seconds: float = 2.0 - probe_timeout_seconds: float = 1.0 - consecutive_failure_limit: int = 3 + # Source hashing and resumable multi-gigabyte uploads can briefly delay the + # synchronous readiness projection without making the service unhealthy. + # Six bounded five-second probes still fail closed, but do not turn normal + # storage pressure into a restart loop that discards upload progress. + probe_timeout_seconds: float = 5.0 + consecutive_failure_limit: int = 6 graceful_shutdown_seconds: float = 12.0 def __post_init__(self) -> None: diff --git a/src/k1link/simulation/projects.py b/src/k1link/simulation/projects.py index 2ae2ce4..51dfb43 100644 --- a/src/k1link/simulation/projects.py +++ b/src/k1link/simulation/projects.py @@ -26,6 +26,7 @@ from k1link.simulation.gaussian_pipeline_gateway import ( PROJECT_SCHEMA: Final = "missioncore.simulation-project/v1" WORLD_MANIFEST_SCHEMA: Final = "missioncore.simulation-world-manifest/v1" +VIEWER_SETTINGS_SCHEMA: Final = "missioncore.simulation-viewer-settings/v1" PROJECT_ID_PATTERN: Final = re.compile(r"^sim-[a-f0-9]{32}$") SOURCE_FILE_ID_PATTERN: Final = re.compile(r"^src-[0-9]{5}-[a-f0-9]{8}$") PROVIDER_JOB_ID_PATTERN: Final = re.compile(r"^gsp-[0-9]{14}-[a-f0-9]{8}$") @@ -142,6 +143,7 @@ class SimulationProjectStore: }, "artifacts": [], "world_manifest": None, + "viewer_settings": _default_viewer_settings(), "error": None, "created_at_utc": now, "updated_at_utc": now, @@ -189,6 +191,19 @@ class SimulationProjectStore: self._write(document) return document + def update_viewer_settings( + self, + project_id: str, + settings: dict[str, Any], + ) -> dict[str, Any]: + normalized = _viewer_settings(settings) + with self._lock: + document = self._read(project_id) + document["viewer_settings"] = normalized + document["updated_at_utc"] = utc_now_iso() + self._write(document) + return document + def upload_state(self, project_id: str, file_id: str) -> tuple[int, int]: with self._lock: document = self._read(project_id) @@ -239,7 +254,7 @@ class SimulationProjectStore: def begin_build(self, project_id: str) -> dict[str, Any]: with self._lock: document = self._read(project_id) - if document["status"] not in {"uploading", "failed"}: + if document["status"] not in {"uploading", "failed", "ready"}: raise SimulationProjectConflictError("simulation project cannot start a build") if any( int(item["uploaded_bytes"]) != int(item["byte_length"]) @@ -371,6 +386,7 @@ class SimulationProjectStore: or document.get("project_id") != project_id ): raise SimulationProjectError("persisted simulation project identity is invalid") + document.setdefault("viewer_settings", _default_viewer_settings()) return document def _write(self, document: dict[str, Any]) -> None: @@ -421,7 +437,7 @@ class SimulationProjectService: def begin_build(self, project_id: str) -> dict[str, Any]: project = self.store.get(project_id) - if project.get("status") == "failed": + if project.get("status") in {"failed", "ready"}: job_id = project["provider"].get("job_id") if isinstance(job_id, str): provider = self.provider_factory() @@ -616,7 +632,7 @@ def _world_manifest(project_id: str, artifacts: list[dict[str, Any]]) -> dict[st }, "transforms": { "world_from_visual": [1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1], - "world_from_collision": [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], + "world_from_collision": [-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1], }, } @@ -628,11 +644,66 @@ def _collision_profile(scene_type: str) -> dict[str, Any]: "seed_position": [0, 1, 0], "capsule_height": 1.6, "capsule_radius": 0.2, - "voxel_size": 0.05, + "voxel_size": 0.2 if scene_type == "outdoor" else 0.1, "mesh_shape": "smooth", } +def _default_viewer_settings() -> dict[str, Any]: + return { + "schema_version": VIEWER_SETTINGS_SCHEMA, + "quality": "high", + "visual": {"inverted": True, "axis": "x"}, + "collision": {"inverted": True, "axis": "y"}, + "camera": { + "invert_horizontal": True, + "invert_vertical": False, + }, + } + + +def _viewer_settings(value: object) -> dict[str, Any]: + if not isinstance(value, dict) or set(value) != { + "schema_version", "quality", "visual", "collision", "camera" + }: + raise SimulationProjectError("simulation viewer settings contract is invalid") + if value.get("schema_version") != VIEWER_SETTINGS_SCHEMA: + raise SimulationProjectError("simulation viewer settings version is unsupported") + quality = value.get("quality") + if quality not in {"low", "medium", "high", "ultra", "maximum"}: + raise SimulationProjectError("simulation viewer quality is invalid") + + def layer(name: str) -> dict[str, Any]: + raw = value.get(name) + if not isinstance(raw, dict) or set(raw) != {"inverted", "axis"}: + raise SimulationProjectError(f"simulation {name} transform is invalid") + inverted = raw.get("inverted") + axis = raw.get("axis") + if not isinstance(inverted, bool) or axis not in {"x", "y", "z"}: + raise SimulationProjectError(f"simulation {name} transform is invalid") + return {"inverted": inverted, "axis": axis} + + camera = value.get("camera") + if not isinstance(camera, dict) or set(camera) != { + "invert_horizontal", "invert_vertical" + }: + raise SimulationProjectError("simulation camera settings are invalid") + horizontal = camera.get("invert_horizontal") + vertical = camera.get("invert_vertical") + if not isinstance(horizontal, bool) or not isinstance(vertical, bool): + raise SimulationProjectError("simulation camera settings are invalid") + return { + "schema_version": VIEWER_SETTINGS_SCHEMA, + "quality": quality, + "visual": layer("visual"), + "collision": layer("collision"), + "camera": { + "invert_horizontal": horizontal, + "invert_vertical": vertical, + }, + } + + def _project_name(value: str) -> str: normalized = " ".join(value.split()) if not 1 <= len(normalized) <= 120: diff --git a/src/k1link/web/simulation_projects_api.py b/src/k1link/web/simulation_projects_api.py index ec76746..97a8581 100644 --- a/src/k1link/web/simulation_projects_api.py +++ b/src/k1link/web/simulation_projects_api.py @@ -12,6 +12,7 @@ from k1link.simulation.gaussian_pipeline_gateway import GaussianPipelineGatewayE from k1link.simulation.projects import ( MAX_UPLOAD_CHUNK_BYTES, PROJECT_SCHEMA, + VIEWER_SETTINGS_SCHEMA, SimulationProjectConflictError, SimulationProjectError, SimulationProjectNotFoundError, @@ -45,6 +46,30 @@ class SimulationProjectUpdate(BaseModel): scene_type: Literal["interior", "outdoor", "object"] +class SimulationLayerViewerSettings(BaseModel): + model_config = ConfigDict(extra="forbid", frozen=True) + + inverted: bool + axis: Literal["x", "y", "z"] + + +class SimulationCameraViewerSettings(BaseModel): + model_config = ConfigDict(extra="forbid", frozen=True) + + invert_horizontal: bool + invert_vertical: bool + + +class SimulationViewerSettings(BaseModel): + model_config = ConfigDict(extra="forbid", frozen=True) + + schema_version: Literal["missioncore.simulation-viewer-settings/v1"] = VIEWER_SETTINGS_SCHEMA + quality: Literal["low", "medium", "high", "ultra", "maximum"] + visual: SimulationLayerViewerSettings + collision: SimulationLayerViewerSettings + camera: SimulationCameraViewerSettings + + class SimulationProjectDocument(BaseModel): model_config = ConfigDict(extra="forbid") @@ -57,6 +82,7 @@ class SimulationProjectDocument(BaseModel): provider: dict[str, Any] artifacts: list[dict[str, Any]] world_manifest: dict[str, Any] | None + viewer_settings: SimulationViewerSettings error: str | None created_at_utc: str updated_at_utc: str @@ -117,6 +143,21 @@ def build_simulation_projects_router( except SimulationProjectError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc + @router.put( + "/projects/{project_id}/viewer-settings", + response_model=SimulationProjectDocument, + ) + def update_viewer_settings( + project_id: str, + request: SimulationViewerSettings, + ) -> dict[str, Any]: + try: + return store.update_viewer_settings(project_id, request.model_dump()) + except SimulationProjectNotFoundError as exc: + raise HTTPException(status_code=404, detail="Проект симуляции не найден.") from exc + except SimulationProjectError as exc: + raise HTTPException(status_code=400, detail=str(exc)) from exc + @router.head("/projects/{project_id}/source/{file_id}") def source_upload_state(project_id: str, file_id: str) -> Response: try: diff --git a/tests/test_simulation_projects.py b/tests/test_simulation_projects.py index c598f2a..9375f97 100644 --- a/tests/test_simulation_projects.py +++ b/tests/test_simulation_projects.py @@ -201,7 +201,7 @@ def test_service_materializes_world_manifest_and_deletes_both_copies(tmp_path: P 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, ] assert ready["world_manifest"]["transforms"]["world_from_collision"] == [ - 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, + -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, ] assert provider.upload_calls == 1 assert provider.submit_calls == 1 @@ -216,7 +216,7 @@ def test_service_materializes_world_manifest_and_deletes_both_copies(tmp_path: P "seed_position": [0, 1, 0], "capsule_height": 1.6, "capsule_radius": 0.2, - "voxel_size": 0.05, + "voxel_size": 0.1, "mesh_shape": "smooth", } service.delete(project["project_id"]) @@ -281,6 +281,41 @@ def test_failed_project_retries_from_retained_source_and_releases_old_job(tmp_pa assert queued["source"]["uploaded_byte_length"] == queued["source"]["total_byte_length"] +def test_ready_project_rebuilds_from_retained_source_and_preserves_viewer_settings( + tmp_path: Path, +) -> None: + store = SimulationProjectStore(tmp_path) + project = store.create( + name="Rebuild scene", + scene_type="outdoor", + source_kind="folder", + files=_folder_files(), + ) + _upload_all(store, project) + store.begin_build(project["project_id"]) + provider = _ReadyProvider() + service = SimulationProjectService( + store, + provider_factory=lambda: provider, + ) # type: ignore[arg-type] + service.process(project["project_id"]) + settings = { + "schema_version": "missioncore.simulation-viewer-settings/v1", + "quality": "medium", + "visual": {"inverted": True, "axis": "z"}, + "collision": {"inverted": True, "axis": "y"}, + "camera": {"invert_horizontal": False, "invert_vertical": True}, + } + store.update_viewer_settings(project["project_id"], settings) + + queued = service.begin_build(project["project_id"]) + + assert provider.deleted == ["gsp-20260826000000-deadbeef"] + assert queued["status"] == "queued" + assert queued["viewer_settings"] == settings + assert queued["source"]["uploaded_byte_length"] == queued["source"]["total_byte_length"] + + def test_api_exposes_same_origin_resumable_upload_contract(tmp_path: Path) -> None: store = SimulationProjectStore(tmp_path) service = SimulationProjectService(store, provider_factory=lambda: None) @@ -296,6 +331,13 @@ def test_api_exposes_same_origin_resumable_upload_contract(tmp_path: Path) -> No }) assert created.status_code == 201 project = created.json() + assert project["viewer_settings"] == { + "schema_version": "missioncore.simulation-viewer-settings/v1", + "quality": "high", + "visual": {"inverted": True, "axis": "x"}, + "collision": {"inverted": True, "axis": "y"}, + "camera": {"invert_horizontal": True, "invert_vertical": False}, + } source_file = project["source"]["files"][0] upload_url = ( f"/api/v1/simulation-worlds/projects/{project['project_id']}" @@ -311,3 +353,21 @@ def test_api_exposes_same_origin_resumable_upload_contract(tmp_path: Path) -> No assert second.status_code == 204 catalog = client.get("/api/v1/simulation-worlds/projects").json() assert catalog["projects"][0]["project_id"] == project["project_id"] + + settings = { + "schema_version": "missioncore.simulation-viewer-settings/v1", + "quality": "medium", + "visual": {"inverted": True, "axis": "z"}, + "collision": {"inverted": False, "axis": "x"}, + "camera": {"invert_horizontal": False, "invert_vertical": True}, + } + saved = client.put( + f"/api/v1/simulation-worlds/projects/{project['project_id']}/viewer-settings", + json=settings, + ) + assert saved.status_code == 200 + assert saved.json()["viewer_settings"] == settings + persisted = client.get( + f"/api/v1/simulation-worlds/projects/{project['project_id']}" + ).json() + assert persisted["viewer_settings"] == settings