import {Group as TrivialGroup} from "../../commons/groups/trivial/Group.js";
import {Teleportation} from "./Teleportation.js";
import relative from "./shaders/relative.glsl";
import teleport from "./shaders/teleport.glsl.mustache";
/**
* Possible value for usesCreeping
* No creeping is used
* @type {number}
*/
export const CREEPING_OFF = 0;
/**
* Possible value for usesCreeping
* Only the creeping defined by the user are used
* @type {number}
*/
export const CREEPING_STRICT = 1;
/**
* Possible value for usesCreeping
* Uses creeping for all possible teleportations,
* if the user did not define the creeping function, the computation is done with a binary search
* @type {number}
*/
export const CREEPING_FULL = 2;
/**
* @class
*
* @classdesc
* Set of teleportations.
* It implicitly a set of generators of a discrete subgroup and a fundamental domain for this subgroup
*/
export class TeleportationSet {
/**
* Constructor
* @param {Array.<{elt:GroupElement, inv:GroupElement}>} neighbors - the list of neighbors when using nearest neighbors.
* The elements come by pair : an element and its inverse.
* defining the structure of the group element and the related functions
* @param {boolean} usesNearestNeighbors
* @param {number} creepingType - type of creeping used for quotient manifold (see description in the constants)
*/
constructor(
neighbors = [],
usesNearestNeighbors = false,
creepingType = CREEPING_OFF
) {
/**
* The list of teleports "generating" the subgroups.
* The order matters (see the class description).
* @type {Teleportation[]}
*/
this.teleportations = [];
/**
* The list of neighbors when using nearest neighbors.
* @type{{elt:GroupElement, inv:GroupElement}[]}
*/
this.neighbors = neighbors;
/**
* Flag : uses nearest neighbor or not (for local SDF)
* Default is false.
* @type{boolean}
*/
this.usesNearestNeighbors = usesNearestNeighbors;
/**
* Flag : type of creeping used
* Default is CREEPING_OFF.
* @type{number}
*/
this.creepingType = creepingType;
}
/**
* Return true if the set uses some kind of creeping
* @return {boolean}
*/
get usesCreeping() {
return this.creepingType === CREEPING_STRICT || this.creepingType === CREEPING_FULL;
}
/**
* Add a teleportation to the list of teleportations
* @param {Function} jsTest - A JS test saying if a teleportation is needed.
* The test is a function with the signature Point -> boolean.
* @param {string} glslTest - a chunk of GLSL performing the same test.
* The test should be encapsulated in a function with signature Point -> bool
* @param {GroupElement} elt - the isometry to apply when teleporting
* @param {GroupElement} inv - the inverse of the isometry (optional)
* If the inverse is not passed as an argument, it is computed automatically.
* @param {string} glslCreep - a chunk of GLSL to move to the boundary defined by the test
* @return {TeleportationSet} - the teleportation set
*/
add(jsTest, glslTest, elt, inv = undefined, glslCreep = undefined) {
this.teleportations.push(new Teleportation(this, jsTest, glslTest, elt, inv, glslCreep));
return this;
}
/**
* Shortcut to the underlying group.
* If the list of teleportations is empty, use the trivial group.
* @type {Group}
*/
get group() {
if (this.teleportations.length !== 0) {
return this.teleportations[0].elt.group;
} else {
return new TrivialGroup();
}
}
/**
* Goes through all the teleportations in the discrete subgroup
* and build the GLSL code performing the associated test.
* @param {ShaderBuilder} shaderBuilder
*/
shader(shaderBuilder) {
this.group.shader(shaderBuilder);
shaderBuilder.addChunk(relative);
for (const teleportation of this.teleportations) {
teleportation.shader(shaderBuilder);
}
for (const pair of this.neighbors) {
shaderBuilder.addUniform(pair.elt.name, 'GroupElement', pair.elt);
shaderBuilder.addUniform(pair.inv.name, 'GroupElement', pair.inv);
}
shaderBuilder.addChunk(teleport(this));
}
}