Start implementing VoronoiOverlay, but there's a looong way to go.

This commit is contained in:
Starbeamrainbowlabs 2019-05-29 21:06:00 +01:00
parent a8444fdde6
commit c1b13cda22
Signed by: sbrl
GPG key ID: 1BE5172E637709C2
5 changed files with 320 additions and 2 deletions

View file

@ -0,0 +1,247 @@
"use strict";
import Vector from './Vector2.mjs';
/**
* Represents a rectangle in 2D space.
* @source https://gist.github.com/d7ff700d3ca57ab5d52ec12a1ee8b8a0
* Changelog:
* 29th May 2019
* Created this changelog
* Added setters to TopLeft, Top, Size, etc.
*/
class Rectangle
{
/**
* The top-left corner of the rectangle.
* @returns {Vector}
*/
get TopLeft() {
return new Vector(this.x, this.y);
}
/**
* Sets the top-left corner of the rectangle.
* @param {Vector} point The point to set it to.
*/
set TopLeft(point) {
this.x = point.x;
this.y = point.y;
}
/**
* The top-right corner of the rectangle.
* @returns {Vector}
*/
get TopRight() {
return new Vector(this.x + this.width, this.y);
}
/**
* Sets the top-right corner of the rectangle.
* @param {Vector} point The point to set it to.
*/
set TopRight(point) {
this.width = point.x - this.x;
this.y = point.y;
}
/**
* The bottom-left corner of the rectangle.
* @returns {Vector}
*/
get BottomLeft() {
return new Vector(this.x, this.y + this.height);
}
/**
* Sets the bottom-left corner of the rectangle.
* @param {Vector} point The point to set it to.
*/
set BottomLeft(point) {
this.width = point.x;
this.y = point.y - this.y;
}
/**
* The bottom-right corner of the rectangle.
* @returns {Vector}
*/
get BottomRight() {
return new Vector(this.x + this.width, this.y + this.height);
}
/**
* Sets the bottom-right corner of the rectangle.
* @param {Vector} point The point to set it to.
*/
set BottomRight(point) {
this.width = point.x - this.x;
this.y = point.y - this.y;
}
/**
* The centre of the rectangle.
* @returns {Vector}
*/
get Centre() {
return new Vector(
this.x + (this.width/2),
this.y + (this.height/2)
);
}
/**
* The Y coordinate of the top of the rectangle.
* @returns {Number}
*/
get Top() {
return this.y;
}
/**
* Sets the Y coordinate of the top of the rectangle.
* @param {Number} y
*/
set Top(y) {
this.y = y;
}
/**
* The Y coordinate of the bottom of the rectangle.
* @returns {Number}
*/
get Bottom() {
return this.y + this.height;
}
/**
* Sets the Y coordinate of the bottom of the rectangle.
* @param {Number} y
*/
set Bottom(y) {
this.height = y - this.y;
}
/**
* The X coordinate of the left side of the rectangle.
* @returns {Number}
*/
get Left() {
return this.x;
}
/**
* Sets the X coordinate of the left of the rectangle.
* @param {Number} x
*/
set Left(x) {
this.x = x;
}
/**
* The X coordinate of the right side of the rectangle.
* @returns {Number}
*/
get Right() {
return this.x + this.width;
}
/**
* Sets the X coordinate of the right of the rectangle.
* @param {Number} x
*/
set Right(x) {
this.x = x - this.x;
}
/**
* The size of this rectangle.
* @returns {Vector}
*/
get Size() {
return new Vector(this.width, this.height);
}
/**
* Sets the size of the rectangle.
* @param {Vector} newSize The new size of the rectangle.
*/
set Size(newSize) {
this.width = newSize.x;
this.height = newSize.y;
}
constructor(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
/**
* Moves this Rectangle by the specified vector.
* @param {Vector} v The amount to move this Rectangle by.
* @return {Rectangle} The current rectangle. Useful for daisy-chaining.
*/
move_by(v) {
this.x += v.x;
this.y += v.y;
return this;
}
/**
* Figures out whether this rectangle overlaps another rectangle.
* @param {Rectangle} otherRectangle The other rectangle to check the overlap of.
* @return {bool} Whether this rectangle overlaps another rectangle.
*/
overlaps(otherRectangle)
{
if(this.Top > otherRectangle.Bottom ||
this.Bottom < otherRectangle.Top ||
this.Left > otherRectangle.Right ||
this.Right < otherRectangle.Left)
return false;
return true;
}
/**
* Works out whether the specified vector falls within this Rectangle.
* @param {Vector} v The vector to test.
* @return {Boolean} Whether the specified vector falls within this Rectangle or not.
*/
is_inside_vector(v) {
return v.x >= this.x && v.y >= this.y &&
v.x <= this.Right && v.y <= this.Bottom;
}
/**
* Works out if this Rectangle falls completely within the specified
* rectangle.
* @param {Rectangle} otherRectangle The other rectangle to compare against.
* @return {Boolean} Whether this Rectangle faalls within the specified Rectangle.
*/
is_inside(otherRectangle) {
if(this.Top >= otherRectangle.Top &&
this.Bottom <= otherRectangle.Bottom &&
this.Left >= otherRectangle.Left &&
this.Right <= otherRectangle.Right)
return true;
return false;
}
/**
* Returns a copy of this rectangle that can be safely edited without affecting the original.
* @returns {Rectangle}
*/
clone()
{
return new Rectangle(this.x, this.y, this.width, this.height);
}
toString() {
return `[Rectangle @ (${this.x}, ${this.y}) ${this.width} x ${this.height}]`;
}
}
/**
* A rectangle with all it's values initalised to zero.
* Don't forget to clone it before editing!
* @type {Rectangle}
*/
Rectangle.Zero = new Rectangle(0, 0, 0, 0);
export default Rectangle;

View file

@ -2,6 +2,7 @@
import XMLWriter from 'xml-writer';
import Rectangle from './Rectangle.mjs';
import Vector2 from './Vector2.mjs';
/*
@ -11,8 +12,12 @@ import Vector2 from './Vector2.mjs';
* @license MPL-2.0
*/
class SvgWriter {
constructor(viewBox) {
// string outputFilename, string widthspec = "100%", string heightspec = "100%", Rectangle? rawViewBox = null
/**
* @param {String} [widthspec="100%"]
* @param {String} [heightspec="100%"]
* @param {Retangle} [viewBox=null]
*/
constructor(widthspec = "100%", heightspec = "100%", viewBox = null) {
this.unitSuffix = "";
// ----------------------------------

View file

@ -1,9 +1,69 @@
"use strict";
import Voronoi from 'voronoi';
import Vector2 from '../Helpers/Vector2.mjs';
import Rectangle from '../Helpers/Rectangle.mjs';
import SvgWriter from '../Helpers/SVGWriter.mjs';
/**
* Generates and manages a single voronoi SVGOverlay layer.
*/
class VoronoiOverlay {
constructor() {
this.border = new Vector2(0.1, 0.1); // lat / long
this.cells = [];
}
addCells(...cells) {
this.cells.push(...cells);
}
/**
* Computes the bounding box of all the currently registered points.
* Includes a border, which is defined by this.border.
* @return {Rectangle} The bounding box of the currently registered points.
*/
computeBoundingBox() {
let result = new Rectangle(Infinity, Infinity, -Infinity, -Infinity);
for(let cell of this.cells) {
if(cell.point.x < result.x) result.x = cell.point.x;
if(cell.point.y < result.y) result.y = cell.point.y;
if(cell.point.x > result.Right) result.Right = cell.point.x;
if(cell.point.y > result.Bottom) result.Bottom = cell.point.y;
}
result.Left -= this.border.x;
result.Right += this.border.x;
result.Top -= this.border.y;
result.Bottom += this.border.y;
return result;
}
render() {
let bounding_box = this.computeBoundingBox();
// Recycle the diagram object if possible
if(typeof VoronoiOverlay.diagram !== "undefined")
VoronoiOverlay.voronoi.recycle(VoronoiOverlay.diagram);
VoronoiOverlay.diagram = VoronoiOverlay.voronoi.compute(
this.cells.map((cell) => cell.point),
bounding_box
);
console.log(VoronoiOverlay.diagram)
let svg = new SvgWriter();
}
}
VoronoiOverlay.voronoi = new Voronoi();
export default VoronoiOverlay;

5
package-lock.json generated
View file

@ -4362,6 +4362,11 @@
"integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==",
"dev": true
},
"voronoi": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/voronoi/-/voronoi-1.0.0.tgz",
"integrity": "sha1-wIPK+pjENZpRgBi/6IYkyyJ6wpE="
},
"xml-writer": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/xml-writer/-/xml-writer-1.7.0.tgz",

View file

@ -28,6 +28,7 @@
"nanomodal": "^5.1.1",
"smartsettings": "^1.2.3",
"tabs": "^0.2.0",
"voronoi": "^1.0.0",
"xml-writer": "^1.7.0"
},
"devDependencies": {