class Border {
constructor(angles, children = null, frame = null, extra = null) {
frame = frame ? frame.clone() : new Frame();
let foundLoop = false;
do {
let result = [];
foundLoop = false;
let flag = false;
for (let a of angles) {
a = (a < 0 ? 360 + a : a) % 360;
if (eq(a, 180)) {
flag = true;
foundLoop = true;
} else if (flag) {
result[result.length - 1] =
(result[result.length - 1] + a + 180) % 360;
flag = false;
} else result.push(a);
}
if (flag) {
const deleted = result.shift();
result[result.length - 1] =
(result[result.length - 1] + deleted + 180) % 360;
const transf = new Frame().displace(1).rotate(deleted);
const transfInvert = transf.clone().invert();
frame = frame.clone().compose(transf);
for (let child of children) {
child.frame = transfInvert.clone().compose(child.frame);
}
}
angles = result;
} while (foundLoop);
this.angles = angles;
this.totalAngle = angles.reduce((a, b) => a + b, 0) % 360;
this.children = children;
this.frame = frame;
this.extra = extra;
this.shifts = 0;
}
clone() {
const ret = new Border(this.angles, this.children, this.frame, this.extra);
ret.shifts = this.shifts;
return ret;
}
shift(n) {
n = [n + this.angles.length] % this.angles.length; // Circular arithmetic
let children = this.children;
if (children) {
let newFrame = new Frame();
for (let i = 0; i < n; i++) {
newFrame.displace(1);
newFrame.rotate(this.angles[i]);
}
newFrame.invert();
children = children.map((child) => {
child = child.clone();
child.frame = newFrame.clone().compose(child.frame);
return child;
});
}
let angles = [...this.angles.slice(n), ...this.angles.slice(0, n)];
let ret = new Border(angles, children, this.frame.clone(), this.extra);
ret.shifts = this.shifts + n;
return ret;
}
mirror() {
const angles = [...this.angles].reverse();
let children = this.children;
if (children) {
let frame = new Frame().displace(1).mirror();
children = children.map((child) => {
child = child.clone();
child.frame = frame.clone().compose(child.frame);
return child;
});
}
return new Border(angles, children, this.frame.clone(), this.extra);
}
join(other, extra = null) {
const newFrame = new Frame(); //other.frame.clone().compose(this.frame);
for (let i = 0; i < this.angles.length - 1; i++) {
newFrame.displace(1);
newFrame.rotate(this.angles[i]);
}
newFrame.displace(1);
newFrame.rotate(180);
let otherClone = other.clone();
otherClone.frame = newFrame;
this.frame = new Frame();
return new Border(
[
...this.angles.slice(0, -2),
this.angles[this.angles.length - 2] + 180 + other.angles[0],
...other.angles.slice(1, -1),
other.angles[other.angles.length - 1] +
180 +
this.angles[this.angles.length - 1]
],
[this, otherClone],
this.frame.clone(),
extra
);
}
points(frame) {
frame = frame || new Frame();
frame = frame.clone().compose(this.frame);
let pts = [[...frame.origin]];
for (let ang of this.angles) {
frame.displace(/*edgeSize*/ 1);
frame.rotate(ang);
pts.push([...frame.origin]);
}
return pts;
}
edgeFrame(n) {
let m = this.angles.length;
n = (n + m) % m;
let frame = this.frame.clone();
for (let i = 0; i < n; i++) {
frame.displace(1);
frame.rotate(this.angles[i]);
}
return frame;
}
traverseComponents(frame, f) {
let result = [];
function traverse(node, frame) {
if (node.children) {
frame = frame.clone();
frame = frame.compose(node.frame);
traverse(node.children[0], frame);
traverse(node.children[1], frame);
} else {
let pts = node.points(frame);
result.push(pts);
if (f) f(pts, node, frame);
}
}
traverse(this, frame);
return result;
}
}