mirror of
https://github.com/ConnectedHumber/Air-Quality-Web
synced 2024-11-22 06:23:01 +00:00
Start implementing VoronoiOverlay, but there's a looong way to go.
This commit is contained in:
parent
a8444fdde6
commit
c1b13cda22
5 changed files with 320 additions and 2 deletions
247
client_src/js/Helpers/Rectangle.mjs
Normal file
247
client_src/js/Helpers/Rectangle.mjs
Normal 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;
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import XMLWriter from 'xml-writer';
|
import XMLWriter from 'xml-writer';
|
||||||
|
|
||||||
|
import Rectangle from './Rectangle.mjs';
|
||||||
import Vector2 from './Vector2.mjs';
|
import Vector2 from './Vector2.mjs';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -11,8 +12,12 @@ import Vector2 from './Vector2.mjs';
|
||||||
* @license MPL-2.0
|
* @license MPL-2.0
|
||||||
*/
|
*/
|
||||||
class SvgWriter {
|
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 = "";
|
this.unitSuffix = "";
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
|
|
@ -1,9 +1,69 @@
|
||||||
"use strict";
|
"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 {
|
class VoronoiOverlay {
|
||||||
constructor() {
|
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;
|
export default VoronoiOverlay;
|
||||||
|
|
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -4362,6 +4362,11 @@
|
||||||
"integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==",
|
"integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==",
|
||||||
"dev": true
|
"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": {
|
"xml-writer": {
|
||||||
"version": "1.7.0",
|
"version": "1.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/xml-writer/-/xml-writer-1.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/xml-writer/-/xml-writer-1.7.0.tgz",
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
"nanomodal": "^5.1.1",
|
"nanomodal": "^5.1.1",
|
||||||
"smartsettings": "^1.2.3",
|
"smartsettings": "^1.2.3",
|
||||||
"tabs": "^0.2.0",
|
"tabs": "^0.2.0",
|
||||||
|
"voronoi": "^1.0.0",
|
||||||
"xml-writer": "^1.7.0"
|
"xml-writer": "^1.7.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
Loading…
Reference in a new issue