"use strict"; /** * @author Mjduniverse * @copyright 2020 Mjduniverse * @license * * Physics Simulator Class Library * * Copyright 2020 Mjduniverse.com * * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software * and associated documentation files (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ /** * * The options that can be used to create a dynamic simulation could be a * CompositeSimulation object, a simulation object or an array * of static objects. * * If an array is chosen, then it is used to create * * @typedef {PhSim.Static|PhSim.Static.Simulation|StaticObject[]} DynSimOptions * @property {HTMLCanvas} canvas - Simulation canvas * @property {Number} initSimIndex - The inital simulation index. If undefined, the simulation index is 0. * @property {HTMLElement} container - The container * */ // Try to import matter-js as a commonJS module var Matter; if(typeof window === "object") { Matter = window.Matter; } else { Matter = require("matter-js"); } /** * Dynamic Simulation Instance Object * * @constructor * @param {DynSimOptions} [dynSimOptions] - The simulation object * @mixes PhSim.PhSimEventTarget * */ function PhSim(dynSimOptions = new PhSim.Static()) { if(dynSimOptions) { Object.assign(this,dynSimOptions); } PhSim.Static.call(this); if(Array.isArray(dynSimOptions.simulations)) { this.simulations = dynSimOptions.simulations; } else if(Array.isArray(dynSimOptions.layers)) { this.simulations[0] = dynSimOptions; } else if(Array.isArray(dynSimOptions.objUniverse)) { this.simulations[0].layers[0] = dynSimOptions; } else if(Array.isArray(dynSimOptions)) { this.simulations[0].layers[0].objUniverse = []; } if(typeof dynSimOptions.wFunctions === "object") { this.wFunctions = dynSimOptions.wFunctions } // Register Plugin if(!dynSimOptions.noUse) { Matter.use(PhSim.matterPlugin); } /** * The static simulation object * @typedef {DynSimOptions} */ this.options = dynSimOptions; /** * Debugging Configuration * @type {Object} */ this.debugging = this.debugging || { logMouseMovePerformance: true } /** * Debugging data * @type {Object} */ this.debuggingData = {} // Configure canvas if(dynSimOptions.canvas) { this.connectCanvas(dynSimOptions.canvas) } else { var newCanvas = document.createElement("canvas"); this.connectCanvas(newCanvas); } // Configure container if(dynSimOptions.container) { this.connectContainer(dynSimOptions.container); } else { var newContainer = document.createElement("div"); this.connectContainer(newContainer); } // Register event keys this.registerKeyEvents(); // Inital Simulation if(dynSimOptions.initSimIndex) { this.gotoSimulationIndex(dynSimOptions.initSimIndex); } else { this.gotoSimulationIndex(0); } } /** * Connect an HTML canvas to the PhSim simulation object. * * @function * @param {HTMLCanvasElement} canvas */ PhSim.prototype.connectCanvas = function(canvas) { /** * Simulation canvas * @type {HTMLCanvasElement} */ this.canvas = canvas; /** * Simulation context for the canvas * @type {CanvasRenderingContext2D} */ this.ctx = canvas.getContext("2d"); this.canvas.width = this.box.w || this.box.width; this.canvas.height = this.box.h || this.box.height; this.registerCanvasEvents(); this.configRender(this.ctx); } /** * Connect a container for the PhSim simulation object. The PhSim canvas is supposed to be * the only child element of the container. * * When set, the container has the simulation canvas appened as a child. * * @function * @param {HTMLElement} c - Container */ PhSim.prototype.connectContainer = function(c) { /** * The simulation container. * This is is supposed to be the wrapping element of the {@link PhSim#canvas|PhSim canvas}. * @type {HTMLElement} */ this.container = c; c.appendChild(this.canvas); c.classList.add("phsim-container"); this.configFilter(c); } /** * Number of frames per second */ PhSim.prototype.delta = 50/3; // 16 frames per second, or 16 frames per 1000ms /** * Boolean property for telling if the simulation has loaded a simulation at least one time. * @type {Boolean} */ PhSim.prototype.init = false; /** * Time for inside the world * @type {Number} */ PhSim.prototype.sl_time = 0; /** * Index of the current simulation. * @default 0 * @type {Number} */ PhSim.prototype.simulationIndex = 0; /** * PhSim status codes for loading simulations. * @readonly * @namespace */ PhSim.statusCodes = { /** * Inital loading status * @readonly * @default * @type {Number} */ INT: 0, /** * This status means that the DynObjects have been loaded. * @readonly * @default * @type {Number} */ LOADED_DYN_OBJECTS: 1, /** * This status means that the sprites have been loaded, if there are any. * If there are no sprites, then this status is set anyway. * @readonly * @default * @type {Number} */ LOADED_SPRITES: 2, /** * This status means that the audio has loaded. * @readonly * @default * @type {Number} */ LOADED_AUDIO: 3, /** * This status means that the simulation is done configuring. * @readonly * @default * @type {Number} */ LOADED_SIMULATION: 4 } /** * Loading status of the dynamic simulation * @type {Number} */ PhSim.prototype.status = 0; /** * x-coordinate of the mouse * @type {Number} */ PhSim.prototype.mouseX = null; /** * y-coordinate of the mouse * @type {Number} */ PhSim.prototype.mouseY = null; /** * Simulation options * @deprecated Due to confusing name. */ PhSim.prototype.sim = null; /** * Current simulation options * @deprecated Due to confusing name. */ PhSim.prototype.simulation = null; /** * Boolean property to tell if the simulation is paused or not. * @type {Boolean} */ PhSim.prototype.paused = true; /** * * @callback PhSimEventCall * @param {PhSim.Events.PhSimEvent} phEvent * */ /** * The matter.js world * Resets when {@link PhSim#gotoSimulationIndex} is executed. * @type {Object} */ PhSim.prototype.matterJSWorld = null; /** * The matter.js engine * Resets when {@link PhSim#gotoSimulationIndex} is executed. * @type {Object} */ PhSim.prototype.matterJSEngine = null; /** * An tree that is used to preserve layer distinctions. * It is an array of arrays. The arrays in this array have {@link PhSimObject} objects. * Resets when {@link PhSim#gotoSimulationIndex} is executed. * @type {PhSimObjectArr[]} */ PhSim.prototype.dynTree = []; /** * Array of objects in the PhSim simulation * Resets when {@link PhSim#gotoSimulationIndex} is executed. * @type {PhSimObjectArr} */ PhSim.prototype.objUniverse = []; /** * Array of static sprite objects that are to be extracted by */ PhSim.prototype.staticSprites = []; PhSim.prototype.staticAudio = []; /** * Number of audio players. * This is reset to 0 whenever {@link PhSim#gotoSimulationIndex} is executed. * @type {Number} */ PhSim.prototype.audioPlayers = 0; /** * Classes * When {@link PhSim#gotoSimulationIndex} is run, this is blanked and repopulated. * @type {Object} */ PhSim.prototype.classes = {}; /** * Background fill style for rendering. * When {@link PhSim#gotoSimulationIndex} is run, the function sets this value to the * value of {@link PhSim.simOptions.box.bgColor} if it is not a {@link Falsey} value; * * @type {String} */ PhSim.prototype.bgFillStyle = "white"; /** * PhSim version * @type {String} */ PhSim.version = "0.1.0-alpha" /** * Loading screen properties * @type {Object} * @property {String} [bgColor = "black"] - Background Color * @property {String} [txtColor = "white"] - Text Color * @property {String} [txtFace = "arial"] - Text Face * @property {String} [txtAlign = "center"] - Text align * @property {String} [txt = "Loading..."] - Loading text * @property {String} [yPos = "center"] - y-position * @property {Number} [txtSize = 20] - Text size */ PhSim.prototype.loading = { bgClr: "black", txtClr: "white", txtFace: "arial", txtAlign: "center", txt: "Loading...", yPos: "center", txtSize: 20 } /** * The `drawLoadingScreen` function draws the loading screen for a simulation change. * The behaviour of the loading screen can be customized by modifing the properties of * {@link PhSim#loading}. * * @function */ PhSim.prototype.drawLoadingScreen = function() { this.ctx.fillStyle = this.loading.bgClr; this.ctx.fillRect(0,0,this.camera.scale,this.canvas.height); this.ctx.fillStyle = this.loading.txtClr; this.ctx.textAlign = this.loading.txtAlign; this.ctx.font = this.loading.txtSize + "px " + this.loading.txtFace; this.ctx.fillText(this.loading.txt,this.canvas.width / 2,this.canvas.height / 2) } if(typeof window === "object") { window.PhSim = PhSim; } if(typeof module === "object") { module.exports = PhSim; } PhSim.Static = require("./objects" ); require("./matterPlugin.js" ); PhSim.EventStack = require("./events/eventStack" ); /** * Object containing array functions to be called. * @type {PhSim.EventStack} */ PhSim.prototype.eventStack = new PhSim.EventStack(); /** * An array stack that is cleared each time the main simulation is changed. * @type {PhSim.EventStack} */ PhSim.prototype.simulationEventStack = new PhSim.EventStack(); PhSim.PhRender = require("./phRender"); PhSim.Sprites = require("./sprites"); PhSim.Audio = require("./audio"); PhSim.Vector = require("./tools/vector"); PhSim.diagRect = require("./tools/diagRect"); PhSim.Vertices = require("./tools/vertex"); PhSim.Centroid = require("./tools/centroid"); // Bounding box functions PhSim.BoundingBox = require("./tools/boundingBox"); PhSim.DynObject = require("./dynObject"); PhSim.Events = require("./events/eventObjects"); require("./lo"); require("./makeQuickly"); require("./filter"); require("./widgets/dynWidget"); require("./audioToggle"); require("./events/registerEvents"); PhSim.PhSimEventTarget = require("./events/eventListener"); Object.assign(PhSim.prototype,PhSim.PhSimEventTarget); require("./query"); require("./gravity"); require("./loop/toggle"); PhSim.prototype.gotoSimulationIndex = require("./loop/gotoSuperlayer"); PhSim.Motion = require("./motion"); require("./set"); require("./loop/update"); require("./widgets/extractWidgets"); PhSim.Camera = require("./dynSimCamera"); PhSim.Game = require("./game"); PhSim.Gradients = require("./gradient"); require("./widgets"); PhSim.calc_skinmesh = require("./calc_skinmesh"); require("./events/simpleEvent"); require("./processVar"); PhSim.ObjLoops = require("./objLoops"); /** * Global event stack * @type {PhSim.EventStack} */ PhSim.prototype.eventStack = new PhSim.EventStack(); /** * Event stack for simulation specfic events * @type {PhSim.EventStack} */ PhSim.prototype.simulationEventStack = new PhSim.EventStack(); /** * Structure giving more human-readable meaning to PhSim status. * @type {String[]} */ PhSim.statusStruct = { 0: "Unloaded", 1: "Initalized", 2: "Loaded Images", 3: "Loaded Audio", 4: "Loaded Simulation" }