primitive 1.0.1 → 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,125 +0,0 @@
1
- import random, { normal } from '../random'
2
-
3
- import rasterize from '../rasterize'
4
- import Scanline from '../scanline'
5
- import Shape from './shape'
6
-
7
- export default class RotatedRectangle extends Shape {
8
- constructor (opts) {
9
- super(opts)
10
- if (!opts) return
11
-
12
- this.x1 = random.int(0, this.width - 1)
13
- this.y1 = random.int(0, this.height - 1)
14
-
15
- this.x2 = Math.max(0, Math.min(this.width - 1, this.x1 + random.int(-16, 16)))
16
- this.y2 = Math.max(0, Math.min(this.height - 1, this.y1 + random.int(-16, 16)))
17
-
18
- this.angle = random.float() * 360
19
- }
20
-
21
- copy () {
22
- const shape = new RotatedRectangle()
23
- shape.width = this.width
24
- shape.height = this.height
25
- shape.x1 = this.x1
26
- shape.y1 = this.y1
27
- shape.x2 = this.x2
28
- shape.y2 = this.y2
29
- shape.angle = this.angle
30
- return shape
31
- }
32
-
33
- mutate () {
34
- const { width, height } = this
35
- const shape = this.copy()
36
- const m = 16
37
-
38
- switch (random.int(0, 2)) {
39
- case 0:
40
- shape.x1 = Math.max(0, Math.min(width - 1, shape.x1 + normal() * m))
41
- shape.y1 = Math.max(0, Math.min(height - 1, shape.y1 + normal() * m))
42
- break
43
-
44
- case 1:
45
- shape.x2 = Math.max(0, Math.min(width - 1, shape.x2 + normal() * m))
46
- shape.y2 = Math.max(0, Math.min(height - 1, shape.y2 + normal() * m))
47
- break
48
-
49
- case 2:
50
- shape.angle = shape.angle + normal() * m
51
- break
52
- }
53
-
54
- return shape
55
- }
56
-
57
- rasterize () {
58
- const points = this.getPoints()
59
- const lines = rasterize(points)
60
- return Scanline.filter(lines, this.width, this.height)
61
- }
62
-
63
- getPoints () {
64
- const { x1, y1, x2, y2, angle } = this
65
-
66
- const xm1 = Math.min(x1, x2)
67
- const xm2 = Math.max(x1, x2)
68
- const ym1 = Math.min(y1, y2)
69
- const ym2 = Math.max(y1, y2)
70
-
71
- const cx = (xm1 + xm2) / 2
72
- const cy = (ym1 + ym2) / 2
73
-
74
- const ox1 = xm1 - cx
75
- const ox2 = xm2 - cx
76
- const oy1 = ym1 - cy
77
- const oy2 = ym2 - cy
78
-
79
- const rads = angle * Math.PI / 180.0
80
- const c = Math.cos(rads)
81
- const s = Math.sin(rads)
82
-
83
- const ulx = (ox1 * c - oy1 * s + cx) | 0
84
- const uly = (ox1 * s + oy1 * c + cy) | 0
85
- const blx = (ox1 * c - oy2 * s + cx) | 0
86
- const bly = (ox1 * s + oy2 * c + cy) | 0
87
- const urx = (ox2 * c - oy1 * s + cx) | 0
88
- const ury = (ox2 * s + oy1 * c + cy) | 0
89
- const brx = (ox2 * c - oy2 * s + cx) | 0
90
- const bry = (ox2 * s + oy2 * c + cy) | 0
91
-
92
- return [
93
- {
94
- x: ulx,
95
- y: uly
96
- },
97
- {
98
- x: urx,
99
- y: ury
100
- },
101
- {
102
- x: brx,
103
- y: bry
104
- },
105
- {
106
- x: blx,
107
- y: bly
108
- }
109
- ]
110
- }
111
-
112
- draw (ctx) {
113
- throw new Error('TODO')
114
- }
115
-
116
- toSVG (attrs = '') {
117
- const points = this.getPoints()
118
- .map((point) => `${point.x} ${point.y}`)
119
- .join(' ')
120
-
121
- return (
122
- `<polygon ${attrs} points="${points}" />`
123
- )
124
- }
125
- }
@@ -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,216 +0,0 @@
1
- import random, { normal } from '../random'
2
-
3
- import Scanline from '../scanline'
4
- import Shape from './shape'
5
-
6
- export default class Triangle extends Shape {
7
- constructor (opts) {
8
- super(opts)
9
- if (!opts) return
10
-
11
- do {
12
- this.x1 = random.int(0, this.width - 1)
13
- this.y1 = random.int(0, this.height - 1)
14
-
15
- this.x2 = this.x1 + random.int(-15, 15)
16
- this.y2 = this.y1 + random.int(-15, 15)
17
-
18
- this.x3 = this.x1 + random.int(-15, 15)
19
- this.y3 = this.y1 + random.int(-15, 15)
20
- } while (!this.isValid())
21
- }
22
-
23
- copy () {
24
- const shape = new Triangle()
25
- shape.width = this.width
26
- shape.height = this.height
27
- shape.x1 = this.x1
28
- shape.y1 = this.y1
29
- shape.x2 = this.x2
30
- shape.y2 = this.y2
31
- shape.x3 = this.x3
32
- shape.y3 = this.y3
33
- return shape
34
- }
35
-
36
- mutate () {
37
- const { width, height } = this
38
- const m = 16
39
- let shape = this.copy()
40
- let n = 0
41
-
42
- while (true) {
43
- switch (random.int(0, 2)) {
44
- case 0:
45
- shape.x1 = Math.max(-m, Math.min(width - 1 + m, (shape.x1 + normal() * m)))
46
- shape.y1 = Math.max(-m, Math.min(height - 1 + m, (shape.y1 + normal() * m)))
47
- break
48
-
49
- case 1:
50
- shape.x2 = Math.max(-m, Math.min(width - 1 + m, (shape.x2 + normal() * m)))
51
- shape.y2 = Math.max(-m, Math.min(height - 1 + m, (shape.y2 + normal() * m)))
52
- break
53
-
54
- case 2:
55
- shape.x3 = Math.max(-m, Math.min(width - 1 + m, (shape.x3 + normal() * m)))
56
- shape.y3 = Math.max(-m, Math.min(height - 1 + m, (shape.y3 + normal() * m)))
57
- break
58
- }
59
-
60
- if (shape.isValid()) {
61
- return shape
62
- }
63
-
64
- if (++n > 128) {
65
- shape = this.copy()
66
- n = 0
67
- }
68
- }
69
- }
70
-
71
- isValid () {
72
- const minDegrees = 15
73
-
74
- let a1, a2, a3
75
- {
76
- let x1 = (this.x2 - this.x1)
77
- let y1 = (this.y2 - this.y1)
78
- let x2 = (this.x3 - this.x1)
79
- let y2 = (this.y3 - this.y1)
80
- const d1 = Math.sqrt(x1 * x1 + y1 * y1)
81
- const d2 = Math.sqrt(x2 * x2 + y2 * y2)
82
- x1 /= d1
83
- y1 /= d1
84
- x2 /= d2
85
- y2 /= d2
86
- a1 = degrees(Math.acos(x1 * x2 + y1 * y2))
87
- }
88
-
89
- {
90
- let x1 = (this.x1 - this.x2)
91
- let y1 = (this.y1 - this.y2)
92
- let x2 = (this.x3 - this.x2)
93
- let y2 = (this.y3 - this.y2)
94
- const d1 = Math.sqrt(x1 * x1 + y1 * y1)
95
- const d2 = Math.sqrt(x2 * x2 + y2 * y2)
96
- x1 /= d1
97
- y1 /= d1
98
- x2 /= d2
99
- y2 /= d2
100
- a2 = degrees(Math.acos(x1 * x2 + y1 * y2))
101
- }
102
-
103
- a3 = 180 - a1 - a2
104
- return a1 > minDegrees && a2 > minDegrees && a3 > minDegrees
105
- }
106
-
107
- rasterize () {
108
- const { width, height } = this
109
- const lines = rasterize(this.x1, this.y1, this.x2, this.y2, this.x3, this.y3)
110
- return Scanline.filter(lines, width, height)
111
- }
112
-
113
- draw (ctx) {
114
- ctx.beginPath()
115
- ctx.moveTo(this.x1, this.y1)
116
- ctx.lineTo(this.x2, this.y2)
117
- ctx.lineTo(this.x3, this.y3)
118
- ctx.fill()
119
- }
120
-
121
- toSVG (attrs = '') {
122
- return (
123
- `<polygon ${attrs} points="${this.x1},${this.y1} ${this.x2},${this.y2} ${this.x3},${this.y3}" />`
124
- )
125
- }
126
- }
127
-
128
- function degrees (radians) {
129
- return 180 * radians / Math.PI
130
- }
131
-
132
- function rasterize (x1, y1, x2, y2, x3, y3) {
133
- let t
134
-
135
- if (y1 > y3) {
136
- t = x1
137
- x1 = x3
138
- x3 = t
139
-
140
- t = y1
141
- y1 = y3
142
- y3 = t
143
- }
144
-
145
- if (y1 > y2) {
146
- t = x1
147
- x1 = x2
148
- x2 = t
149
-
150
- t = y1
151
- y1 = y2
152
- y2 = t
153
- }
154
-
155
- if (y2 > y3) {
156
- t = x2
157
- x2 = x3
158
- x3 = t
159
-
160
- t = y2
161
- y2 = y3
162
- y3 = t
163
- }
164
-
165
- if (y2 === y3) {
166
- return rasterizeBottom(x1, y1, x2, y2, x3, y3)
167
- } else if (y1 === y2) {
168
- return rasterizeTop(x1, y1, x2, y2, x3, y3)
169
- } else {
170
- const x4 = x1 + (((y2 - y1) / (y3 - y1)) * (x3 - x1)) | 0
171
- const y4 = y2
172
-
173
- return []
174
- .concat(rasterizeBottom(x1, y1, x2, y2, x4, y4))
175
- .concat(rasterizeTop(x2, y2, x4, y4, x3, y3))
176
- }
177
- }
178
-
179
- function rasterizeBottom (x1, y1, x2, y2, x3, y3) {
180
- const lines = []
181
-
182
- const s1 = (x2 - x1) / (y2 - y1)
183
- const s2 = (x3 - x1) / (y3 - y1)
184
- let ax = x1
185
- let bx = x1
186
-
187
- for (let y = y1; y <= y2; ++y) {
188
- const a = ax
189
- const b = bx
190
-
191
- ax += s1
192
- bx += s2
193
-
194
- lines.push(new Scanline(y, a, b))
195
- }
196
-
197
- return lines
198
- }
199
-
200
- function rasterizeTop (x1, y1, x2, y2, x3, y3) {
201
- const lines = []
202
-
203
- const s1 = (x3 - x1) / (y3 - y1)
204
- const s2 = (x3 - x2) / (y3 - y2)
205
- let ax = x3
206
- let bx = x3
207
-
208
- for (let y = y3; y >= y1; y--) {
209
- ax -= s1
210
- bx -= s2
211
-
212
- lines.push(new Scanline(y, ax, bx))
213
- }
214
-
215
- return lines
216
- }
@@ -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
- })