class Triangle {
constructor(a, b, c) {
this.p = [a, b, c];
}
area() {
let total = 0;
let [px, py] = this.p[2];
for (let [x, y] of this.p) {
total += (px - x) * (y + py);
[px, py] = [x, y];
}
return total / 2;
}
sideLength(i) {
const j = (i + 1) % 3;
return Math.hypot(this.p[i][0] - this.p[j][0], this.p[i][1] - this.p[j][1]);
}
sideHeight(i) {
const j = (i + 1) % 3,
k = (i + 2) % 3;
const u = [this.p[j][0] - this.p[i][0], this.p[j][1] - this.p[i][1]];
const v = [this.p[k][0] - this.p[j][0], this.p[k][1] - this.p[j][1]];
const ulen = Math.hypot(...u) || 0.0001;
const uhat = [u[0] / ulen, u[1] / ulen];
const uhatDotV = uhat[0] * v[0] + uhat[1] * v[1];
return [v[0] - uhatDotV * uhat[0], v[1] - uhatDotV * uhat[1]];
}
barycenter() {
const sum = this.p.reduce((a, b) => [a[0] + b[0], a[1] + b[1]]);
return [sum[0] / 3, sum[1] / 3];
}
setArea(a) {
let factor = a / this.area();
if (Math.abs(factor) < 1) return this.setArea1(factor);
return this.setArea2(factor);
}
setArea1(factor) {
// Use scaling towards barycenter
//let formerArea = this.area();
let f = Math.sqrt(Math.abs(factor));
let bary = this.barycenter();
let v = [];
this.p.forEach((q) => {
let u = [(q[0] - bary[0]) * f, (q[1] - bary[1]) * f];
v.push(u);
q[0] = bary[0] + u[0];
q[1] = bary[1] + u[1];
});
if (factor < 0) {
// Reflect wrt barycenter using the smallest height as an axis
let { i, h, hgt } = [0, 1, 2]
.map((i) => {
let hgt = this.sideHeight(i);
let h = Math.hypot(...hgt);
return { i, h, hgt };
})
.reduce((a, b) => (a.h < b.h ? a : b));
let j = (i + 1) % 3;
let k = (i + 2) % 3;
let n = [hgt[0] / h, hgt[1] / h];
let a = v[k][0] * n[0] + v[k][1] * n[1];
let b = h - a;
this.p[i] = [this.p[i][0] + n[0] * b * 2, this.p[i][1] + n[1] * b * 2];
this.p[j] = [this.p[j][0] + n[0] * b * 2, this.p[j][1] + n[1] * b * 2];
this.p[k] = [this.p[k][0] - n[0] * a * 2, this.p[k][1] - n[1] * a * 2];
// mutable debug = { hgt, ratio: this.area() / formerArea };
}
}
setArea2(factor) {
const h = [0, 1, 2].map((i) => this.sideHeight(i));
const hlen = h.map(([x, y]) => Math.hypot(x, y));
let i =
Math.abs(factor) < 1
? // Shrink
hlen[0] > hlen[1]
? hlen[0] > hlen[2]
? 0
: 2
: hlen[1] > hlen[2]
? 1
: 2
: // Expand
hlen[0] < hlen[1]
? hlen[0] < hlen[2]
? 0
: 2
: hlen[1] < hlen[2]
? 1
: 2;
let u = h[i],
v = [0, 0];
if (factor < 0) {
factor = -factor;
v = [...u];
u = [-u[0], -u[1]];
}
u = [v[0] + (-u[0] * (factor - 1)) / 3, v[1] + (-u[1] * (factor - 1)) / 3];
const j = (i + 1) % 3,
k = (i + 2) % 3;
this.p[i][0] += u[0];
this.p[i][1] += u[1];
this.p[j][0] += u[0];
this.p[j][1] += u[1];
this.p[k][0] -= u[0] * 2;
this.p[k][1] -= u[1] * 2;
}
}