A simple javascript plotting boilerplate for 2d stuff.
npm install plotboilerplate
!Release
!NPM
For plotting visual 2D data with Javascript on HTML canvas (in 2d-context) or SVG nodes.
!logo
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,
example)
- lines
(docs,
example)
- vectors docs,
example)
- triangles
(docs,
example)
- curves
(docs,
example)
- circles
(docs,
example)
- circle sectors
(docs,
example)
- polygons
(docs,
example)
- ellipses
(docs,
example)
- ellipse sectors
(docs,
example)
- images
(docs,
example)
- text
(docs,
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 159kb.
It once has been my aim to keep the base library less than 80kb – but the more powerful the lib gets
the more difficult this turned out.
``sh`
# Installs the package
$ npm i plotboilerplate
For a full example see main-dist.html :
`html`
The element canvas will be used to draw on.
`javascript`
var pb = new PlotBoilerplate({
canvas: document.getElementById("my-canvas"),
fullSize: true
});
Use SVG elements instead of canvas:
`html`
And pass the SVG element:
`javascript`
var pb = new PlotBoilerplate({
canvas: document.getElementById("my-svg"),
fullSize: true
});
`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
// 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.
A full working demo repository about the Usage with Typescript is here.
Basic Setup in a Codepen.io demo
#### API
See API Documentation for details.
| 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. |
#### 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.");
},
// function
// A callback function that will be triggered after content changed.
onContentChanged: function (event) {
console.log("Event type (DRAWABLES_ADDED or DRAWABLES_REMOVED)", event.type);
console.log("Added drawables:", event.addedDrawables);
console.log("Removed drawables:", event.removedDrawables);
},
// 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
});
`
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);
});
`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 }
}
`
| 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. |
- [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
`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.
`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);
});
| ![]()
IE / Edge | ![]()
Firefox | ![]()
Chrome | ![]()
iOS Safari |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| IE11 & Edge | latest | latest | latest |
- dat.gui by dataarts
- Neolitec's Color.js class
- FileSaver.js
- AlloyFinger.js
- Ray Casting Algorithm by Aaron Digulla
- Hobby Curves in Javascript by Prof. Dr. Edmund Weitz
- hobby.pdf
- jsHobby
- Blake Bowen's Catmull-Rom demo
- mbostock for the great convex-polygon-incircle implementation
- and for circle-tangent-to-three-lines
- Circle Intersection in C++ by Robert King
- The 'Circles and spheres' article by Paul Bourke
- shamansir/draw_svg.js for manipulating SVG path data strings
- opsb's stackoverflow proposal for converting ellipses sectors to SVG arcs.
- contrast-color-algorithm by Martin Sojka's
- Peter James Lu and Paul Steinhardt for their work on Girih patterns
- Cronholm144 for the Girih texture
- Mapbox's Earcut polygon algorithm
- Rosetta-Code for the Sutherland-Hodgman convex polygon clipping algorithm
- Jos de Jong for the very useful math.js library
- Jack Franklin for the howto-module-tutorial
- Narasinham for the very useful vertex-on-ellipse equations
- Tim Čas for the wrapMax/wrapMinMax functions
- Luc Maisonobe for the Ellipse to cubic Bézier math
- Dr. Martin von Gagern for the equidistant points on ellipse math
- Torben Jansen for the SVG-Arc to elliptic-sector conversion
- 3daddict for the js-stl-parser inspiration
- jburkardt for the obj test files.
- @veltman for the awesome geometric skeletonization article
- girih-tiles-spatial.jpg from Lund University, Architecture, Spatial Experiments, 2016.
- Josh Frank's regular expression for parsing SVG path data.
- Rick Moore for the useful javascript-synthesizer howto
- marked Markdown compiler by Christopher Jeffrey.
- Interactive Visualization of Molecular Surface Dynamics for the illustration image which inspired the metaballs demo.
- Darel Rex Finley for the Polygon Inset Calculation article Inset A Polygon By A Fixed, Perpendicular Distance, With C Code Sample
- Fernando Cacciola for A Survey of Polygon Offseting Strategies
- Stephen Schmitt for solvin cubic Beziér intersections with line segments http://mysite.verizon.net/res148h4j/javascript/script_exact_cubic.html
- And _Particle in Cell Consulting LLC_ for refactoring Stephen Schmitt's solution: Computing Intersections Between a Cubic Bezier Curve and a Line
- Richard "RM" for the hints of how to calculate the bounding box of cubic Bézuer curves: Bezier Area
- Cuixiping for the helpful insights on cubic Bézier bounds at Stackoverflow
- Thanks to Ana Tudor for the Multi-Range-Slider tutorial: https://css-tricks.com/multi-thumb-sliders-particular-two-thumb-case/
- Thanks to Dean Taylor for the css linear gradient parser howto: https://stackoverflow.com/questions/20215440/parse-css-gradient-rule-with-javascript-regex
- Thanks to Stefan Gustavson (stegu@itn.liu.se) for the Perlin noise on example C code (https://github.com/MethanePowered/PerlinNoise). And for Peter Eastman (peastman@drizzle.stanford.edu) for Perlin noise optimizations.
- 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
ᓚᘏᗢ