import { Vector } from "../components" ; import { Spline } from "../components/spline" ; import { Particle } from "../particles/particle" ; type ValueOf < T > = T [ keyof T ] ; /**
 * Represents a value that can be used to drive a particle module.
 */ export type ModuleDriverValue < T > = T | Spline < T > | ( ( factor : number , particle ? : Particle ) => T ) ; /**
 * Represents a value that can be used to modify the properties of a particle over it's lifetime.
 */ export type ModuleFunction = ( particle : Particle ) => void ; /**
 * Represents the properties of a particle that are drivable through modules.
 */ export type DrivableProperties = Pick < Particle , "color" | "opacity" | "rotation" | "size" > ; /**
 * Represents the possible, drivable keys of a particle.
 */ export type DrivableKey = keyof DrivableProperties ; /**
 * Represents the possible types of drivable keys.
 */ export type DrivableType = ValueOf < DrivableProperties > ; /**
 * Represents the possible factors of drivable values.
 */ export type DrivableFactor = "lifetime" | "relativeLifetime" | "size" ; remarks example export class ModuleBuilder { /**
     * The specified key of the builder.
     */ protected driverKey ? : DrivableKey ; /**
     * The value to drive the property with.
     */ protected driverValue ? : ModuleDriverValue < DrivableType > ; defaultValue protected factor : DrivableFactor = "lifetime" ; protected isRelative = false ; remarks returns public drive < TKey extends DrivableKey > ( key ) { this . driverKey = key ; return this ; } returns public through ( factor ) { this . factor = factor ; return this ; } returns public by < TDriver extends DrivableType > ( driver ) { this . driverValue = driver ; return this ; } remarks public relative ( isRelative = true ) { this . isRelative = isRelative ; return this ; } remarks public build ( ) { if ( typeof this . driverKey === "undefined" ) { throw new Error ( "No driving key was provided in the module builder. Did you forget a '.drive()' call?" ) ; } if ( typeof this . driverValue === "undefined" ) { throw new Error ( "No driving value was provided in the module builder. Did you forget a '.through()' call?" ) ; } return ( particle ) => { updateDrivenProperty ( particle , this . driverKey , evaluateModuleDriver ( this . driverValue , calculateModuleFactor ( this . factor , particle ) , particle ) , this . isRelative ) ; } ; } } /**
 * Evaluates the module driver using a specified factor.
 */ function evaluateModuleDriver < T > ( driver , factor , particle ) { if ( typeof driver === "object" && "evaluate" in driver ) { return driver . evaluate ( factor ) ; } if ( typeof driver === "function" ) { return ( driver as ( factor : number , particle : Particle ) => T ) ( factor , particle ) ; } return driver ; } /**
 * Calculates a module factor using a specified particle as context.
 */ function calculateModuleFactor ( factor , particle ) { switch ( factor ) { case "lifetime" : return particle . initialLifetime - particle . lifetime ; case "relativeLifetime" : return ( ( particle . initialLifetime - particle . lifetime ) / particle . initialLifetime ) ; case "size" : return particle . size ; default : throw new Error ( `Invalid driving factor '${ factor }'.` ) ; } } remarks function updateDrivenProperty ( particle , key , value , relative = false ) { if ( ! relative ) { particle [ key ] = value as never ; } else { const initial = particle [ "initial" + key [ 0 ] . toUpperCase ( ) + key . substr ( 1 ) ] as DrivableType ; if ( typeof initial === "undefined" ) { throw new Error ( `Unable to use relative chaining with key '${ key }'; no initial value exists.` ) ; } if ( value instanceof Vector ) { updateDrivenProperty ( particle , key , ( initial as Vector ) . add ( value ) ) ; } else if ( typeof value === "number" ) { updateDrivenProperty ( particle , key , ( initial as number ) * value ) ; } else { throw new Error ( `Unable to use relative chaining with particle key '${ key }'; no relative operation for '${ value }' could be inferred.` ) ; } } }