Skip to content

Commit fe4e3a6

Browse files
committed
Initial Commit
0 parents  commit fe4e3a6

21 files changed

Lines changed: 2597 additions & 0 deletions

.babelrc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"presets": [
3+
["env", {
4+
"modules": false
5+
}]
6+
],
7+
"env": {
8+
"production": {
9+
"plugins": ["external-helpers"]
10+
},
11+
"test": {
12+
plugins: ["transform-es2015-modules-commonjs"]
13+
}
14+
}
15+
}

.eslintrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": ["eslint-config-preshape"]
3+
}

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/lib
2+
/node_modules
3+
/npm-debug.log
4+
/yarn-error.log

.npmignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.babelrc
2+
.eslintrc
3+
/node_modules
4+
rollup.config.js

LICENSE.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2018 Harrison Hogg
4+
5+
Permission is hereby granted, free of charge, to any person
6+
obtaining a copy of this software and associated documentation
7+
files (the "Software"), to deal in the Software without
8+
restriction, including without limitation the rights to use,
9+
copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the
11+
Software is furnished to do so, subject to the following
12+
conditions:
13+
14+
The above copyright notice and this permission notice shall be
15+
included in all copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24+
OTHER DEALINGS IN THE SOFTWARE.

README.md

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# SysPlot
2+
3+
A library that systematically generates a plane for plotting shapes, with a variety of algorithms to choose from.
4+
5+
## Install
6+
7+
```
8+
$ yarn add sysplot
9+
```
10+
11+
## Usage
12+
13+
SysPlot is used for generating XY coordinates for placing shapes. It is agnostic about the approach used for the visualisation, be it HTML, SVG, Canvas, WebGL.. etc.
14+
15+
Positions will be given starting from the center and will attempt to fill as much
16+
space as possible.
17+
18+
```js
19+
import SysPlot, { VogelSpiral /* any provided algorithm */ } from 'sysplot';
20+
21+
const sysPlot = new SysPlot();
22+
23+
sysPlot.setConfig({
24+
algorithm: VogelSpiral,
25+
padding: 10,
26+
proportional: true,
27+
spread: 0.25,
28+
});
29+
30+
sysPlot.setBounds(1000, 500);
31+
32+
const shapes = [{
33+
radius: 20,
34+
}, {
35+
width: 30,
36+
height: 50,
37+
}]
38+
39+
/** Give it the shapes to calculate the position for. */
40+
sysPlot.setShape(shapes);
41+
42+
/** Get the positions for those shapes */
43+
const positions = sysPlot.getPositions();
44+
45+
/** Shapes that were unable to be placed will have no position */
46+
const shapesWithValidPositions = zip(positions, shapes).filter(([, p]) => p);
47+
```
48+
49+
## Algorithms
50+
51+
#### `ArchimedesSpiral`
52+
53+
> The Archimedean spiral (also known as the arithmetic spiral) is a spiral named after the 3rd century BC Greek mathematician Archimedes. It is the locus of points corresponding to the locations over time of a point moving away from a fixed point with a constant speed along a line which rotates with constant angular velocity.
54+
>
55+
> [Wikipedia - Archimedes Spiral](https://en.wikipedia.org/wiki/Archimedean_spiral)
56+
57+
#### `ConcentricCircles`
58+
59+
> In geometry, two or more objects are said to be concentric, coaxal, or coaxial when they share the same center or axis.
60+
>
61+
> [Wikipedia - Concentric Circles](https://en.wikipedia.org/wiki/Concentric_objects)
62+
63+
#### `FermatSpiral`
64+
65+
> Fermat's spiral (also known as a [parabolic spiral](https://en.wikipedia.org/wiki/Parabola)) was first discovered by Pierre de Fermat. It is a type of Archimedean spiral
66+
>
67+
> [Wikipedia - Fermat Spiral](https://en.wikipedia.org/wiki/Fermat%27s_spiral)
68+
69+
#### `UlamSpiral`
70+
71+
> The Ulam spiral or prime spiral (in other languages also called the Ulam cloth) is a graphical depiction of the set of [prime numbers](https://en.wikipedia.org/wiki/Prime_number)... It is constructed by writing the positive integers in a square spiral and specially marking the prime numbers.
72+
>
73+
> [Wikipedia - Ulam Spiral](https://en.wikipedia.org/wiki/Ulam_spiral)
74+
75+
#### `VogelSpiral`
76+
77+
> In disc [phyllotaxis](https://en.wikipedia.org/wiki/Phyllotaxis), as in the sunflower and daisy, the mesh of spirals occurs in [Fibonacci numbers](https://en.wikipedia.org/wiki/Fibonacci_number) because divergence (angle of succession in a single spiral arrangement) approaches the [golden ratio](https://en.wikipedia.org/wiki/Golden_ratio).
78+
>
79+
> [Wikipedia - Vogel Spiral](https://en.wikipedia.org/wiki/Fermat%27s_spiral)
80+
81+
## API
82+
83+
#### Config Object
84+
85+
```js
86+
const config = {
87+
/**
88+
* One of the exported algorithm functions mentioned above.
89+
*/
90+
algorithm: Function,
91+
92+
/**
93+
* The amount of padding to be used around the shapes when
94+
* positioning.
95+
*/
96+
padding: Number,
97+
98+
/**
99+
* Retains the aspect ratio for plotting the vector points.
100+
*/
101+
proportional: Boolean,
102+
103+
/**
104+
* A number between 0.1 and 1 that affects the density of the
105+
* vector points. 0.1 being very dense and 1 being very spread
106+
* apart.
107+
*/
108+
spread: Number,
109+
}
110+
```
111+
112+
#### SysPlot()
113+
114+
The main class for storing the environment and generating vectors and positions.
115+
116+
```js
117+
new SysPlot();
118+
```
119+
120+
#### Sysplot.setConfig(configObject)
121+
122+
Updates the config and sets a flag to regenerate the plotting vectors and/or the positions on next `getPositions()` call.
123+
124+
```js
125+
sysPlot.setConfig(configObject);
126+
```
127+
128+
#### SysPlot.setBounds(width, height)
129+
130+
Sets the bounds to generate the plotting vectors for.
131+
132+
```js
133+
const width = 1000;
134+
const height = 500;
135+
136+
sysPlot.setBounds(width, height);
137+
```
138+
139+
#### SysPlot.setShapes(shapes)
140+
141+
Sets the shapes and sets a flag to regenerate positions on next `getPositions()` call.
142+
143+
```js
144+
const shapes = [{
145+
radius: 20,
146+
}, {
147+
width: 30,
148+
height: 50,
149+
}]
150+
151+
sysPlot.setShapes(shapes);
152+
```
153+
154+
#### SysPlot.getPositions()
155+
156+
Gets the positions for the set shapes. Returns XY coordinates in order of the shapes that were set. Any shapes they were unable to be positioned will be null.
157+
158+
```js
159+
const shapes = [...];
160+
const positions = sysPlot.getPositions();
161+
162+
const shapesWithValidPositions = zip(positions, shapes).filter(([, p]) => p);
163+
```
164+
165+
166+
#### SysPlot.getVectors()
167+
168+
Gets the vector points used for positioning (as XY coordinates) that the algorithm generated, radiating out from the center.
169+
170+
```js
171+
sysPlot.getVectors(); // [[x, y], [x, y] ...etc]
172+
```
173+
174+
## Performance
175+
176+
Generating the vector points and positioning the shapes can be intensive depending on the algorithm used, the number of shapes and the spread option. **SysPlot will only generate new vectors and positions if it needs to**, which is determined when certain parts of the config, bounds or shapes change.
177+
178+
Use the `setBounds`, `setConfig` and `setShapes` methods when the environment changes.

package.json

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"name": "sysplot",
3+
"version": "1.0.0",
4+
"description": "A library that systematically generates a plane for plotting shapes, with a variety of algorithms to choose from.",
5+
"main": "lib/sysplot.cjs.js",
6+
"module": "lib/sysplot.esm.js",
7+
"scripts": {
8+
"build": "BABEL_ENV=production rollup -c",
9+
"lint": "eslint --config .eslintrc ./src"
10+
},
11+
"author": "Harry Hogg <harry@hogg.io>",
12+
"license": "MIT",
13+
"repository": {
14+
"type": "git",
15+
"url": "https://github.com/HHogg/sysplot"
16+
},
17+
"keywords": [
18+
"Archimedes",
19+
"Concentric",
20+
"Fermat",
21+
"Spiral",
22+
"Ulam",
23+
"Vogel",
24+
"algorithm",
25+
"plotting"
26+
],
27+
"dependencies": {
28+
"sat": "^0.7.0"
29+
},
30+
"devDependencies": {
31+
"babel-core": "^6.26.3",
32+
"babel-eslint": "^8.2.3",
33+
"babel-plugin-external-helpers": "^6.22.0",
34+
"babel-preset-env": "^1.7.0",
35+
"eslint": "^4.19.1",
36+
"eslint-config-preshape": "^1.0.0",
37+
"eslint-plugin-react": "^7.8.2",
38+
"rollup": "^0.59.4",
39+
"rollup-plugin-babel": "^3.0.4",
40+
"rollup-plugin-commonjs": "^9.1.3",
41+
"rollup-plugin-node-resolve": "^3.3.0"
42+
}
43+
}

rollup.config.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import babel from 'rollup-plugin-babel';
2+
import pkg from './package.json';
3+
4+
export default [{
5+
entry: 'src/index.js',
6+
external: ['sat'],
7+
targets: [
8+
{ dest: pkg.main, format: 'cjs' },
9+
{ dest: pkg.module, format: 'es' },
10+
],
11+
plugins: [
12+
babel({
13+
exclude: ['node_modules/**'],
14+
}),
15+
],
16+
}];

src/SysPlot.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import position from './positioning/position';
2+
3+
export default class SysPlot {
4+
constructor() {
5+
this.config = {};
6+
}
7+
8+
setConfig(config = {}) {
9+
const updateVectors =
10+
this.config.algorithm !== config.algorithm ||
11+
this.config.proportional !== config.proportional ||
12+
this.config.spread !== config.spread;
13+
14+
const updatePositions = updateVectors ||
15+
this.config.padding !== config.padding;
16+
17+
this.config = {
18+
algorithm: config.algorithm,
19+
padding: config.padding,
20+
proportional: config.proportional,
21+
spread: Math.max(0.1, Math.min(1, config.spread)) / 10,
22+
};
23+
24+
if (updateVectors) this.vectors = null;
25+
if (updatePositions) this.positions = null;
26+
}
27+
28+
setBounds(width, height) {
29+
if (width !== this.width || height !== this.height) {
30+
this.width = width;
31+
this.height = height;
32+
this.vectors = null;
33+
this.positions = null;
34+
}
35+
}
36+
37+
setShapes(shapes) {
38+
this.shapes = shapes;
39+
this.positions = null;
40+
}
41+
42+
getVectors() {
43+
if (!this.vectors) {
44+
const { config, width: w, height: h } = this;
45+
const { proportional, spread } = config;
46+
const [xDim, yDim] = proportional ? [w, h] : (w > h ? [w, w] : [h, h]);
47+
48+
this.vectors = this.config.algorithm(
49+
this.width,
50+
this.height,
51+
this.width / 2,
52+
this.height / 2,
53+
(xDim * (spread * (xDim / yDim)))
54+
* this.config.algorithm.NORMALISATION_FACTOR,
55+
(yDim * (spread * (yDim / xDim)))
56+
* this.config.algorithm.NORMALISATION_FACTOR,
57+
);
58+
}
59+
60+
return this.vectors;
61+
}
62+
63+
getPositions() {
64+
if (!this.positions) {
65+
this.positions = position(
66+
this.width,
67+
this.height,
68+
this.getVectors(),
69+
this.shapes,
70+
this.config,
71+
);
72+
}
73+
74+
return this.positions;
75+
}
76+
}

0 commit comments

Comments
 (0)