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.
@@ -1,126 +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 RotatedRectangle extends Shape {
9
- constructor (opts) {
10
- super(opts)
11
- if (!opts) return
12
-
13
- this.x1 = randomInt(0, this.width - 1)
14
- this.y1 = randomInt(0, this.height - 1)
15
-
16
- this.x2 = Math.max(0, Math.min(this.width - 1, this.x1 + randomInt(-16, 16)))
17
- this.y2 = Math.max(0, Math.min(this.height - 1, this.y1 + randomInt(-16, 16)))
18
-
19
- this.angle = Math.random() * 360
20
- }
21
-
22
- copy () {
23
- const shape = new RotatedRectangle()
24
- shape.width = this.width
25
- shape.height = this.height
26
- shape.x1 = this.x1
27
- shape.y1 = this.y1
28
- shape.x2 = this.x2
29
- shape.y2 = this.y2
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.x1 = Math.max(0, Math.min(width - 1, shape.x1 + randomNormal() * m))
42
- shape.y1 = Math.max(0, Math.min(height - 1, shape.y1 + randomNormal() * m))
43
- break
44
-
45
- case 1:
46
- shape.x2 = Math.max(0, Math.min(width - 1, shape.x2 + randomNormal() * m))
47
- shape.y2 = Math.max(0, Math.min(height - 1, shape.y2 + 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 () {
65
- const { x1, y1, x2, y2, angle } = this
66
-
67
- const xm1 = Math.min(x1, x2)
68
- const xm2 = Math.max(x1, x2)
69
- const ym1 = Math.min(y1, y2)
70
- const ym2 = Math.max(y1, y2)
71
-
72
- const cx = (xm1 + xm2) / 2
73
- const cy = (ym1 + ym2) / 2
74
-
75
- const ox1 = xm1 - cx
76
- const ox2 = xm2 - cx
77
- const oy1 = ym1 - cy
78
- const oy2 = ym2 - cy
79
-
80
- const rads = angle * Math.PI / 180.0
81
- const c = Math.cos(rads)
82
- const s = Math.sin(rads)
83
-
84
- const ulx = (ox1 * c - oy1 * s + cx) | 0
85
- const uly = (ox1 * s + oy1 * c + cy) | 0
86
- const blx = (ox1 * c - oy2 * s + cx) | 0
87
- const bly = (ox1 * s + oy2 * c + cy) | 0
88
- const urx = (ox2 * c - oy1 * s + cx) | 0
89
- const ury = (ox2 * s + oy1 * c + cy) | 0
90
- const brx = (ox2 * c - oy2 * s + cx) | 0
91
- const bry = (ox2 * s + oy2 * c + cy) | 0
92
-
93
- return [
94
- {
95
- x: ulx,
96
- y: uly
97
- },
98
- {
99
- x: urx,
100
- y: ury
101
- },
102
- {
103
- x: brx,
104
- y: bry
105
- },
106
- {
107
- x: blx,
108
- y: bly
109
- }
110
- ]
111
- }
112
-
113
- draw (ctx) {
114
- throw new Error('TODO')
115
- }
116
-
117
- toSVG (attrs = '') {
118
- const points = this.getPoints()
119
- .map((point) => `${point.x} ${point.y}`)
120
- .join(' ')
121
-
122
- return (
123
- `<polygon ${attrs} points="${points}" />`
124
- )
125
- }
126
- }
@@ -1,26 +0,0 @@
1
- export default class Shape {
2
- constructor (opts = { }) {
3
- this.width = opts.width
4
- this.height = opts.height
5
- }
6
-
7
- copy () {
8
- throw new Error('TODO')
9
- }
10
-
11
- mutate () {
12
- throw new Error('TODO')
13
- }
14
-
15
- rasterize () {
16
- throw new Error('TODO')
17
- }
18
-
19
- draw (ctx, scale) {
20
- throw new Error('TODO')
21
- }
22
-
23
- toSVG () {
24
- throw new Error('TODO')
25
- }
26
- }
@@ -1,217 +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 Triangle extends Shape {
8
- constructor (opts) {
9
- super(opts)
10
- if (!opts) return
11
-
12
- do {
13
- this.x1 = randomInt(0, this.width - 1)
14
- this.y1 = randomInt(0, this.height - 1)
15
-
16
- this.x2 = this.x1 + randomInt(-15, 15)
17
- this.y2 = this.y1 + randomInt(-15, 15)
18
-
19
- this.x3 = this.x1 + randomInt(-15, 15)
20
- this.y3 = this.y1 + randomInt(-15, 15)
21
- } while (!this.isValid())
22
- }
23
-
24
- copy () {
25
- const shape = new Triangle()
26
- shape.width = this.width
27
- shape.height = this.height
28
- shape.x1 = this.x1
29
- shape.y1 = this.y1
30
- shape.x2 = this.x2
31
- shape.y2 = this.y2
32
- shape.x3 = this.x3
33
- shape.y3 = this.y3
34
- return shape
35
- }
36
-
37
- mutate () {
38
- const { width, height } = this
39
- const m = 16
40
- let shape = this.copy()
41
- let n = 0
42
-
43
- while (true) {
44
- switch (randomInt(0, 2)) {
45
- case 0:
46
- shape.x1 = Math.max(-m, Math.min(width - 1 + m, (shape.x1 + randomNormal() * m)))
47
- shape.y1 = Math.max(-m, Math.min(height - 1 + m, (shape.y1 + randomNormal() * m)))
48
- break
49
-
50
- case 1:
51
- shape.x2 = Math.max(-m, Math.min(width - 1 + m, (shape.x2 + randomNormal() * m)))
52
- shape.y2 = Math.max(-m, Math.min(height - 1 + m, (shape.y2 + randomNormal() * m)))
53
- break
54
-
55
- case 2:
56
- shape.x3 = Math.max(-m, Math.min(width - 1 + m, (shape.x3 + randomNormal() * m)))
57
- shape.y3 = Math.max(-m, Math.min(height - 1 + m, (shape.y3 + randomNormal() * m)))
58
- break
59
- }
60
-
61
- if (shape.isValid()) {
62
- return shape
63
- }
64
-
65
- if (++n > 128) {
66
- shape = this.copy()
67
- n = 0
68
- }
69
- }
70
- }
71
-
72
- isValid () {
73
- const minDegrees = 15
74
-
75
- let a1, a2, a3
76
- {
77
- let x1 = (this.x2 - this.x1)
78
- let y1 = (this.y2 - this.y1)
79
- let x2 = (this.x3 - this.x1)
80
- let y2 = (this.y3 - this.y1)
81
- const d1 = Math.sqrt(x1 * x1 + y1 * y1)
82
- const d2 = Math.sqrt(x2 * x2 + y2 * y2)
83
- x1 /= d1
84
- y1 /= d1
85
- x2 /= d2
86
- y2 /= d2
87
- a1 = degrees(Math.acos(x1 * x2 + y1 * y2))
88
- }
89
-
90
- {
91
- let x1 = (this.x1 - this.x2)
92
- let y1 = (this.y1 - this.y2)
93
- let x2 = (this.x3 - this.x2)
94
- let y2 = (this.y3 - this.y2)
95
- const d1 = Math.sqrt(x1 * x1 + y1 * y1)
96
- const d2 = Math.sqrt(x2 * x2 + y2 * y2)
97
- x1 /= d1
98
- y1 /= d1
99
- x2 /= d2
100
- y2 /= d2
101
- a2 = degrees(Math.acos(x1 * x2 + y1 * y2))
102
- }
103
-
104
- a3 = 180 - a1 - a2
105
- return a1 > minDegrees && a2 > minDegrees && a3 > minDegrees
106
- }
107
-
108
- rasterize () {
109
- const { width, height } = this
110
- const lines = rasterize(this.x1, this.y1, this.x2, this.y2, this.x3, this.y3)
111
- return Scanline.filter(lines, width, height)
112
- }
113
-
114
- draw (ctx) {
115
- ctx.beginPath()
116
- ctx.moveTo(this.x1, this.y1)
117
- ctx.lineTo(this.x2, this.y2)
118
- ctx.lineTo(this.x3, this.y3)
119
- ctx.fill()
120
- }
121
-
122
- toSVG (attrs = '') {
123
- return (
124
- `<polygon ${attrs} points="${this.x1},${this.y1} ${this.x2},${this.y2} ${this.x3},${this.y3}" />`
125
- )
126
- }
127
- }
128
-
129
- function degrees (radians) {
130
- return 180 * radians / Math.PI
131
- }
132
-
133
- function rasterize (x1, y1, x2, y2, x3, y3) {
134
- let t
135
-
136
- if (y1 > y3) {
137
- t = x1
138
- x1 = x3
139
- x3 = t
140
-
141
- t = y1
142
- y1 = y3
143
- y3 = t
144
- }
145
-
146
- if (y1 > y2) {
147
- t = x1
148
- x1 = x2
149
- x2 = t
150
-
151
- t = y1
152
- y1 = y2
153
- y2 = t
154
- }
155
-
156
- if (y2 > y3) {
157
- t = x2
158
- x2 = x3
159
- x3 = t
160
-
161
- t = y2
162
- y2 = y3
163
- y3 = t
164
- }
165
-
166
- if (y2 === y3) {
167
- return rasterizeBottom(x1, y1, x2, y2, x3, y3)
168
- } else if (y1 === y2) {
169
- return rasterizeTop(x1, y1, x2, y2, x3, y3)
170
- } else {
171
- const x4 = x1 + (((y2 - y1) / (y3 - y1)) * (x3 - x1)) | 0
172
- const y4 = y2
173
-
174
- return []
175
- .concat(rasterizeBottom(x1, y1, x2, y2, x4, y4))
176
- .concat(rasterizeTop(x2, y2, x4, y4, x3, y3))
177
- }
178
- }
179
-
180
- function rasterizeBottom (x1, y1, x2, y2, x3, y3) {
181
- const lines = []
182
-
183
- const s1 = (x2 - x1) / (y2 - y1)
184
- const s2 = (x3 - x1) / (y3 - y1)
185
- let ax = x1
186
- let bx = x1
187
-
188
- for (let y = y1; y <= y2; ++y) {
189
- const a = ax
190
- const b = bx
191
-
192
- ax += s1
193
- bx += s2
194
-
195
- lines.push(new Scanline(y, a, b))
196
- }
197
-
198
- return lines
199
- }
200
-
201
- function rasterizeTop (x1, y1, x2, y2, x3, y3) {
202
- const lines = []
203
-
204
- const s1 = (x3 - x1) / (y3 - y1)
205
- const s2 = (x3 - x2) / (y3 - y2)
206
- let ax = x3
207
- let bx = x3
208
-
209
- for (let y = y3; y >= y1; y--) {
210
- ax -= s1
211
- bx -= s2
212
-
213
- lines.push(new Scanline(y, ax, bx))
214
- }
215
-
216
- return lines
217
- }
@@ -1,73 +0,0 @@
1
- import test from 'ava'
2
-
3
- import context from '../context'
4
- // import core from '../core'
5
- import Triangle from './triangle'
6
-
7
- test('new Triangle()', async (t) => {
8
- const image = context.createImage(512, 512)
9
-
10
- for (let i = 0; i < 10000; ++i) {
11
- const shape = new Triangle(image)
12
-
13
- t.true(shape.x1 >= -16)
14
- t.true(shape.x1 < 512 + 16)
15
- t.true(shape.y1 >= -16)
16
- t.true(shape.y1 < 512 + 16)
17
-
18
- t.true(shape.x2 >= -16)
19
- t.true(shape.x2 < 512 + 16)
20
- t.true(shape.y2 >= -16)
21
- t.true(shape.y2 < 512 + 16)
22
-
23
- t.true(shape.x3 >= -16)
24
- t.true(shape.x3 < 512 + 16)
25
- t.true(shape.y3 >= -16)
26
- t.true(shape.y3 < 512 + 16)
27
-
28
- t.true(shape.isValid())
29
-
30
- const shape2 = shape.copy()
31
- t.deepEqual(shape, shape2)
32
- }
33
- })
34
-
35
- test('Triangle.mutate', async (t) => {
36
- const image = context.createImage(512, 512)
37
-
38
- for (let i = 0; i < 10000; ++i) {
39
- const base = new Triangle(image)
40
- Object.freeze(base)
41
- const shape = base.mutate()
42
-
43
- t.is(base.width, shape.width)
44
- t.is(base.height, shape.height)
45
-
46
- t.true(shape.x1 >= -16)
47
- t.true(shape.x1 < 512 + 16)
48
- t.true(shape.y1 >= -16)
49
- t.true(shape.y1 < 512 + 16)
50
-
51
- t.true(shape.x2 >= -16)
52
- t.true(shape.x2 < 512 + 16)
53
- t.true(shape.y2 >= -16)
54
- t.true(shape.y2 < 512 + 16)
55
-
56
- t.true(shape.x3 >= -16)
57
- t.true(shape.x3 < 512 + 16)
58
- t.true(shape.y3 >= -16)
59
- t.true(shape.y3 < 512 + 16)
60
-
61
- t.true(shape.isValid())
62
-
63
- /*
64
- let s = shape
65
- for (let j = 0; j < 5000; ++j) {
66
- s = s.mutate()
67
- const lines = s.rasterize()
68
- core.drawLines(image, { r: 255, g: 0, b: 0, a: 128 }, lines)
69
- await context.saveImage(image, `${j}.png`)
70
- }
71
- */
72
- }
73
- })
package/lib/state.js DELETED
@@ -1,69 +0,0 @@
1
- 'use strict'
2
-
3
- import randomInt from 'random-int'
4
- import shapeFactory from './shapes/factory'
5
-
6
- export default class State {
7
- constructor (opts) {
8
- if (!opts) return
9
-
10
- const {
11
- worker,
12
- shape,
13
- alpha
14
- } = opts
15
-
16
- this.worker = worker
17
- this.shape = shape
18
- this.score = -1
19
-
20
- if (!alpha) {
21
- this.alpha = 128
22
- this.mutateAlpha = true
23
- } else {
24
- this.alpha = alpha
25
- this.mutateAlpha = false
26
- }
27
- }
28
-
29
- copy () {
30
- const state = new State()
31
- state.worker = this.worker
32
- state.shape = this.shape // .copy()
33
- state.score = this.score
34
- state.alpha = this.alpha
35
- state.mutateAlpha = this.mutateAlpha
36
- return state
37
- }
38
-
39
- energy () {
40
- if (this.score < 0) {
41
- this.score = this.worker.energy(this.shape, this.alpha)
42
- }
43
-
44
- return this.score
45
- }
46
-
47
- mutate () {
48
- const state = this.copy()
49
- state.shape = state.shape.mutate()
50
- if (state.mutateAlpha) {
51
- state.alpha = Math.max(1, Math.min(255, (state.alpha + randomInt(-10, 10)) | 0))
52
- }
53
- state.score = -1
54
- return state
55
- }
56
-
57
- static create (worker, shapeType, alpha) {
58
- const shape = shapeFactory(shapeType, {
59
- width: worker.width,
60
- height: worker.height
61
- })
62
-
63
- return new State({
64
- worker,
65
- shape,
66
- alpha
67
- })
68
- }
69
- }
package/lib/worker.js DELETED
@@ -1,50 +0,0 @@
1
- 'use strict'
2
-
3
- import core from './core'
4
-
5
- export default class Worker {
6
- constructor (opts) {
7
- const {
8
- context,
9
- target
10
- } = opts
11
-
12
- this.context = context
13
- this.target = target
14
- this.width = target.width
15
- this.height = target.height
16
-
17
- this.counter = 0
18
- this.current = null
19
- this.score = 0
20
-
21
- this.buffer = context.createImage(this.width, this.height)
22
- }
23
-
24
- init (current, score) {
25
- this.current = current
26
- this.score = score
27
- this.counter = 0
28
- }
29
-
30
- energy (shape, alpha) {
31
- this.counter++
32
-
33
- const lines = shape.rasterize()
34
- const color = core.computeColor(this.target, this.current, lines, alpha)
35
- let score
36
-
37
- if (this.context.PARTIALS) {
38
- core.copyLines(this.buffer, this.current, lines)
39
- core.drawLines(this.buffer, color, lines)
40
- score = core.differencePartial(this.target, this.current, this.buffer, this.score, lines)
41
- } else {
42
- this.buffer.data.set(this.current.data)
43
- core.drawLines(this.buffer, color, lines)
44
- score = core.difference(this.target, this.buffer)
45
- }
46
-
47
- // console.log('worker.energy', this.counter, score)
48
- return score
49
- }
50
- }
package/main.js DELETED
@@ -1,6 +0,0 @@
1
- 'use strict'
2
-
3
- // node commonjs entrypoint
4
-
5
- const _require = require('esm')(module) // eslint-disable-line
6
- module.exports = _require('./module').default
package/main.test.js DELETED
@@ -1,39 +0,0 @@
1
- import test from 'ava'
2
- import path from 'path'
3
-
4
- import primitive from './main'
5
-
6
- const fixturesPath = path.join(__dirname, 'media')
7
-
8
- const fixtures = [
9
- 'monalisa.png',
10
- 'lena.png'
11
- ]
12
-
13
- const shapeTypes = [
14
- 'triangle',
15
- 'ellipse',
16
- 'rotated-ellipse',
17
- 'rectangle',
18
- 'rotated-rectangle',
19
- 'random'
20
- ]
21
-
22
- fixtures.forEach((fixture) => {
23
- const input = path.join(fixturesPath, fixture)
24
-
25
- shapeTypes.forEach((shapeType) => {
26
- test(`${fixture} - ${shapeType}`, async (t) => {
27
- const model = await primitive({
28
- input,
29
- shapeType,
30
- numSteps: 10,
31
- numCandidateShapes: 5,
32
- numCandidateMutations: 30,
33
- log: console.log.bind(console)
34
- })
35
-
36
- t.true(model.score < 1)
37
- })
38
- })
39
- })