@viamrobotics/motion-tools 1.30.0 → 1.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/components/App.svelte +51 -46
  2. package/dist/components/App.svelte.d.ts +1 -1
  3. package/dist/components/Entities/Arrows/Arrows.svelte +4 -7
  4. package/dist/components/Entities/hooks/useEntityEvents.svelte.d.ts +0 -1
  5. package/dist/components/Entities/hooks/useEntityEvents.svelte.js +30 -16
  6. package/dist/components/InputBindings.svelte +0 -43
  7. package/dist/components/KeyboardBindings.svelte +38 -0
  8. package/dist/components/KeyboardBindings.svelte.d.ts +18 -0
  9. package/dist/components/PointerMissBox.svelte +6 -3
  10. package/dist/components/Scene.svelte +34 -45
  11. package/dist/components/SceneProviders.svelte +2 -4
  12. package/dist/components/SceneProviders.svelte.d.ts +1 -3
  13. package/dist/components/Selected.svelte +20 -27
  14. package/dist/components/SelectedTransformControls.svelte +10 -8
  15. package/dist/components/StaticGeometries.svelte +3 -5
  16. package/dist/components/hover/HoveredEntities.svelte +15 -14
  17. package/dist/components/hover/HoveredEntities.svelte.d.ts +17 -2
  18. package/dist/components/hover/HoveredEntity.svelte +8 -5
  19. package/dist/components/hover/HoveredEntity.svelte.d.ts +5 -1
  20. package/dist/components/hover/LinkedHoveredEntity.svelte +7 -11
  21. package/dist/components/hover/LinkedHoveredEntity.svelte.d.ts +1 -0
  22. package/dist/components/overlay/Details.svelte +24 -38
  23. package/dist/components/overlay/Details.svelte.d.ts +3 -1
  24. package/dist/components/overlay/controls/Controls.svelte +0 -2
  25. package/dist/components/overlay/dashboard/Button.svelte +5 -3
  26. package/dist/components/overlay/dashboard/Button.svelte.d.ts +1 -1
  27. package/dist/components/overlay/left-pane/Tree.svelte +13 -10
  28. package/dist/components/overlay/left-pane/TreeContainer.svelte +9 -4
  29. package/dist/components/overlay/left-pane/TreeNode.svelte +6 -4
  30. package/dist/draw.d.ts +1 -0
  31. package/dist/draw.js +1 -1
  32. package/dist/ecs/index.d.ts +1 -0
  33. package/dist/ecs/index.js +1 -0
  34. package/dist/ecs/traits.d.ts +22 -5
  35. package/dist/ecs/traits.js +33 -4
  36. package/dist/ecs/useTag.svelte.d.ts +5 -0
  37. package/dist/ecs/useTag.svelte.js +43 -0
  38. package/dist/hooks/useConfigFrames.svelte.js +1 -1
  39. package/dist/hooks/useEnvironment.svelte.d.ts +1 -1
  40. package/dist/hooks/useLinked.svelte.js +7 -8
  41. package/dist/hooks/useMouseRaycaster.svelte.d.ts +4 -3
  42. package/dist/hooks/useMouseRaycaster.svelte.js +1 -0
  43. package/dist/hooks/useSettings.svelte.d.ts +1 -1
  44. package/dist/lib.d.ts +1 -0
  45. package/dist/lib.js +1 -0
  46. package/dist/loaders/pcd/worker.inline.d.ts +1 -1
  47. package/dist/loaders/pcd/worker.inline.js +1 -1
  48. package/dist/loaders/pcd/worker.js +3 -1
  49. package/dist/plugins/Focus/Focus.svelte +45 -0
  50. package/dist/plugins/Focus/Focus.svelte.d.ts +3 -0
  51. package/dist/plugins/Focus/FocusBox.svelte +75 -0
  52. package/dist/plugins/Focus/FocusBox.svelte.d.ts +3 -0
  53. package/dist/plugins/Focus/provideFocus.svelte.d.ts +1 -0
  54. package/dist/plugins/Focus/provideFocus.svelte.js +61 -0
  55. package/dist/{components → plugins}/MeasureTool/MeasureTool.svelte +6 -8
  56. package/dist/plugins/Selection/Ellipse.svelte +21 -16
  57. package/dist/plugins/Selection/Lasso.svelte +21 -16
  58. package/dist/plugins/Selection/SelectionTool.svelte +10 -3
  59. package/dist/plugins/Selection/relations.d.ts +6 -0
  60. package/dist/plugins/Selection/relations.js +7 -0
  61. package/dist/plugins/Selection/traits.d.ts +0 -5
  62. package/dist/plugins/Selection/traits.js +1 -6
  63. package/dist/plugins/index.d.ts +3 -0
  64. package/dist/plugins/index.js +3 -0
  65. package/dist/snapshot.d.ts +14 -0
  66. package/dist/snapshot.js +23 -0
  67. package/dist/three/arrow.d.ts +2 -0
  68. package/dist/three/arrow.js +3 -1
  69. package/package.json +17 -5
  70. package/dist/components/Focus.svelte +0 -46
  71. package/dist/components/Focus.svelte.d.ts +0 -7
  72. package/dist/hooks/useSelection.svelte.d.ts +0 -33
  73. package/dist/hooks/useSelection.svelte.js +0 -94
  74. /package/dist/{components → plugins}/MeasureTool/MeasurePoint.svelte +0 -0
  75. /package/dist/{components → plugins}/MeasureTool/MeasurePoint.svelte.d.ts +0 -0
  76. /package/dist/{components → plugins}/MeasureTool/MeasureTool.svelte.d.ts +0 -0
@@ -3608,7 +3608,7 @@ void main() {
3608
3608
  #include <fog_fragment>
3609
3609
  }\`,C={alphahash_fragment:Hi,alphahash_pars_fragment:Wi,alphamap_fragment:Xi,alphamap_pars_fragment:qi,alphatest_fragment:Yi,alphatest_pars_fragment:Zi,aomap_fragment:Ji,aomap_pars_fragment:\$i,batching_pars_vertex:Ki,batching_vertex:Qi,begin_vertex:ji,beginnormal_vertex:es,bsdfs:ts,iridescence_fragment:ns,bumpmap_pars_fragment:is,clipping_planes_fragment:ss,clipping_planes_pars_fragment:rs,clipping_planes_pars_vertex:as,clipping_planes_vertex:os,color_fragment:ls,color_pars_fragment:cs,color_pars_vertex:hs,color_vertex:us,common:ds,cube_uv_reflection_fragment:fs,defaultnormal_vertex:ps,displacementmap_pars_vertex:ms,displacementmap_vertex:gs,emissivemap_fragment:_s,emissivemap_pars_fragment:xs,colorspace_fragment:vs,colorspace_pars_fragment:ys,envmap_fragment:Ms,envmap_common_pars_fragment:Ss,envmap_pars_fragment:bs,envmap_pars_vertex:Ts,envmap_physical_pars_fragment:Ns,envmap_vertex:As,fog_vertex:Es,fog_pars_vertex:ws,fog_fragment:Cs,fog_pars_fragment:Rs,gradientmap_pars_fragment:Is,lightmap_pars_fragment:Ps,lights_lambert_fragment:Ds,lights_lambert_pars_fragment:Ls,lights_pars_begin:Us,lights_toon_fragment:Fs,lights_toon_pars_fragment:Os,lights_phong_fragment:Bs,lights_phong_pars_fragment:zs,lights_physical_fragment:Vs,lights_physical_pars_fragment:ks,lights_fragment_begin:Gs,lights_fragment_maps:Hs,lights_fragment_end:Ws,logdepthbuf_fragment:Xs,logdepthbuf_pars_fragment:qs,logdepthbuf_pars_vertex:Ys,logdepthbuf_vertex:Zs,map_fragment:Js,map_pars_fragment:\$s,map_particle_fragment:Ks,map_particle_pars_fragment:Qs,metalnessmap_fragment:js,metalnessmap_pars_fragment:er,morphinstance_vertex:tr,morphcolor_vertex:nr,morphnormal_vertex:ir,morphtarget_pars_vertex:sr,morphtarget_vertex:rr,normal_fragment_begin:ar,normal_fragment_maps:or,normal_pars_fragment:lr,normal_pars_vertex:cr,normal_vertex:hr,normalmap_pars_fragment:ur,clearcoat_normal_fragment_begin:dr,clearcoat_normal_fragment_maps:fr,clearcoat_pars_fragment:pr,iridescence_pars_fragment:mr,opaque_fragment:gr,packing:_r,premultiplied_alpha_fragment:xr,project_vertex:vr,dithering_fragment:yr,dithering_pars_fragment:Mr,roughnessmap_fragment:Sr,roughnessmap_pars_fragment:br,shadowmap_pars_fragment:Tr,shadowmap_pars_vertex:Ar,shadowmap_vertex:Er,shadowmask_pars_fragment:wr,skinbase_vertex:Cr,skinning_pars_vertex:Rr,skinning_vertex:Ir,skinnormal_vertex:Pr,specularmap_fragment:Dr,specularmap_pars_fragment:Lr,tonemapping_fragment:Ur,tonemapping_pars_fragment:Nr,transmission_fragment:Fr,transmission_pars_fragment:Or,uv_pars_fragment:Br,uv_pars_vertex:zr,uv_vertex:Vr,worldpos_vertex:kr,background_vert:Gr,background_frag:Hr,backgroundCube_vert:Wr,backgroundCube_frag:Xr,cube_vert:qr,cube_frag:Yr,depth_vert:Zr,depth_frag:Jr,distance_vert:\$r,distance_frag:Kr,equirect_vert:Qr,equirect_frag:jr,linedashed_vert:ea,linedashed_frag:ta,meshbasic_vert:na,meshbasic_frag:ia,meshlambert_vert:sa,meshlambert_frag:ra,meshmatcap_vert:aa,meshmatcap_frag:oa,meshnormal_vert:la,meshnormal_frag:ca,meshphong_vert:ha,meshphong_frag:ua,meshphysical_vert:da,meshphysical_frag:fa,meshtoon_vert:pa,meshtoon_frag:ma,points_vert:ga,points_frag:_a,shadow_vert:xa,shadow_frag:va,sprite_vert:ya,sprite_frag:Ma},M={common:{diffuse:{value:new O(16777215)},opacity:{value:1},map:{value:null},mapTransform:{value:new w},alphaMap:{value:null},alphaMapTransform:{value:new w},alphaTest:{value:0}},specularmap:{specularMap:{value:null},specularMapTransform:{value:new w}},envmap:{envMap:{value:null},envMapRotation:{value:new w},flipEnvMap:{value:-1},reflectivity:{value:1},ior:{value:1.5},refractionRatio:{value:.98},dfgLUT:{value:null}},aomap:{aoMap:{value:null},aoMapIntensity:{value:1},aoMapTransform:{value:new w}},lightmap:{lightMap:{value:null},lightMapIntensity:{value:1},lightMapTransform:{value:new w}},bumpmap:{bumpMap:{value:null},bumpMapTransform:{value:new w},bumpScale:{value:1}},normalmap:{normalMap:{value:null},normalMapTransform:{value:new w},normalScale:{value:new X(1,1)}},displacementmap:{displacementMap:{value:null},displacementMapTransform:{value:new w},displacementScale:{value:1},displacementBias:{value:0}},emissivemap:{emissiveMap:{value:null},emissiveMapTransform:{value:new w}},metalnessmap:{metalnessMap:{value:null},metalnessMapTransform:{value:new w}},roughnessmap:{roughnessMap:{value:null},roughnessMapTransform:{value:new w}},gradientmap:{gradientMap:{value:null}},fog:{fogDensity:{value:25e-5},fogNear:{value:1},fogFar:{value:2e3},fogColor:{value:new O(16777215)}},lights:{ambientLightColor:{value:[]},lightProbe:{value:[]},directionalLights:{value:[],properties:{direction:{},color:{}}},directionalLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},directionalShadowMatrix:{value:[]},spotLights:{value:[],properties:{color:{},position:{},direction:{},distance:{},coneCos:{},penumbraCos:{},decay:{}}},spotLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},spotLightMap:{value:[]},spotLightMatrix:{value:[]},pointLights:{value:[],properties:{color:{},position:{},decay:{},distance:{}}},pointLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{},shadowCameraNear:{},shadowCameraFar:{}}},pointShadowMatrix:{value:[]},hemisphereLights:{value:[],properties:{direction:{},skyColor:{},groundColor:{}}},rectAreaLights:{value:[],properties:{color:{},position:{},width:{},height:{}}},ltc_1:{value:null},ltc_2:{value:null}},points:{diffuse:{value:new O(16777215)},opacity:{value:1},size:{value:1},scale:{value:1},map:{value:null},alphaMap:{value:null},alphaMapTransform:{value:new w},alphaTest:{value:0},uvTransform:{value:new w}},sprite:{diffuse:{value:new O(16777215)},opacity:{value:1},center:{value:new X(.5,.5)},rotation:{value:0},map:{value:null},mapTransform:{value:new w},alphaMap:{value:null},alphaMapTransform:{value:new w},alphaTest:{value:0}}},gi={basic:{uniforms:q([M.common,M.specularmap,M.envmap,M.aomap,M.lightmap,M.fog]),vertexShader:C.meshbasic_vert,fragmentShader:C.meshbasic_frag},lambert:{uniforms:q([M.common,M.specularmap,M.envmap,M.aomap,M.lightmap,M.emissivemap,M.bumpmap,M.normalmap,M.displacementmap,M.fog,M.lights,{emissive:{value:new O(0)},envMapIntensity:{value:1}}]),vertexShader:C.meshlambert_vert,fragmentShader:C.meshlambert_frag},phong:{uniforms:q([M.common,M.specularmap,M.envmap,M.aomap,M.lightmap,M.emissivemap,M.bumpmap,M.normalmap,M.displacementmap,M.fog,M.lights,{emissive:{value:new O(0)},specular:{value:new O(1118481)},shininess:{value:30},envMapIntensity:{value:1}}]),vertexShader:C.meshphong_vert,fragmentShader:C.meshphong_frag},standard:{uniforms:q([M.common,M.envmap,M.aomap,M.lightmap,M.emissivemap,M.bumpmap,M.normalmap,M.displacementmap,M.roughnessmap,M.metalnessmap,M.fog,M.lights,{emissive:{value:new O(0)},roughness:{value:1},metalness:{value:0},envMapIntensity:{value:1}}]),vertexShader:C.meshphysical_vert,fragmentShader:C.meshphysical_frag},toon:{uniforms:q([M.common,M.aomap,M.lightmap,M.emissivemap,M.bumpmap,M.normalmap,M.displacementmap,M.gradientmap,M.fog,M.lights,{emissive:{value:new O(0)}}]),vertexShader:C.meshtoon_vert,fragmentShader:C.meshtoon_frag},matcap:{uniforms:q([M.common,M.bumpmap,M.normalmap,M.displacementmap,M.fog,{matcap:{value:null}}]),vertexShader:C.meshmatcap_vert,fragmentShader:C.meshmatcap_frag},points:{uniforms:q([M.points,M.fog]),vertexShader:C.points_vert,fragmentShader:C.points_frag},dashed:{uniforms:q([M.common,M.fog,{scale:{value:1},dashSize:{value:1},totalSize:{value:2}}]),vertexShader:C.linedashed_vert,fragmentShader:C.linedashed_frag},depth:{uniforms:q([M.common,M.displacementmap]),vertexShader:C.depth_vert,fragmentShader:C.depth_frag},normal:{uniforms:q([M.common,M.bumpmap,M.normalmap,M.displacementmap,{opacity:{value:1}}]),vertexShader:C.meshnormal_vert,fragmentShader:C.meshnormal_frag},sprite:{uniforms:q([M.sprite,M.fog]),vertexShader:C.sprite_vert,fragmentShader:C.sprite_frag},background:{uniforms:{uvTransform:{value:new w},t2D:{value:null},backgroundIntensity:{value:1}},vertexShader:C.background_vert,fragmentShader:C.background_frag},backgroundCube:{uniforms:{envMap:{value:null},flipEnvMap:{value:-1},backgroundBlurriness:{value:0},backgroundIntensity:{value:1},backgroundRotation:{value:new w}},vertexShader:C.backgroundCube_vert,fragmentShader:C.backgroundCube_frag},cube:{uniforms:{tCube:{value:null},tFlip:{value:-1},opacity:{value:1}},vertexShader:C.cube_vert,fragmentShader:C.cube_frag},equirect:{uniforms:{tEquirect:{value:null}},vertexShader:C.equirect_vert,fragmentShader:C.equirect_frag},distance:{uniforms:q([M.common,M.displacementmap,{referencePosition:{value:new v},nearDistance:{value:1},farDistance:{value:1e3}}]),vertexShader:C.distance_vert,fragmentShader:C.distance_frag},shadow:{uniforms:q([M.lights,M.fog,{color:{value:new O(0)},opacity:{value:1}}]),vertexShader:C.shadow_vert,fragmentShader:C.shadow_frag}};gi.physical={uniforms:q([gi.standard.uniforms,{clearcoat:{value:0},clearcoatMap:{value:null},clearcoatMapTransform:{value:new w},clearcoatNormalMap:{value:null},clearcoatNormalMapTransform:{value:new w},clearcoatNormalScale:{value:new X(1,1)},clearcoatRoughness:{value:0},clearcoatRoughnessMap:{value:null},clearcoatRoughnessMapTransform:{value:new w},dispersion:{value:0},iridescence:{value:0},iridescenceMap:{value:null},iridescenceMapTransform:{value:new w},iridescenceIOR:{value:1.3},iridescenceThicknessMinimum:{value:100},iridescenceThicknessMaximum:{value:400},iridescenceThicknessMap:{value:null},iridescenceThicknessMapTransform:{value:new w},sheen:{value:0},sheenColor:{value:new O(0)},sheenColorMap:{value:null},sheenColorMapTransform:{value:new w},sheenRoughness:{value:1},sheenRoughnessMap:{value:null},sheenRoughnessMapTransform:{value:new w},transmission:{value:0},transmissionMap:{value:null},transmissionMapTransform:{value:new w},transmissionSamplerSize:{value:new X},transmissionSamplerMap:{value:null},thickness:{value:0},thicknessMap:{value:null},thicknessMapTransform:{value:new w},attenuationDistance:{value:0},attenuationColor:{value:new O(0)},specularColor:{value:new O(1,1,1)},specularColorMap:{value:null},specularColorMapTransform:{value:new w},specularIntensity:{value:1},specularIntensityMap:{value:null},specularIntensityMapTransform:{value:new w},anisotropyVector:{value:new X},anisotropyMap:{value:null},anisotropyMapTransform:{value:new w}}]),vertexShader:C.meshphysical_vert,fragmentShader:C.meshphysical_frag};var Bd={[Cn]:"LINEAR_TONE_MAPPING",[Rn]:"REINHARD_TONE_MAPPING",[In]:"CINEON_TONE_MAPPING",[Pn]:"ACES_FILMIC_TONE_MAPPING",[Ln]:"AGX_TONE_MAPPING",[Un]:"NEUTRAL_TONE_MAPPING",[Dn]:"CUSTOM_TONE_MAPPING"};var zd=new Float32Array(16),Vd=new Float32Array(9),kd=new Float32Array(4);var Gd={[Cn]:"Linear",[Rn]:"Reinhard",[In]:"Cineon",[Pn]:"ACESFilmic",[Ln]:"AgX",[Un]:"Neutral",[Dn]:"Custom"};var Hd={[ei]:"SHADOWMAP_TYPE_PCF",[ti]:"SHADOWMAP_TYPE_VSM"};var Wd={[ri]:"ENVMAP_TYPE_CUBE",[Fn]:"ENVMAP_TYPE_CUBE",[ai]:"ENVMAP_TYPE_CUBE_UV"};var Xd={[Fn]:"ENVMAP_MODE_REFRACTION"};var qd={[ni]:"ENVMAP_BLENDING_MULTIPLY",[ii]:"ENVMAP_BLENDING_MIX",[si]:"ENVMAP_BLENDING_ADD"};var Yd=new Uint16Array([12469,15057,12620,14925,13266,14620,13807,14376,14323,13990,14545,13625,14713,13328,14840,12882,14931,12528,14996,12233,15039,11829,15066,11525,15080,11295,15085,10976,15082,10705,15073,10495,13880,14564,13898,14542,13977,14430,14158,14124,14393,13732,14556,13410,14702,12996,14814,12596,14891,12291,14937,11834,14957,11489,14958,11194,14943,10803,14921,10506,14893,10278,14858,9960,14484,14039,14487,14025,14499,13941,14524,13740,14574,13468,14654,13106,14743,12678,14818,12344,14867,11893,14889,11509,14893,11180,14881,10751,14852,10428,14812,10128,14765,9754,14712,9466,14764,13480,14764,13475,14766,13440,14766,13347,14769,13070,14786,12713,14816,12387,14844,11957,14860,11549,14868,11215,14855,10751,14825,10403,14782,10044,14729,9651,14666,9352,14599,9029,14967,12835,14966,12831,14963,12804,14954,12723,14936,12564,14917,12347,14900,11958,14886,11569,14878,11247,14859,10765,14828,10401,14784,10011,14727,9600,14660,9289,14586,8893,14508,8533,15111,12234,15110,12234,15104,12216,15092,12156,15067,12010,15028,11776,14981,11500,14942,11205,14902,10752,14861,10393,14812,9991,14752,9570,14682,9252,14603,8808,14519,8445,14431,8145,15209,11449,15208,11451,15202,11451,15190,11438,15163,11384,15117,11274,15055,10979,14994,10648,14932,10343,14871,9936,14803,9532,14729,9218,14645,8742,14556,8381,14461,8020,14365,7603,15273,10603,15272,10607,15267,10619,15256,10631,15231,10614,15182,10535,15118,10389,15042,10167,14963,9787,14883,9447,14800,9115,14710,8665,14615,8318,14514,7911,14411,7507,14279,7198,15314,9675,15313,9683,15309,9712,15298,9759,15277,9797,15229,9773,15166,9668,15084,9487,14995,9274,14898,8910,14800,8539,14697,8234,14590,7790,14479,7409,14367,7067,14178,6621,15337,8619,15337,8631,15333,8677,15325,8769,15305,8871,15264,8940,15202,8909,15119,8775,15022,8565,14916,8328,14804,8009,14688,7614,14569,7287,14448,6888,14321,6483,14088,6171,15350,7402,15350,7419,15347,7480,15340,7613,15322,7804,15287,7973,15229,8057,15148,8012,15046,7846,14933,7611,14810,7357,14682,7069,14552,6656,14421,6316,14251,5948,14007,5528,15356,5942,15356,5977,15353,6119,15348,6294,15332,6551,15302,6824,15249,7044,15171,7122,15070,7050,14949,6861,14818,6611,14679,6349,14538,6067,14398,5651,14189,5311,13935,4958,15359,4123,15359,4153,15356,4296,15353,4646,15338,5160,15311,5508,15263,5829,15188,6042,15088,6094,14966,6001,14826,5796,14678,5543,14527,5287,14377,4985,14133,4586,13869,4257,15360,1563,15360,1642,15358,2076,15354,2636,15341,3350,15317,4019,15273,4429,15203,4732,15105,4911,14981,4932,14836,4818,14679,4621,14517,4386,14359,4156,14083,3795,13808,3437,15360,122,15360,137,15358,285,15355,636,15344,1274,15322,2177,15281,2765,15215,3223,15120,3451,14995,3569,14846,3567,14681,3466,14511,3305,14344,3121,14037,2800,13753,2467,15360,0,15360,1,15359,21,15355,89,15346,253,15325,479,15287,796,15225,1148,15133,1492,15008,1749,14856,1882,14685,1886,14506,1783,14324,1608,13996,1398,13702,1183]);var Bt=class extends we{constructor(e){super(e),this.littleEndian=!0}load(e,t,n,i){let s=this,r=new et(s.manager);r.setPath(s.path),r.setResponseType("arraybuffer"),r.setRequestHeader(s.requestHeader),r.setWithCredentials(s.withCredentials),r.load(e,function(a){try{t(s.parse(a))}catch(l){i?i(l):console.error(l),s.manager.itemError(e)}},n,i)}_getDataView(e,t,n,i){switch(n){case"F":return i===8?e.getFloat64(t,this.littleEndian):e.getFloat32(t,this.littleEndian);case"I":return i===1?e.getInt8(t):i===2?e.getInt16(t,this.littleEndian):e.getInt32(t,this.littleEndian);case"U":return i===1?e.getUint8(t):i===2?e.getUint16(t,this.littleEndian):e.getUint32(t,this.littleEndian)}}parse(e){function t(d,h){let m=d.length,_=new Uint8Array(h),g=0,y=0,x,b,T;do if(x=d[g++],x<32){if(x++,y+x>h)throw new Error("Output buffer is not large enough");if(g+x>m)throw new Error("Invalid compressed data");do _[y++]=d[g++];while(--x)}else{if(b=x>>5,T=y-((x&31)<<8)-1,g>=m)throw new Error("Invalid compressed data");if(b===7&&(b+=d[g++],g>=m))throw new Error("Invalid compressed data");if(T-=d[g++],y+b+2>h)throw new Error("Output buffer is not large enough");if(T<0)throw new Error("Invalid compressed data");if(T>=y)throw new Error("Invalid compressed data");do _[y++]=_[T++];while(--b+2)}while(g<m);return _}function n(d){let h={},m=new Uint8Array(d),_="",g="",y=0,x=!1,b=m.length;for(;y<b&&x===!1;){let S=String.fromCharCode(m[y++]);S===\`
3610
3610
  \`||S==="\\r"?(g.trim().toLowerCase().startsWith("data")&&(x=!0),g=""):g+=S,_+=S}let T=_.search(/[\\r\\n]DATA\\s(\\S*)\\s/i),A=/[\\r\\n]DATA\\s(\\S*)\\s/i.exec(_.slice(T-1));if(h.data=A[1],h.headerLen=A[0].length+T,h.str=_.slice(0,h.headerLen),h.str=h.str.replace(/#.*/gi,""),h.version=/^VERSION (.*)/im.exec(h.str),h.fields=/^FIELDS (.*)/im.exec(h.str),h.size=/^SIZE (.*)/im.exec(h.str),h.type=/^TYPE (.*)/im.exec(h.str),h.count=/^COUNT (.*)/im.exec(h.str),h.width=/^WIDTH (.*)/im.exec(h.str),h.height=/^HEIGHT (.*)/im.exec(h.str),h.viewpoint=/^VIEWPOINT (.*)/im.exec(h.str),h.points=/^POINTS (.*)/im.exec(h.str),h.version!==null&&(h.version=parseFloat(h.version[1])),h.fields=h.fields!==null?h.fields[1].split(" "):[],h.type!==null&&(h.type=h.type[1].split(" ")),h.width!==null&&(h.width=parseInt(h.width[1])),h.height!==null&&(h.height=parseInt(h.height[1])),h.viewpoint!==null&&(h.viewpoint=h.viewpoint[1]),h.points!==null&&(h.points=parseInt(h.points[1],10)),h.points===null&&(h.points=h.width*h.height),h.size!==null&&(h.size=h.size[1].split(" ").map(function(S){return parseInt(S,10)})),h.count!==null)h.count=h.count[1].split(" ").map(function(S){return parseInt(S,10)});else{h.count=[];for(let S=0,R=h.fields.length;S<R;S++)h.count.push(1)}h.offset={};let E=0;for(let S=0,R=h.fields.length;S<R;S++)h.data==="ascii"?h.offset[h.fields[S]]=S:(h.offset[h.fields[S]]=E,E+=h.size[S]*h.count[S]);return h.rowSize=E,h}let i=n(e),s=[],r=[],a=[],l=[],o=[],c=new O;if(i.data==="ascii"){let d=i.offset,_=new TextDecoder().decode(e).slice(i.headerLen).split(\`
3611
- \`);for(let g=0,y=_.length;g<y;g++){if(_[g]==="")continue;let x=_[g].split(" ");if(d.x!==void 0&&(s.push(parseFloat(x[d.x])),s.push(parseFloat(x[d.y])),s.push(parseFloat(x[d.z]))),d.rgb!==void 0){let b=i.fields.findIndex(I=>I==="rgb"),T=i.type[b],A=parseFloat(x[d.rgb]),E=A;if(T==="F"){let I=new Float32Array(1);I[0]=A,E=new Int32Array(I.buffer)[0]}let S=(E>>16&255)/255,R=(E>>8&255)/255,P=(E>>0&255)/255;c.setRGB(S,R,P,W),a.push(c.r,c.g,c.b)}d.normal_x!==void 0&&(r.push(parseFloat(x[d.normal_x])),r.push(parseFloat(x[d.normal_y])),r.push(parseFloat(x[d.normal_z]))),d.intensity!==void 0&&l.push(parseFloat(x[d.intensity])),d.label!==void 0&&o.push(parseInt(x[d.label]))}}if(i.data==="binary_compressed"){let d=new Uint32Array(e.slice(i.headerLen,i.headerLen+8)),h=d[0],m=d[1],_=t(new Uint8Array(e,i.headerLen+8,h),m),g=new DataView(_.buffer),y=i.offset;for(let x=0;x<i.points;x++){if(y.x!==void 0){let b=i.fields.indexOf("x"),T=i.fields.indexOf("y"),A=i.fields.indexOf("z");s.push(this._getDataView(g,i.points*y.x+i.size[b]*x,i.type[b],i.size[b])),s.push(this._getDataView(g,i.points*y.y+i.size[T]*x,i.type[T],i.size[T])),s.push(this._getDataView(g,i.points*y.z+i.size[A]*x,i.type[A],i.size[A]))}if(y.rgb!==void 0){let b=i.fields.indexOf("rgb"),T=g.getUint8(i.points*y.rgb+i.size[b]*x+2)/255,A=g.getUint8(i.points*y.rgb+i.size[b]*x+1)/255,E=g.getUint8(i.points*y.rgb+i.size[b]*x+0)/255;c.setRGB(T,A,E,W),a.push(c.r,c.g,c.b)}if(y.normal_x!==void 0){let b=i.fields.indexOf("normal_x"),T=i.fields.indexOf("normal_y"),A=i.fields.indexOf("normal_z");r.push(this._getDataView(g,i.points*y.normal_x+i.size[b]*x,i.type[b],i.size[b])),r.push(this._getDataView(g,i.points*y.normal_y+i.size[T]*x,i.type[T],i.size[T])),r.push(this._getDataView(g,i.points*y.normal_z+i.size[A]*x,i.type[A],i.size[A]))}if(y.intensity!==void 0){let b=i.fields.indexOf("intensity");l.push(this._getDataView(g,i.points*y.intensity+i.size[b]*x,i.type[b],i.size[b]))}if(y.label!==void 0){let b=i.fields.indexOf("label");o.push(this._getDataView(g,i.points*y.label+i.size[b]*x,i.type[b],i.size[b]))}}}if(i.data==="binary"){let d=new DataView(e,i.headerLen),h=i.offset;for(let m=0,_=0;m<i.points;m++,_+=i.rowSize){if(h.x!==void 0){let g=i.fields.indexOf("x"),y=i.fields.indexOf("y"),x=i.fields.indexOf("z");s.push(this._getDataView(d,_+h.x,i.type[g],i.size[g])),s.push(this._getDataView(d,_+h.y,i.type[y],i.size[y])),s.push(this._getDataView(d,_+h.z,i.type[x],i.size[x]))}if(h.rgb!==void 0){let g=d.getUint8(_+h.rgb+2)/255,y=d.getUint8(_+h.rgb+1)/255,x=d.getUint8(_+h.rgb+0)/255;c.setRGB(g,y,x,W),a.push(c.r,c.g,c.b)}if(h.normal_x!==void 0){let g=i.fields.indexOf("normal_x"),y=i.fields.indexOf("normal_y"),x=i.fields.indexOf("normal_z");r.push(this._getDataView(d,_+h.normal_x,i.type[g],i.size[g])),r.push(this._getDataView(d,_+h.normal_y,i.type[y],i.size[y])),r.push(this._getDataView(d,_+h.normal_z,i.type[x],i.size[x]))}if(h.intensity!==void 0){let g=i.fields.indexOf("intensity");l.push(this._getDataView(d,_+h.intensity,i.type[g],i.size[g]))}if(h.label!==void 0){let g=i.fields.indexOf("label");o.push(this._getDataView(d,_+h.label,i.type[g],i.size[g]))}}}let u=new Be;s.length>0&&u.setAttribute("position",new he(s,3)),r.length>0&&u.setAttribute("normal",new he(r,3)),a.length>0&&u.setAttribute("color",new he(a,3)),l.length>0&&u.setAttribute("intensity",new he(l,1)),o.length>0&&u.setAttribute("label",new \$e(o,1)),u.computeBoundingSphere();let f=new ze({size:.005});return a.length>0&&(f.vertexColors=!0),new Qe(u,f)}};var Sa=new Bt;globalThis.onmessage=async p=>{let{data:e,id:t}=p.data;if(!(e instanceof Uint8Array)){postMessage({id:t,error:"Invalid data format"});return}try{let n=Sa.parse(e.buffer);if(n.geometry){let i=n.geometry.attributes.position?.array??new Float32Array(0),s=n.geometry.attributes.color?.array??void 0,r=s?new Uint8Array(s.length):void 0;if(r)for(let a=0,l=s.length;a<l;a++)r[a]=Math.round(s[a]*255);postMessage({positions:i,colors:r,id:t},r?[i.buffer,r.buffer]:[i.buffer])}else postMessage({id:t,error:"Failed to extract geometry"})}catch(n){postMessage({id:t,error:n.message})}};})();
3611
+ \`);for(let g=0,y=_.length;g<y;g++){if(_[g]==="")continue;let x=_[g].split(" ");if(d.x!==void 0&&(s.push(parseFloat(x[d.x])),s.push(parseFloat(x[d.y])),s.push(parseFloat(x[d.z]))),d.rgb!==void 0){let b=i.fields.findIndex(I=>I==="rgb"),T=i.type[b],A=parseFloat(x[d.rgb]),E=A;if(T==="F"){let I=new Float32Array(1);I[0]=A,E=new Int32Array(I.buffer)[0]}let S=(E>>16&255)/255,R=(E>>8&255)/255,P=(E>>0&255)/255;c.setRGB(S,R,P,W),a.push(c.r,c.g,c.b)}d.normal_x!==void 0&&(r.push(parseFloat(x[d.normal_x])),r.push(parseFloat(x[d.normal_y])),r.push(parseFloat(x[d.normal_z]))),d.intensity!==void 0&&l.push(parseFloat(x[d.intensity])),d.label!==void 0&&o.push(parseInt(x[d.label]))}}if(i.data==="binary_compressed"){let d=new Uint32Array(e.slice(i.headerLen,i.headerLen+8)),h=d[0],m=d[1],_=t(new Uint8Array(e,i.headerLen+8,h),m),g=new DataView(_.buffer),y=i.offset;for(let x=0;x<i.points;x++){if(y.x!==void 0){let b=i.fields.indexOf("x"),T=i.fields.indexOf("y"),A=i.fields.indexOf("z");s.push(this._getDataView(g,i.points*y.x+i.size[b]*x,i.type[b],i.size[b])),s.push(this._getDataView(g,i.points*y.y+i.size[T]*x,i.type[T],i.size[T])),s.push(this._getDataView(g,i.points*y.z+i.size[A]*x,i.type[A],i.size[A]))}if(y.rgb!==void 0){let b=i.fields.indexOf("rgb"),T=g.getUint8(i.points*y.rgb+i.size[b]*x+2)/255,A=g.getUint8(i.points*y.rgb+i.size[b]*x+1)/255,E=g.getUint8(i.points*y.rgb+i.size[b]*x+0)/255;c.setRGB(T,A,E,W),a.push(c.r,c.g,c.b)}if(y.normal_x!==void 0){let b=i.fields.indexOf("normal_x"),T=i.fields.indexOf("normal_y"),A=i.fields.indexOf("normal_z");r.push(this._getDataView(g,i.points*y.normal_x+i.size[b]*x,i.type[b],i.size[b])),r.push(this._getDataView(g,i.points*y.normal_y+i.size[T]*x,i.type[T],i.size[T])),r.push(this._getDataView(g,i.points*y.normal_z+i.size[A]*x,i.type[A],i.size[A]))}if(y.intensity!==void 0){let b=i.fields.indexOf("intensity");l.push(this._getDataView(g,i.points*y.intensity+i.size[b]*x,i.type[b],i.size[b]))}if(y.label!==void 0){let b=i.fields.indexOf("label");o.push(this._getDataView(g,i.points*y.label+i.size[b]*x,i.type[b],i.size[b]))}}}if(i.data==="binary"){let d=new DataView(e,i.headerLen),h=i.offset;for(let m=0,_=0;m<i.points;m++,_+=i.rowSize){if(h.x!==void 0){let g=i.fields.indexOf("x"),y=i.fields.indexOf("y"),x=i.fields.indexOf("z");s.push(this._getDataView(d,_+h.x,i.type[g],i.size[g])),s.push(this._getDataView(d,_+h.y,i.type[y],i.size[y])),s.push(this._getDataView(d,_+h.z,i.type[x],i.size[x]))}if(h.rgb!==void 0){let g=d.getUint8(_+h.rgb+2)/255,y=d.getUint8(_+h.rgb+1)/255,x=d.getUint8(_+h.rgb+0)/255;c.setRGB(g,y,x,W),a.push(c.r,c.g,c.b)}if(h.normal_x!==void 0){let g=i.fields.indexOf("normal_x"),y=i.fields.indexOf("normal_y"),x=i.fields.indexOf("normal_z");r.push(this._getDataView(d,_+h.normal_x,i.type[g],i.size[g])),r.push(this._getDataView(d,_+h.normal_y,i.type[y],i.size[y])),r.push(this._getDataView(d,_+h.normal_z,i.type[x],i.size[x]))}if(h.intensity!==void 0){let g=i.fields.indexOf("intensity");l.push(this._getDataView(d,_+h.intensity,i.type[g],i.size[g]))}if(h.label!==void 0){let g=i.fields.indexOf("label");o.push(this._getDataView(d,_+h.label,i.type[g],i.size[g]))}}}let u=new Be;s.length>0&&u.setAttribute("position",new he(s,3)),r.length>0&&u.setAttribute("normal",new he(r,3)),a.length>0&&u.setAttribute("color",new he(a,3)),l.length>0&&u.setAttribute("intensity",new he(l,1)),o.length>0&&u.setAttribute("label",new \$e(o,1)),u.computeBoundingSphere();let f=new ze({size:.005});return a.length>0&&(f.vertexColors=!0),new Qe(u,f)}};var Sa=new Bt;globalThis.onmessage=async p=>{let{data:e,id:t}=p.data;if(!(e instanceof Uint8Array)){postMessage({id:t,error:"Invalid data format"});return}try{let n=Sa.parse(e.buffer);if(n.geometry){let i=n.geometry.attributes.position?.array??new Float32Array(0),s=n.geometry.attributes.color?.array??void 0,r=s?new Uint8Array(s.length):void 0;if(r)for(let a=0,l=s.length;a<l;a++)r[a]=Math.round(s[a]*255);postMessage({positions:i,colors:r,id:t},{transfer:r?[i.buffer,r.buffer]:[i.buffer]})}else postMessage({id:t,error:"Failed to extract geometry"})}catch(n){postMessage({id:t,error:n.message})}};})();
3612
3612
  /*! Bundled license information:
3613
3613
 
3614
3614
  three/build/three.core.js:
@@ -22,7 +22,9 @@ globalThis.onmessage = async (event) => {
22
22
  colors[i] = Math.round(colorsFloat[i] * 255);
23
23
  }
24
24
  }
25
- postMessage({ positions, colors, id }, colors ? [positions.buffer, colors.buffer] : [positions.buffer]);
25
+ postMessage({ positions, colors, id }, {
26
+ transfer: colors ? [positions.buffer, colors.buffer] : [positions.buffer],
27
+ });
26
28
  }
27
29
  else {
28
30
  postMessage({ id, error: 'Failed to extract geometry' });
@@ -0,0 +1,45 @@
1
+ <script lang="ts">
2
+ import { Portal } from '@threlte/extras'
3
+ import { PressedKeys } from 'runed'
4
+
5
+ import Button from '../../components/overlay/dashboard/Button.svelte'
6
+ import { traits, useQuery } from '../../ecs'
7
+
8
+ import FocusBox from './FocusBox.svelte'
9
+ import { provideFocus } from './provideFocus.svelte'
10
+
11
+ let focusing = $state(false)
12
+
13
+ provideFocus(() => focusing)
14
+
15
+ const selected = useQuery(traits.Selected)
16
+
17
+ const canFocus = $derived(selected.current.length > 0 || focusing)
18
+
19
+ const keys = new PressedKeys()
20
+
21
+ keys.onKeys('/', () => {
22
+ if (selected.current.length > 0 && !focusing) {
23
+ focusing = true
24
+ } else if (focusing) {
25
+ focusing = false
26
+ }
27
+ })
28
+ </script>
29
+
30
+ <Portal id="dashboard">
31
+ <fieldset class="flex">
32
+ <Button
33
+ icon="focus"
34
+ active={focusing}
35
+ disabled={!canFocus}
36
+ description="Focus selection"
37
+ hotkey="/"
38
+ onclick={() => (focusing = !focusing)}
39
+ />
40
+ </fieldset>
41
+ </Portal>
42
+
43
+ {#if focusing}
44
+ <FocusBox />
45
+ {/if}
@@ -0,0 +1,3 @@
1
+ declare const Focus: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type Focus = ReturnType<typeof Focus>;
3
+ export default Focus;
@@ -0,0 +1,75 @@
1
+ <script lang="ts">
2
+ import { T, useThrelte } from '@threlte/core'
3
+ import { Gizmo, TrackballControls } from '@threlte/extras'
4
+ import { untrack } from 'svelte'
5
+ import { Box3, Vector3 } from 'three'
6
+
7
+ import Camera from '../../components/Camera.svelte'
8
+ import { traits, useQuery } from '../../ecs'
9
+ import { useCameraControls } from '../../hooks/useControls.svelte'
10
+
11
+ const { scene } = useThrelte()
12
+ const cameraControls = useCameraControls()
13
+ const selected = useQuery(traits.Selected)
14
+
15
+ /**
16
+ * Save the main camera controls and their state the instant focus begins —
17
+ * this runs before the TrackballControls below swaps the shared context, so
18
+ * `current` is still the main controls. On teardown we hand `current` back to
19
+ * them (the trackball never restores it) and reset them to the saved view, so
20
+ * exiting focus returns the camera to where it was. camera-controls exposes
21
+ * saveState()/reset(); TrackballControls does not, which also narrows the type.
22
+ */
23
+ $effect.pre(() => {
24
+ const previousControls = untrack(() => cameraControls.current)
25
+ const restorableControls =
26
+ previousControls && 'saveState' in previousControls ? previousControls : undefined
27
+ restorableControls?.saveState()
28
+
29
+ return () => {
30
+ if (!restorableControls) return
31
+ cameraControls.set(restorableControls)
32
+ restorableControls.reset(false)
33
+ }
34
+ })
35
+
36
+ const box = new Box3()
37
+ const vec = new Vector3()
38
+
39
+ let center = $state.raw<[number, number, number]>([0, 0, 0])
40
+ let size = $state.raw<[number, number, number]>([0, 0, 0])
41
+
42
+ /**
43
+ * Frame the camera on the selection captured when focus was entered. Reading
44
+ * the selection untracked leaves this effect with no reactive dependencies,
45
+ * so it runs once on mount and the framing stays put — changing the selection
46
+ * while focused must not re-frame or reset the camera.
47
+ */
48
+ $effect(() => {
49
+ box.makeEmpty()
50
+ for (const entity of untrack(() => selected.current)) {
51
+ const object3d = scene.getObjectByName(entity as unknown as string)
52
+ if (object3d) {
53
+ box.expandByObject(object3d)
54
+ }
55
+ }
56
+
57
+ size = box.getSize(vec).toArray()
58
+ center = box.getCenter(vec).toArray()
59
+ })
60
+ </script>
61
+
62
+ <Camera position={[size[0] + 1, size[0] + 1, size[0] + 1]}>
63
+ <TrackballControls
64
+ target={center}
65
+ oncreate={(ref) => cameraControls.set(ref)}
66
+ >
67
+ <Gizmo placement="bottom-right" />
68
+ </TrackballControls>
69
+ </Camera>
70
+
71
+ <T.Box3Helper
72
+ args={[box, 'red']}
73
+ bvh={{ enabled: false }}
74
+ raycast={() => null}
75
+ />
@@ -0,0 +1,3 @@
1
+ declare const FocusBox: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type FocusBox = ReturnType<typeof FocusBox>;
3
+ export default FocusBox;
@@ -0,0 +1 @@
1
+ export declare const provideFocus: (focusing: () => boolean) => void;
@@ -0,0 +1,61 @@
1
+ import { trait } from 'koota';
2
+ import { untrack } from 'svelte';
3
+ import { relations, traits, useQuery, useWorld } from '../../ecs';
4
+ const HiddenByFocus = trait();
5
+ export const provideFocus = (focusing) => {
6
+ const world = useWorld();
7
+ const selected = useQuery(traits.Selected);
8
+ $effect(() => {
9
+ if (!focusing()) {
10
+ for (const entity of world.query(HiddenByFocus)) {
11
+ entity.remove(HiddenByFocus, traits.Invisible);
12
+ }
13
+ return;
14
+ }
15
+ /**
16
+ * Snapshot the selection at the moment focus is entered. Reading it
17
+ * untracked makes `focusing()` this effect's only dependency, so the
18
+ * focused view stays frozen: selecting or deselecting entities while
19
+ * focused must not change what's hidden. Everything is restored when
20
+ * focus exits.
21
+ */
22
+ const selectedEntities = untrack(() => selected.current);
23
+ /**
24
+ * Entities only render when their `InheritedInvisible` is unset, and that
25
+ * trait is computed by walking `ChildOf` ancestors (see
26
+ * `useInheritedInvisible`). So hiding a selected entity's parent — or its
27
+ * renderable sub-entities, which are `ChildOf` children that never carry
28
+ * `Selected` — makes the selection itself disappear. Keep the whole
29
+ * connected subtree of each selection visible: its ancestors (so the
30
+ * cascade can't reach it) and its descendants (so its geometry shows).
31
+ */
32
+ const keep = new Set();
33
+ const keepSubtree = (entity) => {
34
+ if (keep.has(entity))
35
+ return;
36
+ keep.add(entity);
37
+ for (const child of world.query(relations.ChildOf(entity))) {
38
+ keepSubtree(child);
39
+ }
40
+ };
41
+ for (const entity of selectedEntities) {
42
+ let ancestor = entity.targetFor(relations.ChildOf);
43
+ while (ancestor?.isAlive()) {
44
+ keep.add(ancestor);
45
+ ancestor = ancestor.targetFor(relations.ChildOf);
46
+ }
47
+ keepSubtree(entity);
48
+ }
49
+ /**
50
+ * Hide the rest. Skip already-invisible entities so we don't take
51
+ * ownership of — and later wrongly reveal — user-hidden entities.
52
+ */
53
+ for (const entity of world.query(traits.Name)) {
54
+ if (keep.has(entity))
55
+ continue;
56
+ if (!entity.has(traits.Invisible)) {
57
+ entity.add(HiddenByFocus, traits.Invisible);
58
+ }
59
+ }
60
+ });
61
+ };
@@ -4,16 +4,14 @@
4
4
  import { untrack } from 'svelte'
5
5
  import { type Intersection, Vector3 } from 'three'
6
6
 
7
- import Button from '../overlay/dashboard/Button.svelte'
7
+ import Button from '../../components/overlay/dashboard/Button.svelte'
8
+ import Popover from '../../components/overlay/Popover.svelte'
9
+ import ToggleGroup from '../../components/overlay/ToggleGroup.svelte'
8
10
  import { useMouseRaycaster } from '../../hooks/useMouseRaycaster.svelte'
9
- import { useFocusedEntity } from '../../hooks/useSelection.svelte'
10
11
  import { useSettings } from '../../hooks/useSettings.svelte'
11
12
 
12
- import Popover from '../overlay/Popover.svelte'
13
- import ToggleGroup from '../overlay/ToggleGroup.svelte'
14
13
  import MeasurePoint from './MeasurePoint.svelte'
15
14
 
16
- const focusedEntity = useFocusedEntity()
17
15
  const settings = useSettings()
18
16
 
19
17
  const htmlPosition = new Vector3()
@@ -73,9 +71,9 @@
73
71
  }
74
72
 
75
73
  $effect(() => {
76
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
77
- ;(focusedEntity.current, enabled)
78
- untrack(() => clear())
74
+ if (!enabled) {
75
+ untrack(() => clear())
76
+ }
79
77
  })
80
78
  </script>
81
79
 
@@ -3,7 +3,7 @@
3
3
 
4
4
  import { useThrelte } from '@threlte/core'
5
5
  import earcut from 'earcut'
6
- import { Not } from 'koota'
6
+ import { type Entity, Not } from 'koota'
7
7
  import { Box3, Triangle, Vector3 } from 'three'
8
8
 
9
9
  import { createBufferGeometry } from '../../attribute'
@@ -11,6 +11,7 @@
11
11
  import { useCameraControls } from '../../hooks/useControls.svelte'
12
12
 
13
13
  import Debug from './Debug.svelte'
14
+ import * as selectionRelations from './relations'
14
15
  import * as selectionTraits from './traits'
15
16
  import { getTriangleBoxesFromIndices, getTriangleFromIndex, raycast } from './utils'
16
17
 
@@ -168,7 +169,7 @@
168
169
  max.set(ellipseBox.maxX, ellipseBox.maxY, Number.POSITIVE_INFINITY)
169
170
  box3.set(min, max)
170
171
 
171
- const enclosedPoints: number[] = []
172
+ const enclosedPoints = new Map<Entity, number[]>()
172
173
 
173
174
  for (const pointsEntity of world.query(
174
175
  traits.Points,
@@ -202,7 +203,8 @@
202
203
  getTriangleFromIndex(i, indices, positions, triangle)
203
204
 
204
205
  if (triangle.containsPoint(point)) {
205
- enclosedPoints.push(point.x, point.y, point.z)
206
+ if (!enclosedPoints.has(pointsEntity)) enclosedPoints.set(pointsEntity, [])
207
+ enclosedPoints.get(pointsEntity)!.push(point.x, point.y, point.z)
206
208
  }
207
209
  }
208
210
  }
@@ -211,19 +213,22 @@
211
213
  } as ShapecastCallbacks)
212
214
  }
213
215
 
214
- const ellipseResultGeometry = createBufferGeometry(new Float32Array(enclosedPoints))
215
-
216
- world.spawn(
217
- traits.Name('Ellipse result'),
218
- traits.BufferGeometry(ellipseResultGeometry),
219
- traits.Color({ r: 1, g: 0, b: 0 }),
220
- traits.RenderOrder(999),
221
- traits.Material({ depthTest: false }),
222
- traits.Points,
223
- traits.Removable,
224
- selectionTraits.SelectionEnclosedPoints,
225
- selectionTraits.PointsCapturedBy(ellipse)
226
- )
216
+ for (const [sourceEntity, points] of enclosedPoints) {
217
+ const ellipseResultGeometry = createBufferGeometry(new Float32Array(points))
218
+
219
+ world.spawn(
220
+ traits.Name('Ellipse result'),
221
+ traits.BufferGeometry(ellipseResultGeometry),
222
+ traits.Color({ r: 1, g: 0, b: 0 }),
223
+ traits.RenderOrder(999),
224
+ traits.Material({ depthTest: false }),
225
+ traits.Points,
226
+ traits.Removable,
227
+ selectionTraits.SelectionEnclosedPoints,
228
+ selectionRelations.SelectedFrom(sourceEntity),
229
+ selectionRelations.PointsCapturedBy(ellipse)
230
+ )
231
+ }
227
232
  }
228
233
 
229
234
  const onkeydown = (event: KeyboardEvent) => {
@@ -3,7 +3,7 @@
3
3
 
4
4
  import { useThrelte } from '@threlte/core'
5
5
  import earcut from 'earcut'
6
- import { Not } from 'koota'
6
+ import { type Entity, Not } from 'koota'
7
7
  import { Box3, Triangle, Vector3 } from 'three'
8
8
 
9
9
  import { createBufferGeometry } from '../../attribute'
@@ -11,6 +11,7 @@
11
11
  import { useCameraControls } from '../../hooks/useControls.svelte'
12
12
 
13
13
  import Debug from './Debug.svelte'
14
+ import * as selectionRelations from './relations'
14
15
  import * as selectionTraits from './traits'
15
16
  import { getTriangleBoxesFromIndices, getTriangleFromIndex, raycast } from './utils'
16
17
 
@@ -149,7 +150,7 @@
149
150
  max.set(lassoBox.maxX, lassoBox.maxY, Number.POSITIVE_INFINITY)
150
151
  box3.set(min, max)
151
152
 
152
- const enclosedPoints: number[] = []
153
+ const enclosedPoints = new Map<Entity, number[]>()
153
154
 
154
155
  for (const pointsEntity of world.query(
155
156
  traits.Points,
@@ -183,7 +184,8 @@
183
184
  getTriangleFromIndex(i, indices, positions, triangle)
184
185
 
185
186
  if (triangle.containsPoint(point)) {
186
- enclosedPoints.push(point.x, point.y, point.z)
187
+ if (!enclosedPoints.has(pointsEntity)) enclosedPoints.set(pointsEntity, [])
188
+ enclosedPoints.get(pointsEntity)!.push(point.x, point.y, point.z)
187
189
  }
188
190
  }
189
191
  }
@@ -192,19 +194,22 @@
192
194
  } as ShapecastCallbacks)
193
195
  }
194
196
 
195
- const lassoResultGeometry = createBufferGeometry(new Float32Array(enclosedPoints))
196
-
197
- world.spawn(
198
- traits.Name('Lasso result'),
199
- traits.BufferGeometry(lassoResultGeometry),
200
- traits.Color({ r: 1, g: 0, b: 0 }),
201
- traits.RenderOrder(999),
202
- traits.Material({ depthTest: false }),
203
- traits.Points,
204
- traits.Removable,
205
- selectionTraits.SelectionEnclosedPoints,
206
- selectionTraits.PointsCapturedBy(lasso)
207
- )
197
+ for (const [sourceEntity, points] of enclosedPoints) {
198
+ const lassoResultGeometry = createBufferGeometry(new Float32Array(points))
199
+
200
+ world.spawn(
201
+ traits.Name('Lasso result'),
202
+ traits.BufferGeometry(lassoResultGeometry),
203
+ traits.Color({ r: 1, g: 0, b: 0 }),
204
+ traits.RenderOrder(999),
205
+ traits.Material({ depthTest: false }),
206
+ traits.Points,
207
+ traits.Removable,
208
+ selectionTraits.SelectionEnclosedPoints,
209
+ selectionRelations.PointsCapturedBy(lasso),
210
+ selectionRelations.SelectedFrom(sourceEntity)
211
+ )
212
+ }
208
213
  }
209
214
 
210
215
  const onkeydown = (event: KeyboardEvent) => {
@@ -9,7 +9,7 @@
9
9
  import DashboardButton from '../../components/overlay/dashboard/Button.svelte'
10
10
  import Popover from '../../components/overlay/Popover.svelte'
11
11
  import ToggleGroup from '../../components/overlay/ToggleGroup.svelte'
12
- import { useSelectedEntity } from '../../hooks/useSelection.svelte'
12
+ import { traits, useWorld } from '../../ecs'
13
13
  import { useSettings } from '../../hooks/useSettings.svelte'
14
14
 
15
15
  import Ellipse from './Ellipse.svelte'
@@ -29,11 +29,12 @@
29
29
  let { enabled = false, autoSelectNewEntities = false, children }: Props = $props()
30
30
 
31
31
  const { dom } = useThrelte()
32
+ const world = useWorld()
32
33
  const settings = useSettings()
33
34
  const isSelectionMode = $derived(settings.current.interactionMode === 'select')
34
35
 
35
36
  const selectionPlugin = provideSelectionPlugin()
36
- const selectedEntity = useSelectedEntity()
37
+
37
38
  let selectionType = $state<SelectionType>('lasso')
38
39
 
39
40
  $effect(() => {
@@ -58,7 +59,13 @@
58
59
 
59
60
  const newest = newEntities.at(-1)
60
61
  if (newest === undefined) return
61
- selectedEntity.set(newest)
62
+
63
+ const selected = world.query(traits.Selected)
64
+ for (const entity of selected) {
65
+ entity.remove(traits.Selected)
66
+ }
67
+
68
+ newest.add(traits.Selected)
62
69
  })
63
70
 
64
71
  const rect = new ElementRect(() => dom)
@@ -0,0 +1,6 @@
1
+ export declare const SelectedFrom: import("koota").Relation<import("koota").Trait<Record<string, never>>>;
2
+ /**
3
+ * Captured points are removable, so we want to also destroy
4
+ * the source selection every time a user deletes one.
5
+ */
6
+ export declare const PointsCapturedBy: import("koota").Relation<import("koota").Trait<Record<string, never>>>;
@@ -0,0 +1,7 @@
1
+ import { relation } from 'koota';
2
+ export const SelectedFrom = relation();
3
+ /**
4
+ * Captured points are removable, so we want to also destroy
5
+ * the source selection every time a user deletes one.
6
+ */
7
+ export const PointsCapturedBy = relation({ autoDestroy: 'target' });
@@ -1,11 +1,6 @@
1
1
  export declare const Lasso: import("koota").Trait<() => boolean>;
2
2
  export declare const Ellipse: import("koota").Trait<() => boolean>;
3
3
  export declare const SelectionEnclosedPoints: import("koota").Trait<() => boolean>;
4
- /**
5
- * Captured points are removable, so we want to also destroy
6
- * the source selection every time a user deletes one.
7
- */
8
- export declare const PointsCapturedBy: import("koota").Relation<import("koota").Trait<Record<string, never>>>;
9
4
  export interface AABB {
10
5
  minX: number;
11
6
  minY: number;
@@ -1,12 +1,7 @@
1
- import { relation, trait } from 'koota';
1
+ import { trait } from 'koota';
2
2
  export const Lasso = trait(() => true);
3
3
  export const Ellipse = trait(() => true);
4
4
  export const SelectionEnclosedPoints = trait(() => true);
5
- /**
6
- * Captured points are removable, so we want to also destroy
7
- * the source selection every time a user deletes one.
8
- */
9
- export const PointsCapturedBy = relation({ autoDestroy: 'target' });
10
5
  export const Box = trait({
11
6
  minX: 0,
12
7
  minY: 0,
@@ -1,6 +1,9 @@
1
1
  export { default as SelectionTool } from './Selection/SelectionTool.svelte';
2
2
  export * as selectionTraits from './Selection/traits';
3
+ export * as selectionRelations from './Selection/relations';
3
4
  export { useSelectionPlugin } from './Selection/useSelectionPlugin.svelte';
5
+ export { default as MeasureTool } from './MeasureTool/MeasureTool.svelte';
4
6
  export { default as DrawService } from './DrawService/DrawService.svelte';
5
7
  export { default as Skybox } from './Skybox/Skybox.svelte';
6
8
  export { default as Debug } from './Debug/Debug.svelte';
9
+ export { default as Focus } from './Focus/Focus.svelte';
@@ -1,10 +1,13 @@
1
1
  // Selection
2
2
  export { default as SelectionTool } from './Selection/SelectionTool.svelte';
3
3
  export * as selectionTraits from './Selection/traits';
4
+ export * as selectionRelations from './Selection/relations';
4
5
  export { useSelectionPlugin } from './Selection/useSelectionPlugin.svelte';
6
+ export { default as MeasureTool } from './MeasureTool/MeasureTool.svelte';
5
7
  // DrawService
6
8
  export { default as DrawService } from './DrawService/DrawService.svelte';
7
9
  // Skybox
8
10
  export { default as Skybox } from './Skybox/Skybox.svelte';
9
11
  // Debug
10
12
  export { default as Debug } from './Debug/Debug.svelte';
13
+ export { default as Focus } from './Focus/Focus.svelte';
@@ -31,3 +31,17 @@ export interface ReconcileResult {
31
31
  updated: SnapshotEntity[];
32
32
  }
33
33
  export declare const reconcileSnapshotEntities: (world: World, snapshot: Snapshot, prev: Map<string, SnapshotEntity>) => ReconcileResult;
34
+ export interface SnapshotPointCloud {
35
+ name: string;
36
+ positions: Float32Array;
37
+ colors?: Uint8Array;
38
+ }
39
+ /**
40
+ * Decodes every point-cloud drawing in a snapshot into `{ name, positions, colors }`,
41
+ * keyed and merged by `referenceFrame`. Chunked clouds share a referenceFrame, so their
42
+ * positions/colors are concatenated back into one cloud. Inverse of `DrawPoints`.
43
+ *
44
+ * Colors are gated through `isVertexColors`: only genuine per-vertex color arrays are kept;
45
+ * single-uniform-color arrays (3 bytes) are discarded as `undefined`.
46
+ */
47
+ export declare const decodeDrawnSnapshotPointClouds: (snapshot: Snapshot) => SnapshotPointCloud[];
package/dist/snapshot.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { RenderArmModels } from './buf/draw/v1/scene_pb';
2
2
  import { traits } from './ecs';
3
+ import { asFloat32Array, inMeters, isVertexColors } from './buffer';
3
4
  import { rgbToHex } from './color';
4
5
  import { drawDrawing, drawTransform, updateDrawing, updateModel, updateTransform, uuidBytesToString, } from './draw';
5
6
  /**
@@ -160,3 +161,25 @@ const getRenderArmModels = (renderArmModels) => {
160
161
  }
161
162
  }
162
163
  };
164
+ /**
165
+ * Decodes every point-cloud drawing in a snapshot into `{ name, positions, colors }`,
166
+ * keyed and merged by `referenceFrame`. Chunked clouds share a referenceFrame, so their
167
+ * positions/colors are concatenated back into one cloud. Inverse of `DrawPoints`.
168
+ *
169
+ * Colors are gated through `isVertexColors`: only genuine per-vertex color arrays are kept;
170
+ * single-uniform-color arrays (3 bytes) are discarded as `undefined`.
171
+ */
172
+ export const decodeDrawnSnapshotPointClouds = (snapshot) => {
173
+ const snapshotPointClouds = [];
174
+ for (const drawing of snapshot.drawings) {
175
+ const name = drawing.referenceFrame;
176
+ const geometryType = drawing.physicalObject?.geometryType;
177
+ if (geometryType?.case === 'points') {
178
+ const positions = asFloat32Array(geometryType.value.positions, inMeters);
179
+ const rawColors = drawing.metadata?.colors;
180
+ const colors = isVertexColors(rawColors) ? rawColors : undefined;
181
+ snapshotPointClouds.push({ name, positions, colors });
182
+ }
183
+ }
184
+ return snapshotPointClouds;
185
+ };
@@ -1,4 +1,6 @@
1
1
  import { type BufferGeometry } from 'three';
2
+ /** Total length of the geometry produced by `createArrowGeometry`, in meters. */
3
+ export declare const ARROW_LENGTH = 0.1;
2
4
  /**
3
5
  * Returns one merged geometry for an arrow (box tail + cone head)
4
6
  *
@@ -1,12 +1,14 @@
1
1
  import { BoxGeometry, ConeGeometry } from 'three';
2
2
  import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
3
+ /** Total length of the geometry produced by `createArrowGeometry`, in meters. */
4
+ export const ARROW_LENGTH = 0.1;
3
5
  /**
4
6
  * Returns one merged geometry for an arrow (box tail + cone head)
5
7
  *
6
8
  * Arrow points along +Y with its base at y = 0
7
9
  */
8
10
  export const createArrowGeometry = () => {
9
- const length = 0.1;
11
+ const length = ARROW_LENGTH;
10
12
  const headLength = length * 0.3;
11
13
  const headWidth = headLength * 0.3;
12
14
  const tailLength = length - headLength;