@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
755 lines • 26.3 kB
JavaScript
import { Vector3, TmpVectors } from "../Maths/math.vector.js";
import { HighestCommonFactor } from "../Maths/math.scalar.functions.js";
import { PHI } from "../Maths/math.constants.js";
import { _IsoVector } from "../Maths/math.isovector.js";
/**
* Class representing data for one face OAB of an equilateral icosahedron
* When O is the isovector (0, 0), A is isovector (m, n)
* @internal
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
export class _PrimaryIsoTriangle {
constructor() {
this.cartesian = [];
this.vertices = [];
this.max = [];
this.min = [];
this.closestTo = [];
this.innerFacets = [];
this.isoVecsABOB = [];
this.isoVecsOBOA = [];
this.isoVecsBAOA = [];
this.vertexTypes = [];
// eslint-disable-next-line @typescript-eslint/naming-convention
this.IDATA = new PolyhedronData("icosahedron", "Regular", [
[0, PHI, -1],
[-PHI, 1, 0],
[-1, 0, -PHI],
[1, 0, -PHI],
[PHI, 1, 0],
[0, PHI, 1],
[-1, 0, PHI],
[-PHI, -1, 0],
[0, -PHI, -1],
[PHI, -1, 0],
[1, 0, PHI],
[0, -PHI, 1],
], [
[0, 2, 1],
[0, 3, 2],
[0, 4, 3],
[0, 5, 4],
[0, 1, 5],
[7, 6, 1],
[8, 7, 2],
[9, 8, 3],
[10, 9, 4],
[6, 10, 5],
[2, 7, 1],
[3, 8, 2],
[4, 9, 3],
[5, 10, 4],
[1, 6, 5],
[11, 6, 7],
[11, 7, 8],
[11, 8, 9],
[11, 9, 10],
[11, 10, 6],
]);
}
/**
* Creates the PrimaryIsoTriangle Triangle OAB
* @param m an integer
* @param n an integer
*/
//operators
setIndices() {
let indexCount = 12; // 12 vertices already assigned
const vecToidx = {}; //maps iso-vectors to indexCount;
const m = this.m;
const n = this.n;
let g = m; // hcf of m, n when n != 0
let m1 = 1;
let n1 = 0;
if (n !== 0) {
g = HighestCommonFactor(m, n);
}
m1 = m / g;
n1 = n / g;
let fr; //face to the right of current face
let rot; //rotation about which vertex for fr
let O;
let A;
let B;
const oVec = _IsoVector.Zero();
const aVec = new _IsoVector(m, n);
const bVec = new _IsoVector(-n, m + n);
const oaVec = _IsoVector.Zero();
const abVec = _IsoVector.Zero();
const obVec = _IsoVector.Zero();
let verts = [];
let idx;
let idxR;
let isoId;
let isoIdR;
const closestTo = [];
const vDist = this.vertByDist;
const matchIdx = (f, fr, isoId, isoIdR) => {
idx = f + "|" + isoId;
idxR = fr + "|" + isoIdR;
if (!(idx in vecToidx || idxR in vecToidx)) {
vecToidx[idx] = indexCount;
vecToidx[idxR] = indexCount;
indexCount++;
}
else if (idx in vecToidx && !(idxR in vecToidx)) {
vecToidx[idxR] = vecToidx[idx];
}
else if (idxR in vecToidx && !(idx in vecToidx)) {
vecToidx[idx] = vecToidx[idxR];
}
if (vDist[isoId][0] > 2) {
closestTo[vecToidx[idx]] = [-vDist[isoId][0], vDist[isoId][1], vecToidx[idx]];
}
else {
closestTo[vecToidx[idx]] = [verts[vDist[isoId][0]], vDist[isoId][1], vecToidx[idx]];
}
};
this.IDATA.edgematch = [
[1, "B"],
[2, "B"],
[3, "B"],
[4, "B"],
[0, "B"],
[10, "O", 14, "A"],
[11, "O", 10, "A"],
[12, "O", 11, "A"],
[13, "O", 12, "A"],
[14, "O", 13, "A"],
[0, "O"],
[1, "O"],
[2, "O"],
[3, "O"],
[4, "O"],
[19, "B", 5, "A"],
[15, "B", 6, "A"],
[16, "B", 7, "A"],
[17, "B", 8, "A"],
[18, "B", 9, "A"],
];
/***edges AB to OB***** rotation about B*/
for (let f = 0; f < 20; f++) {
//f current face
verts = this.IDATA.face[f];
O = verts[2];
A = verts[1];
B = verts[0];
isoId = oVec.x + "|" + oVec.y;
idx = f + "|" + isoId;
if (!(idx in vecToidx)) {
vecToidx[idx] = O;
closestTo[O] = [verts[vDist[isoId][0]], vDist[isoId][1]];
}
isoId = aVec.x + "|" + aVec.y;
idx = f + "|" + isoId;
if (!(idx in vecToidx)) {
vecToidx[idx] = A;
closestTo[A] = [verts[vDist[isoId][0]], vDist[isoId][1]];
}
isoId = bVec.x + "|" + bVec.y;
idx = f + "|" + isoId;
if (!(idx in vecToidx)) {
vecToidx[idx] = B;
closestTo[B] = [verts[vDist[isoId][0]], vDist[isoId][1]];
}
//for edge vertices
fr = this.IDATA.edgematch[f][0];
rot = this.IDATA.edgematch[f][1];
if (rot === "B") {
for (let i = 1; i < g; i++) {
abVec.x = m - i * (m1 + n1);
abVec.y = n + i * m1;
obVec.x = -i * n1;
obVec.y = i * (m1 + n1);
isoId = abVec.x + "|" + abVec.y;
isoIdR = obVec.x + "|" + obVec.y;
matchIdx(f, fr, isoId, isoIdR);
}
}
if (rot === "O") {
for (let i = 1; i < g; i++) {
obVec.x = -i * n1;
obVec.y = i * (m1 + n1);
oaVec.x = i * m1;
oaVec.y = i * n1;
isoId = obVec.x + "|" + obVec.y;
isoIdR = oaVec.x + "|" + oaVec.y;
matchIdx(f, fr, isoId, isoIdR);
}
}
fr = this.IDATA.edgematch[f][2];
rot = this.IDATA.edgematch[f][3];
if (rot && rot === "A") {
for (let i = 1; i < g; i++) {
oaVec.x = i * m1;
oaVec.y = i * n1;
abVec.x = m - (g - i) * (m1 + n1); //reversed for BA
abVec.y = n + (g - i) * m1; //reversed for BA
isoId = oaVec.x + "|" + oaVec.y;
isoIdR = abVec.x + "|" + abVec.y;
matchIdx(f, fr, isoId, isoIdR);
}
}
for (let i = 0; i < this.vertices.length; i++) {
isoId = this.vertices[i].x + "|" + this.vertices[i].y;
idx = f + "|" + isoId;
if (!(idx in vecToidx)) {
vecToidx[idx] = indexCount++;
if (vDist[isoId][0] > 2) {
closestTo[vecToidx[idx]] = [-vDist[isoId][0], vDist[isoId][1], vecToidx[idx]];
}
else {
closestTo[vecToidx[idx]] = [verts[vDist[isoId][0]], vDist[isoId][1], vecToidx[idx]];
}
}
}
}
this.closestTo = closestTo;
this.vecToidx = vecToidx;
}
calcCoeffs() {
const m = this.m;
const n = this.n;
const thirdR3 = Math.sqrt(3) / 3;
const LSQD = m * m + n * n + m * n;
this.coau = (m + n) / LSQD;
this.cobu = -n / LSQD;
this.coav = (-thirdR3 * (m - n)) / LSQD;
this.cobv = (thirdR3 * (2 * m + n)) / LSQD;
}
createInnerFacets() {
const m = this.m;
const n = this.n;
for (let y = 0; y < n + m + 1; y++) {
for (let x = this.min[y]; x < this.max[y] + 1; x++) {
if (x < this.max[y] && x < this.max[y + 1] + 1) {
this.innerFacets.push(["|" + x + "|" + y, "|" + x + "|" + (y + 1), "|" + (x + 1) + "|" + y]);
}
if (y > 0 && x < this.max[y - 1] && x + 1 < this.max[y] + 1) {
this.innerFacets.push(["|" + x + "|" + y, "|" + (x + 1) + "|" + y, "|" + (x + 1) + "|" + (y - 1)]);
}
}
}
}
edgeVecsABOB() {
const m = this.m;
const n = this.n;
const B = new _IsoVector(-n, m + n);
for (let y = 1; y < m + n; y++) {
const point = new _IsoVector(this.min[y], y);
const prev = new _IsoVector(this.min[y - 1], y - 1);
const next = new _IsoVector(this.min[y + 1], y + 1);
const pointR = point.clone();
const prevR = prev.clone();
const nextR = next.clone();
pointR.rotate60About(B);
prevR.rotate60About(B);
nextR.rotate60About(B);
const maxPoint = new _IsoVector(this.max[pointR.y], pointR.y);
const maxPrev = new _IsoVector(this.max[pointR.y - 1], pointR.y - 1);
const maxLeftPrev = new _IsoVector(this.max[pointR.y - 1] - 1, pointR.y - 1);
if (pointR.x !== maxPoint.x || pointR.y !== maxPoint.y) {
if (pointR.x !== maxPrev.x) {
// type2
//up
this.vertexTypes.push([1, 0, 0]);
this.isoVecsABOB.push([point, maxPrev, maxLeftPrev]);
//down
this.vertexTypes.push([1, 0, 0]);
this.isoVecsABOB.push([point, maxLeftPrev, maxPoint]);
}
else if (pointR.y === nextR.y) {
// type1
//up
this.vertexTypes.push([1, 1, 0]);
this.isoVecsABOB.push([point, prev, maxPrev]);
//down
this.vertexTypes.push([1, 0, 1]);
this.isoVecsABOB.push([point, maxPrev, next]);
}
else {
// type 0
//up
this.vertexTypes.push([1, 1, 0]);
this.isoVecsABOB.push([point, prev, maxPrev]);
//down
this.vertexTypes.push([1, 0, 0]);
this.isoVecsABOB.push([point, maxPrev, maxPoint]);
}
}
}
}
mapABOBtoOBOA() {
const point = new _IsoVector(0, 0);
for (let i = 0; i < this.isoVecsABOB.length; i++) {
const temp = [];
for (let j = 0; j < 3; j++) {
point.x = this.isoVecsABOB[i][j].x;
point.y = this.isoVecsABOB[i][j].y;
if (this.vertexTypes[i][j] === 0) {
point.rotateNeg120(this.m, this.n);
}
temp.push(point.clone());
}
this.isoVecsOBOA.push(temp);
}
}
mapABOBtoBAOA() {
const point = new _IsoVector(0, 0);
for (let i = 0; i < this.isoVecsABOB.length; i++) {
const temp = [];
for (let j = 0; j < 3; j++) {
point.x = this.isoVecsABOB[i][j].x;
point.y = this.isoVecsABOB[i][j].y;
if (this.vertexTypes[i][j] === 1) {
point.rotate120(this.m, this.n);
}
temp.push(point.clone());
}
this.isoVecsBAOA.push(temp);
}
}
// eslint-disable-next-line @typescript-eslint/naming-convention
MapToFace(faceNb, geodesicData) {
const F = this.IDATA.face[faceNb];
const oidx = F[2];
const aidx = F[1];
const bidx = F[0];
const O = Vector3.FromArray(this.IDATA.vertex[oidx]);
const A = Vector3.FromArray(this.IDATA.vertex[aidx]);
const B = Vector3.FromArray(this.IDATA.vertex[bidx]);
const OA = A.subtract(O);
const OB = B.subtract(O);
const x = OA.scale(this.coau).add(OB.scale(this.cobu));
const y = OA.scale(this.coav).add(OB.scale(this.cobv));
const mapped = [];
let idx;
let tempVec = TmpVectors.Vector3[0];
for (let i = 0; i < this.cartesian.length; i++) {
tempVec = x.scale(this.cartesian[i].x).add(y.scale(this.cartesian[i].y)).add(O);
mapped[i] = [tempVec.x, tempVec.y, tempVec.z];
idx = faceNb + "|" + this.vertices[i].x + "|" + this.vertices[i].y;
geodesicData.vertex[this.vecToidx[idx]] = [tempVec.x, tempVec.y, tempVec.z];
}
}
//statics
/**Creates a primary triangle
* @internal
*/
build(m, n) {
const vertices = [];
const O = _IsoVector.Zero();
const A = new _IsoVector(m, n);
const B = new _IsoVector(-n, m + n);
vertices.push(O, A, B);
//max internal isoceles triangle vertices
for (let y = n; y < m + 1; y++) {
for (let x = 0; x < m + 1 - y; x++) {
vertices.push(new _IsoVector(x, y));
}
}
//shared vertices along edges when needed
if (n > 0) {
const g = HighestCommonFactor(m, n);
const m1 = m / g;
const n1 = n / g;
for (let i = 1; i < g; i++) {
vertices.push(new _IsoVector(i * m1, i * n1)); //OA
vertices.push(new _IsoVector(-i * n1, i * (m1 + n1))); //OB
vertices.push(new _IsoVector(m - i * (m1 + n1), n + i * m1)); // AB
}
//lower rows vertices and their rotations
const ratio = m / n;
for (let y = 1; y < n; y++) {
for (let x = 0; x < y * ratio; x++) {
vertices.push(new _IsoVector(x, y));
vertices.push(new _IsoVector(x, y).rotate120(m, n));
vertices.push(new _IsoVector(x, y).rotateNeg120(m, n));
}
}
}
//order vertices by x and then y
vertices.sort((a, b) => {
return a.x - b.x;
});
vertices.sort((a, b) => {
return a.y - b.y;
});
const min = new Array(m + n + 1);
const max = new Array(m + n + 1);
for (let i = 0; i < min.length; i++) {
min[i] = Infinity;
max[i] = -Infinity;
}
let y = 0;
let x = 0;
const len = vertices.length;
for (let i = 0; i < len; i++) {
x = vertices[i].x;
y = vertices[i].y;
min[y] = Math.min(x, min[y]);
max[y] = Math.max(x, max[y]);
}
//calculates the distance of a vertex from a given primary vertex
const distFrom = (vert, primVert) => {
const v = vert.clone();
if (primVert === "A") {
v.rotateNeg120(m, n);
}
if (primVert === "B") {
v.rotate120(m, n);
}
if (v.x < 0) {
return v.y;
}
return v.x + v.y;
};
const cartesian = [];
const distFromO = [];
const distFromA = [];
const distFromB = [];
const vertByDist = {};
const vertData = [];
let closest = -1;
let dist = -1;
for (let i = 0; i < len; i++) {
cartesian[i] = vertices[i].toCartesianOrigin(new _IsoVector(0, 0), 0.5);
distFromO[i] = distFrom(vertices[i], "O");
distFromA[i] = distFrom(vertices[i], "A");
distFromB[i] = distFrom(vertices[i], "B");
if (distFromO[i] === distFromA[i] && distFromA[i] === distFromB[i]) {
closest = 3;
dist = distFromO[i];
}
else if (distFromO[i] === distFromA[i]) {
closest = 4;
dist = distFromO[i];
}
else if (distFromA[i] === distFromB[i]) {
closest = 5;
dist = distFromA[i];
}
else if (distFromB[i] === distFromO[i]) {
closest = 6;
dist = distFromO[i];
}
if (distFromO[i] < distFromA[i] && distFromO[i] < distFromB[i]) {
closest = 2;
dist = distFromO[i];
}
if (distFromA[i] < distFromO[i] && distFromA[i] < distFromB[i]) {
closest = 1;
dist = distFromA[i];
}
if (distFromB[i] < distFromA[i] && distFromB[i] < distFromO[i]) {
closest = 0;
dist = distFromB[i];
}
vertData.push([closest, dist, vertices[i].x, vertices[i].y]);
}
vertData.sort((a, b) => {
return a[2] - b[2];
});
vertData.sort((a, b) => {
return a[3] - b[3];
});
vertData.sort((a, b) => {
return a[1] - b[1];
});
vertData.sort((a, b) => {
return a[0] - b[0];
});
for (let v = 0; v < vertData.length; v++) {
vertByDist[vertData[v][2] + "|" + vertData[v][3]] = [vertData[v][0], vertData[v][1], v];
}
this.m = m;
this.n = n;
this.vertices = vertices;
this.vertByDist = vertByDist;
this.cartesian = cartesian;
this.min = min;
this.max = max;
return this;
}
}
/** Builds Polyhedron Data
* @internal
*/
export class PolyhedronData {
constructor(
/**
* The name of the polyhedron
*/
name,
/**
* The category of the polyhedron
*/
category,
/**
* vertex data
*/
vertex,
/**
* face data
*/
face) {
this.name = name;
this.category = category;
this.vertex = vertex;
this.face = face;
}
}
/**
* This class Extends the PolyhedronData Class to provide measures for a Geodesic Polyhedron
*/
export class GeodesicData extends PolyhedronData {
/**
* @internal
*/
innerToData(face, primTri) {
for (let i = 0; i < primTri.innerFacets.length; i++) {
this.face.push(primTri.innerFacets[i].map((el) => primTri.vecToidx[face + el]));
}
}
/**
* @internal
*/
mapABOBtoDATA(faceNb, primTri) {
const fr = primTri.IDATA.edgematch[faceNb][0];
for (let i = 0; i < primTri.isoVecsABOB.length; i++) {
const temp = [];
for (let j = 0; j < 3; j++) {
if (primTri.vertexTypes[i][j] === 0) {
temp.push(faceNb + "|" + primTri.isoVecsABOB[i][j].x + "|" + primTri.isoVecsABOB[i][j].y);
}
else {
temp.push(fr + "|" + primTri.isoVecsABOB[i][j].x + "|" + primTri.isoVecsABOB[i][j].y);
}
}
this.face.push([primTri.vecToidx[temp[0]], primTri.vecToidx[temp[1]], primTri.vecToidx[temp[2]]]);
}
}
/**
* @internal
*/
mapOBOAtoDATA(faceNb, primTri) {
const fr = primTri.IDATA.edgematch[faceNb][0];
for (let i = 0; i < primTri.isoVecsOBOA.length; i++) {
const temp = [];
for (let j = 0; j < 3; j++) {
if (primTri.vertexTypes[i][j] === 1) {
temp.push(faceNb + "|" + primTri.isoVecsOBOA[i][j].x + "|" + primTri.isoVecsOBOA[i][j].y);
}
else {
temp.push(fr + "|" + primTri.isoVecsOBOA[i][j].x + "|" + primTri.isoVecsOBOA[i][j].y);
}
}
this.face.push([primTri.vecToidx[temp[0]], primTri.vecToidx[temp[1]], primTri.vecToidx[temp[2]]]);
}
}
/**
* @internal
*/
mapBAOAtoDATA(faceNb, primTri) {
const fr = primTri.IDATA.edgematch[faceNb][2];
for (let i = 0; i < primTri.isoVecsBAOA.length; i++) {
const temp = [];
for (let j = 0; j < 3; j++) {
if (primTri.vertexTypes[i][j] === 1) {
temp.push(faceNb + "|" + primTri.isoVecsBAOA[i][j].x + "|" + primTri.isoVecsBAOA[i][j].y);
}
else {
temp.push(fr + "|" + primTri.isoVecsBAOA[i][j].x + "|" + primTri.isoVecsBAOA[i][j].y);
}
}
this.face.push([primTri.vecToidx[temp[0]], primTri.vecToidx[temp[1]], primTri.vecToidx[temp[2]]]);
}
}
/**
* @internal
*/
orderData(primTri) {
const nearTo = [];
for (let i = 0; i < 13; i++) {
nearTo[i] = [];
}
const close = primTri.closestTo;
for (let i = 0; i < close.length; i++) {
if (close[i][0] > -1) {
if (close[i][1] > 0) {
nearTo[close[i][0]].push([i, close[i][1]]);
}
}
else {
nearTo[12].push([i, close[i][0]]);
}
}
const near = [];
for (let i = 0; i < 12; i++) {
near[i] = i;
}
let nearIndex = 12;
for (let i = 0; i < 12; i++) {
nearTo[i].sort((a, b) => {
return a[1] - b[1];
});
for (let j = 0; j < nearTo[i].length; j++) {
near[nearTo[i][j][0]] = nearIndex++;
}
}
for (let j = 0; j < nearTo[12].length; j++) {
near[nearTo[12][j][0]] = nearIndex++;
}
for (let i = 0; i < this.vertex.length; i++) {
this.vertex[i].push(near[i]);
}
this.vertex.sort((a, b) => {
return a[3] - b[3];
});
for (let i = 0; i < this.vertex.length; i++) {
this.vertex[i].pop();
}
for (let i = 0; i < this.face.length; i++) {
for (let j = 0; j < this.face[i].length; j++) {
this.face[i][j] = near[this.face[i][j]];
}
}
this.sharedNodes = nearTo[12].length;
this.poleNodes = this.vertex.length - this.sharedNodes;
}
/**
* @internal
*/
setOrder(m, faces) {
const adjVerts = [];
const dualFaces = [];
let face = faces.pop();
dualFaces.push(face);
let index = this.face[face].indexOf(m);
index = (index + 2) % 3;
let v = this.face[face][index];
adjVerts.push(v);
let f = 0;
while (faces.length > 0) {
face = faces[f];
if (this.face[face].indexOf(v) > -1) {
// v is a vertex of face f
index = (this.face[face].indexOf(v) + 1) % 3;
v = this.face[face][index];
adjVerts.push(v);
dualFaces.push(face);
faces.splice(f, 1);
f = 0;
}
else {
f++;
}
}
this.adjacentFaces.push(adjVerts);
return dualFaces;
}
/**
* @internal
*/
toGoldbergPolyhedronData() {
const goldbergPolyhedronData = new PolyhedronData("GeoDual", "Goldberg", [], []);
goldbergPolyhedronData.name = "GD dual";
const verticesNb = this.vertex.length;
const map = new Array(verticesNb);
for (let v = 0; v < verticesNb; v++) {
map[v] = [];
}
for (let f = 0; f < this.face.length; f++) {
for (let i = 0; i < 3; i++) {
map[this.face[f][i]].push(f);
}
}
let cx = 0;
let cy = 0;
let cz = 0;
let face = [];
let vertex = [];
this.adjacentFaces = [];
for (let m = 0; m < map.length; m++) {
goldbergPolyhedronData.face[m] = this.setOrder(m, map[m].concat([]));
for (const el of map[m]) {
cx = 0;
cy = 0;
cz = 0;
face = this.face[el];
for (let i = 0; i < 3; i++) {
vertex = this.vertex[face[i]];
cx += vertex[0];
cy += vertex[1];
cz += vertex[2];
}
goldbergPolyhedronData.vertex[el] = [cx / 3, cy / 3, cz / 3];
}
}
return goldbergPolyhedronData;
}
//statics
/**Builds the data for a Geodesic Polyhedron from a primary triangle
* @param primTri the primary triangle
* @internal
*/
static BuildGeodesicData(primTri) {
const geodesicData = new GeodesicData("Geodesic-m-n", "Geodesic", [
[0, PHI, -1],
[-PHI, 1, 0],
[-1, 0, -PHI],
[1, 0, -PHI],
[PHI, 1, 0],
[0, PHI, 1],
[-1, 0, PHI],
[-PHI, -1, 0],
[0, -PHI, -1],
[PHI, -1, 0],
[1, 0, PHI],
[0, -PHI, 1],
], []);
primTri.setIndices();
primTri.calcCoeffs();
primTri.createInnerFacets();
primTri.edgeVecsABOB();
primTri.mapABOBtoOBOA();
primTri.mapABOBtoBAOA();
for (let f = 0; f < primTri.IDATA.face.length; f++) {
primTri.MapToFace(f, geodesicData);
geodesicData.innerToData(f, primTri);
if (primTri.IDATA.edgematch[f][1] === "B") {
geodesicData.mapABOBtoDATA(f, primTri);
}
if (primTri.IDATA.edgematch[f][1] === "O") {
geodesicData.mapOBOAtoDATA(f, primTri);
}
if (primTri.IDATA.edgematch[f][3] === "A") {
geodesicData.mapBAOAtoDATA(f, primTri);
}
}
geodesicData.orderData(primTri);
const radius = 1;
geodesicData.vertex = geodesicData.vertex.map(function (el) {
const a = el[0];
const b = el[1];
const c = el[2];
const d = Math.sqrt(a * a + b * b + c * c);
el[0] *= radius / d;
el[1] *= radius / d;
el[2] *= radius / d;
return el;
});
return geodesicData;
}
}
//# sourceMappingURL=geodesicMesh.js.map