"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"
}