class Path {
constructor() {
this.points = [];
}
get tail() {
return this.points[this.points.length - 1] || new Vector(0, 0);
}
get head() {
return this.points[0] || new Vector(0, 0);
}
addToTail(delta) {
const newPath = new Path();
const point = this.tail.add(delta);
newPath.points = [...this.points, point];
return newPath;
}
get bounds() {
const allX = this.points.map(p => p.x);
const allY = this.points.map(p => p.y);
const deltaX = Math.abs(Math.max(...allX) - Math.min(...allY));
const deltaY = Math.abs(Math.max(...allY) - Math.min(...allY));
return new Vector(deltaX, deltaY);
}
get minPoint() {
const allX = this.points.map(p => p.x);
const allY = this.points.map(p => p.y);
return new Vector(Math.min(...allX), Math.min(...allY));
}
get maxPoint() {
const allX = this.points.map(p => p.x);
const allY = this.points.map(p => p.y);
return new Vector(Math.max(...allX), Math.max(...allY));
}
get center() {
return this.minPoint.add(this.maxPoint).scale(0.5);
}
// adds vector to every one of the points
translate(vector) {
const newPath = new Path();
newPath.points = this.points.map(point => point.add(vector));
return newPath;
}
translateCenterToOrigin() {
return this.translate(this.center.scale(-1));
}
translateMinPointToOrigin() {
return this.translate(this.minPoint.scale(-1));
}
rotate(angle, about = this.center) {
const newPath = new Path();
newPath.points = this.points;
const adjustedToOrigin = newPath.translate(about.scale(-1));
adjustedToOrigin.points = adjustedToOrigin.points.map(point =>
point.rotate(angle)
);
return adjustedToOrigin.translate(about);
}
scale(factor, about = this.center) {
const newPath = new Path();
newPath.points = this.points.map(point => point.scale(factor));
return newPath.translate(about.scale(0.5));
}
join(otherPath, thisDirection = "tail", otherDirection = "head") {
let localPoints = this.points || [];
let otherPoints = otherPath.points || [];
if (thisDirection === "head") {
localPoints = localPoints.reverse();
}
if (otherDirection === "head") {
otherPoints = otherPoints.reverse();
}
const newPath = new Path();
newPath.points = [...localPoints, ...otherPoints];
return newPath;
}
toSimple() {
return this.points.map(vector => [vector.x, vector.y]);
}
static join(...paths) {
return paths.reduce((bigPath, littlePath) => {
return bigPath.join(littlePath);
}, new Path());
}
static translate(paths, vector) {
return paths.map(p => p.translate(vector));
}
static translateMinPointToOrigin(...paths) {
const macro = Path.join(...paths);
const offset = macro.minPoint.scale(-1);
return Path.translate(paths, offset);
}
static bounds(...paths) {
return Path.join(...paths).bounds;
}
static center(...paths) {
return Path.join(...paths).center;
}
static fromSimple(points) {
const newPath = new Path();
newPath.points = points.map(([x, y]) => new Vector(x, y));
return newPath;
}
}