UNPKG

@vladkrutenyuk/three-kvy-core

Version:

Everything you need to create any-complexity 3D apps with Three.js. Empower Three.js with a modular, lifecycle-managed context that seamlessly propagates through objects via reusable features providing structured logic.

4 lines (3 loc) 10.9 kB
import{EventEmitter as t}from"eventemitter3";function e(t,e){const s=t.indexOf(e),r=-1!==s;return r&&t.splice(s,1),r}const s=Object.defineProperties,r=t=>({value:t,writable:!1,configurable:!1}),i=t=>({value:t,enumerable:!1,configurable:!0});var n=Object.freeze({__proto__:null,defineProps:s,notEnumer:i,readOnly:r});function o(t,e){const s=t.parent;null!==s&&e(s)&&o(s,e)}const h=(t,e)=>{if(!t)throw new ReferenceError(`Attempted to access '${e}' before it was initialized.`);return t};class a extends t{static create(t,e){return new a(new t.WebGLRenderer(null==e?void 0:e.renderer),new t.PerspectiveCamera,new t.Scene,new t.Clock,new t.Raycaster)}get camera(){return this._camera}set camera(t){const e=this._camera;this._camera=t,this.cameraChanged(t,e)}get container(){return this._container}get isMounted(){return this._isMounted}get isDestroyed(){return this._isDestroyed}constructor(t,e,i,n,o){super(),this._container=null,this._resizeObserver=null,this._isMounted=!1,this._isDestroyed=!1,this._srcRenderFn=()=>{this.renderer.render(this.scene,this._camera)},this._renderFn=this._srcRenderFn,this.resizeHandler=()=>{const t=this._container;if(!t)return;const e=t.offsetWidth,s=t.offsetHeight,r=this._camera;r.aspect=e/s,r.updateProjectionMatrix(),this.renderer.setSize(e,s),this.emit(c.Resize,e,s),this.render()},s(this,{isThreeContext:r(!0)}),this.renderer=t,this.scene=i,this._camera=e,this.clock=n,this.raycaster=o,this._renderFn=this._srcRenderFn}render(){this.emit(c.RenderBefore),this._renderFn(),this.emit(c.RenderAfter)}overrideRender(t){return this._renderFn=t,this}resetRender(){return this._renderFn=this._srcRenderFn,this}mount(t){if(this._isMounted||this._isDestroyed)return;this._isMounted=!0;const e=this.renderer.domElement;return this._container=t,t.append(e),e.tabIndex=0,e.style.touchAction="none",this.emit(c.Mount,t),this._resizeObserver=new ResizeObserver(this.resizeHandler),this._resizeObserver.observe(t),this.resizeHandler(),this}unmount(){var t;if(this._isMounted)return this._isMounted=!1,null===(t=this._resizeObserver)||void 0===t||t.disconnect(),this._resizeObserver=null,this.renderer.domElement.remove(),this.emit(c.Unmount),this}destroy(){if(this._isDestroyed)return;this._isDestroyed=!0;const t=()=>{console.error("render is called after ThreeContext is destroyed.")};this._renderFn=t,this._srcRenderFn=t,this.unmount(),this.renderer.dispose(),this.emit(c.Destroy),Object.values(c).forEach((t=>this.removeAllListeners(t)))}cameraChanged(t,e){const s=this._camera,r=this._container;r&&(s.aspect=r.offsetWidth/r.offsetHeight),s.updateProjectionMatrix(),this.emit(c.CameraChanged,t,e)}}const c=Object.freeze({RenderBefore:"renderbefore",RenderAfter:"renderafter",Mount:"mount",Unmount:"unmount",Destroy:"destroy",CameraChanged:"camerachanged",Resize:"resize"}),d=Object.freeze({AttCtx:"attachedctx",DetCtx:"detachedctx",FtAdd:"featureadded",FtRem:"featureremoved",Dstr:"destroy"}),u="__kvy_ftblty__";class l extends t{static extract(t){const e=t[u];return void 0!==e&&e.isObjectFeaturability?e:null}static from(t){let e=l.extract(t);return e||(e=new l(t),e)}static destroy(t,e){var s;null===(s=l.extract(t))||void 0===s||s.destroy(e)}get ctx(){return this._ctx}get features(){return[...this._features]}constructor(t){super(),this.isObjectFeaturability=!0,this._ctx=null,this._features=[],this.object=t,t.addEventListener("added",this.onObjectAdded),t.addEventListener("removed",this.onObjectRemoved),s(t,{[u]:i(this),isFeaturable:i(!0)}),t.parent&&this.onObjectAdded({target:t}),this.inheritCtx()}destroy(t){if(this.destroyAllFeatures(),this.object.isRoot&&!t)return;this.detachCtx();const e=this.object;e.removeEventListener("added",this.onObjectAdded),e.removeEventListener("removed",this.onObjectRemoved),delete e[u],delete e.isFeaturable}destroyAllFeatures(){const t=this.features;for(const e of t)e.destroy()}addFeature(t,e,s){const r=new t(this.object,null!=e?e:{});return null==s||s(r),this._features.push(r),r.init(),this.emit(d.FtAdd,r),r}getFeature(t){var e;return null!==(e=this._features.find((e=>e instanceof t)))&&void 0!==e?e:null}getFeatureBy(t){var e;return null!==(e=this._features.find(t))&&void 0!==e?e:null}destroyFeature(t){this._log("destroying feature...");return!!e(this._features,t)&&(t.destroy(),this.emit(d.FtRem,t),!0)}setCtx(t){return t?this.attachCtx(t):this.detachCtx(),this}onObjectAdded({target:t}){const e=l.extract(t);e?(e._log("object added"),e.inheritCtx()):console.error("Object3DFeaturability is not in target object.")}inheritCtx(){var t;const e=this.object,s=e.parent;if(!s)return;const r=null===(t=l.extract(s))||void 0===t?void 0:t._ctx;if(r)return this._log("onAdded parent has ctx"),void this.propagateAttachCtxDown(r);this._log("onAdded parent is just object3d");let i=null;if(o(e,(t=>(i=l.extract(t),!i))),null===i)return;const n=i.ctx;this._log("onAdded found featurable object ancestor"),n&&this.propagateAttachCtxDown(n)}onObjectRemoved({target:t}){const e=l.extract(t);e?(e._log("object removed"),e.propagateDetachCtxDown()):console.error("Object3DFeaturability is not in target object.")}attachCtx(t){if(this._log("attaching ctx..."),null!==this._ctx)return this._log("there is some ctx here"),this._ctx!==t?void console.error("Cannot attach this object. It had attached to another ctx."):void this._log("had attached already");this._ctx=t,this.emit(d.AttCtx,t),t.once(d.Dstr,this.detachCtx,this),this._log("attached ctx")}detachCtx(){if(this._log("detaching ctx..."),null===this._ctx)return;const t=this._ctx;this._ctx=null,this.emit(d.DetCtx,t),t.off(d.Dstr,this.detachCtx,this),this._log("detached ctx")}propagateAttachCtxDown(t){this._log("attaching ctx recursively..."),this.object.traverse((e=>{var s;null===(s=l.extract(e))||void 0===s||s.attachCtx(t)}))}propagateDetachCtxDown(){this._log("detaching ctx recursively..."),this.object.traverse((t=>{var e;null===(e=l.extract(t))||void 0===e||e.detachCtx()}))}_log(t){l.log(this,t)}}l.log=()=>{};class _ extends t{get ctx(){return h(this._ctx,"ctx")}get hasCtx(){return!!this._ctx}constructor(t){super(),this._ctx=null,this.attachCtx=t=>{if(this._ctx){if(this._ctx===t)return;this.detachCtx()}this._ctx=t,this._log("ctx attached"),this.emit(d.AttCtx,t),this._log("useCtx"),this._useCtxReturn=this.useCtx(t),this.initCtxEventMethods(t)},this.detachCtx=()=>{if(!this._ctx)return;const t=this._ctx;this._ctx=null,this.emit(d.DetCtx,t),this._log("ctx detached"),this._useCtxReturn&&(this._log("useCtx cleanup"),this._useCtxReturn(),this._useCtxReturn=void 0)},this._ftblty=l.extract(t),s(this,{id:r(x++),isObject3DFeature:r(!0),object:r(t)}),this.uuid=_.generateUUID(),this._ftblty.on(d.AttCtx,this.ftbltyAttachedToCtxHandler,this),this._ftblty.on(d.DetCtx,this.ftbltyDetachedFromCtxHandler,this),this._log("init")}init(){this._ftblty.ctx&&this.attachCtx(this._ftblty.ctx)}destroy(){this.detachCtx(),this._ftblty.off(d.AttCtx,this.ftbltyAttachedToCtxHandler,this),this._ftblty.off(d.DetCtx,this.ftbltyDetachedFromCtxHandler,this);this._ftblty.destroyFeature(this)&&(this._log("destroyed"),this.emit(d.Dstr),this.onDestroy())}ftbltyAttachedToCtxHandler(t){this._log("obj attached to ctx"),this.attachCtx(t)}ftbltyDetachedFromCtxHandler(t){this._log("obj detached from ctx"),this.detachCtx()}useCtx(t){}onDestroy(){}onBeforeRender(t){}onAfterRender(t){}onResize(t){}onMount(t){}onUnmount(t){}onLoopRun(t){}onLoopStop(t){}_log(t){_.log(this,t)}initCtxEventMethods(t){const e=_.prototype;this.onAfterRender!==e.onAfterRender&&this.iehm(t.three,"renderafter","onAfterRender"),this.onBeforeRender!==e.onBeforeRender&&this.iehm(t.three,"renderbefore","onBeforeRender"),this.onMount!==e.onMount&&this.iehm(t.three,"mount","onMount"),this.onResize!==e.onResize&&this.iehm(t.three,"unmount","onUnmount"),this.onLoopRun!==e.onLoopRun&&this.iehm(t,"looprun","onLoopRun"),this.onLoopStop!==e.onLoopStop&&this.iehm(t,"loopstop","onLoopStop")}iehm(t,e,s){let r=null;const i=i=>{r=function(){this[s](i)},t.on(e,r,this)};this.on(d.AttCtx,i),this.on(d.DetCtx,(()=>{r&&t.off(e,r,this)})),this._ctx&&i(this._ctx)}}_.generateUUID=()=>{try{return crypto.randomUUID()}catch(t){return`${Math.random()}-${Date.now()}`}},_.log=()=>{};let x=0;class f extends t{static create(t,e,s){const r=a.create(t,s);return new f(r,r.scene,e)}get root(){return this._root}get deltaTime(){return this._deltaTime}get time(){return this._time}get isDestroyed(){return this._isDestroyed}get isRunning(){return this._isRunning}constructor(t,e,i){super(),this._time=0,this._deltaTime=0,this._isDestroyed=!1,this._isRunning=!1,this._cleanups={},this._clock=t.clock,s(this,{isCoreContext:r(!0),modules:r({}),three:r(t)});const n=l.from(null!=e?e:t.scene).setCtx(this).object;n.isRoot=!0,this._root=n,i&&this.assignModules(i)}run(){this._isRunning||this._isDestroyed||(this._isRunning=!0,this._clock.start(),this.three.renderer.setAnimationLoop((()=>{ //! its very important to getDelta() before getElapsedTime() this._deltaTime=this._clock.getDelta(),this._time=this._clock.getElapsedTime(),this.three.render()})),this.emit("looprun"))}stop(){this._isRunning=!1,this._clock.stop(),this.three.renderer.setAnimationLoop(null),this.emit("loopstop")}assignModules(t){for(const e in t){const s=t[e];s&&this.assignModule(e,s)}return this}assignModule(t,e){if(this.modules[t])return void console.warn(`Key [${t.toString()}] is already assinged in modules.`);this.modules[t]=e;const s=e;s._ctx=this;const r=s.useCtx(this);this._cleanups[t]=r}removeModule(t){const e=this._cleanups[t];delete this._cleanups[t],e&&"function"==typeof e&&e();this.modules[t]._ctx=void 0,delete this.modules[t]}destroy(){return this._isDestroyed||(this._isDestroyed=!0,this.stop(),this.three.destroy(),this.emit("destroy"),l.destroy(this._root,!0),Object.values(this._cleanups).forEach((t=>t&&t())),["destroy","looprun","loopstop"].forEach((t=>this.removeAllListeners(t)))),this}}class m extends t{get ctx(){return h(this._ctx,"ctx")}get hasCtx(){return!!this._ctx}constructor(){super(),s(this,{isCoreContextModule:r(!0)})}useCtx(t){}}function g(t,e,s,r){return l.from(t).addFeature(e,s,r)}function p(t,e){var s,r;return null!==(r=null===(s=l.extract(t))||void 0===s?void 0:s.getFeature(e))&&void 0!==r?r:null}function v(t,e){var s,r;return null!==(r=null===(s=l.extract(t))||void 0===s?void 0:s.getFeatureBy(e))&&void 0!==r?r:null}function y(t){var e,s;return null!==(s=null===(e=l.extract(t))||void 0===e?void 0:e.features)&&void 0!==s?s:null}function b(t,e){e?t.traverse(l.destroy):l.destroy(t)}const C={removeArrayItem:e,props:n,traverseUp:o,assertDefined:h},R="2.0.0",D="__THREE_KVY_CORE__";"undefined"!=typeof window&&(window[D]?console.warn("WARNING: Multiple instances of `@vladkrutenyuk/three-kvy-core` being imported."):window[D]=R);export{f as CoreContext,m as CoreContextModule,l as Object3DFeaturability,_ as Object3DFeature,R as REVISION,a as ThreeContext,g as addFeature,b as clear,p as getFeature,v as getFeatureBy,y as getFeatures,C as utils};