primitive 1.0.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -0
- package/bin/run.js +16 -0
- package/dist/cli-config-B5hrwe8q.js +1330 -0
- package/dist/oclif/index.js +22785 -0
- package/dist/oclif/proxy-auto-detect.js +71 -0
- package/dist/oclif/root-signup-hint.js +136 -0
- package/man/primitive.1 +111 -0
- package/package.json +131 -67
- package/.babelrc +0 -12
- package/.editorconfig +0 -9
- package/.eslintrc +0 -5
- package/.travis.yml +0 -5
- package/browser.js +0 -109
- package/dist/browser.js +0 -5447
- package/lib/browser-context.js +0 -98
- package/lib/color.js +0 -6
- package/lib/context.js +0 -101
- package/lib/context.test.js +0 -31
- package/lib/core.js +0 -199
- package/lib/core.test.js +0 -70
- package/lib/model.js +0 -168
- package/lib/optimize.js +0 -53
- package/lib/primitive.js +0 -76
- package/lib/rasterize.js +0 -101
- package/lib/scanline.js +0 -25
- package/lib/shapes/ellipse.js +0 -112
- package/lib/shapes/factory.js +0 -52
- package/lib/shapes/rectangle.js +0 -93
- package/lib/shapes/rotated-ellipse.js +0 -99
- package/lib/shapes/rotated-rectangle.js +0 -126
- package/lib/shapes/shape.js +0 -26
- package/lib/shapes/triangle.js +0 -217
- package/lib/shapes/triangle.test.js +0 -73
- package/lib/state.js +0 -69
- package/lib/worker.js +0 -50
- package/main.js +0 -6
- package/main.test.js +0 -39
- package/module.js +0 -126
- package/notes.md +0 -15
- package/readme.md +0 -154
- package/rollup.config.js +0 -28
package/lib/optimize.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import State from './state'
|
|
2
|
-
|
|
3
|
-
export const hillClimb = (state, maxAge) => {
|
|
4
|
-
let bestState = state
|
|
5
|
-
let bestEnergy = state.energy()
|
|
6
|
-
|
|
7
|
-
for (let age = 0; age < maxAge; ++age) {
|
|
8
|
-
const newState = bestState.mutate()
|
|
9
|
-
const newEnergy = newState.energy()
|
|
10
|
-
|
|
11
|
-
if (newEnergy < bestEnergy) {
|
|
12
|
-
bestEnergy = newEnergy
|
|
13
|
-
bestState = newState
|
|
14
|
-
age = -1
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return bestState
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const getBestHillClimbState = (worker, opts) => {
|
|
22
|
-
const state = getBestRandomState(worker, opts)
|
|
23
|
-
return hillClimb(state, opts.numCandidateMutations)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const getBestRandomState = (worker, opts) => {
|
|
27
|
-
const {
|
|
28
|
-
numCandidateShapes,
|
|
29
|
-
shapeType,
|
|
30
|
-
shapeAlpha
|
|
31
|
-
} = opts
|
|
32
|
-
|
|
33
|
-
let bestEnergy = null
|
|
34
|
-
let bestState = null
|
|
35
|
-
|
|
36
|
-
for (let i = 0; i < numCandidateShapes; ++i) {
|
|
37
|
-
const state = State.create(worker, shapeType, shapeAlpha)
|
|
38
|
-
const energy = state.energy()
|
|
39
|
-
|
|
40
|
-
if (!i || energy < bestEnergy) {
|
|
41
|
-
bestEnergy = energy
|
|
42
|
-
bestState = state
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return bestState
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export default {
|
|
50
|
-
hillClimb,
|
|
51
|
-
getBestHillClimbState,
|
|
52
|
-
getBestRandomState
|
|
53
|
-
}
|
package/lib/primitive.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import ow from 'ow'
|
|
2
|
-
|
|
3
|
-
import core from './core'
|
|
4
|
-
import Model from './model'
|
|
5
|
-
|
|
6
|
-
const noop = () => { }
|
|
7
|
-
|
|
8
|
-
export default async (opts) => {
|
|
9
|
-
const {
|
|
10
|
-
context,
|
|
11
|
-
target,
|
|
12
|
-
onStep,
|
|
13
|
-
|
|
14
|
-
// inputSize = undefined, // TODO: support resizing target
|
|
15
|
-
outputSize = undefined,
|
|
16
|
-
|
|
17
|
-
minEnergy = undefined,
|
|
18
|
-
|
|
19
|
-
shapeAlpha = 128,
|
|
20
|
-
shapeType = 'triangle',
|
|
21
|
-
|
|
22
|
-
numCandidates = 1, // [ 1, 32 ]
|
|
23
|
-
numCandidateShapes = 50, // [ 10, 300 ]
|
|
24
|
-
numCandidateMutations = 100, // [ 10, 500 ]
|
|
25
|
-
numCandidateExtras = 0, // [ 0, 16 ]
|
|
26
|
-
|
|
27
|
-
log = noop
|
|
28
|
-
} = opts
|
|
29
|
-
|
|
30
|
-
// validate options
|
|
31
|
-
ow(opts, ow.object.plain.label('opts'))
|
|
32
|
-
ow(target, ow.object.label('target'))
|
|
33
|
-
ow(target.width, ow.number.positive.integer.label('target.width'))
|
|
34
|
-
ow(target.height, ow.number.positive.integer.label('target.height'))
|
|
35
|
-
ow(target.data, ow.any(ow.uint8Array, ow.uint8ClampedArray))
|
|
36
|
-
ow(shapeAlpha, ow.number.integer.greaterThanOrEqual(0).lessThanOrEqual(255).label('shapeAlpha'))
|
|
37
|
-
ow(shapeType, ow.string.nonEmpty.label('shapeType'))
|
|
38
|
-
ow(numCandidates, ow.number.integer.positive.label('numCandidates'))
|
|
39
|
-
ow(numCandidateShapes, ow.number.integer.positive.label('numCandidateShapes'))
|
|
40
|
-
ow(numCandidateMutations, ow.number.integer.positive.label('numCandidateMutations'))
|
|
41
|
-
ow(log, ow.function.label('log'))
|
|
42
|
-
ow(onStep, ow.function.label('onStep'))
|
|
43
|
-
|
|
44
|
-
const backgroundColor = core.getMeanColor(target)
|
|
45
|
-
|
|
46
|
-
const model = new Model({
|
|
47
|
-
context,
|
|
48
|
-
target,
|
|
49
|
-
backgroundColor,
|
|
50
|
-
outputSize,
|
|
51
|
-
numCandidates
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
const step = async (index) => {
|
|
55
|
-
await onStep(model, step)
|
|
56
|
-
|
|
57
|
-
const candidates = model.step({
|
|
58
|
-
shapeType,
|
|
59
|
-
shapeAlpha,
|
|
60
|
-
numCandidateShapes,
|
|
61
|
-
numCandidateMutations,
|
|
62
|
-
numCandidateExtras
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
if (minEnergy && model.score <= minEnergy) {
|
|
66
|
-
return false
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return candidates
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return {
|
|
73
|
-
model,
|
|
74
|
-
step
|
|
75
|
-
}
|
|
76
|
-
}
|
package/lib/rasterize.js
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import Scanline from './scanline'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Converts a polygon to an array of rasterizable scanlines.
|
|
5
|
-
*
|
|
6
|
-
* @param {Array<Object>} points
|
|
7
|
-
* @return {Array<Scanline>}
|
|
8
|
-
*/
|
|
9
|
-
export default (points) => {
|
|
10
|
-
const lines = []
|
|
11
|
-
let edges = []
|
|
12
|
-
|
|
13
|
-
for (let i = 0; i < points.length; ++i) {
|
|
14
|
-
const p1 = points[i]
|
|
15
|
-
const p2 = (i >= points.length - 1 ? points[0] : points[i + 1])
|
|
16
|
-
const p1p2 = bresenham(p1.x | 0, p1.y | 0, p2.x | 0, p2.y | 0)
|
|
17
|
-
edges = edges.concat(p1p2)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const yToXs = new Map()
|
|
21
|
-
for (let i = 0; i < edges.length; ++i) {
|
|
22
|
-
const point = edges[i]
|
|
23
|
-
let xSet = yToXs.get(point.y)
|
|
24
|
-
|
|
25
|
-
if (xSet) {
|
|
26
|
-
xSet.add(point.x)
|
|
27
|
-
} else {
|
|
28
|
-
xSet = new Set()
|
|
29
|
-
xSet.add(point.x)
|
|
30
|
-
yToXs.set(point.y, xSet)
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
yToXs.forEach((xSet, y) => {
|
|
35
|
-
const minMax = minMaxElements(xSet)
|
|
36
|
-
|
|
37
|
-
if (minMax) {
|
|
38
|
-
lines.push(new Scanline(y, minMax.min, minMax.max))
|
|
39
|
-
}
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
return lines
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function bresenham (x1, y1, x2, y2) {
|
|
46
|
-
const points = []
|
|
47
|
-
|
|
48
|
-
let dx = x2 - x1
|
|
49
|
-
const ix = Math.sign(dx)
|
|
50
|
-
dx = Math.abs(dx) * 2
|
|
51
|
-
|
|
52
|
-
let dy = y2 - y1
|
|
53
|
-
const iy = Math.sign(dy)
|
|
54
|
-
dy = Math.abs(dy) * 2
|
|
55
|
-
|
|
56
|
-
points.push({ x: x1, y: y1 })
|
|
57
|
-
|
|
58
|
-
if (dx >= dy) {
|
|
59
|
-
let error = (dy - (dx >> 1))
|
|
60
|
-
|
|
61
|
-
while (x1 !== x2) {
|
|
62
|
-
if (error >= 0 && (error !== 0 || ix > 0)) {
|
|
63
|
-
error -= dx
|
|
64
|
-
y1 += iy
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
error += dy
|
|
68
|
-
x1 += ix
|
|
69
|
-
points.push({ x: x1, y: y1 })
|
|
70
|
-
}
|
|
71
|
-
} else {
|
|
72
|
-
let error = (dx - (dy >> 1))
|
|
73
|
-
|
|
74
|
-
while (y1 !== y2) {
|
|
75
|
-
if (error >= 0 && (error !== 0 || iy > 0)) {
|
|
76
|
-
error -= dy
|
|
77
|
-
x1 += ix
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
error += dx
|
|
81
|
-
y1 += iy
|
|
82
|
-
points.push({ x: x1, y: y1 })
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return points
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function minMaxElements (iterable) {
|
|
90
|
-
let min = null
|
|
91
|
-
let max = null
|
|
92
|
-
|
|
93
|
-
for (let v of iterable) {
|
|
94
|
-
if (!min || v < min) min = v
|
|
95
|
-
if (!max || v > max) max = v
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (min !== null && max !== null) {
|
|
99
|
-
return { min, max }
|
|
100
|
-
}
|
|
101
|
-
}
|
package/lib/scanline.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
export default class Scanline {
|
|
2
|
-
constructor (y, x1, x2) {
|
|
3
|
-
if (x1 > x2) {
|
|
4
|
-
let t = x1
|
|
5
|
-
x1 = x2
|
|
6
|
-
x2 = t
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
this.y = y | 0
|
|
10
|
-
this.x1 = x1 | 0
|
|
11
|
-
this.x2 = x2 | 0
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
static filter (lines, width, height) {
|
|
15
|
-
return lines.filter((line) => {
|
|
16
|
-
if (line.y < 0 || line.y >= height) return false
|
|
17
|
-
if (line.x1 >= width || line.x2 < 0) return false
|
|
18
|
-
|
|
19
|
-
line.x1 = Math.max(0, Math.min(width - 1, line.x1))
|
|
20
|
-
line.x2 = Math.max(0, Math.min(width - 1, line.x2))
|
|
21
|
-
|
|
22
|
-
return (line.x1 < line.x2)
|
|
23
|
-
})
|
|
24
|
-
}
|
|
25
|
-
}
|
package/lib/shapes/ellipse.js
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import randomInt from 'random-int'
|
|
2
|
-
import randomNormal from 'random-normal'
|
|
3
|
-
|
|
4
|
-
import Scanline from '../scanline'
|
|
5
|
-
import Shape from './shape'
|
|
6
|
-
|
|
7
|
-
export default class Ellipse extends Shape {
|
|
8
|
-
constructor (opts) {
|
|
9
|
-
super(opts)
|
|
10
|
-
if (!opts) return
|
|
11
|
-
|
|
12
|
-
this.circle = !!opts.circle
|
|
13
|
-
this.x = randomInt(0, this.width - 1)
|
|
14
|
-
this.y = randomInt(0, this.height - 1)
|
|
15
|
-
|
|
16
|
-
this.rx = randomInt(1, 32)
|
|
17
|
-
this.ry = this.circle
|
|
18
|
-
? this.rx
|
|
19
|
-
: randomInt(1, 32)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
copy () {
|
|
23
|
-
const shape = new Ellipse()
|
|
24
|
-
shape.width = this.width
|
|
25
|
-
shape.height = this.height
|
|
26
|
-
shape.circle = this.circle
|
|
27
|
-
shape.x = this.x
|
|
28
|
-
shape.y = this.y
|
|
29
|
-
shape.rx = this.rx
|
|
30
|
-
shape.ry = this.ry
|
|
31
|
-
return shape
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
bounds () {
|
|
35
|
-
let { x1, y1, x2, y2 } = this
|
|
36
|
-
let t
|
|
37
|
-
|
|
38
|
-
if (x1 > x2) {
|
|
39
|
-
t = x1
|
|
40
|
-
x1 = x2
|
|
41
|
-
x2 = t
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (y1 > y2) {
|
|
45
|
-
t = y1
|
|
46
|
-
y1 = y2
|
|
47
|
-
y2 = t
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return { x1, y1, x2, y2 }
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
mutate () {
|
|
54
|
-
const { width, height } = this
|
|
55
|
-
const shape = this.copy()
|
|
56
|
-
const m = 16
|
|
57
|
-
|
|
58
|
-
switch (randomInt(0, 2)) {
|
|
59
|
-
case 0:
|
|
60
|
-
shape.x = Math.max(0, Math.min(width - 1, (shape.x + randomNormal() * m)))
|
|
61
|
-
shape.y = Math.max(0, Math.min(height - 1, (shape.y + randomNormal() * m)))
|
|
62
|
-
break
|
|
63
|
-
|
|
64
|
-
case 1:
|
|
65
|
-
shape.rx = Math.max(1, Math.min(width - 1, (shape.rx + randomNormal() * m)))
|
|
66
|
-
if (shape.circle) shape.ry = shape.rx
|
|
67
|
-
break
|
|
68
|
-
|
|
69
|
-
case 2:
|
|
70
|
-
shape.ry = Math.max(1, Math.min(height - 1, (shape.ry + randomNormal() * m)))
|
|
71
|
-
if (shape.circle) shape.rx = shape.ry
|
|
72
|
-
break
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return shape
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
rasterize () {
|
|
79
|
-
const { width, height, x, y, rx, ry } = this
|
|
80
|
-
const lines = []
|
|
81
|
-
const aspect = rx / ry
|
|
82
|
-
|
|
83
|
-
for (let dy = 0; dy < ry; ++dy) {
|
|
84
|
-
const y1 = y - dy
|
|
85
|
-
const y2 = y + dy
|
|
86
|
-
|
|
87
|
-
if ((y1 < 0 || y1 >= height) && (y2 < 0 || y2 >= height)) {
|
|
88
|
-
continue
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const s = Math.sqrt(ry * ry - dy * dy) * aspect
|
|
92
|
-
const x1 = Math.max(0, x - s)
|
|
93
|
-
const x2 = Math.min(width - 1, x + s)
|
|
94
|
-
|
|
95
|
-
if (y1 >= 0 && y1 < height) {
|
|
96
|
-
lines.push(new Scanline(y1, x1, x2))
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
if (y2 >= 0 && y2 < height && dy > 0) {
|
|
100
|
-
lines.push(new Scanline(y2, x1, x2))
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return lines
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
toSVG (attrs = '') {
|
|
108
|
-
return (
|
|
109
|
-
`<ellipse ${attrs} cx="${this.x}" cy="${this.y}" rx="${this.rx}" ry="${this.ry}" />`
|
|
110
|
-
)
|
|
111
|
-
}
|
|
112
|
-
}
|
package/lib/shapes/factory.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import randomInt from 'random-int'
|
|
2
|
-
|
|
3
|
-
import Ellipse from './ellipse'
|
|
4
|
-
import Rectangle from './rectangle'
|
|
5
|
-
import RotatedEllipse from './rotated-ellipse'
|
|
6
|
-
import RotatedRectangle from './rotated-rectangle'
|
|
7
|
-
import Triangle from './triangle'
|
|
8
|
-
|
|
9
|
-
const SHAPES = [
|
|
10
|
-
// ellipse and rectangle are redundant with their rotated versions
|
|
11
|
-
// 'ellipse',
|
|
12
|
-
// 'rectangle',
|
|
13
|
-
'rotated-ellipse',
|
|
14
|
-
'rotated-rectangle',
|
|
15
|
-
'triangle'
|
|
16
|
-
]
|
|
17
|
-
|
|
18
|
-
const factory = (shapeType, opts) => {
|
|
19
|
-
switch (shapeType) {
|
|
20
|
-
case 'rectangle':
|
|
21
|
-
case 'rect':
|
|
22
|
-
return new Rectangle(opts)
|
|
23
|
-
|
|
24
|
-
case 'rotated-rectangle':
|
|
25
|
-
case 'rotated-rect':
|
|
26
|
-
return new RotatedRectangle(opts)
|
|
27
|
-
|
|
28
|
-
case 'circle':
|
|
29
|
-
return new Ellipse({
|
|
30
|
-
circle: true,
|
|
31
|
-
...opts
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
case 'ellipse':
|
|
35
|
-
return new Ellipse(opts)
|
|
36
|
-
|
|
37
|
-
case 'rotated-ellipse':
|
|
38
|
-
return new RotatedEllipse(opts)
|
|
39
|
-
|
|
40
|
-
case 'triangle':
|
|
41
|
-
return new Triangle(opts)
|
|
42
|
-
|
|
43
|
-
default:
|
|
44
|
-
return factory(random(), opts)
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function random () {
|
|
49
|
-
return SHAPES[randomInt(SHAPES.length)]
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export default factory
|
package/lib/shapes/rectangle.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import randomInt from 'random-int'
|
|
2
|
-
import randomNormal from 'random-normal'
|
|
3
|
-
|
|
4
|
-
import Scanline from '../scanline'
|
|
5
|
-
import Shape from './shape'
|
|
6
|
-
|
|
7
|
-
export default class Rectangle extends Shape {
|
|
8
|
-
constructor (opts) {
|
|
9
|
-
super(opts)
|
|
10
|
-
if (!opts) return
|
|
11
|
-
|
|
12
|
-
this.x1 = randomInt(0, this.width - 1)
|
|
13
|
-
this.y1 = randomInt(0, this.height - 1)
|
|
14
|
-
|
|
15
|
-
this.x2 = Math.max(0, Math.min(this.width - 1, this.x1 + randomInt(-16, 16)))
|
|
16
|
-
this.y2 = Math.max(0, Math.min(this.height - 1, this.y1 + randomInt(-16, 16)))
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
copy () {
|
|
20
|
-
const shape = new Rectangle()
|
|
21
|
-
shape.width = this.width
|
|
22
|
-
shape.height = this.height
|
|
23
|
-
shape.x1 = this.x1
|
|
24
|
-
shape.y1 = this.y1
|
|
25
|
-
shape.x2 = this.x2
|
|
26
|
-
shape.y2 = this.y2
|
|
27
|
-
return shape
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
bounds () {
|
|
31
|
-
let { x1, y1, x2, y2 } = this
|
|
32
|
-
let t
|
|
33
|
-
|
|
34
|
-
if (x1 > x2) {
|
|
35
|
-
t = x1
|
|
36
|
-
x1 = x2
|
|
37
|
-
x2 = t
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (y1 > y2) {
|
|
41
|
-
t = y1
|
|
42
|
-
y1 = y2
|
|
43
|
-
y2 = t
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return { x1, y1, x2, y2 }
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
mutate () {
|
|
50
|
-
const { width, height } = this
|
|
51
|
-
const shape = this.copy()
|
|
52
|
-
const m = 16
|
|
53
|
-
|
|
54
|
-
switch (randomInt(0, 1)) {
|
|
55
|
-
case 0:
|
|
56
|
-
shape.x1 = Math.max(0, Math.min(width - 1, (shape.x1 + randomNormal() * m)))
|
|
57
|
-
shape.y1 = Math.max(0, Math.min(height - 1, (shape.y1 + randomNormal() * m)))
|
|
58
|
-
break
|
|
59
|
-
|
|
60
|
-
case 1:
|
|
61
|
-
shape.x2 = Math.max(0, Math.min(width - 1, (shape.x2 + randomNormal() * m)))
|
|
62
|
-
shape.y2 = Math.max(0, Math.min(height - 1, (shape.y2 + randomNormal() * m)))
|
|
63
|
-
break
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return shape
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
rasterize () {
|
|
70
|
-
const { x1, y1, x2, y2 } = this.bounds()
|
|
71
|
-
const lines = []
|
|
72
|
-
|
|
73
|
-
for (let y = y1; y <= y2; ++y) {
|
|
74
|
-
lines.push(new Scanline(y, x1, x2))
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return lines
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
draw (ctx) {
|
|
81
|
-
// TODO
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
toSVG (attrs = '') {
|
|
85
|
-
const { x1, y1, x2, y2 } = this.bounds()
|
|
86
|
-
const w = x2 - x1 + 1
|
|
87
|
-
const h = y2 - y1 + 1
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
`<rect ${attrs} x="${x1}" y="${y1}" width="${w}" height="${h}" />`
|
|
91
|
-
)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import randomInt from 'random-int'
|
|
2
|
-
import randomNormal from 'random-normal'
|
|
3
|
-
|
|
4
|
-
import rasterize from '../rasterize'
|
|
5
|
-
import Scanline from '../scanline'
|
|
6
|
-
import Shape from './shape'
|
|
7
|
-
|
|
8
|
-
export default class RotatedEllipse extends Shape {
|
|
9
|
-
constructor (opts) {
|
|
10
|
-
super(opts)
|
|
11
|
-
if (!opts) return
|
|
12
|
-
|
|
13
|
-
this.x = randomInt(0, this.width - 1)
|
|
14
|
-
this.y = randomInt(0, this.height - 1)
|
|
15
|
-
|
|
16
|
-
this.rx = randomInt(1, 32)
|
|
17
|
-
this.ry = randomInt(1, 32)
|
|
18
|
-
|
|
19
|
-
this.angle = Math.random() * 360
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
copy () {
|
|
23
|
-
const shape = new RotatedEllipse()
|
|
24
|
-
shape.width = this.width
|
|
25
|
-
shape.height = this.height
|
|
26
|
-
shape.x = this.x
|
|
27
|
-
shape.y = this.y
|
|
28
|
-
shape.rx = this.rx
|
|
29
|
-
shape.ry = this.ry
|
|
30
|
-
shape.angle = this.angle
|
|
31
|
-
return shape
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
mutate () {
|
|
35
|
-
const { width, height } = this
|
|
36
|
-
const shape = this.copy()
|
|
37
|
-
const m = 16
|
|
38
|
-
|
|
39
|
-
switch (randomInt(0, 2)) {
|
|
40
|
-
case 0:
|
|
41
|
-
shape.x = Math.max(0, Math.min(width - 1, (shape.x + randomNormal() * m)))
|
|
42
|
-
shape.y = Math.max(0, Math.min(height - 1, (shape.y + randomNormal() * m)))
|
|
43
|
-
break
|
|
44
|
-
|
|
45
|
-
case 1:
|
|
46
|
-
shape.rx = Math.max(1, Math.min(width - 1, (shape.rx + randomNormal() * m)))
|
|
47
|
-
shape.ry = Math.max(1, Math.min(height - 1, (shape.ry + randomNormal() * m)))
|
|
48
|
-
break
|
|
49
|
-
|
|
50
|
-
case 2:
|
|
51
|
-
shape.angle = shape.angle + randomNormal() * m
|
|
52
|
-
break
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return shape
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
rasterize () {
|
|
59
|
-
const points = this.getPoints()
|
|
60
|
-
const lines = rasterize(points)
|
|
61
|
-
return Scanline.filter(lines, this.width, this.height)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
getPoints (numPoints = 20) {
|
|
65
|
-
const { x, y, rx, ry, angle } = this
|
|
66
|
-
const points = []
|
|
67
|
-
const rads = angle * Math.PI / 180.0
|
|
68
|
-
const c = Math.cos(rads)
|
|
69
|
-
const s = Math.sin(rads)
|
|
70
|
-
|
|
71
|
-
for (let i = 0; i < numPoints; ++i) {
|
|
72
|
-
const rot = ((360.0 / numPoints) * i) * (Math.PI / 180.0)
|
|
73
|
-
const crx = rx * Math.cos(rot)
|
|
74
|
-
const cry = ry * Math.sin(rot)
|
|
75
|
-
|
|
76
|
-
const tx = (crx * c - cry * s + x) | 0
|
|
77
|
-
const ty = (crx * s + cry * c + y) | 0
|
|
78
|
-
|
|
79
|
-
points.push({ x: tx, y: ty })
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
return points
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
draw (ctx) {
|
|
86
|
-
throw new Error('TODO')
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
toSVG (attrs = '') {
|
|
90
|
-
// TODO: native rotated ellipse will produce smaller and smoother results
|
|
91
|
-
const points = this.getPoints()
|
|
92
|
-
.map((point) => `${point.x} ${point.y}`)
|
|
93
|
-
.join(' ')
|
|
94
|
-
|
|
95
|
-
return (
|
|
96
|
-
`<polygon ${attrs} points="${points}" />`
|
|
97
|
-
)
|
|
98
|
-
}
|
|
99
|
-
}
|