import {Quaternion} from "three";
import {Position} from "../geometry/Position.js";
import {GroupElement} from "./GroupElement.js";
import {Vector} from "../geometry/Vector.js";
/**
* @class
*
* @classdesc
* Relative position.
* A general position is represented as a pair (h,p) where
* - h (cellBoost) is an GroupElement representing an element of a discrete subgroups
* - p (local) is a Position
* The frame represented by the relative position is the image by h of the frame represented by the position p
*
* We split the isometry part (hg) in two pieces.
* The idea is to g should gives a position in the fundamental domain of the (implicit) underlying lattice.
* This will keep the coordinates of g in a bounded range.
*
* For simplicity we also keep track of the inverse of the cellBoost.
*/
class RelPosition {
/**
* Constructor.
* Return the position corresponding to the origin with the reference frame.
*
* @param {TeleportationSet} set - the underlying discrete subgroups.
*/
constructor(set) {
/**
* the local position
* @type {Position}
*/
this.local = new Position();
/**
* the isometry component of the position inside the fundamental domain
* @type {TeleportationSet}
*/
this.set = set;
/**
* the "discrete" component of the isometry par of the boost
* @type {GroupElement}
*/
this.cellBoost = this.set.group.element();
/**
* the inverse of cellBoost
* @type {GroupElement}
*/
this.invCellBoost = this.set.group.element();
}
/**
* Reduce the eventual numerical error of the current boost.
* @return {RelPosition} the current relative position
*/
reduceErrorBoost() {
this.local.reduceErrorBoost();
return this;
}
/**
* Reduce the eventual numerical error of the current facing.
* @return {RelPosition} the current relative position
* @todo To be completed
*/
reduceErrorFacing() {
this.local.reduceErrorFacing();
return this;
}
/**
* Reduce the eventual numerical error of the current facing.
* @return {RelPosition} the current relative position
* @todo To be completed
*/
reduceErrorLocal() {
this.local.reduceError();
return this;
}
/**
* Reduce the eventual numerical error of the current position.
* @return {RelPosition} the current relative position
*/
reduceError() {
this.reduceErrorLocal();
return this;
}
/**
* Facing of the local part of the relative position
* @return {Matrix4}
*/
get facing(){
return this.local.facing;
}
/**
* The underlying local point (i.e. ignoring the cell boost)
* @type {Point}
*/
get localPoint() {
return this.local.point;
}
/**
* The underlying point (taking into account the cell boost)
* @type {Point}
*/
get point() {
return this.local.point.applyIsometry(this.cellBoost.toIsometry());
}
/**
* Return the global isometry (cellBoost * local boost) of the current position
* @return {Isometry}
*/
get globalBoost() {
return this.cellBoost.toIsometry().multiply(this.local.boost);
}
/**
* Return a global position (with no cell boost) representing the current relative position
* @type{Position}
*/
get globalPosition() {
const res = new Position();
res.boost.copy(this.globalBoost);
res.quaternion.copy(this.local.quaternion);
return res;
}
/**
* Reset the position in its default position (boost = identity, quaternion = 1)
* @return {RelPosition} The current position
*/
reset(){
this.local.reset();
this.cellBoost.identity();
this.invCellBoost.identity();
}
/**
* Rotate the facing by `m` (right action of O(3) in the set of positions).
* @param {Quaternion} quaternion - An isometry of the tangent space at the origin, i.e. a matrix in O(3).
* @return {RelPosition} the updated version of the current Position
*/
applyQuaternion(quaternion) {
this.local.applyQuaternion(quaternion);
return this;
}
/**
* Apply if needed a teleportation to bring back the local boos in the fundamental domain
* @return {RelPosition} the current relative position
*/
teleport() {
let inside;
let localPoint;
while (true) {
// compute the location of the local part of the position
localPoint = this.localPoint;
inside = true;
for (const teleportation of this.set.teleportations) {
inside = inside && !teleportation.jsTest(localPoint);
// if we failed the test, a teleportation is needed.
// we perform the teleportation and exit the loop
// (to restart the checks from the beginning).
if (!inside) {
this.local.applyIsometry(teleportation.elt.toIsometry());
this.cellBoost.multiply(teleportation.inv);
// console.log("Elt (inv)", teleportation.inv.toIsometry().matrix.toLog())
// console.log("Boost", this.cellBoost.toIsometry().matrix.toLog())
this.invCellBoost.premultiply(teleportation.elt);
break;
}
}
if (inside) {
break;
}
}
return this;
}
/**
* Flow the current position.
* `v` is the pull back at the origin by the position of the direction in which we flow
* The time by which we flow is the norm of `v`
* This method makes sure that the boost stays in the fundamental domain
* @param {Vector} v - the direction (and length) to follow
* @return {RelPosition} the current relative position
*/
flow(v) {
this.local.flow(v);
this.teleport();
return this;
}
/**
* Fake version of the differential of the exponential map.
* We do not incorporate any teleportation here.
* (See {@link Position} for details)
* @param {Matrix4} matrix - an affine isometry of the tangent space at the origin
* @return {RelPosition}
*/
fakeDiffExpMap(matrix) {
this.local.fakeDiffExpMap(matrix);
return this;
}
/**
* Check if the current position and `position ` are the same.
* Mainly for debugging purposes
* @param {RelPosition} position
* @return {boolean} true if the relative positions are the same, false otherwise
*/
equals(position) {
let res = true;
res = res && this.local.equals(position.local);
res = res && this.cellBoost.equals(position.cellBoost);
return res;
}
/**
* Return a new copy of the current position.
* @return {RelPosition} the clone of the current relative position
*/
clone() {
let res = new RelPosition(this.set);
res.cellBoost.copy(this.cellBoost);
res.invCellBoost.copy(this.invCellBoost);
res.local.copy(this.local);
return res;
}
/**
* Set the current position with the given position.
* @param {RelPosition} position - the relative position to copy
* @return {RelPosition} the current relative position
*/
copy(position) {
this.cellBoost.copy(position.cellBoost);
this.invCellBoost.copy(position.invCellBoost);
this.local.copy(position.local);
return this;
}
}
export {
RelPosition
};