/*jshint esversion: 6 */
// @ts-check
/**
* CS559 3D World Framework Code
*
* GrObject: a "thin wrapper" around Three.JS's Object3D to facilitate
* creating UIs and doing animation
*/
/* students will want to create objects that extend this class */
/** @module GrObject */
/*
* This is the main class for the framework. Most of the work involves extending
* the class `GrObject` defined here.
*/
// these four lines fake out TypeScript into thinking that THREE
// has the same type as the T.js module, so things work for type checking
// type inferencing figures out that THREE has the same type as T
// and then I have to use T (not THREE) to avoid the "UMD Module" warning
/** @type typeof import("./../THREE/threets/index"); */
let T;
// @ts-ignore /** @ignore - JSDoc ignore as well*/
T = THREE;
/**
* This function converts from the specifications given to the `GrObject`
* constructor into the form used internally. It is the best documentation for
* how those descriptions are interpretted.
*
* **Note:** this function is for internal use, but it is exported to convince
* JSDoc to document it.
*
* @param {string|Array} param
*/
export function paramObjFromParam(param) {
let paramObj = { name: "no name", min:0, max: 1, initial: 0};
if (typeof param === "string") {
paramObj.name = param;
} else if (Array.isArray(param)) {
if (param.length > 0 ) { paramObj.name = param[0]; }
if (param.length > 1 ) { paramObj.min = param[1]; }
if (param.length > 2 ) { paramObj.max = param[2]; }
if (param.length > 3 ) { paramObj.initial = param[3];}
}
// make sure the initial value is legal
if (paramObj.initial < paramObj.min) { paramObj.initial = paramObj.min;}
if (paramObj.initial > paramObj.max) { paramObj.initial = paramObj.max;}
return paramObj;
}
/** @class GrObject
*
* GrObjects have:
* - a name - each object should have a unique name (like an id), but this is not
* enforced
* - parameters (these are things that the user may want to control with sliders)
* - geometry / "Object3D" - they kind of serve like three's groups
* note: animation should not update the parameters
*
* any new object should provide methods for:
* - construction - the constructor needs to call the base class constructor
* and provide the parameters and geometry
* - update - which takes an array of paramaters and sets things accordingly
* - advance - which moves the animation ahead a small amount
*
* Note that a `GrObject` does not add itself to the scene (other things take care)
* of that. When the object is added to the world,
*/
export class GrObject {
/**
* The parameter list (if provided) should be either a string
* (with the name of the parameter) or an Array with the first
* value being a string (the name), and the remaining 3 values being
* numbers: min, max, and initial value (all optional).
* @see paramObjFromParam
*
* @param {String} name - unique name for the object
* @param {THREE.Object3D | Array<THREE.Object3D>} objectOrObjects
* @param {Array<string|Array>} [paramInfo] - a list of the parameters for the object
*/
constructor(name,objectOrObjects,paramInfo) {
// simple declarations of defaults so we can easily identify members
/** @type Array<THREE.Object3D> */
this.objects = [];
/** @type Array<Object> */
this.params = [];
this.name = name;
// set up the object list
if (Array.isArray(objectOrObjects)) {
// we were given a list - do a deep copy
objectOrObjects.forEach(function(obj) {
this.objects.push(obj);
});
} else {
// if there is 1 object (there might be zero)
if (objectOrObjects) {
this.objects.push(objectOrObjects);
}
}
// set up the parameters
// we allow specifying parameters in many different ways
// we always convert to lightweight objects
if (paramInfo) { // Totally OK to have none
let self = this;
paramInfo.forEach(function(param) {
// default values for the parameter in case we don't get any
let paramObj = paramObjFromParam(param);
self.params.push(paramObj);
});
}
}
// methods that must be over-ridden
/**
* Advance the object by an amount of time. Time only flows forward
* so use this to figure out how fast things should move.
* In theory, it is always a "step" (1/60th of a second)
* In the past, so many things were stochastic and only computed the
* delta, that this became the norm (if you need to accumulate time
* you can sum the delta)
* time of day is provided so you can make objects that change over the
* course of the day - it is a number between 0-24 (midnight->midnight)
* it does not necessarily change smoothly.
* @param {number} delta
* @param {number} timeOfDay
*/
advance(delta, timeOfDay) {
// by default (base class), does nothing
}
/**
* set the parameter values to new values
* this gets called when the sliders are moved
* @param {Array<Number>} paramValues
*/
update(paramValues) {
}
}