lunchboxjs 0.1.4003 → 0.1.4004

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.
@@ -30,27 +30,36 @@
30
30
  const resizeCanvas = (width, height) => {
31
31
  const renderer = ensureRenderer.value?.instance;
32
32
  const scene = ensuredScene.value.instance;
33
+ const camera = ensuredCamera.value;
33
34
  // ignore if no element
34
- if (!renderer?.domElement || !scene)
35
+ if (!renderer?.domElement || !scene || !camera)
35
36
  return;
36
37
  width = width ?? window.innerWidth;
37
38
  height = height ?? window.innerHeight;
38
39
  // update camera
39
40
  const aspect = width / height;
40
- const camera = ensuredCamera.value;
41
41
  if (camera.type?.toLowerCase() === 'perspectivecamera') {
42
42
  const perspectiveCamera = camera.instance;
43
43
  perspectiveCamera.aspect = aspect;
44
44
  perspectiveCamera.updateProjectionMatrix();
45
45
  }
46
+ else if (camera.type?.toLowerCase() === 'orthographiccamera') {
47
+ // console.log('TODO: ortho camera update')
48
+ const orthoCamera = camera.instance;
49
+ const heightInTermsOfWidth = height / width;
50
+ orthoCamera.top = heightInTermsOfWidth * 10;
51
+ orthoCamera.bottom = -heightInTermsOfWidth * 10;
52
+ orthoCamera.right = 10;
53
+ orthoCamera.left = -10;
54
+ orthoCamera.updateProjectionMatrix();
55
+ }
46
56
  else {
47
- console.log('TODO: ortho camera update');
57
+ console.log('TODO: non-ortho or perspective camera');
48
58
  }
49
59
  // update canvas
50
60
  renderer.setSize(width, height);
51
61
  // render immediately so there's no flicker
52
62
  if (scene && camera.instance) {
53
- // const cameraInstance = scene.traverse(v => )
54
63
  renderer.render(vue.toRaw(scene), vue.toRaw(camera.instance));
55
64
  }
56
65
  };
@@ -95,8 +104,11 @@
95
104
  props: {
96
105
  // These should match the Lunchbox.WrapperProps interface
97
106
  background: String,
107
+ cameraArgs: Array,
98
108
  cameraPosition: Array,
99
109
  dpr: Number,
110
+ ortho: Boolean,
111
+ orthographic: Boolean,
100
112
  rendererProperties: Object,
101
113
  shadow: [Boolean, Object],
102
114
  transparent: Boolean,
@@ -107,6 +119,7 @@
107
119
  const dpr = vue.ref(props.dpr ?? -1);
108
120
  const container = vue.ref();
109
121
  let renderer;
122
+ let camera;
110
123
  let scene;
111
124
  // MOUNT
112
125
  // ====================
@@ -114,12 +127,6 @@
114
127
  // canvas needs to exist
115
128
  if (!canvas.value)
116
129
  throw new Error('missing canvas');
117
- // ensure camera
118
- const camera = ensuredCamera.value.instance;
119
- // move camera if needed
120
- if (camera && props.cameraPosition) {
121
- camera.position.set(...props.cameraPosition);
122
- }
123
130
  // RENDERER
124
131
  // ====================
125
132
  // is there already a renderer?
@@ -176,6 +183,43 @@
176
183
  rendererReady.value = true;
177
184
  return;
178
185
  }
186
+ // CAMERA
187
+ // ====================
188
+ // is there already a camera?
189
+ camera = tryGetNodeWithInstanceType([
190
+ 'PerspectiveCamera',
191
+ 'OrthographicCamera',
192
+ ]);
193
+ // if not, let's create one
194
+ if (!camera) {
195
+ // create ortho camera
196
+ if (props.ortho || props.orthographic) {
197
+ // const size: Vector2 = new Vector2()
198
+ ensuredCamera.value = createNode({
199
+ props: { args: props.cameraArgs ?? [] },
200
+ type: 'OrthographicCamera',
201
+ uuid: fallbackCameraUuid,
202
+ });
203
+ }
204
+ else {
205
+ ensuredCamera.value = createNode({
206
+ props: {
207
+ args: props.cameraArgs ?? [45, 0.5625, 1, 1000],
208
+ },
209
+ type: 'PerspectiveCamera',
210
+ uuid: fallbackCameraUuid,
211
+ });
212
+ }
213
+ cameraReady.value = true;
214
+ camera = ensuredCamera.value;
215
+ }
216
+ else {
217
+ cameraReady.value = true;
218
+ }
219
+ // move camera if needed
220
+ if (camera && props.cameraPosition) {
221
+ camera.instance?.position.set(...props.cameraPosition);
222
+ }
179
223
  // SCENE
180
224
  // ====================
181
225
  scene = ensuredScene.value;
@@ -202,7 +246,7 @@
202
246
  // console.log(scene)
203
247
  update({
204
248
  app: vue.getCurrentInstance().appContext.app,
205
- camera,
249
+ camera: camera.instance,
206
250
  renderer: renderer.instance,
207
251
  scene: scene.instance,
208
252
  });
@@ -849,9 +893,27 @@
849
893
  // ENSURE CAMERA
850
894
  // ====================
851
895
  const fallbackCameraUuid = 'FALLBACK_CAMERA';
852
- const ensuredCamera = buildEnsured(['PerspectiveCamera', 'OrthographicCamera'], fallbackCameraUuid, {
853
- args: [45, 0.5625, 1, 1000],
896
+ const defaultCamera = buildEnsured(['PerspectiveCamera', 'OrthographicCamera'], fallbackCameraUuid, { args: [45, 0.5625, 1, 1000] });
897
+ /** Special value to be changed ONLY in `LunchboxWrapper`.
898
+ * Functions waiting for a Camera need to wait for this to be true. */
899
+ const cameraReady = vue.ref(false);
900
+ const ensuredCamera = vue.computed({
901
+ get() {
902
+ return (cameraReady.value ? defaultCamera.value : null);
903
+ },
904
+ set(val) {
905
+ const t = val.type ?? '';
906
+ const pascalType = t[0].toUpperCase() + t.slice(1);
907
+ overrides[pascalType] = val;
908
+ },
854
909
  });
910
+ // export const ensuredCamera = buildEnsured<THREE.Camera>(
911
+ // ['PerspectiveCamera', 'OrthographicCamera'],
912
+ // fallbackCameraUuid,
913
+ // {
914
+ // args: [45, 0.5625, 1, 1000],
915
+ // }
916
+ // )
855
917
  // ENSURE RENDERER
856
918
  // ====================
857
919
  const fallbackRendererUuid = 'FALLBACK_RENDERER';
@@ -1205,7 +1267,7 @@
1205
1267
  app: opts.app,
1206
1268
  renderer: ensureRenderer.value?.instance,
1207
1269
  scene: ensuredScene.value.instance,
1208
- camera: ensuredCamera.value.instance,
1270
+ camera: ensuredCamera.value?.instance,
1209
1271
  }));
1210
1272
  // prep options
1211
1273
  const { app, renderer, scene, camera } = opts;
@@ -1600,7 +1662,7 @@
1600
1662
  inputActive,
1601
1663
  mousePos,
1602
1664
  };
1603
- const camera = vue.computed(() => ensuredCamera.value.instance);
1665
+ const camera = vue.computed(() => ensuredCamera.value?.instance ?? null);
1604
1666
  const renderer = vue.computed(() => ensureRenderer.value?.instance ?? null);
1605
1667
  const scene = vue.computed(() => ensuredScene.value.instance);
1606
1668
  // CUSTOM RENDER SUPPORT
@@ -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=$.value?.instance,o=U.value.instance;if(!r?.domElement||!o)return;const a=(e=e??window.innerWidth)/(n=n??window.innerHeight),s=k.value;if("perspectivecamera"===s.type?.toLowerCase()){const e=s.instance;e.aspect=a,e.updateProjectionMatrix()}else console.log("TODO: ortho camera update");r.setSize(e,n),o&&s.instance&&r.render(t.toRaw(o),t.toRaw(s.instance))},c=e=>({position:e,top:0,right:0,bottom:0,left:0,width:"100%",height:"100%"}),d={name:"Lunchbox",props:{background:String,cameraPosition:Array,dpr:Number,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;return t.onMounted((()=>{if(!a.value)throw new Error("missing canvas");const o=k.value.instance;if(o&&e.cameraPosition&&o.position.set(...e.cameraPosition),l=B(["WebGLRenderer"]),l)return s.value=!1,void(F.value=!0);{const t={antialias:!0,canvas:a.value.domElement};e.transparent&&(t.alpha=!0),$.value=v({type:"WebGLRenderer",uuid:S,props:{args:[t]}}),F.value=!0;const n=$,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=U.value,p&&p.instance&&e.background&&(p.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),ue.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),te({app:t.getCurrentInstance().appContext.app,camera:o,renderer:l.instance,scene:p.instance})})),t.onBeforeUnmount((()=>{re()})),()=>[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 H.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&&(U.value=r),r}const b=[],x=t.ref(!1);function g({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)&&(W.value,e.instance&&!b.includes(e)&&(o=e,b.push(o),e.eventListenerRemoveFunctions[n].push((()=>(e=>{const t=b.indexOf(e);-1!==t&&b.splice(t,1)})(e))))),"onClick"===n||"onPointerDown"===n||"onPointerUp"===n){const r=t.watch((()=>x.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,E,A;const C=t.ref({x:1/0,y:1/0});let L=!1;let P=[];const M=()=>{const e=W.value?.instance,t=k.value?.instance;if(!e||!t)return;e.setFromCamera(ue.mousePos.value,t);const n=e.intersectObjects(b.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=b.find((t=>t.instance?.uuid===e.object.uuid));t&&o.push({element:t,intersection:e})}else{const t=b.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:b.find((t=>t.instance?.uuid===e.object.uuid)),intersection:e})));o.forEach((({element:e,intersection:t})=>{G({element:e,eventKeys:["onPointerEnter"],intersection:t})})),a.forEach((({element:e,intersection:t})=>{G({element:e,eventKeys:["onPointerOver","onPointerMove"],intersection:t})})),s.forEach((({element:e,intersection:t})=>{G({element:e,eventKeys:["onPointerLeave","onPointerOut"],intersection:t})})),P=[].concat(o,a)},G=({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 H.RendererRootNode(t)),e.lunchboxTree}function B(e){Array.isArray(e)||(e=[e]);for(let t of e)if(O[t])return O[t];for(let t of e){const e=T[t]||s.find((e=>e.type?.toLowerCase()===t.toLowerCase()));if(e){const n=e;return T[t]=n,n}}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=B(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=j(["PerspectiveCamera","OrthographicCamera"],"FALLBACK_CAMERA",{args:[45,.5625,1,1e3]}),S="FALLBACK_RENDERER",D=j(["WebGLRenderer"],S,{}),F=t.ref(!1),$=t.computed({get:()=>F.value?D.value:null,set(e){const t=e.type??"",n=t[0].toUpperCase()+t.slice(1);O[n]=e}}),U=j("Scene","FALLBACK_SCENE"),W=j("Raycaster","AUTO_RAYCASTER",{},(e=>(e=>{if(!e.instance)return;let n=null;n=t.watch((()=>$.value),(e=>{e?.instance&&(L||(w=t=>{const n=(e.instance.domElement.width??1)/ue.dpr.value,r=(e.instance.domElement.height??1)/ue.dpr.value;C.value.x=t.offsetX/n*2-1,C.value.y=-t.offsetY/r*2+1},E=()=>x.value=!0,A=()=>x.value=!1,e.instance.domElement.addEventListener("mousemove",w),e.instance.domElement.addEventListener("mousedown",E),e.instance.domElement.addEventListener("mouseup",A),ne(M),L=!0),n&&n())}),{immediate:!0})})(e))),I=e=>t.defineComponent({inheritAttrs:!1,name:e,render(){return t.h(e,this.$attrs,this.$slots?.default?.()||[])}});var K,_=new Uint8Array(16);function V(){if(!K&&!(K="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 K(_)}var q=/^(?:[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 z(e){return"string"==typeof e&&q.test(e)}for(var H,Y=[],X=0;X<256;++X)Y.push((X+256).toString(16).substr(1));function J(e,t,n){var r=(e=e||{}).random||(e.rng||V)();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=(Y[e[t+0]]+Y[e[t+1]]+Y[e[t+2]]+Y[e[t+3]]+"-"+Y[e[t+4]]+Y[e[t+5]]+"-"+Y[e[t+6]]+Y[e[t+7]]+"-"+Y[e[t+8]]+Y[e[t+9]]+"-"+Y[e[t+10]]+Y[e[t+11]]+Y[e[t+12]]+Y[e[t+13]]+Y[e[t+14]]+Y[e[t+15]]).toLowerCase();if(!z(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??J(),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}(H||(H={}));let Q;(new H.RendererRootNode).minidomType="RootNode";const Z=[],ee=[],te=e=>{Q=requestAnimationFrame((()=>te({app:e.app,renderer:$.value?.instance,scene:U.value.instance,camera:k.value.instance})));const{app:n,renderer:r,scene:o,camera:a}=e;Z.forEach((t=>{t&&t(e)})),r&&o&&a&&(n.customRender?n.customRender(e):r.render(t.toRaw(o),t.toRaw(a))),ee.forEach((t=>{t&&t(e)}))},ne=(e,t=1/0)=>{t===1/0?Z.push(e):Z.splice(t,0,e)},re=()=>{Q&&cancelAnimationFrame(Q)};const oe={x:"position.x",y:"position.y",z:"position.z"},ae=["","parameters"],se=["args","attach","attachArray","key","onAdded","ref","src"],ie=["geometry","material"];function ce(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 de={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 H.RendererDomNode({...t,...e,metaType:"domMeta"})}(o);return e}const a=v(o);return ie.forEach((t=>{e.toLowerCase().endsWith(t)&&(a.props.attach=t)})),a},createText:e=>function(e={}){const t={text:e.text??""};return new H.RendererTextNode({...e,...t,metaType:"textMeta"})}({text:e}),createComment:e=>function(e={}){const t={text:e.text??""};return new H.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=U.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);ce(e,t,e.props.attach,r)}else n.load(e.props.src,(n=>{ce(e,t,e.props.attach,n)}),null,(e=>{throw new Error(e)}))}(e,t):ce(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 g({node:e,key:t,value:n});if(se.includes(t))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 o=e.instance;if(!o)return e;const a=t.replace(/-/g,".");let s,i=oe[a]||a;for(let e=0;e<ae.length&&!s;e++){const t=[ae[e],i].filter(Boolean).join(".");s=s=r.get(o,t)}if(s&&r.isNumber(n)&&s.setScalar)s.setScalar(n);else if(s&&s.set){const e=Array.isArray(n)?n:[n];o[i].set(...e)}else"function"==typeof s?s.bind(e.instance)(...n):void 0!==r.get(o,i,void 0)?r.set(o,i,""===n||n):console.log(`No property ${i} found on ${o}`);const c=o?.texture?.type||o?.type;if("string"==typeof c){const e=c.toLowerCase();switch(!0){case e.includes("material"):o.needsUpdate=!0;break;case e.includes("camera")&&o.updateProjectionMatrix:o.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(){}},ue={dpr:t.ref(1),inputActive:x,mousePos:C},le=t.computed((()=>k.value.instance)),pe=t.computed((()=>$.value?.instance??null)),me=t.computed((()=>U.value.instance));let he=null,fe=null;e.camera=le,e.clearCustomRender=()=>{he?he.clearCustomRender():fe=null},e.createApp=e=>{he=t.createRenderer(de).createApp(e),Object.keys(p).forEach((e=>{he.component(e,p[e])}));const{mount:n}=he;return he.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"});he.rootNode=r;return n(r,...t)},he.extend=e=>((({app:e,...t})=>{Object.keys(t).forEach((n=>{e.component(n,I(n)),u[n]=t[n]}))})({app:he,...e}),he),he.setCustomRender=e=>{he.customRender=e},fe&&(he.setCustomRender(fe),fe=null),he.clearCustomRender=()=>{he.customRender=null},he},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=ue,e.onAfterRender=(e,t=1/0)=>{t===1/0?ee.push(e):ee.splice(t,0,e)},e.onBeforeRender=ne,e.renderer=pe,e.scene=me,e.setCustomRender=e=>{he?he.setCustomRender(e):fe=e},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=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,cameraPosition:Array,dpr:Number,ortho:Boolean,orthographic:Boolean,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=N(["WebGLRenderer"]),l)return s.value=!1,void(W.value=!0);{const t={antialias:!0,canvas:a.value.domElement};e.transparent&&(t.alpha=!0),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=N(["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&&e.cameraPosition&&p.instance?.position.set(...e.cameraPosition),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),me.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),oe({app:t.getCurrentInstance().appContext.app,camera:p.instance,renderer:l.instance,scene:m.instance})})),t.onBeforeUnmount((()=>{se()})),()=>[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,C,E;const A=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(me.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 G(t={}){return e.lunchboxTree||(e.lunchboxTree=new J.RendererRootNode(t)),e.lunchboxTree}function N(e){Array.isArray(e)||(e=[e]);for(let t of e)if(O[t])return O[t];for(let t of e){const e=T[t]||s.find((e=>e.type?.toLowerCase()===t.toLowerCase()));if(e){const n=e;return T[t]=n,n}}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=N(e);if(t)return t;const a=G(),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)/me.dpr.value,r=(e.instance.domElement.height??1)/me.dpr.value;A.value.x=t.offsetX/n*2-1,A.value.y=-t.offsetY/r*2+1},C=()=>b.value=!0,E=()=>b.value=!1,e.instance.domElement.addEventListener("mousemove",w),e.instance.domElement.addEventListener("mousedown",C),e.instance.domElement.addEventListener("mouseup",E),ae(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={}));let te;(new J.RendererRootNode).minidomType="RootNode";const ne=[],re=[],oe=e=>{te=requestAnimationFrame((()=>oe({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;ne.forEach((t=>{t&&t(e)})),r&&o&&a&&(n.customRender?n.customRender(e):r.render(t.toRaw(o),t.toRaw(a))),re.forEach((t=>{t&&t(e)}))},ae=(e,t=1/0)=>{t===1/0?ne.push(e):ne.splice(t,0,e)},se=()=>{te&&cancelAnimationFrame(te)};const ie={x:"position.x",y:"position.y",z:"position.z"},ce=["","parameters"],de=["args","attach","attachArray","key","onAdded","ref","src"],ue=["geometry","material"];function le(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 pe={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 ue.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??G();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);le(e,t,e.props.attach,r)}else n.load(e.props.src,(n=>{le(e,t,e.props.attach,n)}),null,(e=>{throw new Error(e)}))}(e,t):le(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});if(de.includes(t))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 o=e.instance;if(!o)return e;const a=t.replace(/-/g,".");let s,i=ie[a]||a;for(let e=0;e<ce.length&&!s;e++){const t=[ce[e],i].filter(Boolean).join(".");s=s=r.get(o,t)}if(s&&r.isNumber(n)&&s.setScalar)s.setScalar(n);else if(s&&s.set){const e=Array.isArray(n)?n:[n];o[i].set(...e)}else"function"==typeof s?s.bind(e.instance)(...n):void 0!==r.get(o,i,void 0)?r.set(o,i,""===n||n):console.log(`No property ${i} found on ${o}`);const c=o?.texture?.type||o?.type;if("string"==typeof c){const e=c.toLowerCase();switch(!0){case e.includes("material"):o.needsUpdate=!0;break;case e.includes("camera")&&o.updateProjectionMatrix:o.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(){}},me={dpr:t.ref(1),inputActive:b,mousePos:A},he=t.computed((()=>F.value?.instance??null)),fe=t.computed((()=>I.value?.instance??null)),ye=t.computed((()=>K.value.instance));let ve=null,ge=null;e.camera=he,e.clearCustomRender=()=>{ve?ve.clearCustomRender():ge=null},e.createApp=e=>{ve=t.createRenderer(pe).createApp(e),Object.keys(p).forEach((e=>{ve.component(e,p[e])}));const{mount:n}=ve;return ve.mount=(e,...t)=>{const r=G({domElement:"string"==typeof e?document.querySelector(e):e,isLunchboxRootNode:!0,name:"root",metaType:"rootMeta",type:"root",uuid:"LUNCHBOX_ROOT"});ve.rootNode=r;return n(r,...t)},ve.extend=e=>((({app:e,...t})=>{Object.keys(t).forEach((n=>{e.component(n,V(n)),u[n]=t[n]}))})({app:ve,...e}),ve),ve.setCustomRender=e=>{ve.customRender=e},ge&&(ve.setCustomRender(ge),ge=null),ve.clearCustomRender=()=>{ve.customRender=null},ve},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=me,e.onAfterRender=(e,t=1/0)=>{t===1/0?re.push(e):re.splice(t,0,e)},e.onBeforeRender=ae,e.renderer=fe,e.scene=ye,e.setCustomRender=e=>{ve?ve.setCustomRender(e):ge=e},Object.defineProperty(e,"__esModule",{value:!0})}));
@@ -9,27 +9,36 @@ const allNodes = [];
9
9
  const resizeCanvas = (width, height) => {
10
10
  const renderer = ensureRenderer.value?.instance;
11
11
  const scene = ensuredScene.value.instance;
12
+ const camera = ensuredCamera.value;
12
13
  // ignore if no element
13
- if (!renderer?.domElement || !scene)
14
+ if (!renderer?.domElement || !scene || !camera)
14
15
  return;
15
16
  width = width ?? window.innerWidth;
16
17
  height = height ?? window.innerHeight;
17
18
  // update camera
18
19
  const aspect = width / height;
19
- const camera = ensuredCamera.value;
20
20
  if (camera.type?.toLowerCase() === 'perspectivecamera') {
21
21
  const perspectiveCamera = camera.instance;
22
22
  perspectiveCamera.aspect = aspect;
23
23
  perspectiveCamera.updateProjectionMatrix();
24
24
  }
25
+ else if (camera.type?.toLowerCase() === 'orthographiccamera') {
26
+ // console.log('TODO: ortho camera update')
27
+ const orthoCamera = camera.instance;
28
+ const heightInTermsOfWidth = height / width;
29
+ orthoCamera.top = heightInTermsOfWidth * 10;
30
+ orthoCamera.bottom = -heightInTermsOfWidth * 10;
31
+ orthoCamera.right = 10;
32
+ orthoCamera.left = -10;
33
+ orthoCamera.updateProjectionMatrix();
34
+ }
25
35
  else {
26
- console.log('TODO: ortho camera update');
36
+ console.log('TODO: non-ortho or perspective camera');
27
37
  }
28
38
  // update canvas
29
39
  renderer.setSize(width, height);
30
40
  // render immediately so there's no flicker
31
41
  if (scene && camera.instance) {
32
- // const cameraInstance = scene.traverse(v => )
33
42
  renderer.render(toRaw(scene), toRaw(camera.instance));
34
43
  }
35
44
  };
@@ -74,8 +83,11 @@ const LunchboxWrapper = {
74
83
  props: {
75
84
  // These should match the Lunchbox.WrapperProps interface
76
85
  background: String,
86
+ cameraArgs: Array,
77
87
  cameraPosition: Array,
78
88
  dpr: Number,
89
+ ortho: Boolean,
90
+ orthographic: Boolean,
79
91
  rendererProperties: Object,
80
92
  shadow: [Boolean, Object],
81
93
  transparent: Boolean,
@@ -86,6 +98,7 @@ const LunchboxWrapper = {
86
98
  const dpr = ref(props.dpr ?? -1);
87
99
  const container = ref();
88
100
  let renderer;
101
+ let camera;
89
102
  let scene;
90
103
  // MOUNT
91
104
  // ====================
@@ -93,12 +106,6 @@ const LunchboxWrapper = {
93
106
  // canvas needs to exist
94
107
  if (!canvas.value)
95
108
  throw new Error('missing canvas');
96
- // ensure camera
97
- const camera = ensuredCamera.value.instance;
98
- // move camera if needed
99
- if (camera && props.cameraPosition) {
100
- camera.position.set(...props.cameraPosition);
101
- }
102
109
  // RENDERER
103
110
  // ====================
104
111
  // is there already a renderer?
@@ -155,6 +162,43 @@ const LunchboxWrapper = {
155
162
  rendererReady.value = true;
156
163
  return;
157
164
  }
165
+ // CAMERA
166
+ // ====================
167
+ // is there already a camera?
168
+ camera = tryGetNodeWithInstanceType([
169
+ 'PerspectiveCamera',
170
+ 'OrthographicCamera',
171
+ ]);
172
+ // if not, let's create one
173
+ if (!camera) {
174
+ // create ortho camera
175
+ if (props.ortho || props.orthographic) {
176
+ // const size: Vector2 = new Vector2()
177
+ ensuredCamera.value = createNode({
178
+ props: { args: props.cameraArgs ?? [] },
179
+ type: 'OrthographicCamera',
180
+ uuid: fallbackCameraUuid,
181
+ });
182
+ }
183
+ else {
184
+ ensuredCamera.value = createNode({
185
+ props: {
186
+ args: props.cameraArgs ?? [45, 0.5625, 1, 1000],
187
+ },
188
+ type: 'PerspectiveCamera',
189
+ uuid: fallbackCameraUuid,
190
+ });
191
+ }
192
+ cameraReady.value = true;
193
+ camera = ensuredCamera.value;
194
+ }
195
+ else {
196
+ cameraReady.value = true;
197
+ }
198
+ // move camera if needed
199
+ if (camera && props.cameraPosition) {
200
+ camera.instance?.position.set(...props.cameraPosition);
201
+ }
158
202
  // SCENE
159
203
  // ====================
160
204
  scene = ensuredScene.value;
@@ -181,7 +225,7 @@ const LunchboxWrapper = {
181
225
  // console.log(scene)
182
226
  update({
183
227
  app: getCurrentInstance().appContext.app,
184
- camera,
228
+ camera: camera.instance,
185
229
  renderer: renderer.instance,
186
230
  scene: scene.instance,
187
231
  });
@@ -828,9 +872,27 @@ function buildEnsured(pascalCaseTypes, fallbackUuid, props = {}, callback = null
828
872
  // ENSURE CAMERA
829
873
  // ====================
830
874
  const fallbackCameraUuid = 'FALLBACK_CAMERA';
831
- const ensuredCamera = buildEnsured(['PerspectiveCamera', 'OrthographicCamera'], fallbackCameraUuid, {
832
- args: [45, 0.5625, 1, 1000],
875
+ const defaultCamera = buildEnsured(['PerspectiveCamera', 'OrthographicCamera'], fallbackCameraUuid, { args: [45, 0.5625, 1, 1000] });
876
+ /** Special value to be changed ONLY in `LunchboxWrapper`.
877
+ * Functions waiting for a Camera need to wait for this to be true. */
878
+ const cameraReady = ref(false);
879
+ const ensuredCamera = computed({
880
+ get() {
881
+ return (cameraReady.value ? defaultCamera.value : null);
882
+ },
883
+ set(val) {
884
+ const t = val.type ?? '';
885
+ const pascalType = t[0].toUpperCase() + t.slice(1);
886
+ overrides[pascalType] = val;
887
+ },
833
888
  });
889
+ // export const ensuredCamera = buildEnsured<THREE.Camera>(
890
+ // ['PerspectiveCamera', 'OrthographicCamera'],
891
+ // fallbackCameraUuid,
892
+ // {
893
+ // args: [45, 0.5625, 1, 1000],
894
+ // }
895
+ // )
834
896
  // ENSURE RENDERER
835
897
  // ====================
836
898
  const fallbackRendererUuid = 'FALLBACK_RENDERER';
@@ -1184,7 +1246,7 @@ const update = (opts) => {
1184
1246
  app: opts.app,
1185
1247
  renderer: ensureRenderer.value?.instance,
1186
1248
  scene: ensuredScene.value.instance,
1187
- camera: ensuredCamera.value.instance,
1249
+ camera: ensuredCamera.value?.instance,
1188
1250
  }));
1189
1251
  // prep options
1190
1252
  const { app, renderer, scene, camera } = opts;
@@ -1579,7 +1641,7 @@ const globals = {
1579
1641
  inputActive,
1580
1642
  mousePos,
1581
1643
  };
1582
- const camera = computed(() => ensuredCamera.value.instance);
1644
+ const camera = computed(() => ensuredCamera.value?.instance ?? null);
1583
1645
  const renderer = computed(() => ensureRenderer.value?.instance ?? null);
1584
1646
  const scene = computed(() => ensuredScene.value.instance);
1585
1647
  // CUSTOM RENDER SUPPORT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lunchboxjs",
3
- "version": "0.1.4003",
3
+ "version": "0.1.4004",
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",
@@ -8,11 +8,13 @@ import {
8
8
  WritableComputedRef,
9
9
  } from 'vue'
10
10
  import {
11
+ cameraReady,
11
12
  cancelUpdate,
12
13
  createNode,
13
14
  ensuredCamera,
14
15
  ensureRenderer,
15
16
  ensuredScene,
17
+ fallbackCameraUuid,
16
18
  fallbackRendererUuid,
17
19
  MiniDom,
18
20
  rendererReady,
@@ -21,7 +23,7 @@ import {
21
23
  } from '../../core'
22
24
  import { set } from 'lodash'
23
25
  import { globals, Lunch } from '../..'
24
- import { Color } from 'three'
26
+ import { Color, Vector2 } from 'three'
25
27
  import { prepCanvas } from './prepCanvas'
26
28
 
27
29
  /** fixed & fill styling for container */
@@ -42,8 +44,11 @@ export const LunchboxWrapper: ComponentOptions = {
42
44
  props: {
43
45
  // These should match the Lunchbox.WrapperProps interface
44
46
  background: String,
47
+ cameraArgs: Array,
45
48
  cameraPosition: Array,
46
49
  dpr: Number,
50
+ ortho: Boolean,
51
+ orthographic: Boolean,
47
52
  rendererProperties: Object,
48
53
  shadow: [Boolean, Object],
49
54
  transparent: Boolean,
@@ -54,6 +59,7 @@ export const LunchboxWrapper: ComponentOptions = {
54
59
  const dpr = ref(props.dpr ?? -1)
55
60
  const container = ref<MiniDom.RendererDomNode>()
56
61
  let renderer: Lunch.Node<THREE.WebGLRenderer> | null
62
+ let camera: Lunch.Node<THREE.Camera> | null
57
63
  let scene: MiniDom.RendererStandardNode<THREE.Scene>
58
64
 
59
65
  // MOUNT
@@ -62,13 +68,6 @@ export const LunchboxWrapper: ComponentOptions = {
62
68
  // canvas needs to exist
63
69
  if (!canvas.value) throw new Error('missing canvas')
64
70
 
65
- // ensure camera
66
- const camera = ensuredCamera.value.instance
67
- // move camera if needed
68
- if (camera && props.cameraPosition) {
69
- camera.position.set(...props.cameraPosition)
70
- }
71
-
72
71
  // RENDERER
73
72
  // ====================
74
73
  // is there already a renderer?
@@ -139,6 +138,45 @@ export const LunchboxWrapper: ComponentOptions = {
139
138
  return
140
139
  }
141
140
 
141
+ // CAMERA
142
+ // ====================
143
+ // is there already a camera?
144
+ camera = tryGetNodeWithInstanceType([
145
+ 'PerspectiveCamera',
146
+ 'OrthographicCamera',
147
+ ])
148
+ // if not, let's create one
149
+ if (!camera) {
150
+ // create ortho camera
151
+ if (props.ortho || props.orthographic) {
152
+ // const size: Vector2 = new Vector2()
153
+
154
+ ensuredCamera.value = createNode<THREE.OrthographicCamera>({
155
+ props: { args: props.cameraArgs ?? [] },
156
+ type: 'OrthographicCamera',
157
+ uuid: fallbackCameraUuid,
158
+ })
159
+ } else {
160
+ ensuredCamera.value = createNode<THREE.PerspectiveCamera>({
161
+ props: {
162
+ args: props.cameraArgs ?? [45, 0.5625, 1, 1000],
163
+ },
164
+ type: 'PerspectiveCamera',
165
+ uuid: fallbackCameraUuid,
166
+ })
167
+ }
168
+
169
+ cameraReady.value = true
170
+
171
+ camera = ensuredCamera.value
172
+ } else {
173
+ cameraReady.value = true
174
+ }
175
+ // move camera if needed
176
+ if (camera && props.cameraPosition) {
177
+ camera.instance?.position.set(...props.cameraPosition)
178
+ }
179
+
142
180
  // SCENE
143
181
  // ====================
144
182
  scene = ensuredScene.value
@@ -171,7 +209,7 @@ export const LunchboxWrapper: ComponentOptions = {
171
209
  // console.log(scene)
172
210
  update({
173
211
  app: getCurrentInstance()!.appContext.app as Lunch.App,
174
- camera,
212
+ camera: camera.instance,
175
213
  renderer: renderer.instance,
176
214
  scene: scene.instance,
177
215
  })
@@ -4,29 +4,37 @@ import { toRaw } from 'vue'
4
4
  export const resizeCanvas = (width?: number, height?: number) => {
5
5
  const renderer = ensureRenderer.value?.instance
6
6
  const scene = ensuredScene.value.instance
7
+ const camera = ensuredCamera.value
7
8
 
8
9
  // ignore if no element
9
- if (!renderer?.domElement || !scene) return
10
+ if (!renderer?.domElement || !scene || !camera) return
10
11
 
11
12
  width = width ?? window.innerWidth
12
13
  height = height ?? window.innerHeight
13
14
 
14
15
  // update camera
15
16
  const aspect = width / height
16
- const camera = ensuredCamera.value
17
17
  if (camera.type?.toLowerCase() === 'perspectivecamera') {
18
18
  const perspectiveCamera = camera.instance as THREE.PerspectiveCamera
19
19
  perspectiveCamera.aspect = aspect
20
20
  perspectiveCamera.updateProjectionMatrix()
21
+ } else if (camera.type?.toLowerCase() === 'orthographiccamera') {
22
+ // console.log('TODO: ortho camera update')
23
+ const orthoCamera = camera.instance as THREE.OrthographicCamera
24
+ const heightInTermsOfWidth = height / width
25
+ orthoCamera.top = heightInTermsOfWidth * 10
26
+ orthoCamera.bottom = -heightInTermsOfWidth * 10
27
+ orthoCamera.right = 10
28
+ orthoCamera.left = -10
29
+ orthoCamera.updateProjectionMatrix()
21
30
  } else {
22
- console.log('TODO: ortho camera update')
31
+ console.log('TODO: non-ortho or perspective camera')
23
32
  }
24
33
 
25
34
  // update canvas
26
35
  renderer.setSize(width, height)
27
36
  // render immediately so there's no flicker
28
37
  if (scene && camera.instance) {
29
- // const cameraInstance = scene.traverse(v => )
30
38
  renderer.render(toRaw(scene), toRaw(camera.instance))
31
39
  }
32
40
  }
@@ -127,13 +127,35 @@ function buildEnsured<T extends THREE.Object3D>(
127
127
  // ENSURE CAMERA
128
128
  // ====================
129
129
  export const fallbackCameraUuid = 'FALLBACK_CAMERA'
130
- export const ensuredCamera = buildEnsured<THREE.Camera>(
130
+ export const defaultCamera = buildEnsured(
131
131
  ['PerspectiveCamera', 'OrthographicCamera'],
132
132
  fallbackCameraUuid,
133
- {
134
- args: [45, 0.5625, 1, 1000],
135
- }
136
- )
133
+ { args: [45, 0.5625, 1, 1000] }
134
+ ) as unknown as WritableComputedRef<Lunch.Node<THREE.Camera>>
135
+ /** Special value to be changed ONLY in `LunchboxWrapper`.
136
+ * Functions waiting for a Camera need to wait for this to be true. */
137
+ export const cameraReady = ref(false)
138
+
139
+ export const ensuredCamera = computed<Lunch.Node<THREE.Camera> | null>({
140
+ get() {
141
+ return (
142
+ cameraReady.value ? (defaultCamera.value as any) : (null as any)
143
+ ) as any
144
+ },
145
+ set(val: any) {
146
+ const t = val.type ?? ''
147
+ const pascalType = t[0].toUpperCase() + t.slice(1)
148
+ overrides[pascalType] = val as any
149
+ },
150
+ })
151
+
152
+ // export const ensuredCamera = buildEnsured<THREE.Camera>(
153
+ // ['PerspectiveCamera', 'OrthographicCamera'],
154
+ // fallbackCameraUuid,
155
+ // {
156
+ // args: [45, 0.5625, 1, 1000],
157
+ // }
158
+ // )
137
159
 
138
160
  // ENSURE RENDERER
139
161
  // ====================
@@ -14,7 +14,7 @@ export const update: Lunch.UpdateCallback = (opts) => {
14
14
  app: opts.app,
15
15
  renderer: ensureRenderer.value?.instance,
16
16
  scene: ensuredScene.value.instance,
17
- camera: ensuredCamera.value.instance,
17
+ camera: ensuredCamera.value?.instance,
18
18
  })
19
19
  )
20
20
 
package/src/index.ts CHANGED
@@ -30,7 +30,7 @@ export const globals = {
30
30
  mousePos,
31
31
  }
32
32
 
33
- export const camera = computed(() => ensuredCamera.value.instance)
33
+ export const camera = computed(() => ensuredCamera.value?.instance ?? null)
34
34
  export const renderer = computed(() => ensureRenderer.value?.instance ?? null)
35
35
  export const scene = computed(() => ensuredScene.value.instance)
36
36
 
package/src/types.ts CHANGED
@@ -138,8 +138,11 @@ export declare namespace Lunch {
138
138
 
139
139
  interface WrapperProps {
140
140
  background?: string
141
+ cameraArgs?: any[]
141
142
  cameraPosition?: [number, number, number]
142
143
  dpr?: number
144
+ ortho?: boolean
145
+ orthographic?: boolean
143
146
  rendererProperties?: Partial<THREE.WebGLRenderer>
144
147
  shadow?: ShadowSugar
145
148
  transparent?: boolean