I am working on a an effect that create many point and triangulates them to create a simple background animation. However, when I try to scale them down, not all lines are scaled accordingly and are often mismatched.
Here is the code that creates everything
const numNodes = 40;
let ac = [];
let moveAC = [];
const gradient = {
len: 6,
gradients: [
[225, 238, 88],
[0, 188, 212],
[229, 115, 115],
[74, 20, 140],
[129, 199, 132],
[255, 255, 255]
]
}
let grad1 = Math.round(Math.random() * (gradient.len - 1));
let grad2 = Math.round(Math.random() * (gradient.len - 1));
while (grad1 == grad2) {
grad2 = Math.round(Math.random() * (gradient.len - 1));
}
console.log(grad1, grad2);
grad1 = gradient.gradients[grad1];
grad2 = gradient.gradients[grad2];
function initialize_background() {
for (let i = 0; i < numNodes; i++) {
let posX = Math.random() * 100;
let posY = Math.random() * 100;
let coord = [posX, posY]
ac.push(coord);
let vecX = Math.random() * 2 - 1;
let vecY = Math.random() * 2 - 1;
let vector = [vecX, vecY];
moveAC.push(vector);
}
let edgeSmooth = 10;
for (let i = 1; i < edgeSmooth; i++) {
let pos = (i / edgeSmooth) * 100;
ac.push([pos, 0]);
ac.push([pos, 100]);
ac.push([0, pos]);
ac.push([100, pos]);
moveAC.push([0, 0]);
moveAC.push([0, 0]);
moveAC.push([0, 0]);
moveAC.push([0, 0]);
}
ac.push([0, 0]);
ac.push([0, 100]);
ac.push([100, 0]);
ac.push([100, 100]);
moveAC.push([0, 0]);
moveAC.push([0, 0]);
moveAC.push([0, 0]);
moveAC.push([0, 0]);
updateBackground();
}
function updateElementPosition(element, index) {
let speed = 0.1;
let newX = element[0] + moveAC[index][0]*speed;
let newY = element[1] + moveAC[index][1]*speed;
newX = (newX < 0)? 100 : ((newX > 100)? 0 : newX);
newY = (newY < 0)? 100 : ((newY > 100)? 0 : newY);
ac[index] = [newX, newY];
}
async function updateBackground() {
document.querySelectorAll(".triangle").forEach((element) => {
element.remove();
});
for (let i = 0; i < ac.length; i++) {
updateElementPosition(ac[i], i);
}
let triangles = orderCoordinatesToTriangles();
const docFrag = document.createDocumentFragment();
triangles.forEach((element) => {
let child = createTriangle(ac[element[0]], ac[element[1]], ac[element[2]], element[4]);
if (child != null) docFrag.appendChild(child);
});
document.getElementById("background-container").appendChild(docFrag);
await delay(25);
updateBackground();
}
function orderCoordinatesToTriangles() {
let triangles = [];
for (let e1 = 0; e1 < ac.length; e1 ++) {
for (let e2 = e1+1; e2 < ac.length; e2 ++) {
for (let e3 = e2+1; e3 < ac.length; e3 ++) {
let coord = sortToCounterClockwise(e1, e2, e3, ac);
let anyPointsWithin = false;
for (let e4 = 0; e4 < ac.length; e4 ++) {
if (e1 == e4 || e2 == e4 || e3 == e4) {
continue;
}
if (calculateDeterminant3x3(coord, e4)) {
anyPointsWithin = true;
break;
}
}
if (!anyPointsWithin) {
triangles.push(coord);
}
}
}
}
return triangles;
}
function calculateDeterminant3x3(coord, e4) {
const ax = ac[(coord[0])][0];
const ay = ac[coord[0]][1];
const bx = ac[coord[1]][0];
const by = ac[coord[1]][1];
const cx = ac[coord[2]][0];
const cy = ac[coord[2]][1];
const dx = ac[e4][0];
const dy = ac[e4][1];
const det1 = (ax-dx) * calculateDeterminant2x2((by-dy), calcDifferentDet(bx, dx, by, dy), (cy-dy), calcDifferentDet(cx, dx, cy, dy));
const det2 = (ay-dy) * calculateDeterminant2x2((bx-dx), calcDifferentDet(bx, dx, by, dy), (cx-dx), calcDifferentDet(cx, dx, cy, dy));
const det3 = calcDifferentDet(ax, dx, ay, dy) * calculateDeterminant2x2((bx-dx), (by-dy), (cx-dx), (cy-dy));
const result = det1 - det2 + det3;
return (result > 0);
}
// returns (e1 - e2)^2 + (e3 - e4)^2
function calcDifferentDet(e1, e2, e3, e4) {
return Math.pow(e1 - e2, 2) + Math.pow(e3 - e4, 2);
}
// | e1, e2 |
// | e3, e4 |
function calculateDeterminant2x2(e1, e2, e3, e4) {
return ((e1 * e4) - (e2*e3));
}
function sortToCounterClockwise(e1, e2, e3, ac) {
let coord = [];
const centerX = (ac[e1][0] + ac[e2][0] + ac[e3][0])/3;
const centerY = (ac[e1][1] + ac[e2][1] + ac[e3][1])/3;
let angle1 = get_angle([ac[e1][0] - centerX, ac[e1][1] - centerY]);
let angle2 = get_angle([ac[e2][0] - centerX, ac[e2][1] - centerY]);
let angle3 = get_angle([ac[e3][0] - centerX, ac[e3][1] - centerY]);
if (angle1 < 90) angle1 += 360;
if (angle2 < 90) angle2 += 360;
if (angle3 < 90) angle3 += 360;
if (angle1 < angle2 && angle1 < angle3) {
coord.push(e1);
if (angle2 < angle3) {coord.push(e2); coord.push(e3);}
else {coord.push(e3); coord.push(e2);}
}
else if (angle2 < angle1 && angle2 < angle3) {
coord.push(e2);
if (angle1 < angle3) {coord.push(e1); coord.push(e3);}
else {coord.push(e3); coord.push(e1);}
}
else {
coord.push(e3);
if (angle1 < angle2) {coord.push(e1); coord.push(e2);}
else {coord.push(e2); coord.push(e1);}
}
coord.push(centerX);
coord.push(centerY);
return coord;
}
const getScale = (cX, cY, pX, pY, s) => {
sSx = window.innerWidth;
sSy = window.innerHeight;
v = make_vector([pX, pY], [cX, cY]);
u = unit_vector(v);
return (pX + s*u[0])*sSx/100 + "px " + (pY+ s*u[1])*sSy/100 + "px";
}
function createTriangle(p1, p2, p3, centerY) {
const newTriangle = document.createElement("div");
newTriangle.className = "triangle";
let cX = (p1[0] + p2[0] + p3[0]) / 3;
let cY = ((p1[1]) + (p2[1]) + (p3[1])) / 3;
let scale = 0.2;
coord1 = getScale(cX, cY, p1[0], (p1[1]), scale);
coord2 = getScale(cX, cY, p2[0], (p2[1]), scale);
coord3 = getScale(cX, cY, p3[0], (p3[1]), scale);
newTriangle.style.backgroundColor = "rgb(" + colourFunction(0, centerY) + ", " + colourFunction(1, centerY) + ", " + colourFunction(2, centerY) + ")";
newTriangle.style.clipPath = "polygon(" + coord1 + ", " + coord2 + ", " + coord3 + ")";
if (!newTriangle.style.clipPath) {
return null;
}
return newTriangle;
}
function colourFunction(i, cY) {
return (grad1[i] + ((grad2[i] - grad1[i]) * (1 - (cY / 100))));
}
And the code for the vectors is as follows
//Turn current position into a vector given a vector matrix
const get_vector = (e1, e2, a) => {
const wa = a[e2][1] - a[e1][1];
const wb = a[e2][0] - a[e1][0];
return [wb, wa];
};
//make a vector given arrays
const make_vector = (e1, e2) => {
const wa = e2[1] - e1[1];
const wb = e2[0] - e1[0];
return [wb, wa];
};
//Get current width of a vector
const get_width = (v) => {
const wa = Math.pow(v[0], 2);
const wb = Math.pow(v[1], 2);
return Math.sqrt(wa + wb);
};
//Get current angle of a vector with the line <1, 0>
const get_angle = (v) => {
let angle = Math.acos((v[0] / get_width(v)))*180/Math.PI;
if (v[1] < 0) {angle = 360 - angle;}
return angle;
};
//get unit vector
const unit_vector= (v) => {
let width = get_width(v);
return [v[0]/width, v[1]/width];
};
However, I get something similar to this as the following output
Single frame of the animation
Some boarders are thicker than others, and I’m not sure what the issue is