/*jshint esversion: 9 */
/**
* Define a point with cartesian coordinates.
*/
class Point {
/**
* Create a new point.
* @param {number} x The X Coordinate.
* @param {number} y The Y Coordinate.
*/
constructor(x, y) {
this.x = x;
this.y = y;
}
/**
* Add another Point to this point.
* @param {Point} p The Point to be added.
*/
add(p) {
this.x += p.x;
this.y += p.y;
}
/**
* Rotate the point in the coordinate system around another point.
* @param {number} degrees The nunber of degrees clockwise to rotate.
* @param {Point} center The point around which to rotate.
*/
rotate(degrees, center) {
const r = radians(degrees);
const x = this.x - center.x;
const y = this.y - center.y;
this.x = x * cos(r) - y * sin(r) + center.x;
this.y = x * sin(r) + y * cos(r) + center.y;
}
/**
* Vary the point location (both x and y) by up to v.
* @param {number} v The number of pixels to vary the point.
* @returns {Point} This point, after being modified.
*/
vary(v) {
this.x += random(-v, v);
this.y += random(-v, v);
return this;
}
}
/**
* Define a point with polar coordinates.
*/
class Polar {
/**
* Create a new point.
* @param {number} r The distance for the origin.
* @param {number} a The angle in radians.
*/
constructor(r, a) {
this.r = r;
this.a = a;
}
/**
* Convert a Polar to a Point.
* @returns {Point}
*/
toPoint() {
return new Point(this.r * cos(this.a), this.r * sin(this.a));
}
/**
* Convert a Polar to a Point, specifying the origin.
* @param {Point} center The origin.
* @returns {Point}
*/
toPointCenter(center) {
let p = this.toPoint();
p.add(center);
return p;
}
}
/**
* Define a line segment
*/
class Line {
/**
* Create a new line
* @param {Point} begin
* @param {Point} end
*/
constructor(begin, end) {
this.begin = begin;
this.end = end;
}
/**
* Return the length of the line
* @returns {number}
*/
length() {
return Math.sqrt(Math.pow(this.begin.x - this.end.x, 2) + Math.pow(this.begin.y - this.end.y, 2));
}
/**
* Divide the line into segments, returning a list of points.
* @param {number} segments Number of segments.
* @returns {Point[]} List of points.
*/
divide(segments) {
let points = [this.begin];
const xDiff = this.begin.x - this.end.x;
const yDiff = this.begin.y - this.end.y;
for (let i = 1; i <= segments; i++) {
points.push(new Point(this.begin.x - i * xDiff / segments, this.begin.y - i * yDiff / segments));
}
return points;
}
/**
* Convert a line into an array of Points with slight variations.
* @param {number} divisions The number of lines to divide the Line into.
* @param {number} variation The number of pixels of variation in X and Y to allow.
* @returns {Point[]}
*/
handDrawn(divisions, variation) {
if (divisions === undefined) {
divisions = Math.floor(this.length() / 6);
}
if (divisions === 0) {
return [this.begin, this.end];
}
if (variation === undefined) {
variation = 1;
}
let variedPoints = [];
const points = this.divide(divisions);
for (let p = 0; p < points.length; p++) {
if (p === 0 || p === points.length - 1) {
variedPoints.push(points[p]);
} else {
variedPoints.push(points[p].vary(variation));
}
}
return variedPoints;
}
/**
* Find the point at which two lines intersect. The intersection point may not be on either line segment.
* @param {Line} l The line to intersect with
* @returns {Point|undefined}
*/
intersection(l) {
const d = ((l.end.y - l.begin.y) * (this.end.x - this.begin.x)) - ((l.end.x - l.begin.x) * (this.end.y - this.begin.y));
if (d == 0) {
return undefined;
}
const a = this.begin.y - l.begin.y;
const b = this.begin.x - l.begin.x;
const n1 = ((l.end.x - l.begin.x) * a) - ((l.end.y - l.begin.y) * b);
const a1 = n1 / d;
const x = this.begin.x + (a1 * (this.end.x - this.begin.x));
const y = this.begin.y + (a1 * (this.end.y - this.begin.y));
return new Point(x, y);
}
}
/**
* Define a Polygon.
*/
class Polygon {
/**
* Create a polygon.
* @param {Point[] | Polygon} vertices The vertices of the polygon.
*/
constructor(vertices) {
this.boundingRectangle = undefined;
this.vertices = [];
if (typeof vertices !== 'undefined') {
this.vertices = vertices;
}
}
/**
* Add a vertex to the polygon.
* @param {Point} v The vertex to add.
*/
addVertex(v) {
this.vertices.push(v);
this.boundingRectangle = undefined;
}
/**
* Calculates the bounding rectangle. It's worth noting that the first point in the bounding rectangle is the
* upper left corner, and subsequent points proceed clockwise. Certain methods rely on this ordering.
* @returns {Polygon} The bounding rectangle for this polygon.
*/
getBoundingRectangle() {
if (this.boundingRectangle === undefined) {
let minX = this.vertices[0].x;
let minY = this.vertices[0].y;
let maxX = minX;
let maxY = minY;
for (let i = 1; i < this.vertices.length; i++) {
if (this.vertices[i].x < minX)
minX = this.vertices[i].x;
if (this.vertices[i].y < minY)
minY = this.vertices[i].y;
if (this.vertices[i].x > maxX)
maxX = this.vertices[i].x;
if (this.vertices[i].y > maxY)
maxY = this.vertices[i].y;
}
this.boundingRectangle = new Polygon([
new Point(minX, minY),
new Point(maxX, minY),
new Point(maxX, maxY),
new Point(minX, maxY),
]);
}
return this.boundingRectangle;
}
/**
* Calculate the center of the polygon.
* @returns {Point} The center.
*/
getCenter() {
this.getBoundingRectangle();
return new Point(
(this.boundingRectangle.vertices[0].x + this.boundingRectangle.vertices[1].x) / 2,
(this.boundingRectangle.vertices[0].y + this.boundingRectangle.vertices[3].y) / 2,
);
}
/**
* Get the upper left corner (origin) of the bounding rectangle.
* @returns {Point} The upper left corner.
*/
getOrigin() {
this.getBoundingRectangle();
return this.boundingRectangle.vertices[0];
}
/**
* Get the width of the bounding rectangle.
* @returns {number} The difference between max and min X values.
*/
getWidth() {
this.getBoundingRectangle();
return Math.ceil(this.boundingRectangle.vertices[2].x - this.boundingRectangle.vertices[0].x);
}
/**
* Get the height of the bounding rectangle.
* @returns {number} The difference between max and min Y values.
*/
getHeight() {
this.getBoundingRectangle();
return Math.ceil(this.boundingRectangle.vertices[2].y - this.boundingRectangle.vertices[0].y);
}
/**
* Rotate the polygon around a point.
* @param {number} degrees The number of degrees to rotate.
* @param {Point} center The point around which to rotate. If not supplied, the physical center of the polygon is used.
*/
rotate(degrees, center) {
if (typeof (center) === 'undefined') {
center = this.getCenter();
}
for (let i = 0; i < this.vertices.length; i++) {
this.vertices[i].rotate(degrees, center);
}
this.boundingRectangle = undefined;
}
/**
* Make a copy of the polygon.
* @returns {Polygon} New Polygon.
*/
copy() {
const poly = new Polygon();
for (let i = 0; i < this.vertices.length; i++) {
poly.addVertex(new Point(this.vertices[i].x, this.vertices[i].y));
}
return poly;
}
}
/**
* Define a range which a value could take.
*/
class Range {
/**
* Create a new Range.
* @param {number} min The minimum value.
* @param {number} max The maximum value.
*/
constructor(min, max) {
if (min > max) {
this.min = max;
this.max = min;
} else {
this.min = min;
this.max = max;
}
}
/**
* Return a random value, evenly distributed, from the range.
* @returns {number}
*/
rand() {
return random(this.min, this.max);
}
}