lunchboxjs 0.1.4011 → 0.1.4014

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.
package/LICENSE.md ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2022 Breakfast Studio, LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,17 @@
1
1
  Custom Vue 3 renderer for ThreeJS.
2
2
 
3
+ ## Full Docs
4
+
3
5
  [Docs](//docs.lunchboxjs.com)
6
+
7
+ ## Local Dev
8
+
9
+ `npm run dev` to run dev server. From there, edit source code in `/src/...` and examples in `/demo/...` to build and test features.
10
+
11
+ ### Creating Examples
12
+
13
+ Run `npm run demo:create` to create a new demo.
14
+
15
+ ## Docs Dev
16
+
17
+ `npm run docs:dev` to run docs dev. Docs exist as a Vitepress site in the `/docs` folder.
package/dist/.DS_Store ADDED
Binary file
@@ -115,6 +115,7 @@
115
115
  rendererProperties: Object,
116
116
  shadow: [Boolean, Object],
117
117
  transparent: Boolean,
118
+ zoom: Number,
118
119
  },
119
120
  setup(props, context) {
120
121
  const canvas = vue.ref();
@@ -228,6 +229,10 @@
228
229
  const source = (props.cameraLookAt || props.cameraLook);
229
230
  camera.instance.lookAt(...source);
230
231
  }
232
+ // zoom camera if needed
233
+ if (camera && props.zoom !== undefined) {
234
+ camera.instance.zoom = props.zoom;
235
+ }
231
236
  // SCENE
232
237
  // ====================
233
238
  scene = ensuredScene.value;
@@ -539,76 +544,6 @@
539
544
  return node.isLunchboxRootNode;
540
545
  };
541
546
 
542
- /** Create a new Lunchbox comment node. */
543
- function createCommentNode(options = {}) {
544
- const defaults = {
545
- text: options.text ?? '',
546
- };
547
- return new MiniDom.RendererCommentNode({
548
- ...defaults,
549
- ...options,
550
- metaType: 'commentMeta',
551
- });
552
- }
553
- /** Create a new DOM node. */
554
- function createDomNode(options = {}) {
555
- const domElement = document.createElement(options.type ?? '');
556
- const defaults = {
557
- domElement,
558
- };
559
- const node = new MiniDom.RendererDomNode({
560
- ...defaults,
561
- ...options,
562
- metaType: 'domMeta',
563
- });
564
- return node;
565
- }
566
- /** Create a new Lunchbox text node. */
567
- function createTextNode(options = {}) {
568
- const defaults = {
569
- text: options.text ?? '',
570
- };
571
- return new MiniDom.RendererTextNode({
572
- ...options,
573
- ...defaults,
574
- metaType: 'textMeta',
575
- });
576
- }
577
- /** Create a new Lunchbox standard node. */
578
- function createNode(options = {}, props = {}) {
579
- const defaults = {
580
- attached: options.attached ?? [],
581
- attachedArray: options.attachedArray ?? {},
582
- instance: options.instance ?? null,
583
- };
584
- const node = new MiniDom.RendererStandardNode({
585
- ...options,
586
- ...defaults,
587
- metaType: 'standardMeta',
588
- });
589
- if (node.type && !isLunchboxRootNode(node) && !node.instance) {
590
- // if (node.type.includes('Camera')) {
591
- // console.log(node.type, {
592
- // ...node.props,
593
- // ...props,
594
- // })
595
- // console.trace()
596
- // }
597
- node.instance = instantiateThreeObject({
598
- ...node,
599
- props: {
600
- ...node.props,
601
- ...props,
602
- },
603
- });
604
- }
605
- if (node.type === 'scene') {
606
- // manually set scene override
607
- ensuredScene.value = node;
608
- }
609
- return node;
610
- }
611
-
612
547
  const interactables = [];
613
548
  const addInteractable = (target) => {
614
549
  interactables.push(target);
@@ -840,7 +775,8 @@
840
775
  singleType.toLowerCase());
841
776
  // cancel if found example is marked !isDefault
842
777
  if (isMinidomNode(found) &&
843
- (!found.props['is-default'] || !found.props['isDefault'])) {
778
+ (found.props['is-default'] === false ||
779
+ !found.props['isDefault'] === false)) {
844
780
  return null;
845
781
  }
846
782
  // if we have one, save and return
@@ -933,17 +869,10 @@
933
869
  overrides[pascalType] = val;
934
870
  },
935
871
  });
936
- // export const ensuredCamera = buildEnsured<THREE.Camera>(
937
- // ['PerspectiveCamera', 'OrthographicCamera'],
938
- // fallbackCameraUuid,
939
- // {
940
- // args: [45, 0.5625, 1, 1000],
941
- // }
942
- // )
943
872
  // ENSURE RENDERER
944
873
  // ====================
945
874
  const fallbackRendererUuid = 'FALLBACK_RENDERER';
946
- const v = buildEnsured(
875
+ const ensuredRenderer = buildEnsured(
947
876
  // TODO: ensure support for css/svg renderers
948
877
  ['WebGLRenderer'], //, 'CSS2DRenderer', 'CSS3DRenderer', 'SVGRenderer'],
949
878
  fallbackRendererUuid, {});
@@ -952,7 +881,7 @@
952
881
  const rendererReady = vue.ref(false);
953
882
  const ensureRenderer = vue.computed({
954
883
  get() {
955
- return (rendererReady.value ? v.value : null);
884
+ return (rendererReady.value ? ensuredRenderer.value : null);
956
885
  },
957
886
  set(val) {
958
887
  const t = val.type ?? '';
@@ -969,6 +898,80 @@
969
898
  // `unknown` is intentional here - we need to typecast the node since Raycaster isn't an Object3D
970
899
  const ensuredRaycaster = buildEnsured('Raycaster', autoRaycasterUuid, {}, (node) => setupAutoRaycaster(node));
971
900
 
901
+ /** Create a new Lunchbox comment node. */
902
+ function createCommentNode(options = {}) {
903
+ const defaults = {
904
+ text: options.text ?? '',
905
+ };
906
+ return new MiniDom.RendererCommentNode({
907
+ ...defaults,
908
+ ...options,
909
+ metaType: 'commentMeta',
910
+ });
911
+ }
912
+ /** Create a new DOM node. */
913
+ function createDomNode(options = {}) {
914
+ const domElement = document.createElement(options.type ?? '');
915
+ const defaults = {
916
+ domElement,
917
+ };
918
+ const node = new MiniDom.RendererDomNode({
919
+ ...defaults,
920
+ ...options,
921
+ metaType: 'domMeta',
922
+ });
923
+ return node;
924
+ }
925
+ /** Create a new Lunchbox text node. */
926
+ function createTextNode(options = {}) {
927
+ const defaults = {
928
+ text: options.text ?? '',
929
+ };
930
+ return new MiniDom.RendererTextNode({
931
+ ...options,
932
+ ...defaults,
933
+ metaType: 'textMeta',
934
+ });
935
+ }
936
+ /** Create a new Lunchbox standard node. */
937
+ function createNode(options = {}, props = {}) {
938
+ const defaults = {
939
+ attached: options.attached ?? [],
940
+ attachedArray: options.attachedArray ?? {},
941
+ instance: options.instance ?? null,
942
+ };
943
+ const node = new MiniDom.RendererStandardNode({
944
+ ...options,
945
+ ...defaults,
946
+ metaType: 'standardMeta',
947
+ });
948
+ if (node.type && !isLunchboxRootNode(node) && !node.instance) {
949
+ // if (node.type.includes('Camera')) {
950
+ // console.log(node.type, {
951
+ // ...node.props,
952
+ // ...props,
953
+ // })
954
+ // console.trace()
955
+ // }
956
+ node.instance = instantiateThreeObject({
957
+ ...node,
958
+ props: {
959
+ ...node.props,
960
+ ...props,
961
+ },
962
+ });
963
+ }
964
+ // TODO: these manual overrides are a bit brittle - replace?
965
+ if (node.type?.toLowerCase() === 'scene') {
966
+ // manually set scene override
967
+ ensuredScene.value = node;
968
+ }
969
+ else if (node.type?.toLowerCase().endsWith('camera')) {
970
+ ensuredCamera.value = node;
971
+ }
972
+ return node;
973
+ }
974
+
972
975
  const createComponent = (tag) => vue.defineComponent({
973
976
  inheritAttrs: false,
974
977
  name: tag,
@@ -1725,46 +1728,31 @@
1725
1728
  /** The current camera. Often easier to use `useCamera` instead of this. */
1726
1729
  const camera = vue.computed(() => ensuredCamera.value?.instance ?? null);
1727
1730
  /** Run a function using the current camera when it's present. */
1728
- function useCamera(callback, once = true) {
1729
- let destroy;
1730
- destroy = vue.watch(camera, (newVal) => {
1731
+ function useCamera(callback) {
1732
+ return vue.watch(camera, (newVal) => {
1731
1733
  if (!newVal)
1732
1734
  return;
1733
- // TODO: better fix than `any`?
1734
1735
  callback(newVal);
1735
- if (once) {
1736
- destroy?.();
1737
- }
1738
1736
  }, { immediate: true });
1739
1737
  }
1740
1738
  /** The current renderer. Often easier to use `useRenderer` instead of this. */
1741
1739
  const renderer = vue.computed(() => ensureRenderer.value?.instance ?? null);
1742
1740
  /** Run a function using the current renderer when it's present. */
1743
- function useRenderer(callback, once = true) {
1744
- let destroy;
1745
- destroy = vue.watch(renderer, (newVal) => {
1741
+ function useRenderer(callback) {
1742
+ return vue.watch(renderer, (newVal) => {
1746
1743
  if (!newVal)
1747
1744
  return;
1748
- // TODO: better fix than `any`?
1749
1745
  callback(newVal);
1750
- if (once) {
1751
- destroy?.();
1752
- }
1753
1746
  }, { immediate: true });
1754
1747
  }
1755
1748
  /** The current scene. Often easier to use `useScene` instead of this. */
1756
1749
  const scene = vue.computed(() => ensuredScene.value.instance);
1757
1750
  /** Run a function using the current scene when it's present. */
1758
- function useScene(callback, once = true) {
1759
- let destroy;
1760
- destroy = vue.watch(scene, (newVal) => {
1751
+ function useScene(callback) {
1752
+ return vue.watch(scene, (newVal) => {
1761
1753
  if (!newVal)
1762
1754
  return;
1763
- // TODO: better fix than `any`?
1764
1755
  callback(newVal);
1765
- if (once) {
1766
- destroy?.();
1767
- }
1768
1756
  }, { immediate: true });
1769
1757
  }
1770
1758
  // CUSTOM RENDER SUPPORT
@@ -1793,12 +1781,14 @@
1793
1781
  app = vue.createRenderer(nodeOps).createApp(root);
1794
1782
  // register all components
1795
1783
  Object.keys(components).forEach((key) => {
1796
- app.component(key, components[key]);
1784
+ app?.component(key, components[key]);
1797
1785
  });
1798
1786
  // update mount function to match Lunchbox.Node
1799
1787
  const { mount } = app;
1800
1788
  app.mount = (root, ...args) => {
1789
+ // find DOM element to use as app root
1801
1790
  const domElement = (typeof root === 'string' ? document.querySelector(root) : root);
1791
+ // create or find root node
1802
1792
  const rootNode = ensureRootNode({
1803
1793
  domElement,
1804
1794
  isLunchboxRootNode: true,
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vue"),require("three"),require("lodash")):"function"==typeof define&&define.amd?define(["exports","vue","three","lodash"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).LunchboxRenderer={},e.vue,e.three,e.lodash)}(this,(function(e,t,n,r){"use strict";function o(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}})),t.default=e,Object.freeze(t)}var a=o(n);const s=[],i=(e,n)=>{const r=I.value?.instance,o=K.value.instance,a=F.value;if(!r?.domElement||!o||!a)return;const s=(e=e??window.innerWidth)/(n=n??window.innerHeight);if("perspectivecamera"===a.type?.toLowerCase()){const e=a.instance;e.aspect=s,e.updateProjectionMatrix()}else if("orthographiccamera"===a.type?.toLowerCase()){const t=a.instance,r=n/e;t.top=10*r,t.bottom=10*-r,t.right=10,t.left=-10,t.updateProjectionMatrix()}else console.log("TODO: non-ortho or perspective camera");r.setSize(e,n),o&&a.instance&&r.render(t.toRaw(o),t.toRaw(a.instance))},c=e=>({position:e,top:0,right:0,bottom:0,left:0,width:"100%",height:"100%"}),d={name:"Lunchbox",props:{background:String,cameraArgs:Array,cameraLook:Array,cameraLookAt:Array,cameraPosition:Array,dpr:Number,ortho:Boolean,orthographic:Boolean,rendererArguments:Object,rendererProperties:Object,shadow:[Boolean,Object],transparent:Boolean},setup(e,o){const a=t.ref(),s=t.ref(!0),d=t.ref(e.dpr??-1),u=t.ref();let l,p,m;return t.onMounted((()=>{if(!a.value)throw new Error("missing canvas");if(l=G(["WebGLRenderer"]),l)s.value=!1,W.value=!0;else{const t={alpha:e.transparent,antialias:!0,canvas:a.value.domElement,...e.rendererArguments??{}};I.value=v({type:"WebGLRenderer",uuid:$,props:{args:[t]}}),W.value=!0;const n=I,o={shadow:e.shadow};n.value.instance&&o?.shadow&&(n.value.instance.shadowMap.enabled=!0,"object"==typeof o.shadow&&(n.value.instance.shadowMap.type=o.shadow.type)),e.rendererProperties&&Object.keys(e.rendererProperties).forEach((t=>{r.set(n.value,t,e.rendererProperties[t])})),l=n.value}if(p=G(["PerspectiveCamera","OrthographicCamera"]),p?D.value=!0:(e.ortho||e.orthographic?F.value=v({props:{args:e.cameraArgs??[]},type:"OrthographicCamera",uuid:k}):F.value=v({props:{args:e.cameraArgs??[45,.5625,1,1e3]},type:"PerspectiveCamera",uuid:k}),D.value=!0,p=F.value),!p.instance)throw new Error("Error creating camera.");if(p&&e.cameraPosition&&p.instance.position.set(...e.cameraPosition),p&&(e.cameraLookAt||e.cameraLook)){const t=e.cameraLookAt||e.cameraLook;p.instance.lookAt(...t)}if(m=K.value,m&&m.instance&&e.background&&(m.instance.background=new n.Color(e.background)),-1===d.value&&(d.value=window.devicePixelRatio),!l?.instance)throw new Error("missing renderer");l.instance.setPixelRatio(d.value),he.dpr.value=d.value,((e,t,n)=>{const r=e.value?.domElement;if(!r)throw new Error("missing container");i();const o=new ResizeObserver((([e])=>{i()}));r&&o.observe(r),n((()=>{t&&o.unobserve(t)}))})(u,l.instance.domElement,t.onBeforeUnmount);const o=t.getCurrentInstance().appContext.app;for(let e of te)e({app:o,camera:p.instance,renderer:l.instance,scene:m.instance});ae({app:o,camera:p.instance,renderer:l.instance,scene:m.instance})})),t.onBeforeUnmount((()=>{ie()})),()=>[o.slots.default?.()??null,t.h("div",{style:c("absolute"),ref:u},[s.value?t.h("canvas",{style:c("fixed"),class:"lunchbox-canvas",ref:a}):null])]}},u={},l=["canvas","div","LunchboxWrapper"],p={...["mesh","instancedMesh","scene","sprite","object3D","instancedBufferGeometry","bufferGeometry","boxBufferGeometry","circleBufferGeometry","coneBufferGeometry","cylinderBufferGeometry","dodecahedronBufferGeometry","extrudeBufferGeometry","icosahedronBufferGeometry","latheBufferGeometry","octahedronBufferGeometry","parametricBufferGeometry","planeBufferGeometry","polyhedronBufferGeometry","ringBufferGeometry","shapeBufferGeometry","sphereBufferGeometry","tetrahedronBufferGeometry","textBufferGeometry","torusBufferGeometry","torusKnotBufferGeometry","tubeBufferGeometry","wireframeGeometry","parametricGeometry","tetrahedronGeometry","octahedronGeometry","icosahedronGeometry","dodecahedronGeometry","polyhedronGeometry","tubeGeometry","torusKnotGeometry","torusGeometry","sphereGeometry","ringGeometry","planeGeometry","latheGeometry","shapeGeometry","extrudeGeometry","edgesGeometry","coneGeometry","cylinderGeometry","circleGeometry","boxGeometry","material","shadowMaterial","spriteMaterial","rawShaderMaterial","shaderMaterial","pointsMaterial","meshPhysicalMaterial","meshStandardMaterial","meshPhongMaterial","meshToonMaterial","meshNormalMaterial","meshLambertMaterial","meshDepthMaterial","meshDistanceMaterial","meshBasicMaterial","meshMatcapMaterial","lineDashedMaterial","lineBasicMaterial","light","spotLightShadow","spotLight","pointLight","rectAreaLight","hemisphereLight","directionalLightShadow","directionalLight","ambientLight","lightShadow","ambientLightProbe","hemisphereLightProbe","lightProbe","texture","videoTexture","dataTexture","dataTexture3D","compressedTexture","cubeTexture","canvasTexture","depthTexture","textureLoader","group","catmullRomCurve3","points","cameraHelper","camera","perspectiveCamera","orthographicCamera","cubeCamera","arrayCamera","webGLRenderer"].map((e=>t.defineComponent({inheritAttrs:!1,name:e,setup:(n,r)=>()=>t.h(e,r.attrs,r.slots?.default?.()||[])}))).reduce(((e,t)=>(e[t.name]=t,e)),{}),Lunchbox:d};const m=e=>e?.$el&&e?.$el?.hasOwnProperty?.("instance"),h=e=>{if("domMeta"===e?.metaType)return!0;const t="string"==typeof e?e:e?.type;return l.includes(t??"")},f=e=>"standardMeta"===e?.metaType,y=e=>e.isLunchboxRootNode;function v(e={},t={}){const n={attached:e.attached??[],attachedArray:e.attachedArray??{},instance:e.instance??null},r=new J.RendererStandardNode({...e,...n,metaType:"standardMeta"});return!r.type||y(r)||r.instance||(r.instance=function(e){if(!e.type)return null;const t=e.type[0].toUpperCase()+e.type.slice(1),n=u[e.type]||a[t];if(!n)throw`${t} is not part of the THREE namespace! Did you forget to extend? import {extend} from 'lunchbox'; extend({app, YourComponent, ...})`;const r=(e.props.args??[]).map((t=>function({node:e,prop:t}){const n="string"==typeof t&&t.startsWith("$attachedArray"),r=function({node:e,prop:t}){if("string"==typeof t&&t.startsWith("$attachedArray"))return e.attachedArray[t.replace("$attachedArray.","")];if("string"==typeof t&&t.startsWith("$attached"))return e.attached[t.replace("$attached.","")];return t}({node:e,prop:t});return Array.isArray(r)&&n?r:[r]}({node:e,prop:t})));let o=[];r.forEach((e=>{o=o.concat(e)}));return new n(...o)}({...r,props:{...r.props,...t}})),"scene"===r.type&&(K.value=r),r}const g=[],b=t.ref(!1);function x({node:e,key:n,value:r}){var o;if(e.eventListeners[n]||(e.eventListeners[n]=[]),e.eventListenerRemoveFunctions[n]||(e.eventListenerRemoveFunctions[n]=[]),e.eventListeners[n].push(r),R.includes(n)&&(_.value,e.instance&&!g.includes(e)&&(o=e,g.push(o),e.eventListenerRemoveFunctions[n].push((()=>(e=>{const t=g.indexOf(e);-1!==t&&g.splice(t,1)})(e))))),"onClick"===n||"onPointerDown"===n||"onPointerUp"===n){const r=t.watch((()=>b.value),(t=>{const r=P.map((e=>e.element)).findIndex((t=>t.instance&&t.instance.uuid===e.instance?.uuid));-1!==r&&((!t||"onClick"!==n&&"onPointerDown"!==n)&&(t||"onPointerUp"!==n)||e.eventListeners[n].forEach((e=>{e({intersection:P[r].intersection})})))}));e.eventListenerRemoveFunctions[n].push(r)}return e}const R=["onClick","onPointerUp","onPointerDown","onPointerOver","onPointerOut","onPointerEnter","onPointerLeave","onPointerMove"];let w,A,C;const E=t.ref({x:1/0,y:1/0});let L=!1;let P=[];const M=()=>{const e=_.value?.instance,t=F.value?.instance;if(!e||!t)return;e.setFromCamera(he.mousePos.value,t);const n=e.intersectObjects(g.map((e=>e.instance)));let r=[],o=[],a=[];r=P.map((e=>e.intersection)),n?.forEach((e=>{const t=P.findIndex((t=>t.intersection.object===e.object));if(-1===t){const t=g.find((t=>t.instance?.uuid===e.object.uuid));t&&o.push({element:t,intersection:e})}else{const t=g.find((t=>t.instance?.uuid===e.object.uuid));t&&a.push({element:t,intersection:e})}const n=r.findIndex((t=>t.object.uuid===e.object.uuid));-1!==n&&r.splice(n,1)}));const s=r.map((e=>({element:g.find((t=>t.instance?.uuid===e.object.uuid)),intersection:e})));o.forEach((({element:e,intersection:t})=>{B({element:e,eventKeys:["onPointerEnter"],intersection:t})})),a.forEach((({element:e,intersection:t})=>{B({element:e,eventKeys:["onPointerOver","onPointerMove"],intersection:t})})),s.forEach((({element:e,intersection:t})=>{B({element:e,eventKeys:["onPointerLeave","onPointerOut"],intersection:t})})),P=[].concat(o,a)},B=({element:e,eventKeys:t,intersection:n})=>{e&&t.forEach((t=>{e.eventListeners[t]&&e.eventListeners[t].forEach((e=>{e({intersection:n})}))}))};function N(t={}){return e.lunchboxTree||(e.lunchboxTree=new J.RendererRootNode(t)),e.lunchboxTree}function G(e){Array.isArray(e)||(e=[e]);for(let t of e)if(O[t])return O[t];for(let n of e){const e=T[n]||s.find((e=>e.type?.toLowerCase()===n.toLowerCase()));if(!(t=e,"RendererNode"!==t?.minidomType||e.props["is-default"]&&e.props.isDefault))return null;if(e){const t=e;return T[n]=t,t}}var t;return null}e.lunchboxTree=void 0;const T=t.reactive({}),O=t.reactive({});function j(e,n,r={},o=null){Array.isArray(e)||(e=[e]);for(let t of e)T[t]||(T[t]=null),O[t]||(O[t]=null);return t.computed({get(){const t=G(e);if(t)return t;const a=N(),s=v({type:e[0],uuid:n,props:r});return a.addChild(s),T[e[0]]=s,o&&o(s),s},set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);O[n]=e}})}const k="FALLBACK_CAMERA",S=j(["PerspectiveCamera","OrthographicCamera"],k,{args:[45,.5625,1,1e3]}),D=t.ref(!1),F=t.computed({get:()=>D.value?S.value:null,set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);O[n]=e}}),$="FALLBACK_RENDERER",U=j(["WebGLRenderer"],$,{}),W=t.ref(!1),I=t.computed({get:()=>W.value?U.value:null,set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);O[n]=e}}),K=j("Scene","FALLBACK_SCENE"),_=j("Raycaster","AUTO_RAYCASTER",{},(e=>(e=>{if(!e.instance)return;let n=null;n=t.watch((()=>I.value),(e=>{e?.instance&&(L||(w=t=>{const n=(e.instance.domElement.width??1)/he.dpr.value,r=(e.instance.domElement.height??1)/he.dpr.value;E.value.x=t.offsetX/n*2-1,E.value.y=-t.offsetY/r*2+1},A=()=>b.value=!0,C=()=>b.value=!1,e.instance.domElement.addEventListener("mousemove",w),e.instance.domElement.addEventListener("mousedown",A),e.instance.domElement.addEventListener("mouseup",C),se(M),L=!0),n&&n())}),{immediate:!0})})(e))),V=e=>t.defineComponent({inheritAttrs:!1,name:e,render(){return t.h(e,this.$attrs,this.$slots?.default?.()||[])}});var q,z=new Uint8Array(16);function H(){if(!q&&!(q="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return q(z)}var Y=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;function X(e){return"string"==typeof e&&Y.test(e)}for(var J,Q=[],Z=0;Z<256;++Z)Q.push((Z+256).toString(16).substr(1));function ee(e,t,n){var r=(e=e||{}).random||(e.rng||H)();if(r[6]=15&r[6]|64,r[8]=63&r[8]|128,t){n=n||0;for(var o=0;o<16;++o)t[n+o]=r[o];return t}return function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=(Q[e[t+0]]+Q[e[t+1]]+Q[e[t+2]]+Q[e[t+3]]+"-"+Q[e[t+4]]+Q[e[t+5]]+"-"+Q[e[t+6]]+Q[e[t+7]]+"-"+Q[e[t+8]]+Q[e[t+9]]+"-"+Q[e[t+10]]+Q[e[t+11]]+Q[e[t+12]]+Q[e[t+13]]+Q[e[t+14]]+Q[e[t+15]]).toLowerCase();if(!X(n))throw TypeError("Stringified UUID is invalid");return n}(r)}!function(e){e.BaseNode=class{constructor(e={},t){this.parentNode=e?.parentNode??t??null,this.minidomType="MinidomBaseNode",this.uuid=e?.uuid??ee(),s.push(this)}uuid;parentNode;get nextSibling(){if(!this.parentNode)return null;const e=this.parentNode.children.findIndex((e=>e.uuid===this.uuid));return-1!==e&&e<this.parentNode.children.length-1?this.parentNode.children[e+1]:null}insertBefore(e,t){e.removeAsChildFromAnyParents(),e.parentNode=this;const n=this.children.findIndex((e=>e.uuid===t?.uuid));-1!==n?this.children.splice(n,0,e):this.children.push(e)}removeChild(e){const t=this.children.findIndex((t=>t?.uuid===e?.uuid));-1!==t&&this.children.splice(t,1)}children=[];addChild(e){return e&&(e.removeAsChildFromAnyParents(),e.parentNode=this,this.insertBefore(e,null)),this}getPath(){const e=[];let t=this;for(;t;)e.unshift(t),t=t.parentNode;return e}drop(){this.parentNode=null,this.removeAsChildFromAnyParents()}walk(e){const t=[this,...this.children],n=[];let r=!0;for(;t.length&&r;){const o=t.shift();if(o){if(n.includes(o))continue;n.push(o),t.push(...o.children.filter((e=>!n.includes(e)))),r=e(o)}else r=!1}}minidomType;removeAsChildFromAnyParents(){s.forEach((e=>e.removeChild(this)))}};class t extends e.BaseNode{constructor(e={},t){super(e,t),this.minidomType="RendererNode",this.eventListeners={},this.eventListenerRemoveFunctions={},this.name=e.name??"",this.metaType=e.metaType??"standardMeta",this.props=e.props??[],this.type=e.type??""}eventListeners;eventListenerRemoveFunctions;name;metaType;props;type;drop(){super.drop(),Object.keys(this.eventListenerRemoveFunctions).forEach((e=>{this.eventListenerRemoveFunctions[e].forEach((e=>e()))}))}}e.RendererBaseNode=t;class n extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.domElement=e.domElement??document.createElement("div")}domElement;isLunchboxRootNode=!0}e.RendererRootNode=n;class r extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.text=e.text??""}text}e.RendererCommentNode=r;class o extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.domElement=e.domElement??document.createElement("div")}domElement}e.RendererDomNode=o;class a extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.text=e.text??""}text}e.RendererTextNode=a;class i extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.attached=e.attached??[],this.attachedArray=e.attachedArray??{},this.instance=e.instance??null}attached;attachedArray;instance}e.RendererStandardNode=i}(J||(J={}));(new J.RendererRootNode).minidomType="RootNode";const te=[];let ne;const re=[],oe=[],ae=e=>{ne=requestAnimationFrame((()=>ae({app:e.app,renderer:I.value?.instance,scene:K.value.instance,camera:F.value?.instance})));const{app:n,renderer:r,scene:o,camera:a}=e;re.forEach((t=>{t&&t(e)})),r&&o&&a&&(n.customRender?n.customRender(e):r.render(t.toRaw(o),t.toRaw(a))),oe.forEach((t=>{t&&t(e)}))},se=(e,t=1/0)=>{t===1/0?re.push(e):re.splice(t,0,e)},ie=()=>{ne&&cancelAnimationFrame(ne)};const ce={x:"position.x",y:"position.y",z:"position.z"},de=["","parameters"],ue=["args","attach","attachArray","is.default","isDefault","key","onAdded","ref","src"],le=["geometry","material"];function pe(e,t,n,r){const o=r??e.instance,a=t.instance;e.props.attach===n&&(t.attached={[n]:o,...t.attached||{}},a[n]=r??e.instance),e.props.attachArray===n&&(t.attachedArray[e.props.attachArray]||(t.attachedArray[e.props.attachArray]=[]),t.attachedArray[e.props.attachArray].push(o),a[n]=[a[n]])}const me={createElement:(e,t,n,r)=>{const o={type:e};r&&(o.props=r);if(h(e)){const e=function(e={}){const t={domElement:document.createElement(e.type??"")};return new J.RendererDomNode({...t,...e,metaType:"domMeta"})}(o);return e}const a=v(o);return le.forEach((t=>{e.toLowerCase().endsWith(t)&&(a.props.attach=t)})),a},createText:e=>function(e={}){const t={text:e.text??""};return new J.RendererTextNode({...e,...t,metaType:"textMeta"})}({text:e}),createComment:e=>function(e={}){const t={text:e.text??""};return new J.RendererCommentNode({...t,...e,metaType:"commentMeta"})}({text:e}),insert:(e,t,n)=>{let r=t??N();if(r.insertBefore(e,n),"commentMeta"!==e.metaType&&"textMeta"!==e.metaType&&(h(e)&&(h(t)||y(t))&&t.domElement.appendChild(e.domElement),f(e))){let n=r.metaType;if("textMeta"===n||"commentMeta"===n){const e=r.getPath();for(let t=e.length-1;t>=0;t--)if("textMeta"!==e[t].metaType&&"commentMeta"!==e[t].metaType){r=e[t];break}}if("standardMeta"===e.metaType&&"scene"!==e.type&&y(r)){const t=K.value;t.instance&&e&&t.addChild(e),e.instance&&e.instance.isObject3D&&t.instance&&t!==e&&t.instance.add(e.instance)}else f(e)&&e.instance?.isObject3D&&f(r)&&r.instance?.isObject3D&&r.instance?.add?.(e.instance);if(e?.props?.attach&&f(t)&&t?.instance){e.type?.toLowerCase().endsWith("loader")&&e.props.src&&(e.props.attach||e.props.attachArray)?function(e,t){const n=e.instance;if(t.attached=t.attached||{},t.attachedArray=t.attachedArray||{},!e.props.attach)return;if("textureloader"===e.type?.toLowerCase()){const r=n.load(e.props.src);pe(e,t,e.props.attach,r)}else n.load(e.props.src,(n=>{pe(e,t,e.props.attach,n)}),null,(e=>{throw new Error(e)}))}(e,t):pe(e,t,e.props.attach)}e.props?.onAdded&&e.props.onAdded({instance:e.instance})}},nextSibling(e){const t=e.nextSibling;return t||null},parentNode(e){const t=e.parentNode;return t||null},patchProp(e,t,n,o){h(e)?"style"===t?Object.keys(o).forEach((t=>{e.domElement.style[t]=o[t]})):e.domElement.setAttribute(t,o):y(e)||t.startsWith("$")||function({node:e,key:t,value:n}){if((e=>["onClick","onContextMenu","onDoubleClick","onPointerUp","onPointerDown","onPointerOver","onPointerOut","onPointerEnter","onPointerLeave","onPointerMove","onWheel"].includes(e))(t))return x({node:e,key:t,value:n});const o=t.replace(/-/g,"."),a=ce[o]||o;if(ue.includes(t)||ue.includes(a))return e;if(!f(e))return e;if("string"==typeof n&&n.startsWith("$attached")){const t=n.replace("$attached.","");n=r.get(e.attached,t,null)}const s=e.instance;if(!s)return e;let i;for(let e=0;e<de.length&&!i;e++){const t=[de[e],a].filter(Boolean).join(".");i=i=r.get(s,t)}if(i&&r.isNumber(n)&&i.setScalar)i.setScalar(n);else if(i&&i.set){const e=Array.isArray(n)?n:[n];s[a].set(...e)}else"function"==typeof i?i.bind(e.instance)(...n):void 0!==r.get(s,a,void 0)?r.set(s,a,""===n||n):console.log(`No property ${a} found on ${s}`);const c=s?.texture?.type||s?.type;if("string"==typeof c){const e=c.toLowerCase();switch(!0){case e.includes("material"):s.needsUpdate=!0;break;case e.includes("camera")&&s.updateProjectionMatrix:s.updateProjectionMatrix()}}}({node:e,key:t,value:o})},remove:e=>{if(!e)return;const t=Object.keys(O),n=[];e.walk((e=>(n.push(e),!0))),n.forEach((e=>{const n=t.find((t=>O[t]?.uuid===e.uuid));if(n&&(O[n]=null),f(e)){e.instance?.removeFromParent?.();const t="scene"!==e.type&&e.instance?.dispose;t&&t.bind(e.instance)(),e.instance=null}e.drop();const r=s.findIndex((t=>t.uuid===e.uuid));-1!==r&&s.splice(r,1)}))},setElementText(){},setText(){}},he={dpr:t.ref(1),inputActive:b,mousePos:E},fe=t.computed((()=>F.value?.instance??null));const ye=t.computed((()=>I.value?.instance??null));const ve=t.computed((()=>K.value.instance));let ge=null,be=null;e.camera=fe,e.clearCustomRender=()=>{ge?ge.clearCustomRender():be=null},e.createApp=e=>{ge=t.createRenderer(me).createApp(e),Object.keys(p).forEach((e=>{ge.component(e,p[e])}));const{mount:n}=ge;return ge.mount=(e,...t)=>{const r=N({domElement:"string"==typeof e?document.querySelector(e):e,isLunchboxRootNode:!0,name:"root",metaType:"rootMeta",type:"root",uuid:"LUNCHBOX_ROOT"});ge.rootNode=r;return n(r,...t)},ge.extend=e=>((({app:e,...t})=>{Object.keys(t).forEach((n=>{e.component(n,V(n)),u[n]=t[n]}))})({app:ge,...e}),ge),ge.setCustomRender=e=>{ge.customRender=e},be&&(ge.setCustomRender(be),be=null),ge.clearCustomRender=()=>{ge.customRender=null},ge},e.find=function(e){return e=t.isRef(e)?e.value:e,f(e)?e?.instance:m(e)?e?.$el?.instance:t.isVNode(e)?e.el?.instance:null},e.globals=he,e.offAfterRender=e=>{if(isFinite(e))oe.splice(e,1);else{const t=oe.findIndex((t=>t==e));oe.splice(t,1)}},e.offBeforeRender=e=>{if(isFinite(e))re.splice(e,1);else{const t=re.findIndex((t=>t==e));re.splice(t,1)}},e.onAfterRender=(e,t=1/0)=>{t===1/0?oe.push(e):oe.splice(t,0,e)},e.onBeforeRender=se,e.onStart=(e,t=1/0)=>{t===1/0?te.push(e):te.splice(t,0,e)},e.renderer=ye,e.scene=ve,e.setCustomRender=e=>{ge?ge.setCustomRender(e):be=e},e.useCamera=function(e,n=!0){let r;r=t.watch(fe,(t=>{t&&(e(t),n&&r?.())}),{immediate:!0})},e.useRenderer=function(e,n=!0){let r;r=t.watch(ye,(t=>{t&&(e(t),n&&r?.())}),{immediate:!0})},e.useScene=function(e,n=!0){let r;r=t.watch(ve,(t=>{t&&(e(t),n&&r?.())}),{immediate:!0})},Object.defineProperty(e,"__esModule",{value:!0})}));
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vue"),require("three"),require("lodash")):"function"==typeof define&&define.amd?define(["exports","vue","three","lodash"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).LunchboxRenderer={},e.vue,e.three,e.lodash)}(this,(function(e,t,n,r){"use strict";function o(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}})),t.default=e,Object.freeze(t)}var a=o(n);const s=[],i=(e,n)=>{const r=W.value?.instance,o=I.value.instance,a=D.value;if(!r?.domElement||!o||!a)return;const s=(e=e??window.innerWidth)/(n=n??window.innerHeight);if("perspectivecamera"===a.type?.toLowerCase()){const e=a.instance;e.aspect=s,e.updateProjectionMatrix()}else if("orthographiccamera"===a.type?.toLowerCase()){const t=a.instance,r=n/e;t.top=10*r,t.bottom=10*-r,t.right=10,t.left=-10,t.updateProjectionMatrix()}else console.log("TODO: non-ortho or perspective camera");r.setSize(e,n),o&&a.instance&&r.render(t.toRaw(o),t.toRaw(a.instance))},c=e=>({position:e,top:0,right:0,bottom:0,left:0,width:"100%",height:"100%"}),d={name:"Lunchbox",props:{background:String,cameraArgs:Array,cameraLook:Array,cameraLookAt:Array,cameraPosition:Array,dpr:Number,ortho:Boolean,orthographic:Boolean,rendererArguments:Object,rendererProperties:Object,shadow:[Boolean,Object],transparent:Boolean,zoom:Number},setup(e,o){const a=t.ref(),s=t.ref(!0),d=t.ref(e.dpr??-1),u=t.ref();let l,p,m;return t.onMounted((()=>{if(!a.value)throw new Error("missing canvas");if(l=B(["WebGLRenderer"]),l)s.value=!1,U.value=!0;else{const t={alpha:e.transparent,antialias:!0,canvas:a.value.domElement,...e.rendererArguments??{}};W.value=K({type:"WebGLRenderer",uuid:F,props:{args:[t]}}),U.value=!0;const n=W,o={shadow:e.shadow};n.value.instance&&o?.shadow&&(n.value.instance.shadowMap.enabled=!0,"object"==typeof o.shadow&&(n.value.instance.shadowMap.type=o.shadow.type)),e.rendererProperties&&Object.keys(e.rendererProperties).forEach((t=>{r.set(n.value,t,e.rendererProperties[t])})),l=n.value}if(p=B(["PerspectiveCamera","OrthographicCamera"]),p?S.value=!0:(e.ortho||e.orthographic?D.value=K({props:{args:e.cameraArgs??[]},type:"OrthographicCamera",uuid:j}):D.value=K({props:{args:e.cameraArgs??[45,.5625,1,1e3]},type:"PerspectiveCamera",uuid:j}),S.value=!0,p=D.value),!p.instance)throw new Error("Error creating camera.");if(p&&e.cameraPosition&&p.instance.position.set(...e.cameraPosition),p&&(e.cameraLookAt||e.cameraLook)){const t=e.cameraLookAt||e.cameraLook;p.instance.lookAt(...t)}if(p&&void 0!==e.zoom&&(p.instance.zoom=e.zoom),m=I.value,m&&m.instance&&e.background&&(m.instance.background=new n.Color(e.background)),-1===d.value&&(d.value=window.devicePixelRatio),!l?.instance)throw new Error("missing renderer");l.instance.setPixelRatio(d.value),he.dpr.value=d.value,((e,t,n)=>{const r=e.value?.domElement;if(!r)throw new Error("missing container");i();const o=new ResizeObserver((([e])=>{i()}));r&&o.observe(r),n((()=>{t&&o.unobserve(t)}))})(u,l.instance.domElement,t.onBeforeUnmount);const o=t.getCurrentInstance().appContext.app;for(let e of te)e({app:o,camera:p.instance,renderer:l.instance,scene:m.instance});ae({app:o,camera:p.instance,renderer:l.instance,scene:m.instance})})),t.onBeforeUnmount((()=>{ie()})),()=>[o.slots.default?.()??null,t.h("div",{style:c("absolute"),ref:u},[s.value?t.h("canvas",{style:c("fixed"),class:"lunchbox-canvas",ref:a}):null])]}},u={},l=["canvas","div","LunchboxWrapper"],p={...["mesh","instancedMesh","scene","sprite","object3D","instancedBufferGeometry","bufferGeometry","boxBufferGeometry","circleBufferGeometry","coneBufferGeometry","cylinderBufferGeometry","dodecahedronBufferGeometry","extrudeBufferGeometry","icosahedronBufferGeometry","latheBufferGeometry","octahedronBufferGeometry","parametricBufferGeometry","planeBufferGeometry","polyhedronBufferGeometry","ringBufferGeometry","shapeBufferGeometry","sphereBufferGeometry","tetrahedronBufferGeometry","textBufferGeometry","torusBufferGeometry","torusKnotBufferGeometry","tubeBufferGeometry","wireframeGeometry","parametricGeometry","tetrahedronGeometry","octahedronGeometry","icosahedronGeometry","dodecahedronGeometry","polyhedronGeometry","tubeGeometry","torusKnotGeometry","torusGeometry","sphereGeometry","ringGeometry","planeGeometry","latheGeometry","shapeGeometry","extrudeGeometry","edgesGeometry","coneGeometry","cylinderGeometry","circleGeometry","boxGeometry","material","shadowMaterial","spriteMaterial","rawShaderMaterial","shaderMaterial","pointsMaterial","meshPhysicalMaterial","meshStandardMaterial","meshPhongMaterial","meshToonMaterial","meshNormalMaterial","meshLambertMaterial","meshDepthMaterial","meshDistanceMaterial","meshBasicMaterial","meshMatcapMaterial","lineDashedMaterial","lineBasicMaterial","light","spotLightShadow","spotLight","pointLight","rectAreaLight","hemisphereLight","directionalLightShadow","directionalLight","ambientLight","lightShadow","ambientLightProbe","hemisphereLightProbe","lightProbe","texture","videoTexture","dataTexture","dataTexture3D","compressedTexture","cubeTexture","canvasTexture","depthTexture","textureLoader","group","catmullRomCurve3","points","cameraHelper","camera","perspectiveCamera","orthographicCamera","cubeCamera","arrayCamera","webGLRenderer"].map((e=>t.defineComponent({inheritAttrs:!1,name:e,setup:(n,r)=>()=>t.h(e,r.attrs,r.slots?.default?.()||[])}))).reduce(((e,t)=>(e[t.name]=t,e)),{}),Lunchbox:d};const m=e=>e?.$el&&e?.$el?.hasOwnProperty?.("instance"),h=e=>{if("domMeta"===e?.metaType)return!0;const t="string"==typeof e?e:e?.type;return l.includes(t??"")},f=e=>"standardMeta"===e?.metaType,y=e=>e.isLunchboxRootNode,v=[],g=t.ref(!1);function b({node:e,key:n,value:r}){var o;if(e.eventListeners[n]||(e.eventListeners[n]=[]),e.eventListenerRemoveFunctions[n]||(e.eventListenerRemoveFunctions[n]=[]),e.eventListeners[n].push(r),x.includes(n)&&(z.value,e.instance&&!v.includes(e)&&(o=e,v.push(o),e.eventListenerRemoveFunctions[n].push((()=>(e=>{const t=v.indexOf(e);-1!==t&&v.splice(t,1)})(e))))),"onClick"===n||"onPointerDown"===n||"onPointerUp"===n){const r=t.watch((()=>g.value),(t=>{const r=L.map((e=>e.element)).findIndex((t=>t.instance&&t.instance.uuid===e.instance?.uuid));-1!==r&&((!t||"onClick"!==n&&"onPointerDown"!==n)&&(t||"onPointerUp"!==n)||e.eventListeners[n].forEach((e=>{e({intersection:L[r].intersection})})))}));e.eventListenerRemoveFunctions[n].push(r)}return e}const x=["onClick","onPointerUp","onPointerDown","onPointerOver","onPointerOut","onPointerEnter","onPointerLeave","onPointerMove"];let R,w,A;const C=t.ref({x:1/0,y:1/0});let E=!1;let L=[];const P=()=>{const e=z.value?.instance,t=D.value?.instance;if(!e||!t)return;e.setFromCamera(he.mousePos.value,t);const n=e.intersectObjects(v.map((e=>e.instance)));let r=[],o=[],a=[];r=L.map((e=>e.intersection)),n?.forEach((e=>{if(-1===L.findIndex((t=>t.intersection.object===e.object))){const t=v.find((t=>t.instance?.uuid===e.object.uuid));t&&o.push({element:t,intersection:e})}else{const t=v.find((t=>t.instance?.uuid===e.object.uuid));t&&a.push({element:t,intersection:e})}const t=r.findIndex((t=>t.object.uuid===e.object.uuid));-1!==t&&r.splice(t,1)}));const s=r.map((e=>({element:v.find((t=>t.instance?.uuid===e.object.uuid)),intersection:e})));o.forEach((({element:e,intersection:t})=>{M({element:e,eventKeys:["onPointerEnter"],intersection:t})})),a.forEach((({element:e,intersection:t})=>{M({element:e,eventKeys:["onPointerOver","onPointerMove"],intersection:t})})),s.forEach((({element:e,intersection:t})=>{M({element:e,eventKeys:["onPointerLeave","onPointerOut"],intersection:t})})),L=[].concat(o,a)},M=({element:e,eventKeys:t,intersection:n})=>{e&&t.forEach((t=>{e.eventListeners[t]&&e.eventListeners[t].forEach((e=>{e({intersection:n})}))}))};function N(t={}){return e.lunchboxTree||(e.lunchboxTree=new J.RendererRootNode(t)),e.lunchboxTree}function B(e){Array.isArray(e)||(e=[e]);for(let t of e)if(T[t])return T[t];for(let n of e){const e=G[n]||s.find((e=>e.type?.toLowerCase()===n.toLowerCase()));if(!(t=e,"RendererNode"!==t?.minidomType||!1!==e.props["is-default"]&&!1!=!e.props.isDefault))return null;if(e){const t=e;return G[n]=t,t}}var t;return null}e.lunchboxTree=void 0;const G=t.reactive({}),T=t.reactive({});function O(e,n,r={},o=null){Array.isArray(e)||(e=[e]);for(let t of e)G[t]||(G[t]=null),T[t]||(T[t]=null);return t.computed({get(){const t=B(e);if(t)return t;const a=N(),s=K({type:e[0],uuid:n,props:r});return a.addChild(s),G[e[0]]=s,o&&o(s),s},set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);T[n]=e}})}const j="FALLBACK_CAMERA",k=O(["PerspectiveCamera","OrthographicCamera"],j,{args:[45,.5625,1,1e3]}),S=t.ref(!1),D=t.computed({get:()=>S.value?k.value:null,set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);T[n]=e}}),F="FALLBACK_RENDERER",$=O(["WebGLRenderer"],F,{}),U=t.ref(!1),W=t.computed({get:()=>U.value?$.value:null,set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);T[n]=e}}),I=O("Scene","FALLBACK_SCENE"),z=O("Raycaster","AUTO_RAYCASTER",{},(e=>(e=>{if(!e.instance)return;let n=null;n=t.watch((()=>W.value),(e=>{e?.instance&&(E||(R=t=>{const n=(e.instance.domElement.width??1)/he.dpr.value,r=(e.instance.domElement.height??1)/he.dpr.value;C.value.x=t.offsetX/n*2-1,C.value.y=-t.offsetY/r*2+1},w=()=>g.value=!0,A=()=>g.value=!1,e.instance.domElement.addEventListener("mousemove",R),e.instance.domElement.addEventListener("mousedown",w),e.instance.domElement.addEventListener("mouseup",A),se(P),E=!0),n&&n())}),{immediate:!0})})(e)));function K(e={},t={}){const n={attached:e.attached??[],attachedArray:e.attachedArray??{},instance:e.instance??null},r=new J.RendererStandardNode({...e,...n,metaType:"standardMeta"});return!r.type||y(r)||r.instance||(r.instance=function(e){if(!e.type)return null;const t=e.type[0].toUpperCase()+e.type.slice(1),n=u[e.type]||a[t];if(!n)throw`${t} is not part of the THREE namespace! Did you forget to extend? import {extend} from 'lunchbox'; extend({app, YourComponent, ...})`;const r=(e.props.args??[]).map((t=>function({node:e,prop:t}){const n="string"==typeof t&&t.startsWith("$attachedArray"),r=function({node:e,prop:t}){if("string"==typeof t&&t.startsWith("$attachedArray"))return e.attachedArray[t.replace("$attachedArray.","")];if("string"==typeof t&&t.startsWith("$attached"))return e.attached[t.replace("$attached.","")];return t}({node:e,prop:t});return Array.isArray(r)&&n?r:[r]}({node:e,prop:t})));let o=[];r.forEach((e=>{o=o.concat(e)}));return new n(...o)}({...r,props:{...r.props,...t}})),"scene"===r.type?.toLowerCase()?I.value=r:r.type?.toLowerCase().endsWith("camera")&&(D.value=r),r}const _=e=>t.defineComponent({inheritAttrs:!1,name:e,render(){return t.h(e,this.$attrs,this.$slots?.default?.()||[])}});var V,q=new Uint8Array(16);function H(){if(!V&&!(V="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return V(q)}var Y=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;function X(e){return"string"==typeof e&&Y.test(e)}for(var J,Q=[],Z=0;Z<256;++Z)Q.push((Z+256).toString(16).substr(1));function ee(e,t,n){var r=(e=e||{}).random||(e.rng||H)();if(r[6]=15&r[6]|64,r[8]=63&r[8]|128,t){n=n||0;for(var o=0;o<16;++o)t[n+o]=r[o];return t}return function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,n=(Q[e[t+0]]+Q[e[t+1]]+Q[e[t+2]]+Q[e[t+3]]+"-"+Q[e[t+4]]+Q[e[t+5]]+"-"+Q[e[t+6]]+Q[e[t+7]]+"-"+Q[e[t+8]]+Q[e[t+9]]+"-"+Q[e[t+10]]+Q[e[t+11]]+Q[e[t+12]]+Q[e[t+13]]+Q[e[t+14]]+Q[e[t+15]]).toLowerCase();if(!X(n))throw TypeError("Stringified UUID is invalid");return n}(r)}!function(e){e.BaseNode=class{constructor(e={},t){this.parentNode=e?.parentNode??t??null,this.minidomType="MinidomBaseNode",this.uuid=e?.uuid??ee(),s.push(this)}uuid;parentNode;get nextSibling(){if(!this.parentNode)return null;const e=this.parentNode.children.findIndex((e=>e.uuid===this.uuid));return-1!==e&&e<this.parentNode.children.length-1?this.parentNode.children[e+1]:null}insertBefore(e,t){e.removeAsChildFromAnyParents(),e.parentNode=this;const n=this.children.findIndex((e=>e.uuid===t?.uuid));-1!==n?this.children.splice(n,0,e):this.children.push(e)}removeChild(e){const t=this.children.findIndex((t=>t?.uuid===e?.uuid));-1!==t&&this.children.splice(t,1)}children=[];addChild(e){return e&&(e.removeAsChildFromAnyParents(),e.parentNode=this,this.insertBefore(e,null)),this}getPath(){const e=[];let t=this;for(;t;)e.unshift(t),t=t.parentNode;return e}drop(){this.parentNode=null,this.removeAsChildFromAnyParents()}walk(e){const t=[this,...this.children],n=[];let r=!0;for(;t.length&&r;){const o=t.shift();if(o){if(n.includes(o))continue;n.push(o),t.push(...o.children.filter((e=>!n.includes(e)))),r=e(o)}else r=!1}}minidomType;removeAsChildFromAnyParents(){s.forEach((e=>e.removeChild(this)))}};class t extends e.BaseNode{constructor(e={},t){super(e,t),this.minidomType="RendererNode",this.eventListeners={},this.eventListenerRemoveFunctions={},this.name=e.name??"",this.metaType=e.metaType??"standardMeta",this.props=e.props??[],this.type=e.type??""}eventListeners;eventListenerRemoveFunctions;name;metaType;props;type;drop(){super.drop(),Object.keys(this.eventListenerRemoveFunctions).forEach((e=>{this.eventListenerRemoveFunctions[e].forEach((e=>e()))}))}}e.RendererBaseNode=t;class n extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.domElement=e.domElement??document.createElement("div")}domElement;isLunchboxRootNode=!0}e.RendererRootNode=n;class r extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.text=e.text??""}text}e.RendererCommentNode=r;class o extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.domElement=e.domElement??document.createElement("div")}domElement}e.RendererDomNode=o;class a extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.text=e.text??""}text}e.RendererTextNode=a;class i extends e.RendererBaseNode{constructor(e={},t){super(e,t),this.attached=e.attached??[],this.attachedArray=e.attachedArray??{},this.instance=e.instance??null}attached;attachedArray;instance}e.RendererStandardNode=i}(J||(J={}));(new J.RendererRootNode).minidomType="RootNode";const te=[];let ne;const re=[],oe=[],ae=e=>{ne=requestAnimationFrame((()=>ae({app:e.app,renderer:W.value?.instance,scene:I.value.instance,camera:D.value?.instance})));const{app:n,renderer:r,scene:o,camera:a}=e;re.forEach((t=>{t&&t(e)})),r&&o&&a&&(n.customRender?n.customRender(e):r.render(t.toRaw(o),t.toRaw(a))),oe.forEach((t=>{t&&t(e)}))},se=(e,t=1/0)=>{t===1/0?re.push(e):re.splice(t,0,e)},ie=()=>{ne&&cancelAnimationFrame(ne)};const ce={x:"position.x",y:"position.y",z:"position.z"},de=["","parameters"],ue=["args","attach","attachArray","is.default","isDefault","key","onAdded","ref","src"],le=["geometry","material"];function pe(e,t,n,r){const o=r??e.instance,a=t.instance;e.props.attach===n&&(t.attached={[n]:o,...t.attached||{}},a[n]=r??e.instance),e.props.attachArray===n&&(t.attachedArray[e.props.attachArray]||(t.attachedArray[e.props.attachArray]=[]),t.attachedArray[e.props.attachArray].push(o),a[n]=[a[n]])}const me={createElement:(e,t,n,r)=>{const o={type:e};r&&(o.props=r);if(h(e)){const e=function(e={}){const t={domElement:document.createElement(e.type??"")};return new J.RendererDomNode({...t,...e,metaType:"domMeta"})}(o);return e}const a=K(o);return le.forEach((t=>{e.toLowerCase().endsWith(t)&&(a.props.attach=t)})),a},createText:e=>function(e={}){const t={text:e.text??""};return new J.RendererTextNode({...e,...t,metaType:"textMeta"})}({text:e}),createComment:e=>function(e={}){const t={text:e.text??""};return new J.RendererCommentNode({...t,...e,metaType:"commentMeta"})}({text:e}),insert:(e,t,n)=>{let r=t??N();if(r.insertBefore(e,n),"commentMeta"!==e.metaType&&"textMeta"!==e.metaType&&(h(e)&&(h(t)||y(t))&&t.domElement.appendChild(e.domElement),f(e))){let n=r.metaType;if("textMeta"===n||"commentMeta"===n){const e=r.getPath();for(let t=e.length-1;t>=0;t--)if("textMeta"!==e[t].metaType&&"commentMeta"!==e[t].metaType){r=e[t];break}}if("standardMeta"===e.metaType&&"scene"!==e.type&&y(r)){const t=I.value;t.instance&&e&&t.addChild(e),e.instance&&e.instance.isObject3D&&t.instance&&t!==e&&t.instance.add(e.instance)}else f(e)&&e.instance?.isObject3D&&f(r)&&r.instance?.isObject3D&&r.instance?.add?.(e.instance);if(e?.props?.attach&&f(t)&&t?.instance){e.type?.toLowerCase().endsWith("loader")&&e.props.src&&(e.props.attach||e.props.attachArray)?function(e,t){const n=e.instance;if(t.attached=t.attached||{},t.attachedArray=t.attachedArray||{},!e.props.attach)return;if("textureloader"===e.type?.toLowerCase()){const r=n.load(e.props.src);pe(e,t,e.props.attach,r)}else n.load(e.props.src,(n=>{pe(e,t,e.props.attach,n)}),null,(e=>{throw new Error(e)}))}(e,t):pe(e,t,e.props.attach)}e.props?.onAdded&&e.props.onAdded({instance:e.instance})}},nextSibling(e){const t=e.nextSibling;return t||null},parentNode(e){const t=e.parentNode;return t||null},patchProp(e,t,n,o){h(e)?"style"===t?Object.keys(o).forEach((t=>{e.domElement.style[t]=o[t]})):e.domElement.setAttribute(t,o):y(e)||t.startsWith("$")||function({node:e,key:t,value:n}){if((e=>["onClick","onContextMenu","onDoubleClick","onPointerUp","onPointerDown","onPointerOver","onPointerOut","onPointerEnter","onPointerLeave","onPointerMove","onWheel"].includes(e))(t))return b({node:e,key:t,value:n});const o=t.replace(/-/g,"."),a=ce[o]||o;if(ue.includes(t)||ue.includes(a))return e;if(!f(e))return e;if("string"==typeof n&&n.startsWith("$attached")){const t=n.replace("$attached.","");n=r.get(e.attached,t,null)}const s=e.instance;if(!s)return e;let i;for(let e=0;e<de.length&&!i;e++){const t=[de[e],a].filter(Boolean).join(".");i=i=r.get(s,t)}if(i&&r.isNumber(n)&&i.setScalar)i.setScalar(n);else if(i&&i.set){const e=Array.isArray(n)?n:[n];s[a].set(...e)}else"function"==typeof i?i.bind(e.instance)(...n):void 0!==r.get(s,a,void 0)?r.set(s,a,""===n||n):console.log(`No property ${a} found on ${s}`);const c=s?.texture?.type||s?.type;if("string"==typeof c){const e=c.toLowerCase();switch(!0){case e.includes("material"):s.needsUpdate=!0;break;case e.includes("camera")&&s.updateProjectionMatrix:s.updateProjectionMatrix()}}}({node:e,key:t,value:o})},remove:e=>{if(!e)return;const t=Object.keys(T),n=[];e.walk((e=>(n.push(e),!0))),n.forEach((e=>{const n=t.find((t=>T[t]?.uuid===e.uuid));if(n&&(T[n]=null),f(e)){e.instance?.removeFromParent?.();const t="scene"!==e.type&&e.instance?.dispose;t&&t.bind(e.instance)(),e.instance=null}e.drop();const r=s.findIndex((t=>t.uuid===e.uuid));-1!==r&&s.splice(r,1)}))},setElementText(){},setText(){}},he={dpr:t.ref(1),inputActive:g,mousePos:C},fe=t.computed((()=>D.value?.instance??null));const ye=t.computed((()=>W.value?.instance??null));const ve=t.computed((()=>I.value.instance));let ge=null,be=null;e.camera=fe,e.clearCustomRender=()=>{ge?ge.clearCustomRender():be=null},e.createApp=e=>{ge=t.createRenderer(me).createApp(e),Object.keys(p).forEach((e=>{ge?.component(e,p[e])}));const{mount:n}=ge;return ge.mount=(e,...t)=>{const r=N({domElement:"string"==typeof e?document.querySelector(e):e,isLunchboxRootNode:!0,name:"root",metaType:"rootMeta",type:"root",uuid:"LUNCHBOX_ROOT"});ge.rootNode=r;return n(r,...t)},ge.extend=e=>((({app:e,...t})=>{Object.keys(t).forEach((n=>{e.component(n,_(n)),u[n]=t[n]}))})({app:ge,...e}),ge),ge.setCustomRender=e=>{ge.customRender=e},be&&(ge.setCustomRender(be),be=null),ge.clearCustomRender=()=>{ge.customRender=null},ge},e.find=function(e){return e=t.isRef(e)?e.value:e,f(e)?e?.instance:m(e)?e?.$el?.instance:t.isVNode(e)?e.el?.instance:null},e.globals=he,e.offAfterRender=e=>{if(isFinite(e))oe.splice(e,1);else{const t=oe.findIndex((t=>t==e));oe.splice(t,1)}},e.offBeforeRender=e=>{if(isFinite(e))re.splice(e,1);else{const t=re.findIndex((t=>t==e));re.splice(t,1)}},e.onAfterRender=(e,t=1/0)=>{t===1/0?oe.push(e):oe.splice(t,0,e)},e.onBeforeRender=se,e.onStart=(e,t=1/0)=>{t===1/0?te.push(e):te.splice(t,0,e)},e.renderer=ye,e.scene=ve,e.setCustomRender=e=>{ge?ge.setCustomRender(e):be=e},e.useCamera=function(e){return t.watch(fe,(t=>{t&&e(t)}),{immediate:!0})},e.useRenderer=function(e){return t.watch(ye,(t=>{t&&e(t)}),{immediate:!0})},e.useScene=function(e){return t.watch(ve,(t=>{t&&e(t)}),{immediate:!0})},Object.defineProperty(e,"__esModule",{value:!0})}));
@@ -94,6 +94,7 @@ const LunchboxWrapper = {
94
94
  rendererProperties: Object,
95
95
  shadow: [Boolean, Object],
96
96
  transparent: Boolean,
97
+ zoom: Number,
97
98
  },
98
99
  setup(props, context) {
99
100
  const canvas = ref();
@@ -207,6 +208,10 @@ const LunchboxWrapper = {
207
208
  const source = (props.cameraLookAt || props.cameraLook);
208
209
  camera.instance.lookAt(...source);
209
210
  }
211
+ // zoom camera if needed
212
+ if (camera && props.zoom !== undefined) {
213
+ camera.instance.zoom = props.zoom;
214
+ }
210
215
  // SCENE
211
216
  // ====================
212
217
  scene = ensuredScene.value;
@@ -518,76 +523,6 @@ const isLunchboxRootNode = (node) => {
518
523
  return node.isLunchboxRootNode;
519
524
  };
520
525
 
521
- /** Create a new Lunchbox comment node. */
522
- function createCommentNode(options = {}) {
523
- const defaults = {
524
- text: options.text ?? '',
525
- };
526
- return new MiniDom.RendererCommentNode({
527
- ...defaults,
528
- ...options,
529
- metaType: 'commentMeta',
530
- });
531
- }
532
- /** Create a new DOM node. */
533
- function createDomNode(options = {}) {
534
- const domElement = document.createElement(options.type ?? '');
535
- const defaults = {
536
- domElement,
537
- };
538
- const node = new MiniDom.RendererDomNode({
539
- ...defaults,
540
- ...options,
541
- metaType: 'domMeta',
542
- });
543
- return node;
544
- }
545
- /** Create a new Lunchbox text node. */
546
- function createTextNode(options = {}) {
547
- const defaults = {
548
- text: options.text ?? '',
549
- };
550
- return new MiniDom.RendererTextNode({
551
- ...options,
552
- ...defaults,
553
- metaType: 'textMeta',
554
- });
555
- }
556
- /** Create a new Lunchbox standard node. */
557
- function createNode(options = {}, props = {}) {
558
- const defaults = {
559
- attached: options.attached ?? [],
560
- attachedArray: options.attachedArray ?? {},
561
- instance: options.instance ?? null,
562
- };
563
- const node = new MiniDom.RendererStandardNode({
564
- ...options,
565
- ...defaults,
566
- metaType: 'standardMeta',
567
- });
568
- if (node.type && !isLunchboxRootNode(node) && !node.instance) {
569
- // if (node.type.includes('Camera')) {
570
- // console.log(node.type, {
571
- // ...node.props,
572
- // ...props,
573
- // })
574
- // console.trace()
575
- // }
576
- node.instance = instantiateThreeObject({
577
- ...node,
578
- props: {
579
- ...node.props,
580
- ...props,
581
- },
582
- });
583
- }
584
- if (node.type === 'scene') {
585
- // manually set scene override
586
- ensuredScene.value = node;
587
- }
588
- return node;
589
- }
590
-
591
526
  const interactables = [];
592
527
  const addInteractable = (target) => {
593
528
  interactables.push(target);
@@ -819,7 +754,8 @@ function tryGetNodeWithInstanceType(pascalCaseTypes) {
819
754
  singleType.toLowerCase());
820
755
  // cancel if found example is marked !isDefault
821
756
  if (isMinidomNode(found) &&
822
- (!found.props['is-default'] || !found.props['isDefault'])) {
757
+ (found.props['is-default'] === false ||
758
+ !found.props['isDefault'] === false)) {
823
759
  return null;
824
760
  }
825
761
  // if we have one, save and return
@@ -912,17 +848,10 @@ const ensuredCamera = computed({
912
848
  overrides[pascalType] = val;
913
849
  },
914
850
  });
915
- // export const ensuredCamera = buildEnsured<THREE.Camera>(
916
- // ['PerspectiveCamera', 'OrthographicCamera'],
917
- // fallbackCameraUuid,
918
- // {
919
- // args: [45, 0.5625, 1, 1000],
920
- // }
921
- // )
922
851
  // ENSURE RENDERER
923
852
  // ====================
924
853
  const fallbackRendererUuid = 'FALLBACK_RENDERER';
925
- const v = buildEnsured(
854
+ const ensuredRenderer = buildEnsured(
926
855
  // TODO: ensure support for css/svg renderers
927
856
  ['WebGLRenderer'], //, 'CSS2DRenderer', 'CSS3DRenderer', 'SVGRenderer'],
928
857
  fallbackRendererUuid, {});
@@ -931,7 +860,7 @@ fallbackRendererUuid, {});
931
860
  const rendererReady = ref(false);
932
861
  const ensureRenderer = computed({
933
862
  get() {
934
- return (rendererReady.value ? v.value : null);
863
+ return (rendererReady.value ? ensuredRenderer.value : null);
935
864
  },
936
865
  set(val) {
937
866
  const t = val.type ?? '';
@@ -948,6 +877,80 @@ const autoRaycasterUuid = 'AUTO_RAYCASTER';
948
877
  // `unknown` is intentional here - we need to typecast the node since Raycaster isn't an Object3D
949
878
  const ensuredRaycaster = buildEnsured('Raycaster', autoRaycasterUuid, {}, (node) => setupAutoRaycaster(node));
950
879
 
880
+ /** Create a new Lunchbox comment node. */
881
+ function createCommentNode(options = {}) {
882
+ const defaults = {
883
+ text: options.text ?? '',
884
+ };
885
+ return new MiniDom.RendererCommentNode({
886
+ ...defaults,
887
+ ...options,
888
+ metaType: 'commentMeta',
889
+ });
890
+ }
891
+ /** Create a new DOM node. */
892
+ function createDomNode(options = {}) {
893
+ const domElement = document.createElement(options.type ?? '');
894
+ const defaults = {
895
+ domElement,
896
+ };
897
+ const node = new MiniDom.RendererDomNode({
898
+ ...defaults,
899
+ ...options,
900
+ metaType: 'domMeta',
901
+ });
902
+ return node;
903
+ }
904
+ /** Create a new Lunchbox text node. */
905
+ function createTextNode(options = {}) {
906
+ const defaults = {
907
+ text: options.text ?? '',
908
+ };
909
+ return new MiniDom.RendererTextNode({
910
+ ...options,
911
+ ...defaults,
912
+ metaType: 'textMeta',
913
+ });
914
+ }
915
+ /** Create a new Lunchbox standard node. */
916
+ function createNode(options = {}, props = {}) {
917
+ const defaults = {
918
+ attached: options.attached ?? [],
919
+ attachedArray: options.attachedArray ?? {},
920
+ instance: options.instance ?? null,
921
+ };
922
+ const node = new MiniDom.RendererStandardNode({
923
+ ...options,
924
+ ...defaults,
925
+ metaType: 'standardMeta',
926
+ });
927
+ if (node.type && !isLunchboxRootNode(node) && !node.instance) {
928
+ // if (node.type.includes('Camera')) {
929
+ // console.log(node.type, {
930
+ // ...node.props,
931
+ // ...props,
932
+ // })
933
+ // console.trace()
934
+ // }
935
+ node.instance = instantiateThreeObject({
936
+ ...node,
937
+ props: {
938
+ ...node.props,
939
+ ...props,
940
+ },
941
+ });
942
+ }
943
+ // TODO: these manual overrides are a bit brittle - replace?
944
+ if (node.type?.toLowerCase() === 'scene') {
945
+ // manually set scene override
946
+ ensuredScene.value = node;
947
+ }
948
+ else if (node.type?.toLowerCase().endsWith('camera')) {
949
+ ensuredCamera.value = node;
950
+ }
951
+ return node;
952
+ }
953
+
951
954
  const createComponent = (tag) => defineComponent({
952
955
  inheritAttrs: false,
953
956
  name: tag,
@@ -1704,46 +1707,31 @@ const globals = {
1704
1707
  /** The current camera. Often easier to use `useCamera` instead of this. */
1705
1708
  const camera = computed(() => ensuredCamera.value?.instance ?? null);
1706
1709
  /** Run a function using the current camera when it's present. */
1707
- function useCamera(callback, once = true) {
1708
- let destroy;
1709
- destroy = watch(camera, (newVal) => {
1710
+ function useCamera(callback) {
1711
+ return watch(camera, (newVal) => {
1710
1712
  if (!newVal)
1711
1713
  return;
1712
- // TODO: better fix than `any`?
1713
1714
  callback(newVal);
1714
- if (once) {
1715
- destroy?.();
1716
- }
1717
1715
  }, { immediate: true });
1718
1716
  }
1719
1717
  /** The current renderer. Often easier to use `useRenderer` instead of this. */
1720
1718
  const renderer = computed(() => ensureRenderer.value?.instance ?? null);
1721
1719
  /** Run a function using the current renderer when it's present. */
1722
- function useRenderer(callback, once = true) {
1723
- let destroy;
1724
- destroy = watch(renderer, (newVal) => {
1720
+ function useRenderer(callback) {
1721
+ return watch(renderer, (newVal) => {
1725
1722
  if (!newVal)
1726
1723
  return;
1727
- // TODO: better fix than `any`?
1728
1724
  callback(newVal);
1729
- if (once) {
1730
- destroy?.();
1731
- }
1732
1725
  }, { immediate: true });
1733
1726
  }
1734
1727
  /** The current scene. Often easier to use `useScene` instead of this. */
1735
1728
  const scene = computed(() => ensuredScene.value.instance);
1736
1729
  /** Run a function using the current scene when it's present. */
1737
- function useScene(callback, once = true) {
1738
- let destroy;
1739
- destroy = watch(scene, (newVal) => {
1730
+ function useScene(callback) {
1731
+ return watch(scene, (newVal) => {
1740
1732
  if (!newVal)
1741
1733
  return;
1742
- // TODO: better fix than `any`?
1743
1734
  callback(newVal);
1744
- if (once) {
1745
- destroy?.();
1746
- }
1747
1735
  }, { immediate: true });
1748
1736
  }
1749
1737
  // CUSTOM RENDER SUPPORT
@@ -1772,12 +1760,14 @@ const createApp = (root) => {
1772
1760
  app = createRenderer(nodeOps).createApp(root);
1773
1761
  // register all components
1774
1762
  Object.keys(components).forEach((key) => {
1775
- app.component(key, components[key]);
1763
+ app?.component(key, components[key]);
1776
1764
  });
1777
1765
  // update mount function to match Lunchbox.Node
1778
1766
  const { mount } = app;
1779
1767
  app.mount = (root, ...args) => {
1768
+ // find DOM element to use as app root
1780
1769
  const domElement = (typeof root === 'string' ? document.querySelector(root) : root);
1770
+ // create or find root node
1781
1771
  const rootNode = ensureRootNode({
1782
1772
  domElement,
1783
1773
  isLunchboxRootNode: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lunchboxjs",
3
- "version": "0.1.4011",
3
+ "version": "0.1.4014",
4
4
  "scripts": {
5
5
  "dev": "vite -c utils/vite.config.ts",
6
6
  "build": "vue-tsc --noEmit && vite build -c utils/vite.config.ts",
@@ -11,10 +11,10 @@
11
11
  "prepare": "npm run build:lib",
12
12
  "docs:dev": "vitepress dev docs",
13
13
  "docs:build": "vitepress build docs",
14
- "docs:serve": "vitepress serve docs"
14
+ "docs:serve": "vitepress serve docs",
15
+ "demo:create": "node utils/createExample"
15
16
  },
16
17
  "dependencies": {
17
- "lodash": "4.17.21",
18
18
  "uuid": "8.3.2",
19
19
  "vue": "^3.2.16"
20
20
  },
@@ -26,7 +26,11 @@
26
26
  "@types/uuid": "8.3.1",
27
27
  "@vitejs/plugin-vue": "^1.9.3",
28
28
  "chroma-js": "2.1.2",
29
+ "kolorist": "1.5.1",
30
+ "lodash": "4.17.21",
29
31
  "nice-color-palettes": "3.0.0",
32
+ "prompt": "1.3.0",
33
+ "prompts": "2.4.2",
30
34
  "rollup-plugin-delete": "2.0.0",
31
35
  "rollup-plugin-terser": "7.0.2",
32
36
  "typescript": "^4.4.3",
@@ -36,7 +40,7 @@
36
40
  "vue-tsc": "^0.3.0"
37
41
  },
38
42
  "peerDependencies": {
39
- "three": "^0.133.1"
43
+ "three": "0.137.5"
40
44
  },
41
45
  "files": [
42
46
  "dist",
package/src/.DS_Store ADDED
Binary file
@@ -56,6 +56,7 @@ export const LunchboxWrapper: ComponentOptions = {
56
56
  rendererProperties: Object,
57
57
  shadow: [Boolean, Object],
58
58
  transparent: Boolean,
59
+ zoom: Number,
59
60
  },
60
61
  setup(props: Lunch.WrapperProps, context) {
61
62
  const canvas = ref<MiniDom.RendererDomNode>()
@@ -184,6 +185,10 @@ export const LunchboxWrapper: ComponentOptions = {
184
185
  const source = (props.cameraLookAt || props.cameraLook)!
185
186
  camera.instance.lookAt(...source)
186
187
  }
188
+ // zoom camera if needed
189
+ if (camera && props.zoom !== undefined) {
190
+ ;(camera.instance as THREE.OrthographicCamera).zoom = props.zoom
191
+ }
187
192
 
188
193
  // SCENE
189
194
  // ====================
Binary file
@@ -1,6 +1,7 @@
1
1
  import { isLunchboxRootNode } from '../utils'
2
2
  import { instantiateThreeObject, MiniDom, ensuredScene } from '.'
3
3
  import { Lunch } from '..'
4
+ import { ensuredCamera } from './ensure'
4
5
 
5
6
  /** Create a new Lunchbox comment node. */
6
7
  export function createCommentNode(options: Partial<Lunch.CommentMeta> = {}) {
@@ -74,9 +75,12 @@ export function createNode<T extends object = THREE.Object3D>(
74
75
  })
75
76
  }
76
77
 
77
- if (node.type === 'scene') {
78
+ // TODO: these manual overrides are a bit brittle - replace?
79
+ if (node.type?.toLowerCase() === 'scene') {
78
80
  // manually set scene override
79
81
  ensuredScene.value = node as Lunch.Node<THREE.Scene>
82
+ } else if (node.type?.toLowerCase().endsWith('camera')) {
83
+ ensuredCamera.value = node as Lunch.Node<THREE.Camera>
80
84
  }
81
85
 
82
86
  return node
@@ -41,7 +41,8 @@ export function tryGetNodeWithInstanceType<T extends THREE.Object3D>(
41
41
  // cancel if found example is marked !isDefault
42
42
  if (
43
43
  isMinidomNode(found) &&
44
- (!found.props['is-default'] || !found.props['isDefault'])
44
+ (found.props['is-default'] === false ||
45
+ !found.props['isDefault'] === false)
45
46
  ) {
46
47
  return null
47
48
  }
@@ -157,18 +158,10 @@ export const ensuredCamera = computed<Lunch.Node<THREE.Camera> | null>({
157
158
  },
158
159
  })
159
160
 
160
- // export const ensuredCamera = buildEnsured<THREE.Camera>(
161
- // ['PerspectiveCamera', 'OrthographicCamera'],
162
- // fallbackCameraUuid,
163
- // {
164
- // args: [45, 0.5625, 1, 1000],
165
- // }
166
- // )
167
-
168
161
  // ENSURE RENDERER
169
162
  // ====================
170
163
  export const fallbackRendererUuid = 'FALLBACK_RENDERER'
171
- export const v = buildEnsured(
164
+ export const ensuredRenderer = buildEnsured(
172
165
  // TODO: ensure support for css/svg renderers
173
166
  ['WebGLRenderer'], //, 'CSS2DRenderer', 'CSS3DRenderer', 'SVGRenderer'],
174
167
  fallbackRendererUuid,
@@ -180,7 +173,9 @@ export const rendererReady = ref(false)
180
173
 
181
174
  export const ensureRenderer = computed<Lunch.Node<THREE.WebGLRenderer> | null>({
182
175
  get() {
183
- return (rendererReady.value ? (v.value as any) : (null as any)) as any
176
+ return (
177
+ rendererReady.value ? (ensuredRenderer.value as any) : (null as any)
178
+ ) as any
184
179
  },
185
180
  set(val: any) {
186
181
  const t = val.type ?? ''
package/src/index.ts CHANGED
@@ -1,11 +1,4 @@
1
- import {
2
- computed,
3
- createRenderer,
4
- Component,
5
- ref,
6
- watch,
7
- WatchStopHandle,
8
- } from 'vue'
1
+ import { computed, createRenderer, Component, ref, watch } from 'vue'
9
2
  import { nodeOps } from './nodeOps'
10
3
  import {
11
4
  // createdCamera,
@@ -47,20 +40,13 @@ export const globals = {
47
40
  export const camera = computed(() => ensuredCamera.value?.instance ?? null)
48
41
  /** Run a function using the current camera when it's present. */
49
42
  export function useCamera<T extends THREE.Camera = THREE.PerspectiveCamera>(
50
- callback: (cam: T) => void,
51
- once = true
43
+ callback: (cam: T) => void
52
44
  ) {
53
- let destroy: WatchStopHandle
54
- destroy = watch(
45
+ return watch(
55
46
  camera,
56
47
  (newVal) => {
57
48
  if (!newVal) return
58
-
59
- // TODO: better fix than `any`?
60
- callback(newVal as any)
61
- if (once) {
62
- destroy?.()
63
- }
49
+ callback(newVal as unknown as T)
64
50
  },
65
51
  { immediate: true }
66
52
  )
@@ -70,20 +56,13 @@ export function useCamera<T extends THREE.Camera = THREE.PerspectiveCamera>(
70
56
  export const renderer = computed(() => ensureRenderer.value?.instance ?? null)
71
57
  /** Run a function using the current renderer when it's present. */
72
58
  export function useRenderer<T extends THREE.Renderer = THREE.WebGLRenderer>(
73
- callback: (rend: T) => void,
74
- once = true
59
+ callback: (rend: T) => void
75
60
  ) {
76
- let destroy: WatchStopHandle
77
- destroy = watch(
61
+ return watch(
78
62
  renderer,
79
63
  (newVal) => {
80
64
  if (!newVal) return
81
-
82
- // TODO: better fix than `any`?
83
- callback(newVal as any)
84
- if (once) {
85
- destroy?.()
86
- }
65
+ callback(newVal as unknown as T)
87
66
  },
88
67
  { immediate: true }
89
68
  )
@@ -92,21 +71,12 @@ export function useRenderer<T extends THREE.Renderer = THREE.WebGLRenderer>(
92
71
  /** The current scene. Often easier to use `useScene` instead of this. */
93
72
  export const scene = computed(() => ensuredScene.value.instance)
94
73
  /** Run a function using the current scene when it's present. */
95
- export function useScene(
96
- callback: (newScene: THREE.Scene) => void,
97
- once = true
98
- ) {
99
- let destroy: WatchStopHandle
100
- destroy = watch(
74
+ export function useScene(callback: (newScene: THREE.Scene) => void) {
75
+ return watch(
101
76
  scene,
102
77
  (newVal) => {
103
78
  if (!newVal) return
104
-
105
- // TODO: better fix than `any`?
106
79
  callback(newVal as any)
107
- if (once) {
108
- destroy?.()
109
- }
110
80
  },
111
81
  { immediate: true }
112
82
  )
@@ -142,15 +112,17 @@ export const createApp = (root: Component) => {
142
112
 
143
113
  // register all components
144
114
  Object.keys(components).forEach((key) => {
145
- app!.component(key, (components as any)[key])
115
+ app?.component(key, (components as any)[key])
146
116
  })
147
117
 
148
118
  // update mount function to match Lunchbox.Node
149
119
  const { mount } = app
150
120
  app.mount = (root, ...args) => {
121
+ // find DOM element to use as app root
151
122
  const domElement = (
152
123
  typeof root === 'string' ? document.querySelector(root) : root
153
124
  ) as HTMLElement
125
+ // create or find root node
154
126
  const rootNode = ensureRootNode({
155
127
  domElement,
156
128
  isLunchboxRootNode: true,
@@ -5,7 +5,6 @@ import { overrides } from '../core'
5
5
  export const remove = (node: MiniDom.RendererBaseNode) => {
6
6
  if (!node) return
7
7
  const overrideKeys = Object.keys(overrides)
8
-
9
8
  // prep subtree
10
9
  const subtree: MiniDom.BaseNode[] = []
11
10
  node.walk((descendant) => {
package/src/types.ts CHANGED
@@ -150,5 +150,6 @@ export declare namespace Lunch {
150
150
  rendererProperties?: Partial<THREE.WebGLRenderer>
151
151
  shadow?: ShadowSugar
152
152
  transparent?: boolean
153
+ zoom?: number
153
154
  }
154
155
  }