lunchboxjs 0.1.4013 → 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/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
@@ -544,76 +544,6 @@
544
544
  return node.isLunchboxRootNode;
545
545
  };
546
546
 
547
- /** Create a new Lunchbox comment node. */
548
- function createCommentNode(options = {}) {
549
- const defaults = {
550
- text: options.text ?? '',
551
- };
552
- return new MiniDom.RendererCommentNode({
553
- ...defaults,
554
- ...options,
555
- metaType: 'commentMeta',
556
- });
557
- }
558
- /** Create a new DOM node. */
559
- function createDomNode(options = {}) {
560
- const domElement = document.createElement(options.type ?? '');
561
- const defaults = {
562
- domElement,
563
- };
564
- const node = new MiniDom.RendererDomNode({
565
- ...defaults,
566
- ...options,
567
- metaType: 'domMeta',
568
- });
569
- return node;
570
- }
571
- /** Create a new Lunchbox text node. */
572
- function createTextNode(options = {}) {
573
- const defaults = {
574
- text: options.text ?? '',
575
- };
576
- return new MiniDom.RendererTextNode({
577
- ...options,
578
- ...defaults,
579
- metaType: 'textMeta',
580
- });
581
- }
582
- /** Create a new Lunchbox standard node. */
583
- function createNode(options = {}, props = {}) {
584
- const defaults = {
585
- attached: options.attached ?? [],
586
- attachedArray: options.attachedArray ?? {},
587
- instance: options.instance ?? null,
588
- };
589
- const node = new MiniDom.RendererStandardNode({
590
- ...options,
591
- ...defaults,
592
- metaType: 'standardMeta',
593
- });
594
- if (node.type && !isLunchboxRootNode(node) && !node.instance) {
595
- // if (node.type.includes('Camera')) {
596
- // console.log(node.type, {
597
- // ...node.props,
598
- // ...props,
599
- // })
600
- // console.trace()
601
- // }
602
- node.instance = instantiateThreeObject({
603
- ...node,
604
- props: {
605
- ...node.props,
606
- ...props,
607
- },
608
- });
609
- }
610
- if (node.type === 'scene') {
611
- // manually set scene override
612
- ensuredScene.value = node;
613
- }
614
- return node;
615
- }
616
-
617
547
  const interactables = [];
618
548
  const addInteractable = (target) => {
619
549
  interactables.push(target);
@@ -939,17 +869,10 @@
939
869
  overrides[pascalType] = val;
940
870
  },
941
871
  });
942
- // export const ensuredCamera = buildEnsured<THREE.Camera>(
943
- // ['PerspectiveCamera', 'OrthographicCamera'],
944
- // fallbackCameraUuid,
945
- // {
946
- // args: [45, 0.5625, 1, 1000],
947
- // }
948
- // )
949
872
  // ENSURE RENDERER
950
873
  // ====================
951
874
  const fallbackRendererUuid = 'FALLBACK_RENDERER';
952
- const v = buildEnsured(
875
+ const ensuredRenderer = buildEnsured(
953
876
  // TODO: ensure support for css/svg renderers
954
877
  ['WebGLRenderer'], //, 'CSS2DRenderer', 'CSS3DRenderer', 'SVGRenderer'],
955
878
  fallbackRendererUuid, {});
@@ -958,7 +881,7 @@
958
881
  const rendererReady = vue.ref(false);
959
882
  const ensureRenderer = vue.computed({
960
883
  get() {
961
- return (rendererReady.value ? v.value : null);
884
+ return (rendererReady.value ? ensuredRenderer.value : null);
962
885
  },
963
886
  set(val) {
964
887
  const t = val.type ?? '';
@@ -975,6 +898,80 @@
975
898
  // `unknown` is intentional here - we need to typecast the node since Raycaster isn't an Object3D
976
899
  const ensuredRaycaster = buildEnsured('Raycaster', autoRaycasterUuid, {}, (node) => setupAutoRaycaster(node));
977
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
+
978
975
  const createComponent = (tag) => vue.defineComponent({
979
976
  inheritAttrs: false,
980
977
  name: tag,
@@ -1731,46 +1728,31 @@
1731
1728
  /** The current camera. Often easier to use `useCamera` instead of this. */
1732
1729
  const camera = vue.computed(() => ensuredCamera.value?.instance ?? null);
1733
1730
  /** Run a function using the current camera when it's present. */
1734
- function useCamera(callback, once = true) {
1735
- let destroy;
1736
- destroy = vue.watch(camera, (newVal) => {
1731
+ function useCamera(callback) {
1732
+ return vue.watch(camera, (newVal) => {
1737
1733
  if (!newVal)
1738
1734
  return;
1739
- // TODO: better fix than `any`?
1740
1735
  callback(newVal);
1741
- if (once) {
1742
- destroy?.();
1743
- }
1744
1736
  }, { immediate: true });
1745
1737
  }
1746
1738
  /** The current renderer. Often easier to use `useRenderer` instead of this. */
1747
1739
  const renderer = vue.computed(() => ensureRenderer.value?.instance ?? null);
1748
1740
  /** Run a function using the current renderer when it's present. */
1749
- function useRenderer(callback, once = true) {
1750
- let destroy;
1751
- destroy = vue.watch(renderer, (newVal) => {
1741
+ function useRenderer(callback) {
1742
+ return vue.watch(renderer, (newVal) => {
1752
1743
  if (!newVal)
1753
1744
  return;
1754
- // TODO: better fix than `any`?
1755
1745
  callback(newVal);
1756
- if (once) {
1757
- destroy?.();
1758
- }
1759
1746
  }, { immediate: true });
1760
1747
  }
1761
1748
  /** The current scene. Often easier to use `useScene` instead of this. */
1762
1749
  const scene = vue.computed(() => ensuredScene.value.instance);
1763
1750
  /** Run a function using the current scene when it's present. */
1764
- function useScene(callback, once = true) {
1765
- let destroy;
1766
- destroy = vue.watch(scene, (newVal) => {
1751
+ function useScene(callback) {
1752
+ return vue.watch(scene, (newVal) => {
1767
1753
  if (!newVal)
1768
1754
  return;
1769
- // TODO: better fix than `any`?
1770
1755
  callback(newVal);
1771
- if (once) {
1772
- destroy?.();
1773
- }
1774
1756
  }, { immediate: true });
1775
1757
  }
1776
1758
  // CUSTOM RENDER SUPPORT
@@ -1799,12 +1781,14 @@
1799
1781
  app = vue.createRenderer(nodeOps).createApp(root);
1800
1782
  // register all components
1801
1783
  Object.keys(components).forEach((key) => {
1802
- app.component(key, components[key]);
1784
+ app?.component(key, components[key]);
1803
1785
  });
1804
1786
  // update mount function to match Lunchbox.Node
1805
1787
  const { mount } = app;
1806
1788
  app.mount = (root, ...args) => {
1789
+ // find DOM element to use as app root
1807
1790
  const domElement = (typeof root === 'string' ? document.querySelector(root) : root);
1791
+ // create or find root node
1808
1792
  const rootNode = ensureRootNode({
1809
1793
  domElement,
1810
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=z.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,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=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(p&&void 0!==e.zoom&&(p.instance.zoom=e.zoom),m=z.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&&(z.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)&&(K.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=K.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})=>{N({element:e,eventKeys:["onPointerEnter"],intersection:t})})),a.forEach((({element:e,intersection:t})=>{N({element:e,eventKeys:["onPointerOver","onPointerMove"],intersection:t})})),s.forEach((({element:e,intersection:t})=>{N({element:e,eventKeys:["onPointerLeave","onPointerOut"],intersection:t})})),P=[].concat(o,a)},N=({element:e,eventKeys:t,intersection:n})=>{e&&t.forEach((t=>{e.eventListeners[t]&&e.eventListeners[t].forEach((e=>{e({intersection:n})}))}))};function B(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||!1!==e.props["is-default"]&&!1!=!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=B(),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}}),z=j("Scene","FALLBACK_SCENE"),K=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))),_=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:I.value?.instance,scene:z.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??B();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=z.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((()=>z.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=B({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,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})}));
@@ -523,76 +523,6 @@ const isLunchboxRootNode = (node) => {
523
523
  return node.isLunchboxRootNode;
524
524
  };
525
525
 
526
- /** Create a new Lunchbox comment node. */
527
- function createCommentNode(options = {}) {
528
- const defaults = {
529
- text: options.text ?? '',
530
- };
531
- return new MiniDom.RendererCommentNode({
532
- ...defaults,
533
- ...options,
534
- metaType: 'commentMeta',
535
- });
536
- }
537
- /** Create a new DOM node. */
538
- function createDomNode(options = {}) {
539
- const domElement = document.createElement(options.type ?? '');
540
- const defaults = {
541
- domElement,
542
- };
543
- const node = new MiniDom.RendererDomNode({
544
- ...defaults,
545
- ...options,
546
- metaType: 'domMeta',
547
- });
548
- return node;
549
- }
550
- /** Create a new Lunchbox text node. */
551
- function createTextNode(options = {}) {
552
- const defaults = {
553
- text: options.text ?? '',
554
- };
555
- return new MiniDom.RendererTextNode({
556
- ...options,
557
- ...defaults,
558
- metaType: 'textMeta',
559
- });
560
- }
561
- /** Create a new Lunchbox standard node. */
562
- function createNode(options = {}, props = {}) {
563
- const defaults = {
564
- attached: options.attached ?? [],
565
- attachedArray: options.attachedArray ?? {},
566
- instance: options.instance ?? null,
567
- };
568
- const node = new MiniDom.RendererStandardNode({
569
- ...options,
570
- ...defaults,
571
- metaType: 'standardMeta',
572
- });
573
- if (node.type && !isLunchboxRootNode(node) && !node.instance) {
574
- // if (node.type.includes('Camera')) {
575
- // console.log(node.type, {
576
- // ...node.props,
577
- // ...props,
578
- // })
579
- // console.trace()
580
- // }
581
- node.instance = instantiateThreeObject({
582
- ...node,
583
- props: {
584
- ...node.props,
585
- ...props,
586
- },
587
- });
588
- }
589
- if (node.type === 'scene') {
590
- // manually set scene override
591
- ensuredScene.value = node;
592
- }
593
- return node;
594
- }
595
-
596
526
  const interactables = [];
597
527
  const addInteractable = (target) => {
598
528
  interactables.push(target);
@@ -918,17 +848,10 @@ const ensuredCamera = computed({
918
848
  overrides[pascalType] = val;
919
849
  },
920
850
  });
921
- // export const ensuredCamera = buildEnsured<THREE.Camera>(
922
- // ['PerspectiveCamera', 'OrthographicCamera'],
923
- // fallbackCameraUuid,
924
- // {
925
- // args: [45, 0.5625, 1, 1000],
926
- // }
927
- // )
928
851
  // ENSURE RENDERER
929
852
  // ====================
930
853
  const fallbackRendererUuid = 'FALLBACK_RENDERER';
931
- const v = buildEnsured(
854
+ const ensuredRenderer = buildEnsured(
932
855
  // TODO: ensure support for css/svg renderers
933
856
  ['WebGLRenderer'], //, 'CSS2DRenderer', 'CSS3DRenderer', 'SVGRenderer'],
934
857
  fallbackRendererUuid, {});
@@ -937,7 +860,7 @@ fallbackRendererUuid, {});
937
860
  const rendererReady = ref(false);
938
861
  const ensureRenderer = computed({
939
862
  get() {
940
- return (rendererReady.value ? v.value : null);
863
+ return (rendererReady.value ? ensuredRenderer.value : null);
941
864
  },
942
865
  set(val) {
943
866
  const t = val.type ?? '';
@@ -954,6 +877,80 @@ const autoRaycasterUuid = 'AUTO_RAYCASTER';
954
877
  // `unknown` is intentional here - we need to typecast the node since Raycaster isn't an Object3D
955
878
  const ensuredRaycaster = buildEnsured('Raycaster', autoRaycasterUuid, {}, (node) => setupAutoRaycaster(node));
956
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
+
957
954
  const createComponent = (tag) => defineComponent({
958
955
  inheritAttrs: false,
959
956
  name: tag,
@@ -1710,46 +1707,31 @@ const globals = {
1710
1707
  /** The current camera. Often easier to use `useCamera` instead of this. */
1711
1708
  const camera = computed(() => ensuredCamera.value?.instance ?? null);
1712
1709
  /** Run a function using the current camera when it's present. */
1713
- function useCamera(callback, once = true) {
1714
- let destroy;
1715
- destroy = watch(camera, (newVal) => {
1710
+ function useCamera(callback) {
1711
+ return watch(camera, (newVal) => {
1716
1712
  if (!newVal)
1717
1713
  return;
1718
- // TODO: better fix than `any`?
1719
1714
  callback(newVal);
1720
- if (once) {
1721
- destroy?.();
1722
- }
1723
1715
  }, { immediate: true });
1724
1716
  }
1725
1717
  /** The current renderer. Often easier to use `useRenderer` instead of this. */
1726
1718
  const renderer = computed(() => ensureRenderer.value?.instance ?? null);
1727
1719
  /** Run a function using the current renderer when it's present. */
1728
- function useRenderer(callback, once = true) {
1729
- let destroy;
1730
- destroy = watch(renderer, (newVal) => {
1720
+ function useRenderer(callback) {
1721
+ return watch(renderer, (newVal) => {
1731
1722
  if (!newVal)
1732
1723
  return;
1733
- // TODO: better fix than `any`?
1734
1724
  callback(newVal);
1735
- if (once) {
1736
- destroy?.();
1737
- }
1738
1725
  }, { immediate: true });
1739
1726
  }
1740
1727
  /** The current scene. Often easier to use `useScene` instead of this. */
1741
1728
  const scene = computed(() => ensuredScene.value.instance);
1742
1729
  /** Run a function using the current scene when it's present. */
1743
- function useScene(callback, once = true) {
1744
- let destroy;
1745
- destroy = watch(scene, (newVal) => {
1730
+ function useScene(callback) {
1731
+ return watch(scene, (newVal) => {
1746
1732
  if (!newVal)
1747
1733
  return;
1748
- // TODO: better fix than `any`?
1749
1734
  callback(newVal);
1750
- if (once) {
1751
- destroy?.();
1752
- }
1753
1735
  }, { immediate: true });
1754
1736
  }
1755
1737
  // CUSTOM RENDER SUPPORT
@@ -1778,12 +1760,14 @@ const createApp = (root) => {
1778
1760
  app = createRenderer(nodeOps).createApp(root);
1779
1761
  // register all components
1780
1762
  Object.keys(components).forEach((key) => {
1781
- app.component(key, components[key]);
1763
+ app?.component(key, components[key]);
1782
1764
  });
1783
1765
  // update mount function to match Lunchbox.Node
1784
1766
  const { mount } = app;
1785
1767
  app.mount = (root, ...args) => {
1768
+ // find DOM element to use as app root
1786
1769
  const domElement = (typeof root === 'string' ? document.querySelector(root) : root);
1770
+ // create or find root node
1787
1771
  const rootNode = ensureRootNode({
1788
1772
  domElement,
1789
1773
  isLunchboxRootNode: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lunchboxjs",
3
- "version": "0.1.4013",
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",
package/src/.DS_Store ADDED
Binary file
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
@@ -158,18 +158,10 @@ export const ensuredCamera = computed<Lunch.Node<THREE.Camera> | null>({
158
158
  },
159
159
  })
160
160
 
161
- // export const ensuredCamera = buildEnsured<THREE.Camera>(
162
- // ['PerspectiveCamera', 'OrthographicCamera'],
163
- // fallbackCameraUuid,
164
- // {
165
- // args: [45, 0.5625, 1, 1000],
166
- // }
167
- // )
168
-
169
161
  // ENSURE RENDERER
170
162
  // ====================
171
163
  export const fallbackRendererUuid = 'FALLBACK_RENDERER'
172
- export const v = buildEnsured(
164
+ export const ensuredRenderer = buildEnsured(
173
165
  // TODO: ensure support for css/svg renderers
174
166
  ['WebGLRenderer'], //, 'CSS2DRenderer', 'CSS3DRenderer', 'SVGRenderer'],
175
167
  fallbackRendererUuid,
@@ -181,7 +173,9 @@ export const rendererReady = ref(false)
181
173
 
182
174
  export const ensureRenderer = computed<Lunch.Node<THREE.WebGLRenderer> | null>({
183
175
  get() {
184
- 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
185
179
  },
186
180
  set(val: any) {
187
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) => {