UNPKG

crmod-api

Version:

A dynamic mod/asset generator for Cosmic Reach

502 lines (446 loc) 12 kB
import { Vector3 } from "three"; /** * Defines a direction. * * @class Direction */ class Direction { /** * The direction map for this direction. * * @type {DirectionMap} * @memberof Direction */ directionMap; /** * The name of this direction. * * @type {string} * @memberof Direction */ name; /** * The vector for this direction. * * @type {Vector3} * @memberof Direction */ vector; /** * Initializes a new Direction. * @param {string} name * @param {number} x * @param {number} y * @param {number} z * @memberof Direction */ constructor(name, x, y, z) { /** @type {DirectionMap} */ this.directionMap = null; /** @type {string} */ this.name = name; this.vector = new Vector3(x, y, z); } *[Symbol.iterator]() { yield* this.vector.toArray(); } /** * Sets the direction map for this direction. * * @param {DirectionMap} directionMap * @memberof Direction */ setDirectionMap(directionMap) { this.directionMap = directionMap; } /** * The x component of the direction. * * @readonly * @type {number} * @memberof Direction */ get x() { return this.vector.x; } /** * The y component of the direction. * * @readonly * @type {number} * @memberof Direction */ get y() { return this.vector.y; } /** * The z component of the direction. * * @readonly * @type {number} * @memberof Direction */ get z() { return this.vector.z; } /** * The name of the direction with the first letter capitalized. * * @readonly * @memberof Direction */ get uppercaseName() { return this.name[0].toUpperCase() + this.name.slice(1); } /** * Checks whether a direction name is equal to this direction name. * * @param {string} name The name to check. * @return {boolean} * @memberof Direction */ is(name) { return this.name.toLowerCase() == name.toLowerCase(); } /** * Returns the inverse of this direction. * * @return {Direction} * @memberof Direction */ inverse() { return this.directionMap.inverse(this); } /** * Returns the string representation of the direction. * * @return {string} * @memberof Direction */ toString() { return this.name; } } /** * Defines a list of {@link Direction|Directions}. * * @class DirectionList */ class DirectionList { /** * The {@link Direction|Directions} in the list. * * @type {Set<Direction>} * @memberof DirectionList */ directions; /** * Initializes a new DirectionList. * @param {Array<Direction>} [directions=[]] The {@link Direction|Directions} to initialize the list with. * @memberof DirectionList */ constructor(directions = []) { this.directions = new Set; for(const direction of directions) this.directions.add(direction); } *[Symbol.iterator]() { yield* this.directions; } /** * Adds a {@link Direction} to the list. * * @param {Direction} direction The {@link Direction} to add. * @memberof DirectionList */ add(direction) { this.directions.add(direction); } /** * Removes a {@link Direction} from the list. * * @param {Direction} direction The {@link Direction} to remove. * @memberof DirectionList */ remove(direction) { this.directions.delete(direction); } /** * Checks whether a {@link Direction} exists in the list. * * @param {Direction} direction The {@link Direction} to check. * @return {boolean} * @memberof DirectionList */ has(direction) { return this.directions.has(direction); } /** * The number of {@link Direction|Directions} in the list. * * @readonly * @memberof DirectionList */ get size() { return this.directions.size; } /** * Executes a provided function once per each {@link Direction} in the list, in insertion order. * @param {function(Direction, Number, Set)} callback */ forEach(callback) { this.directions.forEach(callback); } /** * Checks whether a direction with the specified name exists in the list. * * @param {string} name The name of the direction to check. * @return {boolean} * @memberof DirectionList */ hasDirection(name) { for(const direction of this.directions) { if(direction.is(name)) return true; } return false; } /** * Removes a direction with the specified name from the list. * * @param {string} name * @memberof DirectionList */ removeDirection(name) { for(const direction of this.directions) { if(direction.is(name)) this.directions.delete(direction); } } /** * Creates a bitmask from a {@link DirectionMap}. * * @param {DirectionMap} directionMap * @return {number} * @memberof DirectionList */ createBitmask(directionMap) { let bitmask = 0; const directions = Array.from(directionMap.values()); for(let i = 0; i < directions.length; i++) { bitmask |= (this.directions.has(directions[i]) << i); } return bitmask; } /** * Inverts a {@link DirectionMap}. * * @param {DirectionMap} directionMap * @return {*} * @memberof DirectionList */ invert(directionMap) { const clone = this.clone(); const directions = new Set; directionMap.directions.forEach(direction => { if(!clone.directions.has(direction)) directions.add(direction); }); clone.directions.clear(); for(const direction of directions) clone.add(direction); return clone; } /** * Clones the direction list into a new instance. * * @param {string} [id=this.id] * @return {DirectionList} A new instance of the current direction list. * @memberof DirectionList */ clone() { return new DirectionList(this); } /** * Returns the string representation of the direction list. * * @return {*} * @memberof DirectionList */ toString() { if(this.directions.size == 0) return "none"; return Array.from(this.directions).map(v => v.name).join("-"); } } /** * Defines a map of {@link Direction|Directions}. * * @class DirectionMap */ class DirectionMap { /** * The {@link Direction|Directions} in the list. * * @type {Map<String, Direction>} * @memberof DirectionList */ directions; /** * Initializes a new DirectionMap. * @param {Array<Direction>} [directions=[]] The {@link Direction|Directions} to initialize the map with. * @memberof DirectionMap */ constructor(directions = []) { /** @type {Map<String, Direction>} */ this.directions = new Map; for(const direction of directions) this.addDirection(direction); } *[Symbol.iterator]() { yield* this.directions.values(); } /** * Adds a {@link Direction} to the map. * * @param {Direction} direction The {@link Direction} to add. * @memberof DirectionMap */ addDirection(direction) { direction.setDirectionMap(this); this.directions.set(direction.name, direction); } /** * Gets a {@link Direction} from the map by name. * * @param {string} name The name of the direction to get. * @return {Direction} * @memberof DirectionMap */ getDirection(name) { return this.directions.get(name.toLowerCase()); } /** * Gets the {@link Direction|Directions} within the map. * * @return {Array<Direction>} * @memberof DirectionMap */ values() { return this.directions.values(); } /** * Gets the direction names within the map. * * @return {Array<string>} * @memberof DirectionMap */ keys() { return this.directions.keys(); } /** * Returns the inverse of a direction. * * @param {Direction} direction The direction to invert. * @returns {Direction} */ inverse(direction) { const inverse = direction.vector.clone().multiplyScalar(-1); return this.vectorToDirection(inverse); } /** * Returns the direction closest to a vector. * * @param {Vector3} vector The vector to compare. */ vectorToDirection(vector) { let lowestDistance = Infinity; let lowestDistanceDirection; for(const direction of this.directions.values()) { /** @type {Vector3} */ const directionVector = direction.vector.clone(); const distance = directionVector.normalize().distanceTo(vector.clone().normalize()); if(distance < lowestDistance) { lowestDistance = distance; lowestDistanceDirection = direction; } } return lowestDistanceDirection; } /** * Returns the adjacency list for the direction map. * @returns {Array<DirectionList>} */ combinations() { const keys = Array.from(this.directions.keys()); const totalCombinations = 2 ** keys.length; const combinations = new Array; for(let i = 0; i < totalCombinations; i++) { const bits = i.toString(2).padStart(keys.length, "0"); const combination = keys.filter((_, i) => bits[i] == "1").map(k => this.directions.get(k)); combinations.push(new DirectionList(combination)); } return combinations; } } /** * Defines a set of predefined {@link Direction|Directions}. * * @class Directions */ class Directions { /** * Capitalizes the first character of a direction name. * * @static * @param {string} direction The direction name to capitalize. * @return {string} * @memberof Directions */ static capitalize(direction) { return direction[0].toUpperCase() + direction.slice(1); } /** * Uncapitalizes all charactera of a direction name. * * @static * @param {string} direction * @return {string} * @memberof Directions */ static uncapitalize(direction) { return direction.toLowerCase(); } /** * The cardinal directions. * * @static * @memberof Directions */ static cardinals = new DirectionMap([ new Direction("north", 0, 0, 1), new Direction("east", 1, 0, 0), new Direction("south", 0, 0, -1), new Direction("west", -1, 0, 0), new Direction("up", 0, 1, 0), new Direction("down", 0, -1, 0) ]); /** * The relative directions. * * @static * @memberof Directions */ static relative = new DirectionMap([ new Direction("front", 0, 0, 1), new Direction("right", 1, 0, 0), new Direction("back", 0, 0, -1), new Direction("left", -1, 0, 0), new Direction("top", 0, 1, 0), new Direction("bottom", 0, -1, 0) ]); /** * The directions for a simple block. * * @static * @memberof Directions */ static simpleBlock = new DirectionMap([ new Direction("front", 0, 0, 1), new Direction("back", 0, 0, -1), new Direction("side", 0, 0, 0), ]); } export default Directions; export { Direction, DirectionList, DirectionMap };