class R3 { constructor(options) { if (typeof options === 'undefined') { options = {}; } Object.assign(this, options); } } /** * static Version - Current R3 version */ R3.Version = '3.0.255'; /** * static CompileDate - Current compile date of R3 */ R3.CompileDate = '2021 Oct 02 - 14:30:13 pm'; class System { constructor(options) { if (typeof options === 'undefined') { options = {}; } Object.assign(this, options); } } System.DOM = 0x0; System.INPUT = 0x1; System.LINKING = 0x2; System.RENDER = 0x3; System.RUNTIME = 0x4; System.SOCKET = 0x5; System.STORAGE = 0x6; System.MAX_SYSTEM = 0x7; class SystemDOM extends System { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * Start() * - Starts the system by registering subscriptions to events * @param options */ static Start(options) { SystemDOM.Subscriptions['DOM_COMPONENT_INITIALIZED'] = Event.Subscribe( Event.DOM_COMPONENT_INITIALIZED, SystemDOM.OnDomComponentInitialized ); SystemDOM.Started = true; console.log('Started system: SystemDOM'); } /** * Stop() * - Stops the system by removing these subscriptions to events * @param options */ static Stop(options) { SystemDOM.Subscriptions['DOM_COMPONENT_INITIALIZED'].remove(); delete SystemDOM.Subscriptions['DOM_COMPONENT_INITIALIZED']; SystemDOM.Started = false; console.log('Stopped system: SystemDOM'); } /** * OnDomComponentInitialized() * - Listens to events of type Event.DOM_COMPONENT_INITIALIZED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnDomComponentInitialized(object) { } } /** * static Started - Indicates whether or not this system is running or not */ SystemDOM.Started = false; /** * static Subscriptions - An association object which hold the subscription handles for Events this system is listening * to. The system can stop receiving events by calling remove() on a handle. */ SystemDOM.Subscriptions = {}; class SystemInput extends System { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * Start() * - Starts the system by registering subscriptions to events * @param options */ static Start(options) { SystemInput.Subscriptions['SLIDER_ENTITY_INITIALIZED'] = Event.Subscribe( Event.SLIDER_ENTITY_INITIALIZED, SystemInput.OnSliderEntityInitialized ); SystemInput.Subscriptions['TOUCH_START'] = Event.Subscribe( Event.TOUCH_START, SystemInput.OnTouchStart ); SystemInput.Subscriptions['TOUCH_END'] = Event.Subscribe( Event.TOUCH_END, SystemInput.OnTouchEnd ); SystemInput.Subscriptions['TOUCH_MOVE'] = Event.Subscribe( Event.TOUCH_MOVE, SystemInput.OnTouchMove ); SystemInput.Subscriptions['TOUCH_CANCEL'] = Event.Subscribe( Event.TOUCH_CANCEL, SystemInput.OnTouchCancel ); SystemInput.Subscriptions['KEYBOARD_DOWN'] = Event.Subscribe( Event.KEYBOARD_DOWN, SystemInput.OnKeyboardDown ); SystemInput.Subscriptions['KEYBOARD_UP'] = Event.Subscribe( Event.KEYBOARD_UP, SystemInput.OnKeyboardUp ); SystemInput.Subscriptions['MOUSE_DOWN'] = Event.Subscribe( Event.MOUSE_DOWN, SystemInput.OnMouseDown ); SystemInput.Subscriptions['MOUSE_UP'] = Event.Subscribe( Event.MOUSE_UP, SystemInput.OnMouseUp ); SystemInput.Subscriptions['MOUSE_MOVE'] = Event.Subscribe( Event.MOUSE_MOVE, SystemInput.OnMouseMove ); SystemInput.Subscriptions['MOUSE_WHEEL'] = Event.Subscribe( Event.MOUSE_WHEEL, SystemInput.OnMouseWheel ); SystemInput.Started = true; console.log('Started system: SystemInput'); } /** * Stop() * - Stops the system by removing these subscriptions to events * @param options */ static Stop(options) { SystemInput.Subscriptions['SLIDER_ENTITY_INITIALIZED'].remove(); delete SystemInput.Subscriptions['SLIDER_ENTITY_INITIALIZED']; SystemInput.Subscriptions['TOUCH_START'].remove(); delete SystemInput.Subscriptions['TOUCH_START']; SystemInput.Subscriptions['TOUCH_END'].remove(); delete SystemInput.Subscriptions['TOUCH_END']; SystemInput.Subscriptions['TOUCH_MOVE'].remove(); delete SystemInput.Subscriptions['TOUCH_MOVE']; SystemInput.Subscriptions['TOUCH_CANCEL'].remove(); delete SystemInput.Subscriptions['TOUCH_CANCEL']; SystemInput.Subscriptions['KEYBOARD_DOWN'].remove(); delete SystemInput.Subscriptions['KEYBOARD_DOWN']; SystemInput.Subscriptions['KEYBOARD_UP'].remove(); delete SystemInput.Subscriptions['KEYBOARD_UP']; SystemInput.Subscriptions['MOUSE_DOWN'].remove(); delete SystemInput.Subscriptions['MOUSE_DOWN']; SystemInput.Subscriptions['MOUSE_UP'].remove(); delete SystemInput.Subscriptions['MOUSE_UP']; SystemInput.Subscriptions['MOUSE_MOVE'].remove(); delete SystemInput.Subscriptions['MOUSE_MOVE']; SystemInput.Subscriptions['MOUSE_WHEEL'].remove(); delete SystemInput.Subscriptions['MOUSE_WHEEL']; SystemInput.Started = false; console.log('Stopped system: SystemInput'); } /** * OnSliderEntityInitialized() * - Listens to events of type Event.SLIDER_ENTITY_INITIALIZED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnSliderEntityInitialized(object) { console.log('Slider Entity Initialized'); } /** * OnTouchStart() * - Listens to events of type Event.TOUCH_START and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnTouchStart(object) { } /** * OnTouchEnd() * - Listens to events of type Event.TOUCH_END and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnTouchEnd(object) { } /** * OnTouchMove() * - Listens to events of type Event.TOUCH_MOVE and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnTouchMove(object) { } /** * OnTouchCancel() * - Listens to events of type Event.TOUCH_CANCEL and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnTouchCancel(object) { } /** * OnKeyboardDown() * - Listens to events of type Event.KEYBOARD_DOWN and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnKeyboardDown(object) { } /** * OnKeyboardUp() * - Listens to events of type Event.KEYBOARD_UP and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnKeyboardUp(object) { } /** * OnMouseDown() * - Listens to events of type Event.MOUSE_DOWN and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnMouseDown(object) { } /** * OnMouseUp() * - Listens to events of type Event.MOUSE_UP and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnMouseUp(object) { } /** * OnMouseMove() * - Listens to events of type Event.MOUSE_MOVE and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnMouseMove(object) { } /** * OnMouseWheel() * - Listens to events of type Event.MOUSE_WHEEL and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnMouseWheel(object) { } } /** * static Started - Indicates whether or not this system is running or not */ SystemInput.Started = false; /** * static Subscriptions - An association object which hold the subscription handles for Events this system is listening * to. The system can stop receiving events by calling remove() on a handle. */ SystemInput.Subscriptions = {}; class SystemLinking extends System { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * Start() * - Starts the system by registering subscriptions to events * @param options */ static Start(options) { SystemLinking.Subscriptions['OBJECT_CREATED'] = Event.Subscribe( Event.OBJECT_CREATED, SystemLinking.OnObjectCreated ); SystemLinking.Subscriptions['COMPONENT_INITIALIZED'] = Event.Subscribe( Event.COMPONENT_INITIALIZED, SystemLinking.OnComponentInitialized ); SystemLinking.Subscriptions['ENTITY_INITIALIZED'] = Event.Subscribe( Event.ENTITY_INITIALIZED, SystemLinking.OnEntityInitialized ); SystemLinking.Subscriptions['INSTANCE_CREATED'] = Event.Subscribe( Event.INSTANCE_CREATED, SystemLinking.OnInstanceCreated ); SystemLinking.Subscriptions['CREATE_INSTANCE_BEFORE'] = Event.Subscribe( Event.CREATE_INSTANCE_BEFORE, SystemLinking.OnCreateInstanceBefore ); SystemLinking.Subscriptions['OBJECT_PROPERTY_UPDATE'] = Event.Subscribe( Event.OBJECT_PROPERTY_UPDATE, SystemLinking.OnObjectPropertyUpdate ); SystemLinking.Subscriptions['OBJECT_PROPERTY_UPDATED'] = Event.Subscribe( Event.OBJECT_PROPERTY_UPDATED, SystemLinking.OnObjectPropertyUpdated ); SystemLinking.Started = true; console.log('Started system: SystemLinking'); } /** * Stop() * - Stops the system by removing these subscriptions to events * @param options */ static Stop(options) { SystemLinking.Subscriptions['OBJECT_CREATED'].remove(); delete SystemLinking.Subscriptions['OBJECT_CREATED']; SystemLinking.Subscriptions['COMPONENT_INITIALIZED'].remove(); delete SystemLinking.Subscriptions['COMPONENT_INITIALIZED']; SystemLinking.Subscriptions['ENTITY_INITIALIZED'].remove(); delete SystemLinking.Subscriptions['ENTITY_INITIALIZED']; SystemLinking.Subscriptions['INSTANCE_CREATED'].remove(); delete SystemLinking.Subscriptions['INSTANCE_CREATED']; SystemLinking.Subscriptions['CREATE_INSTANCE_BEFORE'].remove(); delete SystemLinking.Subscriptions['CREATE_INSTANCE_BEFORE']; SystemLinking.Subscriptions['OBJECT_PROPERTY_UPDATE'].remove(); delete SystemLinking.Subscriptions['OBJECT_PROPERTY_UPDATE']; SystemLinking.Subscriptions['OBJECT_PROPERTY_UPDATED'].remove(); delete SystemLinking.Subscriptions['OBJECT_PROPERTY_UPDATED']; SystemLinking.Started = false; console.log('Stopped system: SystemLinking'); } /** * SanityChecks() * - When an object gets some value assigned to a property, we need to check if this property is a required * property, and if so, the value assigned to this property needs to conform to the type restrictions enforced by * the requirements objects. * @param r3Object * @param property * @param value */ static SanityChecks( r3Object, property, value ) { for (let r = 0; r < r3Object.required.length; r++) { /** * First we check if this is a required property */ if (!r3Object.required[r].hasOwnProperty(property)) { return; } /** * We know this property is required - so continue.. */ if (r3Object.required[r][property] instanceof Array) { /** * First we need to check that this value conforms to the requirement of being an Array */ if (!(value instanceof Array)) { throw new TypeError('The property ' + property + ' of ' + r3Object.name + ' was not properly defined - it should be an array'); } if (value.length === 0) { return; } /** * Check all items in the array are valid objects of type r3Object.required[property] */ for (let i = 0; i < value.length; i++) { r3Object.required[r][property].map( function (constructor) { if (!(value[i] instanceof constructor)) { throw new TypeError('The property ' + property + ' of this object is not of the correct type'); } } ); } } else { if (value === null) { return; } if (typeof value === 'undefined') { return; } if (!(value instanceof r3Object.required[r][property])) { throw new TypeError('The property ' + property + ' of ' + r3Object.name + ' is not of the correct type') } } } } /** * SetParentChildRelationships() * - When an object gets some value assigned to a property, and it is a required property, we need to ensure that * the proper parent / child relationships are maintained * @param r3Object * @param property * @param value */ static SetParentChildRelationships( r3Object, property, value ) { if (value instanceof Array) { /** * This object could have its property set to an empty array. * In this case - we should check its existing components and have their relationships * severed */ // if (value.length === 0) { // // if (r3Object[property] instanceof Array) { // for (let i = 0; i < r3Object[property].length; i++) { // if (r3Object[property][i] instanceof R3Object) { // r3Object.dirty = true; // Utils.RemoveFromArray(r3Object[property][i].parents, r3Object); // } // } // } // // } else { /** * We need to check if we removed an item from the existing array */ if (r3Object[property].length > 0) { /** * Find the missing value (if any) from the old array */ let unlinked = r3Object[property].reduce( function(result, object) { if (value.indexOf(object) === -1) { result.push(object); } return result; }, [] ); unlinked.map( function(unlinkObject) { Utils.RemoveFromArray(unlinkObject.parents, r3Object); Utils.RemoveFromArray(r3Object.children, unlinkObject); if (r3Object instanceof Entity) { Utils.RemoveFromArray(r3Object.components, unlinkObject); } } ); } for (let i = 0; i < value.length; i++) { if (value[i] instanceof R3Object) { Utils.PushUnique(value[i].parents, r3Object); Utils.PushUnique(r3Object.children, value[i]); if (r3Object instanceof Entity) { Utils.PushUnique(r3Object.components, value[i]); } } } } if (value instanceof R3Object) { Utils.PushUnique(value.parents, r3Object); Utils.PushUnique(r3Object.children, value); if (r3Object instanceof Entity) { Utils.PushUnique(r3Object.components, value); } } if (value === null || typeof value === 'undefined') { if (r3Object[property] instanceof R3Object) { Utils.RemoveFromArray(r3Object[property].parents, r3Object); Utils.RemoveFromArray(r3Object.children, r3Object[property]); if (r3Object instanceof Entity) { Utils.RemoveFromArray(r3Object.components, r3Object[property]); } } } } /** * DetermineReadyState() * - When an object is assigned some value to one of its required properties, we should determine the 'readiness' * state of this R3 Object. Ready objects, can have their instances created. * @param r3Object * @param property * @param value */ static DetermineReadyState( r3Object, property, value ) { console.log('Determining ready state for ' + r3Object.name); // if (r3Object.required[property] instanceof Array) { // // if (value.length === 0) { // return false; // } // // for (let i = 0; i < value.length; i++) { // if (value[i].initialized !== true) { // return false; // } // } // // return true; // // } else { // // if (value === null) { // return false; // } else { // // if (value.initialized !== true) { // return false; // } // // return true; // } // } // for (let requiredProperty in this.required) { // if (this.required.hasOwnProperty(requiredProperty)) { // if (requiredProperty === property) { // if (!isReady(property, value)) { // this.initialized = false; // return; // } // } else { // if (!isReady(requiredProperty, this[requiredProperty])) { // this.initialized = false; // return; // } // } // } // } } /** * ActivateInstances() * - No comment */ static ActivateInstances() { // if (this instanceof Component && this.initialized) { // this.createInstance(); // } // // if (this instanceof Entity && this.initialized && !this.started) { // this.start(); // } } /** * OnObjectCreated() * - Listens to events of type Event.OBJECT_CREATED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnObjectCreated(object) { console.log('Object created : ' + object.name); } /** * OnComponentInitialized() * - Listens to events of type Event.COMPONENT_INITIALIZED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnComponentInitialized(object) { console.log('component initialized'); } /** * OnEntityInitialized() * - Listens to events of type Event.ENTITY_INITIALIZED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnEntityInitialized(object) { console.log('entity initialized'); } /** * OnInstanceCreated() * - Listens to events of type Event.INSTANCE_CREATED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnInstanceCreated(object) { console.log('instance created'); } /** * OnCreateInstanceBefore() * - Listens to events of type Event.CREATE_INSTANCE_BEFORE and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return boolean delayInstance which indicates whether or not instance creation is delayed (handled) by * another system. (i.e. where instance creation order is of importance) */ static OnCreateInstanceBefore(object) { for (let i = 0; i < SystemLinking.BlacklistedComponents.length; i++) { if (object instanceof SystemLinking.BlacklistedComponents) { /** * If an object is 'Blacklisted' - we need to fire an event * to notify other systems that this component requests * to have its instance created. * * Should no system respond to this event we should continue * to create the instance */ let delayInstance = false; Event.Emit( Event.BLACKLISTED_COMPONENT_INSTANCE_REQUEST, object, function(response) { delayInstance = response; } ); return delayInstance; } } return false; } /** * OnObjectPropertyUpdate() * - Listens to events of type Event.OBJECT_PROPERTY_UPDATE and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnObjectPropertyUpdate(object) { let {value, property, r3Object} = object; // object = object.object; console.log('Object property update: ' + r3Object.constructor.name + '.' + property); /** * First we need to perform some sanity checks on the property */ SystemLinking.SanityChecks(r3Object, property, value); /** * Set the parent relationships */ SystemLinking.SetParentChildRelationships(r3Object, property, value); /** * Determine the ready state of the r3Object */ SystemLinking.DetermineReadyState(r3Object, property, value); /** * The value was unassigned - remove the parent relationships of the existing * components (if available) */ // if (value === null || typeof value === 'undefined') { // if (r3Object[property] instanceof R3Object) { // r3Object.dirty = true; // Utils.RemoveFromArray(r3Object[property].parents, r3Object); // } // } // // if (!r3Object.underConstruction) { // // if (r3Object.initialized) { // /** // * Check if this object will still be initialized after this assignment completes // */ // r3Object.setInitialized(property, value); // if (!r3Object.initialized) { // //We set this object back to initialized because it is still initialized - it WILL be not initialized in the future // r3Object.initialized = true; // r3Object.stop(); // } // } // } } /** * OnObjectPropertyUpdated() * - Listens to events of type Event.OBJECT_PROPERTY_UPDATED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnObjectPropertyUpdated(object) { let {r3Object} = object; if (!r3Object.underConstruction) { /** * At this point - the object entity would have been brought to a stop * if it had been transitioned into an uninitialized state by setting one of * its required properties to a null, empty or undefined state. * We can safely clear the dirty flag. */ if (r3Object.dirty) { r3Object.dirty = false; } r3Object.initializeDepth = 0; r3Object.initialize(); } } } /** * static Started - Indicates whether or not this system is running or not */ SystemLinking.Started = false; /** * static Subscriptions - An association object which hold the subscription handles for Events this system is listening * to. The system can stop receiving events by calling remove() on a handle. */ SystemLinking.Subscriptions = {}; /** * static BlacklistedComponents - A list of component constructors which should not be permitted to create instances immediately */ SystemLinking.BlacklistedComponents = []; class SystemRender extends System { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * Start() * - Starts the system by registering subscriptions to events * @param options */ static Start(options) { SystemRender.Subscriptions['INSTANCE_CREATED'] = Event.Subscribe( Event.INSTANCE_CREATED, SystemRender.OnInstanceCreated ); SystemRender.Started = true; console.log('Started system: SystemRender'); } /** * Stop() * - Stops the system by removing these subscriptions to events * @param options */ static Stop(options) { SystemRender.Subscriptions['INSTANCE_CREATED'].remove(); delete SystemRender.Subscriptions['INSTANCE_CREATED']; SystemRender.Started = false; console.log('Stopped system: SystemRender'); } /** * OnInstanceCreated() * - Listens to events of type Event.INSTANCE_CREATED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnInstanceCreated(object) { if (object instanceof R3.Component.DOM) { if (object.runtime instanceof R3.Runtime.DOM.Document) { document.body.appendChild(object.instance); } } if (object instanceof R3.Component.Graphics.Image) { if (object.runtime instanceof R3.Runtime.Image.WebImage) { document.body.appendChild(object.instance); } } } } /** * static Started - Indicates whether or not this system is running or not */ SystemRender.Started = false; /** * static Subscriptions - An association object which hold the subscription handles for Events this system is listening * to. The system can stop receiving events by calling remove() on a handle. */ SystemRender.Subscriptions = {}; class SystemRuntime extends System { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * Start() * - Starts the system by registering subscriptions to events * @param options */ static Start(options) { SystemRuntime.Subscriptions['RUNTIME_CREATED'] = Event.Subscribe( Event.RUNTIME_CREATED, SystemRuntime.OnRuntimeCreated ); SystemRuntime.Subscriptions['GET_RUNTIME'] = Event.Subscribe( Event.GET_RUNTIME, SystemRuntime.OnGetRuntime ); SystemRuntime.Subscriptions['PROJECT_INITIALIZED'] = Event.Subscribe( Event.PROJECT_INITIALIZED, SystemRuntime.OnProjectInitialized ); SystemRuntime.Subscriptions['GET_API_URL'] = Event.Subscribe( Event.GET_API_URL, SystemRuntime.OnGetApiUrl ); SystemRuntime.Started = true; console.log('Started system: SystemRuntime'); } /** * Stop() * - Stops the system by removing these subscriptions to events * @param options */ static Stop(options) { SystemRuntime.Subscriptions['RUNTIME_CREATED'].remove(); delete SystemRuntime.Subscriptions['RUNTIME_CREATED']; SystemRuntime.Subscriptions['GET_RUNTIME'].remove(); delete SystemRuntime.Subscriptions['GET_RUNTIME']; SystemRuntime.Subscriptions['PROJECT_INITIALIZED'].remove(); delete SystemRuntime.Subscriptions['PROJECT_INITIALIZED']; SystemRuntime.Subscriptions['GET_API_URL'].remove(); delete SystemRuntime.Subscriptions['GET_API_URL']; SystemRuntime.Started = false; console.log('Stopped system: SystemRuntime'); } /** * OnRuntimeCreated() * - Listens to events of type Event.RUNTIME_CREATED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnRuntimeCreated(object) { console.log('Runtime created : ' + object.name); } /** * OnGetRuntime() * - Listens to events of type Event.GET_RUNTIME and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnGetRuntime(object) { /** * DOM and Input Components are typically managed through * the DOM. */ if ( object instanceof R3.Component.DOM || object instanceof R3.Component.Input ) { if (SystemRuntime.CurrentProject === null) { return new R3.Runtime.DOM.Document(); } else { console.log('TODO: implement a project based DOM runtime'); } } if (object instanceof R3.Component.Graphics.Image) { if (SystemRuntime.CurrentProject === null) { return new R3.Runtime.Image.WebImage(); } else { console.log('TODO: implement a project based Image runtime'); } } } /** * OnProjectInitialized() * - Listens to events of type Event.PROJECT_INITIALIZED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnProjectInitialized(object) { Utils.PushUnique(SystemRuntime.Projects, object); SystemRuntime.CurrentProject = object; } /** * OnGetApiUrl() * - Listens to events of type Event.GET_API_URL and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnGetApiUrl(object) { if (SystemRuntime.CurrentProject === null) { /** * Check if we are running server side or client side */ if (process && process.env) { const {API_URL} = process.env; if (typeof API_URL === 'undefined') { throw new Error('No API_URL defined for this process'); } return API_URL; } else { return 'http://api.r3js.local'; } } } } /** * static Started - Indicates whether or not this system is running or not */ SystemRuntime.Started = false; /** * static Subscriptions - An association object which hold the subscription handles for Events this system is listening * to. The system can stop receiving events by calling remove() on a handle. */ SystemRuntime.Subscriptions = {}; /** * static Projects - No comment */ SystemRuntime.Projects = []; /** * static CurrentProject - No comment */ SystemRuntime.CurrentProject = null; /** * static RuntimeCoder - No comment */ SystemRuntime.RuntimeCoder = {}; /** * static RuntimeDOM - No comment */ SystemRuntime.RuntimeDOM = {}; /** * static RuntimeGUI - No comment */ SystemRuntime.RuntimeGUI = {}; /** * static RuntimeGraphics - No comment */ SystemRuntime.RuntimeGraphics = {}; /** * static RuntimePhysics - No comment */ SystemRuntime.RuntimePhysics = {}; /** * static RuntimeStatistics - No comment */ SystemRuntime.RuntimeStatistics = {}; class SystemSocket extends System { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * Start() * - Starts the system by registering subscriptions to events * @param options */ static Start(options) { SystemSocket.Started = true; console.log('Started system: SystemSocket'); } /** * Stop() * - Stops the system by removing these subscriptions to events * @param options */ static Stop(options) { SystemSocket.Started = false; console.log('Stopped system: SystemSocket'); } } /** * static Started - Indicates whether or not this system is running or not */ SystemSocket.Started = false; /** * static Subscriptions - An association object which hold the subscription handles for Events this system is listening * to. The system can stop receiving events by calling remove() on a handle. */ SystemSocket.Subscriptions = {}; class SystemStorage extends System { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * Start() * - Starts the system by registering subscriptions to events * @param options */ static Start(options) { SystemStorage.Subscriptions['IMAGE_INSTANCE_CREATED'] = Event.Subscribe( Event.IMAGE_INSTANCE_CREATED, SystemStorage.OnImageInstanceCreated ); SystemStorage.Started = true; console.log('Started system: SystemStorage'); } /** * Stop() * - Stops the system by removing these subscriptions to events * @param options */ static Stop(options) { SystemStorage.Subscriptions['IMAGE_INSTANCE_CREATED'].remove(); delete SystemStorage.Subscriptions['IMAGE_INSTANCE_CREATED']; SystemStorage.Started = false; console.log('Stopped system: SystemStorage'); } /** * OnImageInstanceCreated() * - Listens to events of type Event.IMAGE_INSTANCE_CREATED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnImageInstanceCreated(object) { Event.Emit( Event.GET_API_URL, null, function(apiUrl) { console.log('about to download image at: ' + apiUrl + object.external_path); } ); } } /** * static Started - Indicates whether or not this system is running or not */ SystemStorage.Started = false; /** * static Subscriptions - An association object which hold the subscription handles for Events this system is listening * to. The system can stop receiving events by calling remove() on a handle. */ SystemStorage.Subscriptions = {}; class Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } Object.assign(this, options); } } Runtime.BASE_CODER = 0x0; Runtime.BASE_DOM = 0x1; Runtime.BASE_GUI = 0x2; Runtime.BASE_GRAPHICS = 0x3; Runtime.BASE_IMAGE = 0x4; Runtime.BASE_PHYSICS = 0x5; Runtime.BASE_SOCKET = 0x6; Runtime.BASE_STATISTICS = 0x7; Runtime.CODE_MIRROR = 0x8; Runtime.DOCUMENT = 0x9; Runtime.CONTROL_KIT = 0xa; Runtime.THREE = 0xb; Runtime.NODE_JS_IMAGE = 0xc; Runtime.WEB_IMAGE = 0xd; Runtime.BULLET = 0xe; Runtime.STATS = 0xf; class RuntimeCoder extends Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * name - Name of the runtime */ if (typeof options.name === 'undefined') { options.name = this.constructor.name; } Object.assign(this, options); Event.Emit(Event.RUNTIME_CREATED, this); } } class RuntimeCodeMirror extends RuntimeCoder { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { } } class RuntimeDOM extends Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * name - Name of the runtime */ if (typeof options.name === 'undefined') { options.name = this.constructor.name; } Object.assign(this, options); Event.Emit(Event.RUNTIME_CREATED, this); } } class RuntimeDocument extends RuntimeDOM { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { if (component instanceof R3.Component.DOM.Canvas) { let canvas = document.createElement('canvas'); canvas.setAttribute('width', component.width); canvas.setAttribute('height', component.height); canvas.setAttribute('style', component.style); return canvas; } } } class RuntimeGUI extends Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * name - Name of the runtime */ if (typeof options.name === 'undefined') { options.name = this.constructor.name; } Object.assign(this, options); Event.Emit(Event.RUNTIME_CREATED, this); } } class RuntimeControlKit extends RuntimeGUI { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { } } class RuntimeGraphics extends Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * name - Name of the runtime */ if (typeof options.name === 'undefined') { options.name = this.constructor.name; } Object.assign(this, options); Event.Emit(Event.RUNTIME_CREATED, this); } } class RuntimeThree extends RuntimeGraphics { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { } } class RuntimeImage extends Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * name - Name of the runtime */ if (typeof options.name === 'undefined') { options.name = this.constructor.name; } Object.assign(this, options); Event.Emit(Event.RUNTIME_CREATED, this); } } class RuntimeNodeJSImage extends RuntimeImage { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { } } class RuntimeWebImage extends RuntimeImage { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { if (component instanceof R3.Component.Graphics.Image) { let image = document.createElement('img'); image.setAttribute('src', component.src); image.setAttribute('alt', component.alt); return image; } } } class RuntimePhysics extends Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * name - Name of the runtime */ if (typeof options.name === 'undefined') { options.name = this.constructor.name; } Object.assign(this, options); Event.Emit(Event.RUNTIME_CREATED, this); } } class RuntimeBullet extends RuntimePhysics { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { } } class RuntimeSocket extends Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * name - Name of the runtime */ if (typeof options.name === 'undefined') { options.name = this.constructor.name; } Object.assign(this, options); Event.Emit(Event.RUNTIME_CREATED, this); } } class RuntimeStatistics extends Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * name - Name of the runtime */ if (typeof options.name === 'undefined') { options.name = this.constructor.name; } Object.assign(this, options); Event.Emit(Event.RUNTIME_CREATED, this); } } class RuntimeStats extends RuntimeStatistics { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { } } class Event { constructor(options) { if (typeof options === 'undefined') { options = {}; } Object.assign(this, options); } /** * async() * - Simply calls 'Async()' passing it the arguments * @param eventId * @param data * @param clientCallback * @param clientErrorCallback */ async( eventId, data, clientCallback, clientErrorCallback ) { return Event.Async( eventId, data, clientCallback, clientErrorCallback ); } /** * emit() * - Simply calls 'Emit()' passing it the arguments * @param eventId * @param data * @param clientCallback * @param clientErrorCallback */ emit( eventId, data, clientCallback, clientErrorCallback ) { return Event.Emit( eventId, data, clientCallback, clientErrorCallback ); } /** * subscribe() * - Simply calls 'Subscribe()' passing it the arguments * @param eventId * @param callback */ subscribe( eventId, callback ) { return Event.Subscribe(eventId, callback.bind(this)); } /** * Async() * - Calls all subscription functions registered to eventId with data, clientCallback and clientErrorCallback as * arguments. If an error occurs during clientCallback it additionally will execute clientErrorCallback with the * error as argument. * @param eventId * @param data * @param clientCallback * @param clientErrorCallback */ static Async( eventId, data, clientCallback, clientErrorCallback ) { if (Event.Subscriptions.hasOwnProperty(eventId)) { let subscriptionIds = Object.keys(Event.Subscriptions[eventId]); subscriptionIds.map( function(subscriptionId) { try { Event.Subscriptions[eventId][subscriptionId](data, clientCallback, clientErrorCallback); } catch (error) { if (clientErrorCallback) { clientErrorCallback(error); } else { console.error(error); throw error; } } } ) } } /** * Emit() * - Calls all subscription functions registered to eventId with data as arg. Calls clientCallback directly after * the event result is obtained, passing it the result. If an exception occurs during execution, the * clientErrorCallback is called with the error as argument. * @param eventId * @param data * @param clientCallback * @param clientErrorCallback */ static Emit( eventId, data, clientCallback, clientErrorCallback ) { if (Event.Subscriptions.hasOwnProperty(eventId)) { let subscriptionIds = Object.keys(Event.Subscriptions[eventId]); subscriptionIds.map( function(subscriptionId) { try { let result = Event.Subscriptions[eventId][subscriptionId](data); if (clientCallback) { clientCallback(result); } } catch (error) { if (clientErrorCallback) { clientErrorCallback(error); } else { console.error(error); throw error; } } } ) } } /** * Subscribe() * - Subscribes to 'eventName', ex. Event.BEFORE_RENDER and executes 'callback()' when eventName is raised * @param eventId * @param callback * @returns {Object} - A handle to the subscription which can be removed by calling handle.remove() */ static Subscribe( eventId, callback ) { let subscriptionId = Utils.RandomId(10); if (Event.Subscriptions.hasOwnProperty(eventId)) { if (Event.Subscriptions[eventId][subscriptionId]) { throw new Error('A component can only subscribe to a particular event ID once'); } Event.Subscriptions[eventId][subscriptionId] = callback; } else { Event.Subscriptions[eventId] = {}; Event.Subscriptions[eventId][subscriptionId] = callback; } /** * Return a handle to the caller to allow us to unsubscribe to this event */ return { fn: callback, remove: function (eventId, subscriptionId) { return function () { /** * Stop listening for this event from this component */ delete Event.Subscriptions[eventId][subscriptionId]; /** * If the length of listeners is 0, stop referencing this event * @type {string[]} */ let listeners = Object.keys(Event.Subscriptions[eventId]); if (listeners.length === 0) { delete Event.Subscriptions[eventId]; } return true; } }(eventId, subscriptionId), subscriptionId : subscriptionId }; } /** * Some nice Events handling * @type {{}} */ static Subscriptions = {}; } Event.BEFORE_RENDER = 0x1; Event.BLACKLISTED_COMPONENT_INSTANCE_REQUEST = 0x2; Event.CANVAS_COMPONENT_INITIALIZED = 0x3; Event.COMPONENT_CREATED = 0x4; Event.COMPONENT_INITIALIZED = 0x5; Event.CREATE_INSTANCE_BEFORE = 0x6; Event.DISPOSE_INSTANCE = 0x7; Event.DISPOSE_OBJECT = 0x8; Event.DOM_COMPONENT_INITIALIZED = 0x9; Event.ENTITY_CREATED = 0xa; Event.ENTITY_INITIALIZED = 0xb; Event.ENTITY_STARTED = 0xc; Event.FINAL_INITIALIZE = 0xd; Event.GET_API_URL = 0xe; Event.GET_RUNTIME = 0xf; Event.GET_WINDOW_SIZE = 0x10; Event.GRAPHICS_COMPONENT_INITIALIZED = 0x11; Event.IMAGE_COMPONENT_INITIALIZED = 0x12; Event.IMAGE_INSTANCE_CREATED = 0x13; Event.INPUT_COMPONENT_INITIALIZED = 0x14; Event.INSTANCE_CREATED = 0x15; Event.INSTANCE_DISPOSED = 0x16; Event.KEYBOARD_DOWN = 0x17; Event.KEYBOARD_UP = 0x18; Event.MOUSE_DOWN = 0x19; Event.MOUSE_MOVE = 0x1a; Event.MOUSE_UP = 0x1b; Event.MOUSE_WHEEL = 0x1c; Event.OBJECT_CREATED = 0x1d; Event.OBJECT_PROPERTY_UPDATE = 0x1e; Event.OBJECT_PROPERTY_UPDATED = 0x1f; Event.PAUSE = 0x20; Event.PROJECT_INITIALIZED = 0x21; Event.RESTART = 0x22; Event.RUNTIME_CREATED = 0x23; Event.SLIDER_ENTITY_INITIALIZED = 0x24; Event.START = 0x25; Event.TOUCH_CANCEL = 0x26; Event.TOUCH_COMPONENT_INITIALIZED = 0x27; Event.TOUCH_END = 0x28; Event.TOUCH_MOVE = 0x29; Event.TOUCH_START = 0x2a; Event.UPDATE_FROM_INSTANCE_AFTER = 0x2b; Event.UPDATE_FROM_INSTANCE_BEFORE = 0x2c; Event.UPDATE_INSTANCE_AFTER = 0x2d; Event.UPDATE_INSTANCE_BEFORE = 0x2e; Event.UPDATE_INSTANCE_PROPERTY = 0x2f; Event.UPDATE_PROPERTY_FROM_INSTANCE = 0x30; Event.MAX_EVENTS = 0x31; Event.GetEventName = function(eventId) { switch(eventId) { case 0x1 : return 'before_render'; case 0x2 : return 'blacklisted_component_instance_request'; case 0x3 : return 'canvas_component_initialized'; case 0x4 : return 'component_created'; case 0x5 : return 'component_initialized'; case 0x6 : return 'create_instance_before'; case 0x7 : return 'dispose_instance'; case 0x8 : return 'dispose_object'; case 0x9 : return 'dom_component_initialized'; case 0xa : return 'entity_created'; case 0xb : return 'entity_initialized'; case 0xc : return 'entity_started'; case 0xd : return 'final_initialize'; case 0xe : return 'get_api_url'; case 0xf : return 'get_runtime'; case 0x10 : return 'get_window_size'; case 0x11 : return 'graphics_component_initialized'; case 0x12 : return 'image_component_initialized'; case 0x13 : return 'image_instance_created'; case 0x14 : return 'input_component_initialized'; case 0x15 : return 'instance_created'; case 0x16 : return 'instance_disposed'; case 0x17 : return 'keyboard_down'; case 0x18 : return 'keyboard_up'; case 0x19 : return 'mouse_down'; case 0x1a : return 'mouse_move'; case 0x1b : return 'mouse_up'; case 0x1c : return 'mouse_wheel'; case 0x1d : return 'object_created'; case 0x1e : return 'object_property_update'; case 0x1f : return 'object_property_updated'; case 0x20 : return 'pause'; case 0x21 : return 'project_initialized'; case 0x22 : return 'restart'; case 0x23 : return 'runtime_created'; case 0x24 : return 'slider_entity_initialized'; case 0x25 : return 'start'; case 0x26 : return 'touch_cancel'; case 0x27 : return 'touch_component_initialized'; case 0x28 : return 'touch_end'; case 0x29 : return 'touch_move'; case 0x2a : return 'touch_start'; case 0x2b : return 'update_from_instance_after'; case 0x2c : return 'update_from_instance_before'; case 0x2d : return 'update_instance_after'; case 0x2e : return 'update_instance_before'; case 0x2f : return 'update_instance_property'; case 0x30 : return 'update_property_from_instance'; default : throw new Error('Event type not defined : ' + eventId); } }; class R3Object extends Event { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * id - Each Object receives an 15 digit random ID which uniquely identifies it everywhere (client and * server side) */ if (typeof options.id === 'undefined') { options.id = Utils.RandomId(15); } /** * name - Each Object has a name */ if (typeof options.name === 'undefined') { options.name = this.constructor.name + '(' + options.id + ')'; } /** * dirty - When dirty is true, it means that this object is in transition from having a child Object to * not having this child Object, and will probably end up in an uninitialized state. During the * next few clock cycles this child Object will have its parent reference to this Object removed. */ if (typeof options.dirty === 'undefined') { options.dirty = false; } /** * initialized - A boolean which indicates whether or not this Object has initialized */ if (typeof options.initialized === 'undefined') { options.initialized = false; } /** * initializeDepth - The amount of times this Object passed through initialize() functions */ if (typeof options.initializeDepth === 'undefined') { options.initializeDepth = 0; } /** * parents - All Objects could have parent(s) */ if (typeof options.parents === 'undefined') { options.parents = []; } /** * children - All Objects could have some children */ if (typeof options.children === 'undefined') { options.children = []; } /** * required - All Objects could have some required Objects which need to initialize before this Object can * initialize */ if (typeof options.required === 'undefined') { options.required = [Object]; } Object.assign(this, options); this.emit(Event.OBJECT_CREATED, this); if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Initializes this Object if all of its required Objects are initialized */ initialize() { this.initialized = true; if (this.initializeDepth === this.maxDepth) { throw new Error('You should not try to instantiate this base class - extend it rather...'); } else { this.initializeDepth++; } } } R3Object.PROJECT = 0x0; R3Object.ENTITY = 0x1; R3Object.ENTITY_SLIDER = 0x2; R3Object.COMPONENT = 0x3; R3Object.COMPONENT_CODE = 0x4; R3Object.COMPONENT_DOM = 0x5; R3Object.COMPONENT_CANVAS = 0x6; R3Object.COMPONENT_GEOMETRY = 0x7; R3Object.COMPONENT_BUFFER_GEOMETRY = 0x8; R3Object.COMPONENT_PLANE_GEOMETRY = 0x9; R3Object.COMPONENT_GRAPHICS = 0xa; R3Object.COMPONENT_IMAGE = 0xb; R3Object.COMPONENT_INPUT = 0xc; R3Object.COMPONENT_TOUCH = 0xd; R3Object.COMPONENT_MATERIAL = 0xe; R3Object.COMPONENT_MESH = 0xf; R3Object.COMPONENT_TEXTURE = 0x10; R3Object.MAX_R3OBJECT = 0x11; class Project extends R3Object { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.initialized === 'undefined') { options.initialized = false; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Initializes this Object if all of its required Objects are initialized */ initialize() { super.initialize(); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } } class Entity extends R3Object { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); Object.assign(this, options); this.emit(Event.ENTITY_CREATED, this); if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Starts the Entity if all of its sub-Components are initialized */ initialize() { super.initialize(); Event.Emit(Event.ENTITY_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { throw new Error('You should not try to instantiate this base class - extend it rather...'); } else { this.initializeDepth++; } } } Entity.SLIDER = 0x0; Entity.MAX_ENTITY = 0x1; /** [0]images=[ComponentImage] - We need a list of at least one Image which to slide [0]canvas=ComponentCanvas - We need a Canvas to attach our Input events [0]touch=ComponentTouch - We need a Touch Component to respond to TOUCH Events **/ class EntitySlider extends Entity { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * components - A list of components that this entity is composed of */ if (typeof options.components === 'undefined') { options.components = []; } /** * started - Indicates whether or not this entity is active (subscribing to events) or not */ if (typeof options.started === 'undefined') { options.started = false; } /** * subscriptions - An association object which hold the subscription handles for Events this system is listening * to. The system can stop receiving events by calling remove() on a handle. */ if (typeof options.subscriptions === 'undefined') { options.subscriptions = {}; } /** * We need to assign some options here to the entity * because SystemLinking requires 'components[]' to * exist when determining parent / child relationships */ Object.assign(this, options); /** * canvas - The Canvas Component to which this entity binds its custom code components */ if (typeof options.canvas === 'undefined') { options.canvas = null; } /** * touch - The slider component reacts to TOUCH Events, for this a touch component is required */ if (typeof options.touch === 'undefined') { options.touch = null; } /** * images - The Image Components which will be used to slide from one image to the next */ if (typeof options.images === 'undefined') { options.images = []; } /** * images - We need a list of at least one Image which to slide */ if (typeof options.required[0] === 'undefined') { options.required[0] = {}; } options.required[0].images = [ComponentImage]; this.imagesBackup = options.images; Object.defineProperty( this, 'images', { configurable : true, enumerable : true, set: function(x) { Event.Emit( Event.OBJECT_PROPERTY_UPDATE, { r3Object : this, property : 'images', value : x } ); this.imagesBackup = x; Event.Emit( Event.OBJECT_PROPERTY_UPDATED, { r3Object : this, property : 'images', value : x } ); return x; }, get : function() { return this.imagesBackup; } } ) /** * canvas - We need a Canvas to attach our Input events */ if (typeof options.required[0] === 'undefined') { options.required[0] = {}; } options.required[0].canvas = ComponentCanvas; this.canvasBackup = options.canvas; Object.defineProperty( this, 'canvas', { configurable : true, enumerable : true, set: function(x) { Event.Emit( Event.OBJECT_PROPERTY_UPDATE, { r3Object : this, property : 'canvas', value : x } ); this.canvasBackup = x; Event.Emit( Event.OBJECT_PROPERTY_UPDATED, { r3Object : this, property : 'canvas', value : x } ); return x; }, get : function() { return this.canvasBackup; } } ) /** * touch - We need a Touch Component to respond to TOUCH Events */ if (typeof options.required[0] === 'undefined') { options.required[0] = {}; } options.required[0].touch = ComponentTouch; this.touchBackup = options.touch; Object.defineProperty( this, 'touch', { configurable : true, enumerable : true, set: function(x) { Event.Emit( Event.OBJECT_PROPERTY_UPDATE, { r3Object : this, property : 'touch', value : x } ); this.touchBackup = x; Event.Emit( Event.OBJECT_PROPERTY_UPDATED, { r3Object : this, property : 'touch', value : x } ); return x; }, get : function() { return this.touchBackup; } } ) this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Starts the Entity if all of its sub-Components are initialized. Calls the parent initialize(). */ initialize() { super.initialize(); Event.Emit(Event.SLIDER_ENTITY_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * start() * - Starts the entity by subscribing to all events */ start() { this.subscriptions['BEFORE_RENDER'] = this.subscribe( Event.BEFORE_RENDER, this.OnBeforeRender ); this.subscriptions['TOUCH_START'] = this.subscribe( Event.TOUCH_START, this.OnTouchStart ); this.subscriptions['TOUCH_MOVE'] = this.subscribe( Event.TOUCH_MOVE, this.OnTouchMove ); this.subscriptions['TOUCH_END'] = this.subscribe( Event.TOUCH_END, this.OnTouchEnd ); this.started = true; if (this instanceof R3.Entity) { this.emit( Event.ENTITY_STARTED, this ); } console.log('Started transient system: EntitySlider'); } /** * stop() * - Stops this entity by removing all subscriptions to events */ stop() { this.subscriptions['BEFORE_RENDER'].remove(); delete this.subscriptions['BEFORE_RENDER']; this.subscriptions['TOUCH_START'].remove(); delete this.subscriptions['TOUCH_START']; this.subscriptions['TOUCH_MOVE'].remove(); delete this.subscriptions['TOUCH_MOVE']; this.subscriptions['TOUCH_END'].remove(); delete this.subscriptions['TOUCH_END']; this.started = false; console.log('Stopped transient system: EntitySlider'); } /** * OnBeforeRender() * - Listens to events of type Event.BEFORE_RENDER and executes this function. * @param data (The event data passed as argument - typically an R3Object) * @return null */ OnBeforeRender(data) { } /** * OnTouchStart() * - Listens to events of type Event.TOUCH_START and executes this function. * @param data (The event data passed as argument - typically an R3Object) * @return null */ OnTouchStart(data) { } /** * OnTouchMove() * - Listens to events of type Event.TOUCH_MOVE and executes this function. * @param data (The event data passed as argument - typically an R3Object) * @return null */ OnTouchMove(data) { } /** * OnTouchEnd() * - Listens to events of type Event.TOUCH_END and executes this function. * @param data (The event data passed as argument - typically an R3Object) * @return null */ OnTouchEnd(data) { } } class Component extends R3Object { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); Object.assign(this, options); this.emit(Event.COMPONENT_CREATED, this); if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Should raises an event(s) which indicates that this object initialized */ initialize() { super.initialize(); Event.Emit(Event.COMPONENT_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { throw new Error('You should not try to instantiate this base class - extend it rather...'); } else { this.initializeDepth++; } } /** * createInstance() * - Creates an instance of this object based on the current runtime */ createInstance() { this.emit( Event.CREATE_INSTANCE_BEFORE, this, function(delayInstance) { if (delayInstance === true) { console.log('Instance creation delayed for ' + this.name); } else { /** * Set the runtime */ this.setRuntime(); /** * Let the runtime build the instance */ this.instance = this.runtime.buildInstance(this); /** * Notify anyone who might be interested */ this.emit(Event.INSTANCE_CREATED, this); } }.bind(this) ); } /** * dispose() * - Disposes of this object by disposing the instance first. */ dispose() { this.subscribe( Event.INSTANCE_DISPOSED, function(object) { if (object === this) { this.emit(Event.DISPOSE_OBJECT, this); } } ); this.disposeInstance(); } /** * disposeInstance() * - Disposes of the runtime instance. */ disposeInstance() { console.log('Disposing instance of ' + this.name); this.emit(Event.DISPOSE_INSTANCE, this); } /** * setRuntime() * - Sets the runtime property of this component required for constructing an instance of this component. */ setRuntime() { this.emit( Event.GET_RUNTIME, this, function(runtime) { this.runtime = runtime; }.bind(this) ) } } Component.CODE = 0x0; Component.DOM = 0x1; Component.CANVAS = 0x2; Component.GEOMETRY = 0x3; Component.BUFFER_GEOMETRY = 0x4; Component.PLANE_GEOMETRY = 0x5; Component.GRAPHICS = 0x6; Component.IMAGE = 0x7; Component.INPUT = 0x8; Component.TOUCH = 0x9; Component.MATERIAL = 0xa; Component.MESH = 0xb; Component.TEXTURE = 0xc; Component.MAX_COMPONENT = 0xd; class ComponentCode extends Component { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentDOM extends Component { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); Event.Emit(Event.DOM_COMPONENT_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); if (property === 'instance') { this.instance.instance = this.instance; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'instance', instanceProperty : 'instance' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); if (property === 'instance' || property === 'all') { this.instance = this.instance.instance; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'instance', instanceProperty : 'instance' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentCanvas extends ComponentDOM { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } /** * type - No comment */ if (typeof options.type === 'undefined') { options.type = 'canvas'; } /** * width - The initial width of the canvas (You can override it with CSS) */ if (typeof options.width === 'undefined') { options.width = 500; } /** * height - The initial height of the canvas (You can override it with CSS) */ if (typeof options.height === 'undefined') { options.height = 500; } /** * style - No comment */ if (typeof options.style === 'undefined') { options.style = 'border:1px solid #00bb00;'; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); Event.Emit(Event.CANVAS_COMPONENT_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); if (property === 'width') { this.instance.width = this.width; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'width', instanceProperty : 'width' } ); if (property !== 'all') { return; } } if (property === 'height') { this.instance.height = this.height; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'height', instanceProperty : 'height' } ); if (property !== 'all') { return; } } if (property === 'style') { this.instance.style = this.style; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'style', instanceProperty : 'style' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); if (property === 'width' || property === 'all') { this.width = this.instance.width; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'width', instanceProperty : 'width' } ); if (property !== 'all') { return; } } if (property === 'height' || property === 'all') { this.height = this.instance.height; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'height', instanceProperty : 'height' } ); if (property !== 'all') { return; } } if (property === 'style' || property === 'all') { this.style = this.instance.style; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'style', instanceProperty : 'style' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentGeometry extends Component { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentBufferGeometry extends ComponentGeometry { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentPlaneGeometry extends ComponentBufferGeometry { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentGraphics extends Component { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); Event.Emit(Event.GRAPHICS_COMPONENT_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentImage extends ComponentGraphics { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } /** * src - The src attribute of this image, defaults to a 15% opaque 1x1 green pixel */ if (typeof options.src === 'undefined') { options.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TpSotDu0g4pChumhBVMRRq1CECqFWaNXB5NIvaNKQpLg4Cq4FBz8Wqw4uzro6uAqC4AeIm5uToouU+L+k0CLWg+N+vLv3uHsHCPUy06yucUDTbTOViIuZ7KoYeEUvwgghgFGZWcacJCXRcXzdw8fXuxjP6nzuzxFScxYDfCLxLDNMm3iDeHrTNjjvE0dYUVaJz4nHTLog8SPXFY/fOBdcFnhmxEyn5okjxGKhjZU2ZkVTI54ijqqaTvlCxmOV8xZnrVxlzXvyFwZz+soy12kOIYFFLEGCCAVVlFCGjRitOikWUrQf7+AfdP0SuRRylcDIsYAKNMiuH/wPfndr5ScnvKRgHOh+cZyPYSCwCzRqjvN97DiNE8D/DFzpLX+lDsx8kl5radEjoH8buLhuacoecLkDDDwZsim7kp+mkM8D72f0TVkgfAv0rXm9Nfdx+gCkqavkDXBwCIwUKHu9w7t72nv790yzvx9QEXKZwXQv9wAAAAZiS0dEADIANAAxUB4d7wAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+UJEw0JHX/9/lIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAADUlEQVQI12Ng+M+gBgADKAEm2Ka93QAAAABJRU5ErkJggg=='; } /** * alt - The alt attribute of this image */ if (typeof options.alt === 'undefined') { options.alt = '15% opaque 1x1 green pixel'; } /** * fileName - Name of the image under which it is stored */ if (typeof options.fileName === 'undefined') { options.fileName = Utils.LowerUnderscore(options.name); } /** * extension - Extension of the file name including the '.' (ex. '.jpg') */ if (typeof options.extension === 'undefined') { options.extension = '.png'; } /** * external_path - Path to the image relative to the project defined API URL */ if (typeof options.external_path === 'undefined') { options.external_path = '/images/' + options.id + '.png'; } /** * internal_path - Server side path on the server to the file, excluding filename */ if (typeof options.internal_path === 'undefined') { options.internal_path = '/tmp/' + options.id + '.png'; } /** * contentType - Content type of the file (based on the extension, ex. 'image/jpeg') */ if (typeof options.contentType === 'undefined') { options.contentType = 'image/png'; } /** * size - Size of the file in bytes */ if (typeof options.size === 'undefined') { options.size = 565; } /** * width - Width of the image in pixels */ if (typeof options.width === 'undefined') { options.width = 1; } /** * height - height of the image in pixels */ if (typeof options.height === 'undefined') { options.height = 1; } /** * orientation - The orientation of the image, one of 'square', 'landscape', 'portrait' */ if (typeof options.orientation === 'undefined') { options.orientation = 'square'; } if (options.extension.match(/(png)$/i)) { options.contentType = 'image/png'; } if (options.extension.match(/(jpg|jpeg)$/i)) { options.contentType = 'image/jpeg'; } if (options.extension.match(/(gif)$/i)) { options.contentType = 'image/gif'; } if (options.width > options.height) { options.orientation = 'landscape'; } if (options.width < options.height) { options.orientation = 'portrait'; } if (options.width === options.height) { options.orientation = 'square'; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); Event.Emit(Event.IMAGE_COMPONENT_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); if (property === 'src') { this.instance.src = this.src; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'src', instanceProperty : 'src' } ); if (property !== 'all') { return; } } if (property === 'alt') { this.instance.alt = this.alt; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'alt', instanceProperty : 'alt' } ); if (property !== 'all') { return; } } if (property === 'external_path') { this.instance.external_path = this.external_path; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'external_path', instanceProperty : 'external_path' } ); if (property !== 'all') { return; } } if (property === 'internal_path') { this.instance.internal_path = this.internal_path; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'internal_path', instanceProperty : 'internal_path' } ); if (property !== 'all') { return; } } if (property === 'size') { this.instance.size = this.size; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'size', instanceProperty : 'size' } ); if (property !== 'all') { return; } } if (property === 'width') { this.instance.width = this.width; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'width', instanceProperty : 'width' } ); if (property !== 'all') { return; } } if (property === 'height') { this.instance.height = this.height; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'height', instanceProperty : 'height' } ); if (property !== 'all') { return; } } if (property === 'orientation') { this.instance.orientation = this.orientation; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'orientation', instanceProperty : 'orientation' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); if (property === 'src' || property === 'all') { this.src = this.instance.src; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'src', instanceProperty : 'src' } ); if (property !== 'all') { return; } } if (property === 'alt' || property === 'all') { this.alt = this.instance.alt; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'alt', instanceProperty : 'alt' } ); if (property !== 'all') { return; } } if (property === 'external_path' || property === 'all') { this.external_path = this.instance.external_path; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'external_path', instanceProperty : 'external_path' } ); if (property !== 'all') { return; } } if (property === 'internal_path' || property === 'all') { this.internal_path = this.instance.internal_path; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'internal_path', instanceProperty : 'internal_path' } ); if (property !== 'all') { return; } } if (property === 'size' || property === 'all') { this.size = this.instance.size; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'size', instanceProperty : 'size' } ); if (property !== 'all') { return; } } if (property === 'width' || property === 'all') { this.width = this.instance.width; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'width', instanceProperty : 'width' } ); if (property !== 'all') { return; } } if (property === 'height' || property === 'all') { this.height = this.instance.height; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'height', instanceProperty : 'height' } ); if (property !== 'all') { return; } } if (property === 'orientation' || property === 'all') { this.orientation = this.instance.orientation; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'orientation', instanceProperty : 'orientation' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentInput extends Component { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); Event.Emit(Event.INPUT_COMPONENT_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } /** [0]domComponent=ComponentDOM **/ class ComponentTouch extends ComponentInput { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } /** * domComponent - A Touch Component requires a Canvas component to bind to. */ if (typeof options.domComponent === 'undefined') { options.domComponent = null; } /** * domComponent - No comment */ if (typeof options.required[0] === 'undefined') { options.required[0] = {}; } options.required[0].domComponent = ComponentDOM; this.domComponentBackup = options.domComponent; Object.defineProperty( this, 'domComponent', { configurable : true, enumerable : true, set: function(x) { Event.Emit( Event.OBJECT_PROPERTY_UPDATE, { r3Object : this, property : 'domComponent', value : x } ); this.domComponentBackup = x; Event.Emit( Event.OBJECT_PROPERTY_UPDATED, { r3Object : this, property : 'domComponent', value : x } ); return x; }, get : function() { return this.domComponentBackup; } } ) this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); Event.Emit(Event.TOUCH_COMPONENT_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); if (property === 'domComponent') { this.instance.domComponent = this.domComponent; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'domComponent', instanceProperty : 'domComponent' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); if (property === 'domComponent' || property === 'all') { this.domComponent = this.instance.domComponent; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'domComponent', instanceProperty : 'domComponent' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentMaterial extends Component { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentMesh extends Component { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentTexture extends Component { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class Utils { constructor(options) { if (typeof options === 'undefined') { options = {}; } Object.assign(this, options); } static GetFirstParent(object, constructor) { if (Utils.UndefinedOrNull(constructor)) { throw new Error('You need to specify a constructor'); } if (object.parent === null) { return null; } if (object.parent instanceof constructor) { return object.parent; } else { return Utils.GetFirstParent(object.parent, constructor); } }; static SyntaxHighlight(json) { if (typeof json != 'string') { json = JSON.stringify(json, undefined, 2); } json = json.replace(/&/g, '&').replace(//g, '>'); return json.replace(/("(\u[a-zA-Z0-9]{4}|\[^u]|[^\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { let cls = 'number'; if (/^"/.test(match)) { if (/:$/.test(match)) { cls = 'key'; } else { cls = 'string'; } } else if (/true|false/.test(match)) { cls = 'boolean'; } else if (/null/.test(match)) { cls = 'null'; } return '' + match + ''; }); }; static GetParentProject(component) { if (Utils.UndefinedOrNull(component.parent)) { throw new Error('Parent not found'); } if (component.parent instanceof R3.Project) { return component.parent; } return Utils.GetParentProject(component.parent); }; static GetParents(component, parents) { if (Utils.UndefinedOrNull(parents)) { parents = []; } if (Utils.UndefinedOrNull(component.parent)) { return parents; } parents.push(component.parent); return Utils.GetParents(component.parent, parents); }; /** * @return {boolean} */ static Instance(component) { return Utils.Defined(component) && Utils.Defined(component.instance); }; /** * Utils.RemoveFromSelect * @param select * @param id * @returns {boolean} * @constructor */ static RemoveFromSelect(select, id) { let i; for (i = 0; i < select.options.length; i++) { if (select.options[i].value === id) { select.remove(i); return true; } } return false; }; /** * Utils.GetSelectIndex * * Get the select index of given id * * @param select * @param id * @returns boolean true if successful * * @constructor */ static SetSelectIndex(select, id) { for (let i = 0; i < select.options.length; i++) { if (select.options[i].value === id) { select.selectedIndex = i; return true; } } return false; }; static SortSelect(select) { let tmp = []; let i; for (i = 1; i < select.options.length; i++) { tmp[i-1] = []; tmp[i-1][0] = select.options[i].text; tmp[i-1][1] = select.options[i].value; } tmp.sort(); select.options = [select.options[0]]; for (i = 0; i < tmp.length; i++) { select.options[i+1] = new Option(tmp[i][0], tmp[i][1]); } return; }; /** * Gets the parent of object whith property of optional type constructor. If index is specified, get the parent of the * object with property[index] - which means the property should be an array * @param object * @param property * @param index * @param constructor * @returns {*} * @constructor */ static GetParent(object, property, index, constructor) { if (Utils.UndefinedOrNull(constructor)) { constructor = null; } if (Utils.UndefinedOrNull(index)) { index = null; } if (object.parent) { /** * Parent defined */ if (object.parent.hasOwnProperty(property)) { if (constructor) { if (index) { if (object.parent[property][index] instanceof constructor) { return object.parent[property][index]; } else { if (typeof object.parent.getParent === 'function') { return object.parent.getParent(property, index, constructor); } else { console.warn('getParent not defined on API object : ' + object.parent + ' - you should avoid having these messsages'); return null; } } } else { if (object.parent[property] instanceof constructor) { return object.parent[property]; } else { if (typeof object.parent.getParent === 'function') { return object.parent.getParent(property, index, constructor); } else { console.warn('getParent not defined on API object : ' + object.parent + ' - you should avoid having these messsages'); return null; } } } } else { if (index) { return object.parent[property][index]; } else { return object.parent[property]; } } } else { /** * This parent does not have the property - go a level higher */ if (typeof object.parent.getParent === 'function') { return object.parent.getParent(property, index, constructor); } else { console.warn('getParent not defined on API object : ' + object.parent + ' - you should avoid having these messsages'); return null; } } } else { /** * No parent defined */ console.warn('property : ' + property + ' of type ' + constructor + ' was not found in the parent chain'); return null; } }; /** * Strips image extension from given path * @param imagePath * @constructor */ static StripImageExtension(imagePath) { return imagePath.replace(/(\.png$|\.gif$|\.jpeg$|\.jpg$)/,'') }; /** * Returns true if unloaded * @param component * @returns {boolean} * @constructor */ static Unloaded(component) { if ( Utils.UndefinedOrNull(component) || Utils.UndefinedOrNull(component.instance) ) { return true; } return false; }; /** * * @param component * @returns {boolean} * @constructor */ static Loaded(component) { if (component && component.instance) { return true; } return false; }; static BuildVectorSource(result, name, dimension) { if (dimension === 2) { result[name] = {}; result[name].x = false; result[name].y = false; return; } if (dimension === 3) { result[name] = {}; result[name].x = false; result[name].y = false; result[name].y = false; return; } if (dimension === 4) { result[name] = {}; result[name].x = false; result[name].y = false; result[name].z = false; result[name].w = false; return; } console.warn('unknown dimension : ' + dimension); }; /** * Returns all 'instances' of the array, or null if an 'instance' is undefined * @constructor * @param array */ static GetArrayInstances(array) { return array.reduce( function(result, object) { if (result === null) { return result; } if (Utils.UndefinedOrNull(object.instance)) { result = null; } else { result.push(object.instance); } return result; }, [] ); }; static SortFacesByMaterialIndex(faces) { /** * Sorts faces according to material index because later we will create * groups for each vertice group */ faces.sort(function(a, b) { if (a.materialIndex < b.materialIndex) { return -1; } if (a.materialIndex > b.materialIndex) { return 1; } return 0; }); return faces; }; static BuildQuaternionSource(result, name) { result[name] = {}; result[name].axis = {}; result[name].axis.x = false; result[name].axis.y = false; result[name].axis.z = false; result[name].angle = false; result[name].x = false; result[name].y = false; result[name].z = false; result[name].w = false; }; static GetRuntime() { let result = null; R3.Event.Emit( R3.Event.GET_RUNTIME, null, function(runtime) { result = runtime; } ); return result; }; /** * Returns the window size or null * @returns {*} * @constructor */ static GetWindowSize() { let size = null; R3.Event.Emit( R3.Event.GET_WINDOW_SIZE, null, function(data) { size = data; }.bind(this) ); return size; }; /** * Convenience function to update object width and height members with window size * @param object * @constructor */ static UpdateWindowSize(object) { let size = Utils.GetWindowSize(); object.width = size.width; object.height = size.height; }; /** * Returns id of object with the name if it exists in the array, otherwise null * @param name * @param array * @returns {*} * @constructor */ static ObjectIdWithNameInArray(name, array) { return array.reduce( function(result, object) { if (result) { return result; } if (name === object.name) { return object.id; } return null; }, null ); }; /** * Gets random int exclusive of maximum but inclusive of minimum * @param min * @param max * @returns {*} * @constructor */ static GetRandomInt(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive }; /** * Gets random int inclusive of minimum and maximum * @param min * @param max * @returns {*} * @constructor */ static GetRandomIntInclusive(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min + 1)) + min; //The maximum is inclusive and the minimum is inclusive }; static InterpolateArray(data, fitCount) { let linearInterpolate = function(before, after, atPoint) { return before + (after - before) * atPoint; }; let newData = []; let springFactor = Number((data.length - 1) / (fitCount - 1)); newData[0] = data[0]; // for new allocation for ( let i = 1; i < fitCount - 1; i++) { let tmp = i * springFactor; let before = Number(Math.floor(tmp)).toFixed(); let after = Number(Math.ceil(tmp)).toFixed(); let atPoint = tmp - before; newData[i] = linearInterpolate(data[before], data[after], atPoint); } newData[fitCount - 1] = data[data.length - 1]; // for new allocation return newData; }; /** * Undefined or null check * @param variable * @returns {boolean} * @constructor */ static UndefinedOrNull( variable ) { return typeof variable === 'undefined' || variable === null; }; /** * The variable is not undefined and not null * @param variable * @returns {boolean} * @constructor */ static Defined( variable ) { return typeof variable !== 'undefined' && variable !== null; }; /** * Gets function parameters * @param fn * @constructor */ static GetParameters(fn) { let FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; let FN_ARG_SPLIT = /,/; let FN_ARG = /^\s*(_?)(.+?)\s*$/; let STRIP_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,\)]*(('(?:\'|[^'\r\n])*')|("(?:\"|[^"\r\n])*"))|(\s*=[^,\)]*))/mg; let parameters, fnText, argDecl; if (typeof fn !== 'function') { parameters = []; fnText = fn.toString().replace(STRIP_COMMENTS, ''); argDecl = fnText.match(FN_ARGS); argDecl[1].split(FN_ARG_SPLIT).forEach(function(arg) { arg.replace(FN_ARG, function(all, underscore, name) { parameters.push(name); }); }); } else { throw Error("not a function") } return parameters; }; /** * Returns either an ID of the object or Null * @param object * @returns {null} * @constructor */ static IdOrNull(object) { if (Utils.UndefinedOrNull(object)) { return null; } else { if (Utils.UndefinedOrNull(object.id)) { console.warn('saving an object reference with no ID : ', object); return null; } return object.id; } }; /** * Limit a property to values between -pi and +pi * @param property * @param objectProperty * @returns {{configurable?: boolean, enumerable?: boolean, value?, writable?: boolean, get?: Function, set?: Function}} * @constructor */ static LimitToPI(property, objectProperty) { let store = objectProperty; return { get : function() { return store; }, set : function(value) { while (value > Math.PI) { value -= (Math.PI * 2); } while (value < -(Math.PI)) { value += (Math.PI * 2); } store = value; } }; }; /** * Returns an array of IDs representing the objects * @param array * @returns [] * @constructor */ static IdArrayOrEmptyArray(array) { if (Utils.UndefinedOrNull(array)) { return []; } else { return array.map(function(item) { if (Utils.UndefinedOrNull(item.id)) { throw new Error('No ID found while trying to store IDs to array'); } return item.id }); } }; /** * Generates a random ID * @returns {string} * @constructor */ static RandomId(length) { if (Utils.UndefinedOrNull(length)) { length = 10; } return Math.random().toString(36).substr(2, length); }; static InvertWindingOrder(triangles) { for (let i = 0; i < triangles.length; i++) { let v1 = triangles[i].v1; triangles[i].v1 = triangles[i].v2; triangles[i].v2 = v1; let backupUV = triangles[i].triangle.v1uv; triangles[i].triangle.v1uv = triangles[i].triangle.v2uv; triangles[i].triangle.v2uv = backupUV; } return triangles; }; /** * Inverts a mesh winding order (and its instance) * @param mesh R3.D3.Mesh * @returns {*} * @constructor */ static InvertMeshWindingOrder(mesh) { mesh.faces.forEach( function(face) { let tmpV1 = face.v1; face.v1 = face.v2; face.v2 = tmpV1; let tmpV1uv = face.v1uv; face.v1uv = face.v2uv; face.v2uv = tmpV1uv; }.bind(this) ); //mesh.computeNormals = true; //mesh.createInstance(); }; /** * This function resets a the winding order of a mesh from a reference point V (the average center of the mesh) */ static ResetWindingOrder(faces, vertices) { let vertexList = new R3.API.Vector3.Points(); for (let v = 0; v < vertices.length; v++) { vertexList.add(new R3.API.Vector3( vertices[v].position.x, vertices[v].position.y, vertices[v].position.z )); } let V = vertexList.average(); let triangles = []; for (let s = 0; s < faces.length; s += 3) { let v0 = faces[s]; let v1 = faces[s+1]; let v2 = faces[s+2]; triangles.push( { v0 : v0, v1 : v1, v2 : v2, edges : [ {v0: v0, v1: v1}, {v0: v1, v1: v2}, {v0: v2, v1: v0} ], winding : 0, edgeIndex : -1, processed : false } ); } for (let i = 0; i < triangles.length; i++) { if ( R3.API.Vector3.clockwise( vertices[triangles[i].v0].position, vertices[triangles[i].v1].position, vertices[triangles[i].v2].position, V ) ) { console.log('clockwise'); let bv1 = triangles[i].v1; triangles[i].v1 = triangles[i].v2; triangles[i].v2 = bv1; } else { console.log('not clockwise'); } } return triangles; }; /** * This function resets the winding order for triangles in faces, given an initial triangle and orientation edge * used pseudocode from * http://stackoverflow.com/questions/17036970/how-to-correct-winding-of-triangles-to-counter-clockwise-direction-of-a-3d-mesh * We need to use a graph traversal algorithm, * lets assume we have method that returns neighbor of triangle on given edge * * neighbor_on_egde( next_tria, edge ) * * to_process = set of pairs triangle and orientation edge, initial state is one good oriented triangle with any edge on it * processed = set of processed triangles; initial empty * * while to_process is not empty: * next_tria, orientation_edge = to_process.pop() * add next_tria in processed * if next_tria is not opposite oriented than orientation_edge: * change next_tria (ABC) orientation (B<->C) * for each edge (AB) in next_tria: * neighbor_tria = neighbor_on_egde( next_tria, edge ) * if neighbor_tria exists and neighbor_tria not in processed: * to_process add (neighbor_tria, edge opposite oriented (BA)) * @param faces R3.D3.Face[] * @param orientationEdge R3.API.Vector2 * @returns {Array} */ static FixWindingOrder(faces, orientationEdge) { /** * Checks if a Face belonging to a TriangleEdge has already been processed * @param processed TriangleEdge[] * @param triangle Face * @returns {boolean} */ function inProcessed(processed, triangle) { for (let i = 0; i < processed.length; i++) { if (processed[i].triangle.equals(triangle)) { return true; } } return false; } /** * Returns a neighbouring triangle on a specific edge - preserving the edge orientation * @param edge R3.API.Vector2 * @param faces R3.D3.Face[] * @param currentTriangle * @returns {*} */ function neighbourOnEdge(edge, faces, currentTriangle) { for (let i = 0; i < faces.length; i++) { if ( (faces[i].v0 === edge.x && faces[i].v1 === edge.y) || (faces[i].v1 === edge.x && faces[i].v2 === edge.y) || (faces[i].v2 === edge.x && faces[i].v0 === edge.y) || (faces[i].v0 === edge.y && faces[i].v1 === edge.x) || (faces[i].v1 === edge.y && faces[i].v2 === edge.x) || (faces[i].v2 === edge.y && faces[i].v0 === edge.x) ) { let triangle = new R3.D3.API.Face( null, null, faces[i].v0index, faces[i].v1index, faces[i].v2index, faces[i].materialIndex, faces[i].uvs ); if (triangle.equals(currentTriangle)) { continue; } return new R3.D3.TriangleEdge( triangle, edge ); } } return null; } let toProcess = [ new R3.D3.TriangleEdge( new R3.D3.API.Face( null, null, faces[0].v0index, faces[0].v1index, faces[0].v2index, faces[0].materialIndex, faces[0].uvs ), orientationEdge ) ]; let processed = []; while (toProcess.length > 0) { let triangleEdge = toProcess.pop(); /** * If edge is the same orientation (i.e. the edge order is the same as the given triangle edge) it needs to be reversed * to have the same winding order) */ if ( (triangleEdge.triangle.v0index === triangleEdge.edge.x && triangleEdge.triangle.v1index === triangleEdge.edge.y) || (triangleEdge.triangle.v1index === triangleEdge.edge.x && triangleEdge.triangle.v2index === triangleEdge.edge.y) || (triangleEdge.triangle.v2index === triangleEdge.edge.x && triangleEdge.triangle.v0index === triangleEdge.edge.y) ) { let backupV = triangleEdge.triangle.v1index; triangleEdge.triangle.v1index = triangleEdge.triangle.v2index; triangleEdge.triangle.v2index = backupV; // let backupUV = triangleEdge.triangle.v1uv; // triangleEdge.triangle.v1uv = triangleEdge.triangle.v2uv; // triangleEdge.triangle.v2uv = backupUV; // let backupUV = triangleEdge.triangle.uvs[0][1]; triangleEdge.triangle.uvs[0][1] = triangleEdge.triangle.uvs[0][2]; triangleEdge.triangle.uvs[0][2] = backupUV; } processed.push(triangleEdge); let edges = [ new R3.API.Vector2( triangleEdge.triangle.v0index, triangleEdge.triangle.v1index ), new R3.API.Vector2( triangleEdge.triangle.v1index, triangleEdge.triangle.v2index ), new R3.API.Vector2( triangleEdge.triangle.v2index, triangleEdge.triangle.v0index ) ]; for (let j = 0; j < edges.length; j++) { let neighbour = neighbourOnEdge(edges[j], faces, triangleEdge.triangle); if (neighbour && !inProcessed(processed, neighbour.triangle)) { toProcess.push(neighbour); } } } /** * In processed - we will have some duplicates - only add the unique ones * @type {Array} */ let triangles = []; for (let i = 0; i < processed.length; i++) { let found = false; for (let k = 0; k < triangles.length; k++) { if (triangles[k].equals(processed[i].triangle)){ found = true; break; } } if (!found) { triangles.push(processed[i].triangle); } } return triangles; }; /** * This is a work-around function to fix polys which don't triangulate because * they could lie on Z-plane (XZ or YZ)) - we translate the poly to the origin, systematically rotate the poly around * Z then Y axis * @param verticesFlat [] * @param grain is the amount to systematically rotate the poly by - a finer grain means a more accurate maximum XY * @return [] */ static FixPolyZPlane(verticesFlat, grain) { if ((verticesFlat.length % 3) !== 0 && !(verticesFlat.length > 9)) { console.log("The vertices are not in the right length : " + verticesFlat.length); } let vertices = []; let points = new R3.API.Quaternion.Points(); for (let i = 0; i < verticesFlat.length; i += 3) { points.add(new R3.API.Vector3( verticesFlat[i], verticesFlat[i + 1], verticesFlat[i + 2] )); } points.toOrigin(); points.maximizeXDistance(grain); points.maximizeYDistance(grain); for (i = 0; i < points.vectors.length; i++) { vertices.push( [ points.vectors[i].x, points.vectors[i].y ] ); } return vertices; }; static MovingAverage(period) { let nums = []; return function(num) { nums.push(num); if (nums.length > period) nums.splice(0,1); // remove the first element of the array let sum = 0; for (let i in nums) sum += nums[i]; let n = period; if (nums.length < period) n = nums.length; return(sum/n); } }; static Intersect(a, b) { let t; /** * Loop over shortest array */ if (b.length > a.length) { t = b; b = a; a = t; } return a.filter( /** * Check if exists * @param e * @returns {boolean} */ function(e) { return (b.indexOf(e) > -1); } ).filter( /** * Remove Duplicates * @param e * @param i * @param c * @returns {boolean} */ function(e, i, c) { return c.indexOf(e) === i; } ); }; static Difference(a, b) { let t; /** * Loop over shortest array */ if (b.length > a.length) { t = b; b = a; a = t; } return a.filter( /** * Check if exists * @param e * @returns {boolean} */ function(e) { return (b.indexOf(e) === -1); } ).filter( /** * Remove Duplicates * @param e * @param i * @param c * @returns {boolean} */ function(e, i, c) { return c.indexOf(e) === i; } ); }; /** * Push only if not in there already * @param array * @param object * @constructor */ static PushUnique(array, object) { if (array.indexOf(object) === -1) { array.push(object); } }; static RemoveFromArray(array, object) { let index = array.indexOf(object); if (index !== -1) { array.splice(index, 1); } } /** * Checks whether or not the object is empty * @param obj * @returns {boolean} * @constructor */ static IsEmpty(obj) { return (Object.keys(obj).length === 0 && obj.constructor === Object); }; static IsString(member) { return (typeof member === 'string'); }; static IsBoolean(member) { return (member === true || member === false); }; static IsColor(member) { return (member instanceof R3.Color); }; static IsNumber(member) { return (typeof member === 'number'); }; static IsVector2(member) { return ( member instanceof R3.API.Vector2 || member instanceof R3.Vector2 ); }; static IsVector3(member) { return ( member instanceof R3.API.Vector3 || member instanceof R3.Vector3 ); }; static IsVector4(member) { return ( member instanceof R3.API.Vector4 || member instanceof R3.Vector4 || member instanceof R3.API.Quaternion || member instanceof R3.Quaternion ); }; static IsObject(member) { let type = typeof member; return type === 'function' || type === 'object' && !!member; }; /** * @return {string} */ static LowerUnderscore(name) { let string = name.toLowerCase().replace(/\s+/g, '_'); string = string.replace(/-/g, '_'); string = string.replace(/\_+/g, '_'); return string; }; static UpperCaseWordsSpaces(input) { let word = input.replace(/[-_]/g, ' '); word = word.replace(/\s+/, ' '); let words = word.split(' '); return words.reduce( function(result, word) { result += word[0].toUpperCase() + word.substr(1); return result + ' '; }, '' ).trim(); }; /** * @return {string} */ static UpperCaseUnderscore(word) { let str = ''; word.split('').map( function(letter){ if (letter === letter.toUpperCase()) { str += '_' + letter; } else { str += letter.toUpperCase(); } }); str = str.replace(new RegExp('^_'),''); return str; }; /** * Returns Left Padded Text - ex. length 5, padchar 0, string abc = '00abc' * @param length * @param padChar * @param string * @returns {string} * @constructor */ static PaddedText(length, padChar, string) { let pad = ""; for (let x = 0; x < length; x++) { pad += padChar; } return pad.substring(0, pad.length - string.length) + string; }; } console.log('r3.js - version ' + R3.Version + ' compiled ' + R3.CompileDate); Component.Code = ComponentCode; Component.DOM = ComponentDOM; Component.DOM.Canvas = ComponentCanvas; Component.Geometry = ComponentGeometry; Component.Geometry.BufferGeometry = ComponentBufferGeometry; Component.Geometry.BufferGeometry.PlaneGeometry = ComponentPlaneGeometry; Component.Graphics = ComponentGraphics; Component.Graphics.Image = ComponentImage; Component.Input = ComponentInput; Component.Input.Touch = ComponentTouch; Component.Material = ComponentMaterial; Component.Mesh = ComponentMesh; Component.Texture = ComponentTexture; Entity.Slider = EntitySlider; R3Object.Project = Project; R3Object.Entity = Entity; R3Object.Entity.Slider = EntitySlider; R3Object.Component = Component; R3Object.Component.Code = ComponentCode; R3Object.Component.DOM = ComponentDOM; R3Object.Component.DOM.Canvas = ComponentCanvas; R3Object.Component.Geometry = ComponentGeometry; R3Object.Component.Geometry.BufferGeometry = ComponentBufferGeometry; R3Object.Component.Geometry.BufferGeometry.PlaneGeometry = ComponentPlaneGeometry; R3Object.Component.Graphics = ComponentGraphics; R3Object.Component.Graphics.Image = ComponentImage; R3Object.Component.Input = ComponentInput; R3Object.Component.Input.Touch = ComponentTouch; R3Object.Component.Material = ComponentMaterial; R3Object.Component.Mesh = ComponentMesh; R3Object.Component.Texture = ComponentTexture; Runtime.Coder = RuntimeCoder; Runtime.Coder.CodeMirror = RuntimeCodeMirror; Runtime.DOM = RuntimeDOM; Runtime.DOM.Document = RuntimeDocument; Runtime.GUI = RuntimeGUI; Runtime.GUI.ControlKit = RuntimeControlKit; Runtime.Graphics = RuntimeGraphics; Runtime.Graphics.Three = RuntimeThree; Runtime.Image = RuntimeImage; Runtime.Image.NodeJSImage = RuntimeNodeJSImage; Runtime.Image.WebImage = RuntimeWebImage; Runtime.Physics = RuntimePhysics; Runtime.Physics.Bullet = RuntimeBullet; Runtime.Socket = RuntimeSocket; Runtime.Statistics = RuntimeStatistics; Runtime.Statistics.Stats = RuntimeStats; System.DOM = SystemDOM; System.Input = SystemInput; System.Linking = SystemLinking; System.Render = SystemRender; System.Runtime = SystemRuntime; System.Socket = SystemSocket; System.Storage = SystemStorage; R3.System = System; R3.Runtime = Runtime; R3.Event = Event; R3.Utils = Utils; R3.Object = R3Object; R3.Project = Project; R3.Entity = Entity; R3.Component = Component; R3.Code = R3Object.Component.Code; R3.Canvas = R3Object.Component.DOM.Canvas; R3.PlaneGeometry = R3Object.Component.Geometry.BufferGeometry.PlaneGeometry; R3.Image = R3Object.Component.Graphics.Image; R3.Touch = R3Object.Component.Input.Touch; R3.Material = R3Object.Component.Material; R3.Mesh = R3Object.Component.Mesh; R3.Texture = R3Object.Component.Texture; R3.System.DOM.Start(); R3.System.Input.Start(); R3.System.Linking.Start(); R3.System.Render.Start(); R3.System.Runtime.Start(); R3.System.Socket.Start(); R3.System.Storage.Start();