UNPKG

plotboilerplate

Version:

A simple javascript plotting boilerplate for 2d stuff.

576 lines (461 loc) 35 kB
[![License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](http://opensource.org/licenses/MIT) ![Release](https://img.shields.io/github/v/release/ikaroskappler/plotboilerplate) ![NPM](https://img.shields.io/npm/v/plotboilerplate) # An interactive Javascript Plotting Boilerplate For plotting visual 2D data with Javascript on HTML canvas (in 2d-context) or SVG nodes. ![logo](https://plotboilerplate.io/repo/logo-128.png) This is a simple collection of useful functions, methods, classes, algorithms and concepts which I often use for the visualization of 2D geometries. Basic features are - adding elements like - vertices ([docs](https://plotboilerplate.io/docs_typedoc/classes/vertex.html "Vertex class"), [example](https://plotboilerplate.io/repo/demos/basic-Vertex "Vertex example")) - lines ([docs](https://plotboilerplate.io/docs_typedoc/classes/line.html "Line class"), [example](https://plotboilerplate.io/repo/demos/basic-Line "Line example")) - vectors [docs](https://plotboilerplate.io/docs_typedoc/classes/vector.html "Vector class"), [example](https://plotboilerplate.io/repo/demos/basic-Vector "Vector example")) - triangles ([docs](https://plotboilerplate.io/docs_typedoc/classes/triangle.html "Triangle class"), [example](https://plotboilerplate.io/repo/demos/basic-Triangle "Triangle example")) - curves ([docs](https://plotboilerplate.io/docs_typedoc/classes/cubicbeziercurve.html "CubicBezierCurve class"), [example](https://plotboilerplate.io/repo/demos/basic-BezierPath "BezierPath example")) - circles ([docs](https://plotboilerplate.io/docs_typedoc/classes/circle.html "Circle class"), [example](https://plotboilerplate.io/repo/demos/basic-Circle "Circle example")) - circle sectors ([docs](https://plotboilerplate.io/docs_typedoc/classes/circlesector.html "CircleSector class"), [example](https://plotboilerplate.io/repo/demos/basic-CircleSector "CircleSector example")) - polygons ([docs](https://plotboilerplate.io/docs_typedoc/classes/polygon.html "Polygon class"), [example](https://plotboilerplate.io/repo/demos/basic-Polygon "Polygon example")) - ellipses ([docs](https://plotboilerplate.io/docs_typedoc/classes/vellipse.html "VEllipse class"), [example](https://plotboilerplate.io/repo/demos/basic-VEllipse "VEllipse example")) - ellipse sectors ([docs](https://plotboilerplate.io/docs_typedoc/classes/vellipsesector.html "VEllipseSector class"), [example](https://plotboilerplate.io/repo/demos/basic-VEllipseSector "VEllipseSector example")) - images ([docs](https://plotboilerplate.io/docs_typedoc/classes/pbimage.html "PBImage class"), [example](https://plotboilerplate.io/repo/demos/basic-PBImage "PBImage example")) - text ([docs](https://plotboilerplate.io/docs_typedoc/classes/pbtext.html "PBText class"), [example](https://plotboilerplate.io/repo/demos/basic-PBText "PBText example")) - configuration of the canvas behavior - fullsize and auto-resizing - enable/disable mouse, touch or keyboard interaction - draw rasters - default colors and darkmode - mouse and touch interaction - zoom - pan - drag and select elements - keyboard interaction customizable The compressed library has 135kb. ## Install the package via npm ```sh # Installs the package $ npm i plotboilerplate ``` ## The HTML file For a full example see [main-dist.html](https://github.com/IkarosKappler/plotboilerplate/blob/master/main-dist.html "main-dist.html") : ```html <canvas id="my-canvas"> Your browser does not support the canvas tag. </canvas> ``` The element canvas will be used to draw on. ## The javascript ```javascript var pb = new PlotBoilerplate({ canvas: document.getElementById("my-canvas"), fullSize: true }); ``` ### Alternative with SVG elements Use SVG elements instead of canvas: ```html <svg id="my-svg"></svg> ``` And pass the SVG element: ```javascript var pb = new PlotBoilerplate({ canvas: document.getElementById("my-svg"), fullSize: true }); ``` ## Add elements to your canvas ```javascript // Create two points: // The origin is at the visual center by default. var pointA = new Vertex(-100, -100); var pointB = new Vertex(100, 100); pb.add(new Line(pointA, pointB)); // When point A is moved by the user // then move point B in the opposite direction pointA.listeners.addDragListener(function (e) { pointB.sub(e.params.dragAmount); pb.redraw(); }); // and when point B is moved // then move point A pointB.listeners.addDragListener(function (e) { pointA.sub(e.params.dragAmount); pb.redraw(); }); ``` ### Typescript ```typescript // Usage with Typescript could look like this import { PlotBoilerplate, Vertex, Line } from "plotboilerplate"; globalThis.addEventListener("load", () => { const pointA: Vertex = new Vertex(100, -100); const pointB: Vertex = new Vertex(-100, 100); console.log(pointA, pointB); const line: Line = new Line(pointA, pointB); const pb: PlotBoilerplate = new PlotBoilerplate({ canvas: document.getElementById("my-canvas"), fullSize: true }); pb.add(line); }); ``` For a guide of how to [Getting Started click here](https://github.com/IkarosKappler/plotboilerplate/blob/master/Getting-Started.md "Getting Started click here."). A full working demo repository about the [Usage with Typescript is here](https://github.com/IkarosKappler/plotboilerplate-typescript-example "usage with Typescript is here"). ### Codepen Demos [Basic Setup in a Codepen.io demo](https://codepen.io/ikaroskappler/pen/ZEjamVb) ### Screenshot ![Simple Demo](https://plotboilerplate.io/repo/screenshots/screenshot-20190220_3_simpledemo.png "The simple demo") <a class="btn btn-link" href="https://plotboilerplate.io/repo/demos/00-simple/index.html" title="And the simple demo is here">And the simple demo is here</a> #### API See [API Documentation](https://plotboilerplate.io/docs.html "API Documentation") for details. ## Screenshot ![Current demo](https://plotboilerplate.io/repo/screenshots/preview-image-large.png "Current demo") <a class="btn btn-link" href="https://plotboilerplate.io/repo/main-dist.html" title="See the demo">See the demo</a> ## Examples and Demos [Examples and Demos](https://github.com/IkarosKappler/plotboilerplate/blob/master/examples.md "Examples and Demos") ## Initialization parameters <div class="table-wrapper" markdown="block"> | Name | Type | Default | Description | | ------------------------ | ----------------------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `canvas` | _HTMLCanvasElement_ \| _SVGElement_ \| _string_ | `null` | The canvas or its query selector string (required). | | `fullsize` | _boolean_ | `true` | If `true`, then the canvas will always claim tha max available screen size. | | `fitToParent` | _boolean_ | `true` | If `true`, then the canvas will alway claim the max available parent container size. | | `scaleX` | _number_ | `1.0` | The initial horizontal zoom. Default is 1.0. | | `scaleY` | _number_ | `1.0` | The initial vertical zoom. Default is 1.0. | | `offsetX` | _number_ | `0.0` | The initial offset. Default is 0.0. Note that autoAdjustOffset=true overrides these values. | | `offsetY` | _number_ | `0.0` | The initial offset. Default is 0.0. Note that autoAdjustOffset=true overrides these values. | | `drawGrid` | _boolean_ | `true` | Specifies if the raster should be drawn. | | `rasterScaleX` | _number_ | `1.0` | Define the default horizontal raster scale. | | `rasterScaleY` | _number_ | `1.0` | Define the default vertical raster scale. | | `rasterGrid` | _boolean_ | `true` | If set to true the background grid will be drawn rastered. | | `rasterAdjustFactor` | _number_ | `2.0` | The exponential limit for wrapping down the grid. (2.0 means: halve the grid each 2.0\*n zoom step). | | `drawOrigin` | _boolean_ | `false` | Draw a crosshair at (0,0). | | `autoAdjustOffset` | _boolean_ | `true` | When set to true then the origin of the XY plane will be re-adjusted automatically (see the params offsetAdjust{X,Y}Percent for more). | | `offsetAdjustXPercent` | _number_ | `50` | The x- and y- fallback position for the origin after resizing the canvas. | | `offsetAdjustYPercent` | _number_ | `50` | The x- and y- fallback position for the origin after resizing the canvas. | | `defaultCanvasWidth` | _number_ | `1024` | The canvas size fallback if no automatic resizing is switched on. | | `defaultCanvasHeight` | _number_ | `768` | The canvas size fallback if no automatic resizing is switched on. | | `canvasWidthFactor` | _number_ | `1.0` | Two scaling factors (width and height) upon the canvas size. In combination with cssScale{X,Y} this can be used to obtain sub pixel resolutions for retina displays. | | `canvasHeightFactor` | _number_ | `1.0` | Two scaling factors (width and height) upon the canvas size. In combination with cssScale{X,Y} this can be used to obtain sub pixel resolutions for retina displays. | | `cssScaleX` | _number_ | `1.0` | Visually resize the canvas using CSS transforms (scale x). | | `cssScaleY` | _number_ | `1.0` | Visually resize the canvas using CSS transforms (scale y). | | `cssUniformScale` | _boolean_ | `1.0` | If set to true only cssScaleX applies for both dimensions. | | `autoDetectRetina` | _boolean_ | `true` | When set to true (default) the canvas will try to use the display's pixel ratio. | | `backgroundColor` | _string_ | `#ffffff` | A background color (CSS string) for the canvas. | | `redrawOnResize` | _boolean_ | `true` | Switch auto-redrawing on resize on/off (some applications might want to prevent automatic redrawing to avoid data loss from the drae buffer). | | `drawBezierHandleLines` | _boolean_ | `true` | Indicates if Bézier curve handle points should be drawn. | | `drawBezierHandlePoints` | _boolean_ | `true` | Indicates if Bézier curve handle points should be drawn. | | `preClear` | _function_ | `null` | A callback function that will be triggered just before the draw function clears the canvas (before anything else was drawn). | | `preDraw` | _function_ | `null` | A callback function that will be triggered just before the draw function starts. | | `postDraw` | _function_ | `null` | A callback function that will be triggered right after the drawing process finished. | | `enableMouse` | _boolean_ | `true` | Indicates if the application should handle touch events for you. | | `enableTouch` | _boolean_ | `true` | Indicates if the application should handle touch events for you. | | `enableKeys` | _boolean_ | `true` | Indicates if the application should handle key events for you. | | `enableMouseWheel` | _boolean_ | `true` | Indicates if the application should handle mouse wheelevents for you. | | `enableSVGExport` | _boolean_ | `true` | Indicates if the SVG export should be enabled (default is true). | | `enableGL` | _boolean_ | `false` | [Experimental] Indicates if the application should use the experimental WebGL features. | </div> #### Example ```javascript var pb = new PlotBoilerplate({ // HTMLCanvasElement | SVGElement | string // Your canvas element in the DOM (required). canvas: document.getElementById("my-canvas"), // boolean // If set to true the canvas will gain full window size. fullSize: true, // boolean // If set to true the canvas will gain the size of its parent // container. // @overrides fullSize fitToParent: true, // float // The initial zoom. Default is 1.0. scaleX: 1.0, scaleY: 1.0, // float // The initial offset. Default is 0.0. Note that autoAdjustOffset=true overrides these values. offsetX: 0.0, offsetY: 0.0, // Specifies if the raster should be drawn. drawGrid: true, // If set to true the background grid will be drawn rastered. rasterGrid: true, // float // The exponential limit for wrapping down the grid. // (2.0 means: halve the grid each 2.0*n zoom step). rasterAdjustFactor: 2.0, // Draw a crosshair at (0,0). drawOrigin: false, // boolean // When set to true then the origin of the XY plane will // be re-adjusted automatically (see the params // offsetAdjust{X,Y}Percent for more). autoAdjustOffset: true, // float // The x- and y- fallback position for the origin after // resizing the canvas. offsetAdjustXPercent: 50, offsetAdjustYPercent: 50, // int // The canvas size fallback if no automatic resizing // is switched on. defaultCanvasWidth: 1024, defaultCanvasHeight: 768, // float // Two scaling factors (width and height) upon the canvas size. // In combination with cssScale{X,Y} this can be used to obtain // sub pixel resolutions for retina displays. canvasWidthFactor: 1.0, canvasHeightFactor: 1.0, // float // Visually resize the canvas using CSS transforms (scale). cssScaleX: 1.0, cssScaleY: 1.0, // boolean // If set to true only cssScaleX applies for both dimensions. cssUniformScale: true, // boolean // When set to true (default) the canvas will try to use the display's pixel ratio. autoDetectRetina: true, // string // A background color (CSS string) for the canvas. backgroundColor: isDarkmode ? "#000000" : "#ffffff", // boolean // Switch auto-redrawing on resize on/off (some applications // might want to prevent automatic redrawing to avoid data // loss from the drae buffer). redrawOnResize: true, // boolean // Indicates if Bézier curve handles should be drawn (used for // editors, no required in pure visualizations). drawBezierHandleLines: true, // boolean // Indicates if Bézier curve handle points should be drawn. drawBezierHandlePoints: true, // function // A callback function that will be triggered just before the // draw function clears the canvas (before anything else was drawn). preClear: function () { console.log("before clearing the canvas on redraw."); }, // function // A callback function that will be triggered just before the // draw function starts. preDraw: function (draw, fill) { console.log("after clearing and before drawing."); }, // function // A callback function that will be triggered right after the // drawing process finished. postDraw: function (draw, fill) { console.log("after drawing."); }, // boolean // Indicates if the application should handle mouse events for you. enableMouse: true, // boolean // Indicates if the application should handle touch events for you. enableTouch: true, // boolean // Indicates if the application should handle key events for you. enableKeys: true, // boolean // Indicates if the application should handle mouse wheelevents for you. enableMouseWheel: true, // boolean (default true) // Use this to disable panning completely. enablePan: true, // boolean (default true) // Use this to disable zooming completely. enableZoom: true, // Indicates if the SVG export should be enabled (default is true). enableSVGExport: true, // boolean // Indicates if the application should use the experimental WebGL features. enableGL: false }); ``` ## Events The Vertex class has basic drag event support: ```javascript var vert = new Vertex(100, 100); vert.listeners.addDragListener(function (e) { // e is of type Event. // You are encouraged to use the values in the object e.params console.log("vertex was dragged by: ", "x=" + e.params.dragAmount.x, "y=" + e.params.dragAmount.y); }); ``` ### The e.params object ```javascript { // The canvas that fired the event. element : [HTMLElement], // The event name. // Default: 'drag' name : string, // The current drag position. pos : { x : number, y : number }, // A mouse button indicator (if mouse event). // 0=left, 1=middle, 2=right button : number, // A flag indicating if event comes from left mouse button. leftButton : boolean, // A flag indicating if event comes from middle mouse button. middleButton : boolean, // A flag indicating if event comes from right mouse button. rightButton : boolean, // A mouse-down-position: position where the dragging // started. This will not change during one drag process. mouseDownPos : { x : number, y : number }, // The most recent drag position (position before // current drag step). draggedFrom : { x : number, y : number }, // True if this is a drag event (nothing else available the moment). wasDragged : boolean, // The x-y-amount of the current drag step. // This is the difference between the recent drag step // and the actual drag position. dragAmount : { x : number, y : number } } ``` <div class="table-wrapper" markdown="block"> | Name | Type | Example value | Description | | -------------- | ------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------- | | `element` | _HTMLCanvasElement_ | `[HTMLCanvasElement]` | The canvas that fired the event. | | `name` | _string_ | `drag` | The event name (default is 'drag'). | | `pos` | _position_ | `{ x : 20, y : 50 }` | The current drag position. | | `button` | _number_ | `0` | A mouse button indicator (if mouse event). 0=left, 1=middle, 2=right | | `leftButton` | _boolean_ | `true` | A flag indicating if event comes from left mouse button. | | `middleButton` | _boolean_ | `false` | A flag indicating if event comes from middle mouse button. | | `rightButton` | _boolean_ | `false` | A flag indicating if event comes from right mouse button. | | `mouseDownPos` | _position_ | `{ x : 0, y : 20 }` | A mouse-down-position: position where the dragging started. This will not change during one drag process. | | `draggedFrom` | _position_ | `{ x : 10, y : -5 }` | The most recent drag position (position before current drag step). | | `wasDragged` | _boolean_ | `true` | True if this is a drag event (nothing else available at the moment). | | `dragAmount` | _position_ | `{ x : 100, y : 34 }` | The x-y-amount of the current drag step. This is the difference between the recent drag step and the actual drag position. | </div> ## Mouse, Keyboard and Touch interaction - [SHIFT] + [Click] : Select/Deselect vertex - [Y] + [Click]: Toggle Bézier auto-adjustment for clicked bézier path point - [ALT or SPACE] + [Mousedown] + [Drag] : Pan the area - [Mousewheel-up] : Zoom in - [Mousewheel-down] : Zoom out - Touch & move (1 finger): Move item - Touch & move (2 fingers): Pan the area - Touch & pinch: Zoom in/out ## Custom keyboard events ```javascript new KeyHandler({ trackAll: true }) .down("enter", function () { console.log("ENTER was hit."); }) .press("enter", function () { console.log("ENTER was pressed."); }) .up("enter", function () { console.log("ENTER was released."); }) .down("e", function () { console.log("e was hit. shift is pressed?", keyHandler.isDown("shift")); }) .up("spacebar", function () { console.log("spacebar was released."); }); ``` For a list of all supported key codes see [Full list of supported key codes](https://github.com/IkarosKappler/plotboilerplate/blob/master/keycodes.md "Full list of supported key codes"). ## Custom mouse event ```javascript new MouseHandler(document.getElementById("mycanvas")) .drag(function (e) { console.log("Mouse dragged: " + JSON.stringify(e)); if (e.params.leftMouse); else if (e.params.rightMouse); }) .move(function (e) { console.log("Mouse moved: " + JSON.stringify(e.params)); }) .up(function (e) { console.log("Mouse up. Was dragged?", e.params.wasDragged); }) .down(function (e) { console.log("Mouse down."); }) .click(function (e) { console.log("Click."); }) .wheel(function (e) { console.log("Wheel. delta=" + e.deltaY); }); ``` ## Build the package [Compile and build howto](https://github.com/IkarosKappler/plotboilerplate/blob/master/build.md "Compile and build howto") ## Browsers support | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24" height="24" />](http://godban.github.io/browsers-support-badges/)<br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24" height="24" />](http://godban.github.io/browsers-support-badges/)<br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24" height="24" />](http://godban.github.io/browsers-support-badges/)<br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari-ios/safari-ios_48x48.png" alt="iOS Safari" width="24" height="24" />](http://godban.github.io/browsers-support-badges/)<br>iOS Safari | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | IE11 & Edge | latest | latest | latest | ### Credits - [dat.gui by dataarts](https://github.com/dataarts/dat.gui "dat.gui dataarts") - [Neolitec's Color.js class](https://gist.github.com/neolitec/1344610 "Neolitec's Color.js class") - [FileSaver.js](https://www.npmjs.com/package/file-saver "FileSaver.js") - [AlloyFinger.js](https://github.com/AlloyTeam/AlloyFinger "AlloyFinger.js") - [Ray Casting Algorithm](https://stackoverflow.com/questions/22521982/check-if-point-is-inside-a-polygon "Ray Casting Algorithm") by Aaron Digulla - [Hobby Curves in Javascript](http://weitz.de/hobby/ "Hobby Curves in Javascript") by [Prof. Dr. Edmund Weitz](http://weitz.de) - [hobby.pdf](https://ctan.mc1.root.project-creative.net/graphics/pgf/contrib/hobby/hobby.pdf "hobby.pdf") - [jsHobby](https://github.com/loopspace/jsHobby "jsHobby") - [Blake Bowen's Catmull-Rom demo](https://codepen.io/osublake/pen/BowJed "Blake Bowen's Catmull-Rom demo") - [mbostock](https://github.com/mbostock "mbostok") for the great [convex-polygon-incircle implementation](https://observablehq.com/@mbostock/convex-polygon-incircle "convex-polygon-incircle implementation") - and for [circle-tangent-to-three-lines](https://observablehq.com/@mbostock/circle-tangent-to-three-lines "circle-tangent-to-three-lines") - [Circle Intersection in C++ by Robert King](https://stackoverflow.com/questions/3349125/circle-circle-intersection-points "Circle Intersection in C++ by Robert King") - [The 'Circles and spheres' article by Paul Bourke](http://paulbourke.net/geometry/circlesphere/ "the 'Circles and spheres' article by Paul Bourke") - [shamansir/draw_svg.js](https://gist.github.com/shamansir/6294f8cfdd555a9d1b9e182007dd0c2f "shamansir/draw_svg.js") for manipulating SVG path data strings - [opsb's stackoverflow proposal](https://stackoverflow.com/questions/5736398/how-to-calculate-the-svg-path-for-an-arc-of-a-circle "opsb's stackoverflow proposal") for converting ellipses sectors to SVG arcs. - [contrast-color-algorithm](https://gamedev.stackexchange.com/questions/38536/given-a-rgb-color-x-how-to-find-the-most-contrasting-color-y/38542#38542" "contrast-color-algorithm") by Martin Sojka's - [Peter James Lu](https://www.peterlu.com/) and [Paul Steinhardt](https://paulsteinhardt.org/) for their work on [Girih patterns](https://physics.princeton.edu//~steinh/peterlu_SOM7_sm.pdf "Girih") - Cronholm144 for the [Girih texture](https://commons.wikimedia.org/wiki/File:Girih_tiles.svg "Girih texture") - [Mapbox's Earcut polygon algorithm](https://github.com/mapbox/earcut "Mapbox's Earcut polygon algorithm") - [Rosetta-Code for the Sutherland-Hodgman convex polygon clipping algorithm](https://rosettacode.org/wiki/Sutherland-Hodgman_polygon_clipping#JavaScript "Rosetta-Code for the Sutherland-Hodgman convex polygon clipping algorithm") - [Jos de Jong](https://github.com/josdejong) for the very useful [math.js library](https://github.com/josdejong/mathjs) - Jack Franklin for the [howto-module-tutorial](https://blog.logrocket.com/publishing-node-modules-typescript-es-modules/) - Narasinham for the very useful [vertex-on-ellipse equations](https://math.stackexchange.com/questions/22064/calculating-a-point-that-lies-on-an-ellipse-given-an-angle) - [Tim Čas](https://stackoverflow.com/users/485088/tim-%c4%8cas "Tim Čas") for the [wrapMax/wrapMinMax functions](https://stackoverflow.com/questions/4633177/c-how-to-wrap-a-float-to-the-interval-pi-pi "wrapMax/wrapMinMax functions") - Luc Maisonobe for the [Ellipse to cubic Bézier math](http://www.spaceroots.org/documents/ellipse/node22.html "Ellipse to cubic Bézier math") - Dr. Martin von Gagern for the [equidistant points on ellipse math](https://math.stackexchange.com/questions/172766/calculating-equidistant-points-around-an-ellipse-arc "equidistant points on ellipse math") - [Torben Jansen](https://twitter.com/torbenjansen_ "Torben Jansen") for the [SVG-Arc to elliptic-sector conversion](https://observablehq.com/@toja/ellipse-and-elliptical-arc-conversion "SVG-Arc to elliptic-sector conversion") - [3daddict](https://github.com/3daddict) for the [js-stl-parser inspiration](https://github.com/3daddict/js-stl-parser/blob/master/index.js "js-stl-parser inspiration") - [jburkardt](https://people.sc.fsu.edu/~jburkardt/data/obj/obj.html "jburkardt") for the obj test files. - [@veltman](https://noahveltman.com/) for the awesome [geometric skeletonization article](https://observablehq.com/@veltman/centerline-labeling) - [girih-tiles-spatial.jpg from Lund University, Architecture, Spatial Experiments, 2016.](https://spatialexperiments.wordpress.com/2016/09/19/islamic-geometry/girih-tiles-4/) - [Josh Frank's regular expression for parsing SVG path data.](https://javascript.plainenglish.io/june-3-parsing-and-validating-svg-paths-with-regex-7bd0e245115) - [Rick Moore for the useful javascript-synthesizer howto](https://medium.com/geekculture/building-a-modular-synth-with-web-audio-api-and-javascript-d38ccdeca9ea) - [marked Markdown compiler](https://marked.js.org/) by Christopher Jeffrey. - [Interactive Visualization of Molecular Surface Dynamics](https://www.computer.org/csdl/journal/tg/2009/06/ttg2009061391/13rRUwInvsI) for the illustration image which inspired the metaballs demo. ## Todos [What needs to be done](https://github.com/IkarosKappler/plotboilerplate/blob/master/todos.md "Future To-Dos") ## Known bugs - SVG resizing does not work in Firefox (aspect ratio is always kept, even if clip box changes). Please use PNGs until this is fixed. - The BBTree.iterator() fails if the tree is empty! (Demos) - The minifid BBTree and BBTreeCollection files do not export anything. The un-minified does. Why that? - Arcs and ellipses break when non-uniform scaling (scalex!=scaley) is applied. Convert them to Bézier curves before drawing. - Currently no more known. Please report bugs. Oh look, a cat ᓚᘏᗢ