├── screenshots
├── minmax.png
├── hexstruct.png
└── mondrian.png
├── .gitignore
├── .travis.yml
├── examples
├── OctopodII.es
├── Grinder.es
├── frameinframe.es
├── testminmaxsize.es
├── menger.es
├── city.es
├── mondrian.es
├── PanelStruct.es
├── Konstrukt.es
├── HexStruct.es
├── Schmiegel.es
└── Noct.es
├── pack-examples.sh
├── package.json
├── structure.ts
├── README.md
├── mega-structure.html
├── gulpfile.js
├── progress.ts
├── synthesizer-webworker.ts
├── mega-structure.ts
├── codemirror-eisen-script-mode.js
├── eisen-script.peg
└── synthesizer.ts
/screenshots/minmax.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/mega-structure/master/screenshots/minmax.png
--------------------------------------------------------------------------------
/screenshots/hexstruct.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/mega-structure/master/screenshots/hexstruct.png
--------------------------------------------------------------------------------
/screenshots/mondrian.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/donmccurdy/mega-structure/master/screenshots/mondrian.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bundle.js
2 | eisen-script.js
3 | synthesizer-webworker.js
4 | examples-generated.ts
5 | id_rsa
6 | id_rsa.pub
7 | node_modules
8 | typings
9 | package-lock.json
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '7'
4 | before_script:
5 | - npm install -g gulp
6 | script:
7 | - gulp
8 | - gulp install
9 | deploy:
10 | provider: pages
11 | skip_cleanup: true
12 | github_token: $GITHUB_TOKEN
13 | local_dir: _site
14 | on:
15 | branch: master
16 |
17 |
--------------------------------------------------------------------------------
/examples/OctopodII.es:
--------------------------------------------------------------------------------
1 | set maxdepth 100
2 |
3 | 10 * { ry 36 sat 0.9 } 30 * { ry 10 } 1 * { h 30 b 0.8 sat 0.8 a 0.3 } r1
4 |
5 | rule r1 w 20 {
6 | { s 0.9 rz 5 h 5 rx 5 x 1 } r1
7 | { s 1 0.2 0.5 } box
8 | }
9 |
10 | rule r1 w 20 {
11 | { s 0.99 rz -5 h 5 rx -5 x 1 } r1
12 | { s 1 0.2 0.5 } box
13 | }
14 |
15 | rule r1 {
16 | }
--------------------------------------------------------------------------------
/pack-examples.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 |
4 | echo "// Code auto-generated by $(basename $0)."
5 | echo "// Do not edit."
6 | echo "var fs = require('fs');"
7 | echo "var EisenScripts : { [s: string]: string; } = {};"
8 | for i in $(ls examples/*.es); do
9 | base=$(basename $i)
10 | echo "EisenScripts[\"${base%%.es}\"]=fs.readFileSync('$i', 'ascii');";
11 | done
12 | echo "export = EisenScripts;"
13 |
--------------------------------------------------------------------------------
/examples/Grinder.es:
--------------------------------------------------------------------------------
1 | // Grinder.es
2 |
3 | set maxobjects 117600
4 | grinder
5 |
6 | rule grinder {
7 | 36 * { ry 10 z 1.2 b 0.99 } 1 * { ry 34 rx 34 } arm
8 | }
9 |
10 | rule arm {
11 |
12 | { rz 2 rx 3 z 0.22 x 0.3 ry 10 s 0.999 x 0.5 sat 0.9995 hue 0.05 } arm
13 | xbox
14 | }
15 |
16 | rule arm {
17 |
18 | { rz 2 rx 3 z 0.22 x 0.3 ry 10 s 0.99 x 0.5 sat 0.9995 hue 0.15 } arm
19 | xbox
20 | }
21 |
22 | rule xbox {
23 | { b 0.7 } box
24 | }
25 |
--------------------------------------------------------------------------------
/examples/frameinframe.es:
--------------------------------------------------------------------------------
1 | set background white
2 |
3 | { hue 20 sat 0.8 } r2
4 |
5 | rule r2 maxdepth 20 {
6 | { s 0.75 rz 10 b 0.9 } r2
7 | frame
8 | }
9 |
10 | rule frame {
11 | { s 0.1 1.1 0.1 x 5 z 5 } box
12 | { s 0.1 1.1 0.1 x 5 z -5 } box
13 | { s 0.1 1.1 0.1 x -5 z 5 } box
14 | { s 0.1 1.1 0.1 x -5 z -5 } box
15 |
16 | { s 1 0.1 0.1 y 5 z 5 } box
17 | { s 1 0.1 0.1 y 5 z -5 } box
18 | { s 1 0.1 0.1 y -5 z 5 } box
19 | { s 1 0.1 0.1 y -5 z -5 } box
20 |
21 | { s 0.1 0.1 1 y 5 x 5 } box
22 | { s 0.1 0.1 1 y 5 x -5 } box
23 | { s 0.1 0.1 1 y -5 x 5 } box
24 | { s 0.1 0.1 1 y -5 x -5 } box
25 | }
26 |
--------------------------------------------------------------------------------
/examples/testminmaxsize.es:
--------------------------------------------------------------------------------
1 | set minsize 0.8 // or 0.4, or 0.2
2 |
3 | set maxdepth 600
4 | set background #333
5 | { h 30 sat 0.2 h -67 b 0.8 } spiral
6 |
7 | rule spiral w 100 {
8 | box2
9 | { y 0.4 rx 90 hue 1 s 0.995 b 0.999 } spiral
10 | }
11 |
12 | rule spiral w 100 {
13 | box2
14 | { y 0.4 rx 90 hue -1 rz -90 s 0.995 b 0.999 } spiral
15 | }
16 |
17 | rule spiral w 100 {
18 | box2
19 | { y 0.4 rx 90 hue 0 rz 90 s 0.995 b 0.995 } spiral
20 | }
21 |
22 | rule spiral w 3 {
23 | { rz 5 s 1 1 1 } spiral
24 | { ry 4 h 3 s 1 1 1 } spiral
25 | }
26 |
27 | rule box2 {
28 | { s 1 5 1 } box
29 | }
30 |
31 | rule box2 {
32 | { s 5 1 1 } box
33 | }
34 |
35 | rule box2 {
36 | }
--------------------------------------------------------------------------------
/examples/menger.es:
--------------------------------------------------------------------------------
1 | R1
2 |
3 | rule R1 maxdepth 3 > c2 {
4 | { s 1/3 x -1 y -1 } R1
5 | { s 1/3 x -1 y -1 z -1 } R1
6 | { s 1/3 x -1 y -1 z +1 } R1
7 | { s 1/3 x 1 y -1 } R1
8 | { s 1/3 x 1 y -1 z -1 } R1
9 | { s 1/3 x 1 y -1 z +1 } R1
10 | { s 1/3 y -1 z -1 } R1
11 | { s 1/3 y -1 z +1 } R1
12 | { s 1/3 x -1 y 1 } R1
13 | { s 1/3 x -1 y 1 z -1 } R1
14 | { s 1/3 x -1 y 1 z +1 } R1
15 | { s 1/3 x 1 y 1 } R1
16 | { s 1/3 x 1 y 1 z -1 } R1
17 | { s 1/3 x 1 y 1 z +1 } R1
18 | { s 1/3 y 1 z -1 } R1
19 | { s 1/3 y 1 z +1 } R1
20 | { s 1/3 x -1 z -1 } R1
21 | { s 1/3 x -1 z +1 } R1
22 | { s 1/3 x 1 z -1 } R1
23 | { s 1/3 x 1 z +1 } R1
24 | }
25 |
26 | rule c2 {
27 | box
28 | }
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/examples/city.es:
--------------------------------------------------------------------------------
1 | {color white} r1
2 | {x 0 y 0 z -0.05 s 5 5 0.1 color grey}box
3 | //rule 1, fills the square
4 |
5 | rule r1 md 4{
6 | { x -0.25 y 0.25 s 0.5 0.5 0.75 rz 180} r1
7 | { x 0.25 y -0.25 s 0.5 0.5 1 rz 0} r1
8 | { x -0.25 y -0.25 s 0.5 0.5 1 rz 90} r1
9 | tower}
10 |
11 | rule r1 md 5 {
12 | { x -0.25 y 0.25 s 0.5 0.5 1 rz 0} r1
13 | { x 0.25 y -0.25 s 0.5 0.5 0.75 rz 90} r1
14 | { x -0.25 y -0.25 s 0.5 0.5 1 rz 90} r1
15 | tower}
16 |
17 | rule tower w 1{
18 | {z 0.01 rz 3 s 0.8}tower
19 | base}
20 | rule tower w 1{
21 | {z 0.01 rz 3 s 1.01}tower
22 | base}
23 | rule tower w 1{
24 | {z 0.01 rz 3 s 1.02}tower
25 | base}
26 | rule tower w 1{
27 | {z 0.01 rz 3 s 0.95}tower
28 | base}
29 | rule tower w 0.5{}
30 |
31 | rule base {
32 | {x 0.25 y 0.25 s 0.45 0.45 0.01 sat 0.3}box}
--------------------------------------------------------------------------------
/examples/mondrian.es:
--------------------------------------------------------------------------------
1 | // Mondrian Cube.
2 |
3 | mondrian
4 |
5 | rule mondrian {
6 | // The six faces of the cube
7 | a2
8 | { x -0.5 z -0.5 ry 90 } a2
9 | { x +0.5 z -0.5 ry 90 } a2
10 | { z -1 } a2
11 | { y +0.5 z -0.5 rx 90 } a2
12 | { y -0.5 z -0.5 rx 90 } a2
13 | }
14 |
15 | rule a2 w 2 maxdepth 2 > d {
16 | // Split into two halves in x direction
17 | { s 0.333 1 1 x -1 } a2
18 | { s 0.666 1 1 x 0.26 } a2
19 | }
20 |
21 | rule a2 w 2 maxdepth 2 > d {
22 | // Split into two halves in y direction
23 | { s 1 0.333 1 y -1 } a2
24 | { s 1 0.666 1 y 0.26 } a2
25 | }
26 |
27 |
28 | rule a2 { d }
29 |
30 | // This rule chooses a random color.
31 | rule d { { s 1 1 0.02 color #F00 } square }
32 | rule d { { s 1 1 0.02 color #0404A2 } square }
33 | rule d { { s 1 1 0.02 color #FFFE33 } square }
34 | rule d w 2 { { s 1 1 0.02 color #FFF } square }
35 |
36 | // Draws a framed square
37 | rule square {
38 | { s 0.95 0.95 1 } box
39 | { s 0.05 1 1 b 0 x -10 } box
40 | { s 0.05 1 1 b 0 x 10 } box
41 | { s 1.05 0.05 1 b 0 y -10 } box
42 | { s 1.05 0.05 1 b 0 y 10 } box
43 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mega-structure",
3 | "version": "1.0.0",
4 | "description": "A structure synth clone",
5 | "main": "mega-structure.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/ssrb/mega-structure.git"
12 | },
13 | "keywords": [
14 | "generative",
15 | "art"
16 | ],
17 | "author": "ssrb",
18 | "license": "BSD-2-Clause",
19 | "bugs": {
20 | "url": "https://github.com/ssrb/mega-structure/issues"
21 | },
22 | "homepage": "https://github.com/ssrb/mega-structure#readme",
23 | "dependencies": {
24 | "@types/browserify": "^12.0.33",
25 | "@types/codemirror": "0.0.56",
26 | "@types/gl-matrix": "^2.4.0",
27 | "@types/jquery": "^3.3.1",
28 | "@types/node": "^8.10.1",
29 | "@types/pegjs": "^0.10.0",
30 | "@types/seedrandom": "^2.4.27",
31 | "@types/three": "^0.93.0",
32 | "@types/tinycolor2": "^1.4.0",
33 | "bootstrap": "^4.1.0",
34 | "brfs": "^1.4.3",
35 | "browserify": "^16.1.1",
36 | "codemirror": "^5.36.0",
37 | "gl-matrix": "^2.4.0",
38 | "gulp": "^3.9.1",
39 | "gulp-peg": "^0.2.0",
40 | "gulp-util": "^3.0.7",
41 | "gulp-file": "^0.4.0",
42 | "jquery": "^3.3.1",
43 | "npm": "^5.8.0",
44 | "run-sequence": "^1.1.5",
45 | "seedrandom": "^2.4.3",
46 | "three": "^0.93.0",
47 | "three-orbit-controls": "^82.1.0",
48 | "tinycolor2": "^1.4.1",
49 | "tsify": "^3.0.4",
50 | "typescript": "^2.8.1",
51 | "typescript-collections": "^1.1.2",
52 | "uglifyify": "^3.0.1",
53 | "vinyl-source-stream": "^1.1.0",
54 | "watchify": "^3.7.0"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/structure.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016, Sebastien Sydney Robert Bigot
2 | // All rights reserved.
3 | //
4 | // Redistribution and use in source and binary forms, with or without
5 | // modification, are permitted provided that the following conditions are met:
6 | //
7 | // 1. Redistributions of source code must retain the above copyright notice, this
8 | // list of conditions and the following disclaimer.
9 | // 2. Redistributions in binary form must reproduce the above copyright notice,
10 | // this list of conditions and the following disclaimer in the documentation
11 | // and/or other materials provided with the distribution.
12 | //
13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 | //
24 | // The views and conclusions contained in the software and documentation are those
25 | // of the authors and should not be interpreted as representing official policies,
26 | // either expressed or implied, of the FreeBSD Project.
27 | import * as tinycolor from 'tinycolor2';
28 |
29 | export interface ShapeInstance {
30 | shape: string;
31 | geospace: Float32Array;
32 | colorspace: tinycolor.ColorFormats.HSVA;
33 | }
--------------------------------------------------------------------------------
/examples/PanelStruct.es:
--------------------------------------------------------------------------------
1 | set background #444
2 |
3 | set raytracer::light [0,5000,5000]
4 | set raytracer::samples 6
5 | set raytracer::ambient-occlusion-samples 2
6 | set raytracer::phong [0.6,0.6,0.9]
7 |
8 | { color white z 4.5 } Core
9 |
10 | rule Core
11 | {
12 | { y 4.5 } PanelStruct
13 | }
14 |
15 | // Panelstruct
16 |
17 | rule PanelStruct w 1 md 4
18 | {
19 | { ry 90 } Panel
20 | { y 2.0 } PanelStruct
21 | }
22 |
23 | rule PanelStruct2 w 0
24 | {
25 | {} Panel
26 | }
27 |
28 | rule Panel md 5
29 | {
30 | {}PanelPart
31 | { ry -60 x 0.325 z 0.6 } Panel
32 | }
33 |
34 | rule Panel2 md 5 w 50
35 | {
36 | {}PanelPart
37 | { ry 60 x 0.325 z 0.6 } Panel
38 | }
39 |
40 | rule Panel2 md 1 w 0
41 | {
42 | }
43 |
44 | rule PanelPart w 4
45 | {
46 | { rz 90 s 0.2 0.05 0.1 } beamAssembly
47 | { s 0.05 2.0 0.8 } box
48 | }
49 |
50 | rule PanelPart w 2
51 | {
52 | { b 0.5 rz 90 s 0.2 0.05 0.1 } beamAssembly
53 | { b 0.5 s 0.05 2.0 0.8 } box
54 | }
55 |
56 | // Beam
57 |
58 | rule beamAssembly w 4
59 | {
60 | { z -5 } beam
61 | { z 5 } beam
62 |
63 | { s 1 0.2 11 y 12 } box
64 | { s 1 0.2 11 y -12 } box
65 |
66 | { z 0.4 } vertPanel
67 | { z 5 } vertPanel
68 | { z -5 } vertPanel
69 | }
70 |
71 | rule beamAssembly w 2
72 | {
73 | }
74 |
75 | rule vertPanel
76 | {
77 | {s 1 4 0.2} box
78 | {s 1 1 0.5} box
79 | }
80 |
81 | rule vertPanel md 15 > end
82 | {
83 | widePane
84 | { y 1 } vertPanel
85 | }
86 |
87 | rule end
88 | {
89 | }
90 |
91 | rule vertPanel
92 | {
93 | {s 1 1 0.5 } box
94 | {s 0.2 4 0.2 x 2 } box
95 | {s 0.2 4 0.2 x -2 } box
96 | {s 1 0.2 0.2 y 10 } box
97 | {s 1 0.2 0.2 y -10 } box
98 | }
99 |
100 | rule beam
101 | {
102 | { s 0.2 5 0.2 } box
103 | }
104 |
105 | rule widePane
106 | {
107 | thinBeam1
108 | { y 5 } thinBeam1
109 | { x 4.9 y 2.5 } thinBeamVert
110 | { x -4.9 y 2.5 } thinBeamVert
111 | pane
112 | }
113 |
114 | rule pane
115 | {
116 | {s 10 5 0.05 y 0.5} box
117 | }
118 |
119 | rule pane
120 | {
121 | { s 10 2.5 0.05 y 0.5 } box
122 | }
123 |
124 | rule thinBeam1
125 | {
126 | { s 10 0.2 0.2 } box
127 | }
128 |
129 | rule thinBeamVert
130 | {
131 | { s 0.2 5 0.2 } box
132 | }
--------------------------------------------------------------------------------
/examples/Konstrukt.es:
--------------------------------------------------------------------------------
1 | set background #000
2 | #define _md 34
3 | #define _rz 0
4 | #define _zoom 1
5 |
6 | set maxdepth _md
7 |
8 | set background #222
9 |
10 | set raytracer::light [0,5000,5000]
11 | set raytracer::samples 8
12 | set raytracer::ambient-occlusion-samples 2
13 | set raytracer::phong [0.5,0.5,0.125]
14 |
15 | { rz _rz s _zoom } r0
16 |
17 | rule r0 {
18 | 3 * { rz 120 } R1
19 | 3 * { rz 120 } R2
20 | }
21 |
22 | rule R1 {
23 | { x -0.8 rz 6 ry 12 s 0.97 } R1
24 | { color white ry 90 s 1 } Panel
25 | }
26 |
27 | rule R2 {
28 | set seed initial
29 | { x -1.0 rz 6 ry 12 s 0.98 } R2
30 | { color white ry 90 s 0.75 } Panel
31 | }
32 |
33 | rule Panel md 2 > Panel2
34 | {
35 | {}PanelPart
36 | { y 1.0 rz -60 y 1.0 } Panel
37 | }
38 |
39 | rule Panel2 md 4 w 50
40 | {
41 | PanelPart
42 | { y 1.0 rz -60 y 1.0 } Panel2
43 | }
44 |
45 | rule Panel2 w 2
46 | {
47 | }
48 |
49 | rule PanelPart w 4
50 | {
51 | { rz 90 s 0.2 0.05 0.1 } beamAssembly
52 | { s 0.05 2.0 0.8 } box
53 | }
54 |
55 | rule PanelPart w 2
56 | {
57 | { b 0.5 rz 90 s 0.2 0.05 0.1 } beamAssembly
58 | { b 0.5 s 0.05 2.0 0.8 } box
59 | }
60 |
61 | // Beam
62 |
63 | rule beamAssembly w 4
64 | {
65 | { z -5 } beam
66 | { z 5 } beam
67 |
68 | { s 1 0.2 11 y 12 } box
69 | { s 1 0.2 11 y -12 } box
70 |
71 | { z 0.4 } vertPanel
72 | { z 5 } vertPanel
73 | { z -5 } vertPanel
74 | }
75 |
76 | rule beamAssembly w 2
77 | {
78 | }
79 |
80 | rule vertPanel
81 | {
82 | {s 1 4 0.2} box
83 | {s 1 1 0.5} box
84 | }
85 |
86 | rule vertPanel md 15 > end
87 | {
88 | widePane
89 | { y 1 } vertPanel
90 | }
91 |
92 | rule end
93 | {
94 | }
95 |
96 | rule vertPanel
97 | {
98 | {s 1 1 0.5 } box
99 | {s 0.2 4 0.2 x 2 } box
100 | {s 0.2 4 0.2 x -2 } box
101 | {s 1 0.2 0.2 y 10 } box
102 | {s 1 0.2 0.2 y -10 } box
103 | }
104 |
105 | rule beam
106 | {
107 | { s 0.2 5 0.2 } box
108 | }
109 |
110 | rule widePane
111 | {
112 | thinBeam1
113 | { y 5 } thinBeam1
114 | { x 4.9 y 2.5 } thinBeamVert
115 | { x -4.9 y 2.5 } thinBeamVert
116 | pane
117 | }
118 |
119 | rule pane
120 | {
121 | {s 10 5 0.05 y 0.5} box
122 | }
123 |
124 | rule pane
125 | {
126 | { s 10 2.5 0.05 y 0.5 } box
127 | }
128 |
129 | rule thinBeam1
130 | {
131 | { s 10 0.2 0.2 } box
132 | }
133 |
134 | rule thinBeamVert
135 | {
136 | { s 0.2 5 0.2 } box
137 | }
--------------------------------------------------------------------------------
/examples/HexStruct.es:
--------------------------------------------------------------------------------
1 | set background #444
2 |
3 | set raytracer::light [0,5000,5000]
4 | set raytracer::samples 8
5 | set raytracer::ambient-occlusion-samples 2
6 | set raytracer::phong [0.6,0.6,0.9]
7 |
8 | { color white } Core
9 |
10 | rule Core
11 | {
12 | SphereStuct
13 | }
14 |
15 | rule SphereStuct w 40 md 15
16 | {
17 | ubox
18 | dbox
19 | { x 2 y 3.25 ry 12 } SphereStuct
20 | }
21 |
22 | rule SphereStuct w 14 { r2 }
23 |
24 | rule r2 w 10
25 | {
26 | {} r2
27 | }
28 |
29 | rule r2 { SphereStuct }
30 |
31 | rule dbox w 8 maxdepth 3
32 | {
33 | { x 0 y -6.5 rx 3.6 } dbox
34 | { ry 6 rx -1.8 } Panel
35 | }
36 | rule dbox w 8 maxdepth 2
37 | {
38 | { x 0 y -6.5 rx 3.6 } dbox
39 | { b 0.5 ry 6 rx -1.8 } Panel
40 | }
41 | rule dbox { }
42 |
43 | rule ubox w 8 maxdepth 3
44 | {
45 | { x 0 y 6.5 rx -3.6 } ubox
46 | { ry 6 rx 1.8 } Panel
47 | }
48 | rule ubox w 8 maxdepth 2
49 | {
50 | { x 0 y 6.5 rx -3.6 } ubox
51 | { b 0.5 ry 6 rx 1.8 } Panel
52 | }
53 | rule ubox { }
54 |
55 | rule Panel md 1 w 1
56 | {
57 | { y 1.0 rz -60 y 1.0 } Panel
58 | }
59 |
60 | rule Panel md 6 w 16
61 | {
62 | PanelPart
63 | { y 1.0 rz -60 y 1.0 } Panel
64 | }
65 | rule PanelPart
66 | {
67 | { rz 90 s 0.2 0.2 0.1 } beamAssembly
68 | { s 0.1 1.75 1 } box
69 | }
70 |
71 | // Beam
72 |
73 | rule beamAssembly w 1
74 | {
75 | { z -5 } beam
76 | { z 5 } beam
77 |
78 | { s 1 0.2 11 y 12 } box
79 | { s 1 0.2 11 y -12 } box
80 |
81 | { z 0.4 } vertPanel
82 | { z 5 } vertPanel
83 | { z -5 } vertPanel
84 | }
85 |
86 | rule beamAssembly w 6
87 | {
88 | }
89 |
90 | rule vertPanel
91 | {
92 | {s 1 4 0.2} box
93 | {s 1 1 0.5} box
94 | }
95 |
96 | rule vertPanel md 15 > end
97 | {
98 | widePane
99 | { y 1 } vertPanel
100 | }
101 |
102 | rule end
103 | {
104 | }
105 |
106 | rule vertPanel
107 | {
108 | {s 1 1 0.5 } box
109 | {s 0.2 4 0.2 x 2 } box
110 | {s 0.2 4 0.2 x -2 } box
111 | {s 1 0.2 0.2 y 10 } box
112 | {s 1 0.2 0.2 y -10 } box
113 | }
114 |
115 | rule beam
116 | {
117 | { s 0.2 5 0.2 } box
118 | }
119 |
120 | rule widePane
121 | {
122 | thinBeam1
123 | { y 5 } thinBeam1
124 | { x 4.9 y 2.5 } thinBeamVert
125 | { x -4.9 y 2.5 } thinBeamVert
126 | pane
127 | }
128 |
129 | rule pane
130 | {
131 | {s 10 5 0.05 y 0.5} box
132 | }
133 |
134 | rule pane
135 | {
136 | { s 10 2.5 0.05 y 0.5 } box
137 | }
138 |
139 | rule thinBeam1
140 | {
141 | { s 10 0.2 0.2 } box
142 | }
143 |
144 | rule thinBeamVert
145 | {
146 | { s 0.2 5 0.2 } box
147 | }
--------------------------------------------------------------------------------
/examples/Schmiegel.es:
--------------------------------------------------------------------------------
1 | set background white
2 | //{ s 50 0.1 50 y -6 color white } box
3 |
4 | { b 0.99 sat 0.1 hue 0} r1
5 |
6 |
7 | rule r1 maxdepth 4 > void {
8 | { s 1/3 x -0.95 y -0.95 } r2
9 | { s 1/3 x -0.95 y -0.95 z -0.95 } r2
10 | { s 1/3 x -0.95 y -0.95 z +0.95 } r2
11 | { s 1/3 x 0.95 y -0.95 } r2
12 | { s 1/3 x 0.95 y -0.95 z -0.95 } r2
13 | { s 1/3 x 0.95 y -0.95 z +0.95 } r2
14 | { s 1/3 y -0.95 z -0.95 } r2
15 | { s 1/3 y -0.95 z +0.95 } r2
16 | { s 1/3 x -0.95 y 0.95 } r2
17 | { s 1/3 x -0.95 y 0.95 z -0.95 } r2
18 | { s 1/3 x -0.95 y 0.95 z +0.95 } r2
19 | { s 1/3 x 0.95 y 0.95 } r2
20 | { s 1/3 x 0.95 y 0.95 z -0.95 } r2
21 | { s 1/3 x 0.95 y 0.95 z +0.95 } r2
22 | { s 1/3 y 0.95 z -0.95 } r2
23 | { s 1/3 y 0.95 z +0.95 } r2
24 | { s 1/3 x -0.95 z -0.95 } r2
25 | { s 1/3 x -0.95 z +0.95 } r2
26 | { s 1/3 x 0.95 z -0.95 } r2
27 | { s 1/3 x 0.95 z +0.95 } r2
28 | }
29 |
30 | rule void {
31 | }
32 |
33 | rule r2 {
34 | { hue 37 s 0.9 } frameR
35 | }
36 | rule r2 {
37 | { hue 8 s 0.9 } frameR
38 | }
39 |
40 |
41 |
42 |
43 | //Make Whacky
44 | rule frameR {
45 | { rx 1 ry 2 rz 3} frame
46 | }
47 | rule frameR {
48 | { rx 2 ry 3 rz 1} frame
49 | }
50 | rule frameR {
51 | { rx 3 ry 1 rz 2} frame
52 | }
53 |
54 | rule frameR {
55 | { rx -1 ry -2 rz -3} frame
56 | }
57 | rule frameR {
58 | { rx -2 ry -3 rz -1} frame
59 | }
60 | rule frameR {
61 | { rx -3 ry -1 rz -2} frame
62 | }
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | rule r2 {
71 | { hue 19 } r1
72 | }
73 |
74 |
75 | rule r2 {
76 | { hue 20 } r1
77 | }
78 |
79 |
80 | rule frame {
81 | { s 0.05 1.05 0.05 x 10 z 10 } box
82 | { s 0.05 1.05 0.05 x 10 z -10 } box
83 | { s 0.05 1.05 0.05 x -10 z 10 } box
84 | { s 0.05 1.05 0.05 x -10 z -10 } box
85 |
86 | { s 1 0.05 0.05 y 10 z 10 } box
87 | { s 1 0.05 0.05 y 10 z -10 } box
88 | { s 1 0.05 0.05 y -10 z 10 } box
89 | { s 1 0.05 0.05 y -10 z -10 } box
90 |
91 | { s 0.05 0.05 1 y 10 x 10 } box
92 | { s 0.05 0.05 1 y 10 x -10 } box
93 | { s 0.05 0.05 1 y -10 x 10 } box
94 | { s 0.05 0.05 1 y -10 x -10 } box
95 |
96 | { s 1.04 h 180 b 0.9} sphere
97 | }
98 |
99 | rule frame {
100 | { s 0.05 1.05 0.05 x 10 z 10 } box
101 | { s 0.05 1.05 0.05 x 10 z -10 } box
102 | { s 0.05 1.05 0.05 x -10 z 10 } box
103 | { s 0.05 1.05 0.05 x -10 z -10 } box
104 |
105 | { s 1 0.05 0.05 y 10 z 10 } box
106 | { s 1 0.05 0.05 y 10 z -10 } box
107 | { s 1 0.05 0.05 y -10 z 10 } box
108 | { s 1 0.05 0.05 y -10 z -10 } box
109 |
110 | { s 0.05 0.05 1 y 10 x 10 } box
111 | { s 0.05 0.05 1 y 10 x -10 } box
112 | { s 0.05 0.05 1 y -10 x 10 } box
113 | { s 0.05 0.05 1 y -10 x -10 } box
114 |
115 | { s 1.04 h 240 b 0.9} sphere
116 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mega-structure
2 |
3 | [](https://travis-ci.org/ssrb/mega-structure)
4 |
5 | mega-structure is a structure synthesizer for the web based on [Mikael Hvidtfeldt](http://hvidtfeldts.net)'s eisenscript language.
6 |
7 | 
8 |
9 | 
10 |
11 | 
12 |
13 | The [Structure Synth](http://structuresynth.sourceforge.net) application provides the reference implementation for the language.
14 |
15 | A similar project is called [Eisenscript](https://github.com/after12am/eisenscript).
16 |
17 | ### Eisenscript syntax
18 |
19 | #### Termination criteria
20 |
21 | * **set maxdepth [integer]**: break a generation path as soon as a it's [integer] long;
22 | * **set maxobjects [integer]**: stop as soon as [integer] objects have been created;
23 | * **set minsize/maxsize [float]**: break a generation path when the local coordinate frame diagonal is below/above [float] units;
24 | * **set seed [integer]**: seed the PRNG used to choose between rules;
25 | * **set background [color]**: self-explanatory
26 |
27 | #### Rule modifiers
28 |
29 | * **md / maxdepth [integer]**: break a generation path if it includes [integer] call to that rule;
30 | * **md / maxdepth [integer] > [rulename]**: same as above, failover rule [rulename] once the limit is reached;
31 | * **w / weight [float]**: how likely the rule is going to be selected in case multiple rules have the same name. Default weight is 1.
32 |
33 | #### Transformations
34 |
35 | ##### Geometric transformations
36 |
37 | * **x [float]**: [float] units translation along the X axis;
38 | * **y [float]**: as above;
39 | * **z [float]**: as above;
40 | * **rx [float]**: [float] degrees rotation about an axis colinear to X going through local coordinate (0, 0.5, 0.5);
41 | * **ry [float]**: as above;
42 | * **rz [float]**: as above;
43 | * **s [float]**: uniformely scale by [float] along the 3 axis;
44 | * **s [f1] [f2] [f3]**: scale along the X axis by [f1], along Y by [f2], along Z by [f3];
45 | * **m [f1] ... [f9]**: 3x3 generic matrix transformation;
46 | * **fx**: X axis mirror (flip sign of the frame x coordinates);
47 | * **fy**: as above;
48 | * **fz**: as above;
49 |
50 | ##### Color space transformations
51 |
52 | * **h / hue [float]**: add [float] degrees to the current color hue. Result is [0-360] normalized;
53 | * **sat [float]**: multiply the current color saturation by [float]. Result is [0-1] clamped;
54 | * **b / brightness [float]**: multiply the current color brightness (HSV value) by [float]. Result is [0-1] clamped;
55 | * **a / alpha [float]**: multiply the current color alpha by [float]. Result is [0-1] clamped;
56 | * **color [color]**: set the current color to [color]
57 |
58 |
--------------------------------------------------------------------------------
/mega-structure.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | mega-structure
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
73 |
74 |
78 |
79 |
80 |
81 |
82 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var source = require('vinyl-source-stream');
3 | var browserify = require('browserify');
4 | var watchify = require('watchify');
5 | var tsify = require('tsify');
6 | var uglify = require('uglifyify');
7 | var runSequence = require('run-sequence');
8 | var peg = require('gulp-peg');
9 | var gutil = require('gulp-util');
10 | var newfile = require('gulp-file');
11 |
12 | gulp.task('.npm.clean', function (cb) {
13 | var del = require('del');
14 | del(['node_modules/'], cb);
15 | });
16 |
17 | gulp.task('watch', function () {
18 | var bundler = watchify(browserify({ debug: true })
19 | .add('synthesizer.ts')
20 | .plugin(tsify)
21 | .transform(browserifyShader));
22 |
23 | bundler.on('update', rebundle)
24 |
25 | function rebundle() {
26 | return bundler.bundle()
27 | .pipe(source('bundle.js'))
28 | .pipe(gulp.dest('.'))
29 | }
30 |
31 | return rebundle();
32 | });
33 |
34 | gulp.task('.peg', function () {
35 | return gulp.src("eisen-script.peg")
36 | .pipe(peg().on("error", gutil.log))
37 | .pipe(gulp.dest('.'))
38 | });
39 |
40 | gulp.task('.examples', function (cb) {
41 | var exec = require('child_process').exec;
42 | exec('bash ./pack-examples.sh > examples-generated.ts', function (err, stdout, stderr) {
43 | console.log(stdout);
44 | console.log(stderr);
45 | cb(err);
46 | });
47 | });
48 |
49 | gulp.task('.ui', function () {
50 | var bundler = browserify({ debug: true })
51 | .add('./mega-structure.ts')
52 | .plugin(tsify, { target: 'es5' })
53 | .transform('brfs')
54 |
55 | return bundler.bundle()
56 | .pipe(source('bundle.js'))
57 | .pipe(gulp.dest('.'));
58 | });
59 |
60 | gulp.task('.synth', function () {
61 | var bundler = browserify({ debug: true })
62 | .add('./synthesizer-webworker.ts')
63 | .plugin(tsify, { target: 'es5' })
64 | return bundler.bundle()
65 | .pipe(source('synthesizer-webworker.js'))
66 | .pipe(gulp.dest('.'));
67 | });
68 |
69 | gulp.task('.ui.release', function () {
70 | var bundler = browserify()
71 | .add('./mega-structure.ts')
72 | .plugin(tsify, { target: 'es5' })
73 | .transform('brfs')
74 | .transform(uglify);
75 |
76 | return bundler.bundle()
77 | .pipe(source('bundle.js'))
78 | .pipe(gulp.dest('.'));
79 | });
80 |
81 | gulp.task('.synth.release', function () {
82 | var bundler = browserify()
83 | .add('./synthesizer-webworker.ts')
84 | .plugin(tsify, { target: 'es5' })
85 | .transform(uglify);
86 |
87 | return bundler.bundle()
88 | .pipe(source('synthesizer-webworker.js'))
89 | .pipe(gulp.dest('.'));
90 | });
91 |
92 | gulp.task('install', function () {
93 | gulp.src([
94 | "mega-structure.html",
95 | "bundle.js",
96 | "synthesizer-webworker.js",
97 | "codemirror-eisen-script-mode.js",
98 | "node_modules/jquery/dist/**",
99 | "node_modules/bootstrap/dist/**",
100 | "node_modules/codemirror/**"
101 | ], {base: "."}).pipe(gulp.dest('_site'));
102 |
103 | newfile('.nojekyll', '').pipe(gulp.dest('_site'));
104 | });
105 |
106 | gulp.task('default', function (callback) {
107 | runSequence('.peg',
108 | '.examples',
109 | '.ui.release',
110 | '.synth.release',
111 | callback);
112 | });
113 |
--------------------------------------------------------------------------------
/examples/Noct.es:
--------------------------------------------------------------------------------
1 | set background #222
2 |
3 | set raytracer::light [0,5000,5000]
4 | set raytracer::samples 8
5 | set raytracer::ambient-occlusion-samples 2
6 | set raytracer::phong [0.5,0.5,0.125]
7 |
8 | { color white } Core
9 |
10 | rule Core
11 | {
12 | { y -1.5 } OuterRingStruct
13 | //{ s 1.1 0.5 1.1 x -12 y -1.25 } Stair
14 | }
15 |
16 | // Stairs
17 |
18 | rule Stair
19 | {
20 | 700 * { ry 0.95 y 0.1 z 0.2 } Step
21 | }
22 |
23 | rule Step
24 | {
25 | { s 1 0.025 0.1 } box
26 | { b 0.25 s 1.5 0.01 0.01 } box
27 | { b 0.25 ry 0.95 rx -29.4 x 0.75 s 0.1 0.025 0.25 } box
28 | { b 0.25 ry 0.95 rx -23.4 x -0.75 s 0.1 0.025 0.25 } box
29 | }
30 |
31 | // OuterRing
32 |
33 | rule OuterRingStruct md 4
34 | {
35 | { x 1 z 10 } OuterRingSup1
36 | { ry 11.25 y 2 } OuterRingStruct
37 | }
38 |
39 | rule OuterRingSup1 md 10 > OuterRingSup2
40 | {
41 | { ry 5.625 }OuterRingPart
42 | { ry 11.25 x 2 } OuterRingSup1
43 | }
44 |
45 | rule OuterRingSup2 w 15 md 22
46 | {
47 | { ry 5.625 }OuterRingPart
48 | { ry 11.25 x 2 } OuterRingSup2
49 | }
50 |
51 | rule OuterRingSup2 w 5
52 | {
53 | }
54 |
55 | rule OuterRingPart
56 | {
57 | { b 0.25 y -1.5 rx -90 } Panel
58 | { b 0.4 a 0.5 y 1.0 s 2.0 1 0.1 } box
59 | { b 0.4 a 0.5 z 2 y 1.0 s 2.4 1 0.1 } box
60 | { } Ringsub
61 | }
62 |
63 | // Ring
64 |
65 | rule Ringsub w 1
66 | {
67 | //{ ry 90 x -0.25 s 2 1.25 2.5 } thinPanelSquare1
68 | //{ ry 90 x 0.05 s 2 1.25 2.5 } thinPanelSquare1
69 | //{ b 0.25 ry 180 rz 90 s 0.5 0.5 0.4 } beamAssembly
70 | // { b 0.25 rz 90 s 1 2 0.75 z 0.5 y -0.5 }RodBox
71 | { s 1.25 } rodrow
72 | }
73 |
74 | rule Ringsub w 300 md 5
75 | {
76 | //{ ry 90 x -0.25 s 2 1.25 2.5 } thinPanelSquare1
77 | //{ ry 90 x 0.05 s 2 1.25 2.5 } thinPanelSquare1
78 | { s 1.25 } rodrow
79 | { z 0.5 } Ringsub
80 | }
81 |
82 | rule Ringsub w 10
83 | {
84 | { s 1.25 } rodrow
85 | }
86 |
87 | rule Panel
88 | {
89 | 2 * { z 2 } PanelSub
90 | //{ b 0.5 a 0.5 z 3 s 0.5 0.1 1.75 } box
91 | { y -1.9 z 3 s 0.05 0.05 1.925 } box
92 | }
93 |
94 | rule PanelSub
95 | {
96 | { s 2.2 0.25 0.125 } box
97 | { y -1 s 0.05 1.75 0.05 } box
98 | { y -1.925 s 2.4 0.1 0.05 } box
99 |
100 | //{ a 0.5 b 0.25 y 1.75 s 0.75 3 0.05 } box
101 | }
102 |
103 | // Boxform
104 |
105 | rule boxForm1 w 15
106 | {
107 | boxForm2
108 | }
109 |
110 | rule boxForm1 w 1
111 | {
112 | }
113 |
114 | rule boxForm2
115 | {
116 | {} RodBox
117 | { x 0.5521 z -0.5 s 1.2 }thinPanelSquare1
118 | { x -0.5524 z -0.5 s 1.2 }thinPanelSquare1
119 | { ry 90 s 1.2 }thinPanelSquare1
120 | { ry 90 x 1 s 1.2 }thinPanelSquare1
121 | { rz 90 z -0.5 s 1.2 }thinPanelSquare1
122 | { rz 90 z -0.5 x 1 s 1.2 }thinPanelSquare1
123 | }
124 |
125 | rule thinPanelSquare1 w 6
126 | {
127 | thinPanelSquare2
128 | }
129 |
130 | rule thinPanelSquare1 w 4
131 | {
132 | }
133 |
134 | rule thinPanelSquare2
135 | {
136 | //{ ry 90 rz 90 y -0.75 x 0.5 } rodrow
137 | { y 0.5 s 0.075 5 0.75 } box
138 | }
139 |
140 | rule thinPanelSquare2 w 75
141 | {
142 | }
143 |
144 | rule rodrow
145 | {
146 | { s 2.1 0.25 0.25 y 2 } box
147 | }
148 |
149 | rule RodBox
150 | {
151 | RodBoxP1
152 | { z -1 rx 90 } RodBoxP1
153 | }
154 |
155 | rule RodBoxP1
156 | {
157 | RodBoxSide
158 | { x 0.5 z -0.5 ry 90 } RodBoxSide
159 | { x -0.5 z -0.5 ry -90 } RodBoxSide
160 | { z -1 ry 180 }RodBoxSide
161 | }
162 |
163 | rule RodBoxSide
164 | {
165 | {} Rod
166 | { y 1 } Rod
167 | }
168 |
169 | rule Rod w 4
170 | {
171 | { s 0.965 0.2054 0.116 } box
172 | Rod2
173 | }
174 |
175 | rule Rod w 1
176 | {
177 | }
178 |
179 | rule Rod2 w 1
180 | {
181 | { s 1.435 0.02 0.02 } box
182 | }
183 |
184 | rule Rod2 w 1
185 | {
186 | { s 1.763 0.02 0.02 } box
187 | }
188 |
189 | // Beam
190 |
191 | rule beamAssembly
192 | {
193 | { z -5 } beam
194 | { z 5 } beam
195 |
196 | { s 1 0.2 11 y 12 } box
197 | { s 1 0.2 11 y -12 } box
198 |
199 | { z 5 } vertPanel
200 | { z -5 } vertPanel
201 | }
202 |
203 | rule vertPanel
204 | {
205 | { s 1 4 0.2 } box
206 | { s 1 1 0.5 } box
207 | }
208 |
209 | rule vertPanel md 1 w 7
210 | {
211 | widePane
212 | { y 1 } vertPanel
213 | }
214 |
215 | rule vertPanel w 1
216 | {
217 | {s 1 1 0.5 } box
218 | {s 0.2 4 0.2 x 2 } box
219 | {s 0.2 4 0.2 x -2 } box
220 | {s 1 0.2 0.2 y 10 } box
221 | {s 1 0.2 0.2 y -10 } box
222 | }
223 |
224 | rule beam
225 | {
226 | { s 0.2 5 0.2 } box
227 | }
228 |
229 | rule widePane
230 | {
231 | thinBeam1
232 | { y 5 } thinBeam1
233 | { x 4.9 y 2.5 } thinBeamVert
234 | { x -4.9 y 2.5 } thinBeamVert
235 | pane
236 | }
237 |
238 | rule pane
239 | {
240 | {s 10 5 0.05 y 0.5} box
241 | }
242 |
243 | rule pane
244 | {
245 | { s 10 2.5 0.05 y 0.5 } box
246 | }
247 |
248 | rule thinBeam1
249 | {
250 | { s 10 0.2 0.2 } box
251 | }
252 |
253 | rule thinBeamVert
254 | {
255 | { s 0.2 5 0.2 } box
256 | }
--------------------------------------------------------------------------------
/progress.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016, Sebastien Sydney Robert Bigot
2 | // All rights reserved.
3 | //
4 | // Redistribution and use in source and binary forms, with or without
5 | // modification, are permitted provided that the following conditions are met:
6 | //
7 | // 1. Redistributions of source code must retain the above copyright notice, this
8 | // list of conditions and the following disclaimer.
9 | // 2. Redistributions in binary form must reproduce the above copyright notice,
10 | // this list of conditions and the following disclaimer in the documentation
11 | // and/or other materials provided with the distribution.
12 | //
13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 | //
24 | // The views and conclusions contained in the software and documentation are those
25 | // of the authors and should not be interpreted as representing official policies,
26 | // either expressed or implied, of the FreeBSD Project.
27 |
28 | import * as THREE from 'three';
29 |
30 | declare function postMessage(message: any)
31 |
32 | export class Progress extends THREE.Mesh {
33 |
34 | public constructor() {
35 |
36 | var canvas = document.createElement('canvas');
37 | canvas.width = canvas.height = 1024;
38 | var texture = new THREE.Texture(canvas);
39 |
40 | super(new THREE.PlaneBufferGeometry(1, 1), new THREE.MeshBasicMaterial({
41 | map: texture,
42 | depthWrite: false,
43 | depthTest: false,
44 | transparent: true
45 | }));
46 |
47 | this.texture = texture;
48 | this.texture.needsUpdate = true;
49 |
50 | this.canvas = canvas;
51 |
52 | this.rotateX(Math.PI);
53 | this.visible = false;
54 |
55 | this.nshapes = 0;
56 | this.nprocessed = 0;
57 |
58 | this.completeTick = -1;
59 | }
60 |
61 | public init() {
62 | this.visible = true;
63 | this.nshapes = 0;
64 | this.nprocessed = 0;
65 | this.completeTick = -1;
66 | }
67 |
68 | public update(msg: any) {
69 | this.nshapes = msg.nshapes;
70 | this.nprocessed = msg.nprocessed;
71 | }
72 |
73 | public setPixelSize(size: number) {
74 | this.canvas.width = this.canvas.height = Math.pow(2, Math.ceil(Math.log(size) / Math.log(2)));
75 | }
76 |
77 | public animate(tick: number) {
78 |
79 | var context = this.canvas.getContext('2d');
80 | context.globalAlpha = 1;
81 |
82 | var fadeoutStart = 500;
83 | var fadeoutDuration = 1000;
84 |
85 | if (this.nshapes > 0 && this.nprocessed == this.nshapes) {
86 | if (this.completeTick == -1) {
87 | this.completeTick = tick;
88 | } else if (tick - this.completeTick < fadeoutStart) {
89 | } else if (tick - this.completeTick < fadeoutStart + fadeoutDuration) {
90 | context.globalAlpha = 1 - (tick - this.completeTick - fadeoutStart) / fadeoutDuration;
91 | } else {
92 | this.visible = false;
93 | }
94 | }
95 |
96 | context.clearRect(0, 0, this.canvas.width, this.canvas.height);
97 |
98 | var progressX = this.canvas.width / 2;
99 | var progressY = this.canvas.height / 2;
100 | var halfProgressWidth = this.canvas.width / 8;
101 | var halfProgressHeight = halfProgressWidth / 10;
102 |
103 | var textX = progressX;
104 | var textY = progressY - 3 * halfProgressHeight;
105 | var fontSize = 4 * halfProgressHeight + "px serif";
106 |
107 | context.font = fontSize;
108 | context.textAlign = "center";
109 | context.fillStyle = 'white';
110 | context.strokeStyle = 'black';
111 | context.fillText(this.nshapes + " shapes", textX, textY);
112 | context.strokeText(this.nshapes + " shapes", textX, textY);
113 |
114 | context.fillStyle = 'white';
115 | context.strokeStyle = 'white';
116 |
117 | context.beginPath();
118 | context.moveTo(progressX - halfProgressWidth, progressY - halfProgressHeight);
119 | context.lineTo(progressX - halfProgressWidth, progressY + halfProgressHeight);
120 | context.lineTo(progressX + halfProgressWidth, progressY + halfProgressHeight);
121 | context.lineTo(progressX + halfProgressWidth, progressY - halfProgressHeight);
122 | context.lineTo(progressX - halfProgressWidth, progressY - halfProgressHeight);
123 | context.closePath();
124 |
125 | context.stroke();
126 |
127 | var proccessedRatio = this.nprocessed / this.nshapes;
128 |
129 | context.beginPath();
130 | context.moveTo(progressX - halfProgressWidth, progressY - halfProgressHeight);
131 | context.lineTo(progressX - halfProgressWidth, progressY + halfProgressHeight);
132 | context.lineTo(progressX - halfProgressWidth * (1 - 2 * proccessedRatio), progressY + halfProgressHeight);
133 | context.lineTo(progressX - halfProgressWidth * (1 - 2 * proccessedRatio), progressY - halfProgressHeight);
134 | context.lineTo(progressX - halfProgressWidth, progressY - halfProgressHeight);
135 | context.closePath();
136 |
137 | context.fill();
138 |
139 | this.texture.needsUpdate = true;
140 | }
141 |
142 | canvas: HTMLCanvasElement;
143 | texture: THREE.Texture;
144 | nshapes: number;
145 | nprocessed: number;
146 | completeTick: number;
147 | };
148 |
--------------------------------------------------------------------------------
/synthesizer-webworker.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016, Sebastien Sydney Robert Bigot
2 | // All rights reserved.
3 | //
4 | // Redistribution and use in source and binary forms, with or without
5 | // modification, are permitted provided that the following conditions are met:
6 | //
7 | // 1. Redistributions of source code must retain the above copyright notice, this
8 | // list of conditions and the following disclaimer.
9 | // 2. Redistributions in binary form must reproduce the above copyright notice,
10 | // this list of conditions and the following disclaimer in the documentation
11 | // and/or other materials provided with the distribution.
12 | //
13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 | //
24 | // The views and conclusions contained in the software and documentation are those
25 | // of the authors and should not be interpreted as representing official policies,
26 | // either expressed or implied, of the FreeBSD Project.
27 |
28 | var glmat = require('gl-matrix');
29 | import * as tinycolor from 'tinycolor2';
30 | import * as collections from 'typescript-collections';
31 | import { Synthesizer } from './synthesizer';
32 | import { ShapeInstance } from './structure';
33 |
34 | // A cube
35 | var cubetriangles = [0, 1, 2, 1, 2, 3,
36 | 4, 5, 6, 5, 6, 7,
37 | 0, 1, 4, 1, 4, 5,
38 | 2, 3, 6, 3, 6, 7,
39 | 0, 2, 4, 2, 4, 6,
40 | 1, 3, 5, 3, 5, 7];
41 |
42 | var cubevertices = [[0, 0, 0, 1],
43 | [0, 0, 1, 1],
44 | [0, 1, 0, 1],
45 | [0, 1, 1, 1],
46 | [1, 0, 0, 1],
47 | [1, 0, 1, 1],
48 | [1, 1, 0, 1],
49 | [1, 1, 1, 1]];
50 |
51 | // An ico-sphere
52 | var spheretriangles = [0, 4, 2, 2, 4, 1,
53 | 1, 4, 3, 3, 4, 0,
54 | 0, 2, 5, 2, 1, 5,
55 | 1, 3, 5, 3, 0, 5];
56 |
57 | var spherevertices = [[1, 0, 0, 1],
58 | [-1, 0, 0, 1],
59 | [0, 1, 0, 1],
60 | [0, -1, 0, 1],
61 | [0, 0, 1, 1],
62 | [0, 0, -1, 1]];
63 |
64 | for (var vi = 0; vi < cubevertices.length; ++vi) {
65 | cubevertices[vi][0] -= 0.5;
66 | cubevertices[vi][1] -= 0.5;
67 | cubevertices[vi][2] -= 0.5;
68 | }
69 |
70 | var cubetransformed = new Array(cubevertices.length);
71 | for (var i = 0; i < cubetransformed.length; ++i) {
72 | cubetransformed[i] = [0, 0, 0, 1];
73 | }
74 |
75 | for (var i = 0; i < 3; ++i) {
76 | spheretriangles = Subdivide(spheretriangles, spherevertices);
77 | }
78 |
79 | for (var vi = 0; vi < spherevertices.length; ++vi) {
80 | spherevertices[vi][0] /= 2;
81 | spherevertices[vi][1] /= 2;
82 | spherevertices[vi][2] /= 2;
83 | }
84 |
85 | var sphereretransformed = new Array(spherevertices.length);
86 | for (var i = 0; i < sphereretransformed.length; ++i) {
87 | sphereretransformed[i] = [0, 0, 0, 1];
88 | }
89 |
90 |
91 | interface GeoGenProgressFunc {
92 | (ngenerated: number, done: boolean): void;
93 | }
94 |
95 | function AllocBuffers(structure: ShapeInstance[], progress: GeoGenProgressFunc): [Float32Array, Float32Array] {
96 | var nverts = 0;
97 | for (var si = 0; si < structure.length; ++si) {
98 | switch (structure[si].shape) {
99 | case "box":
100 | nverts += 36;
101 | break;
102 | case "sphere":
103 | nverts += 4 * 4 * 4 * 24;
104 | break;
105 | };
106 | }
107 | return [new Float32Array(3 * nverts), new Float32Array(3 * nverts)];
108 | }
109 |
110 | function GenerateShapeGeometry(
111 | triangles: number[],
112 | reference: number[][],
113 | transformed: number[][],
114 | transform: Float32Array,
115 | rgb: any,
116 | position: Float32Array,
117 | color: Float32Array,
118 | pi: number,
119 | ci: number
120 | ): [number, number] {
121 | for (var vi = 0; vi < reference.length; ++vi) {
122 | glmat.vec4.transformMat4(transformed[vi], reference[vi], transform);
123 | }
124 | for (var ti = 0; ti < triangles.length; ++ti) {
125 | var vert = transformed[triangles[ti]];
126 | position[pi++] = vert[0];
127 | position[pi++] = vert[1];
128 | position[pi++] = vert[2];
129 | color[ci++] = rgb.r / 255;
130 | color[ci++] = rgb.g / 255;
131 | color[ci++] = rgb.b / 255;
132 | // Alpha not supported yet
133 | }
134 | return [pi, ci];
135 | }
136 |
137 | function VertexForEdge(
138 | edges: collections.Dictionary<[number, number], number>,
139 | vertices: number[][],
140 | v0: number,
141 | v1: number
142 | ) {
143 | if (v0 > v1) {
144 | var tmp = v1;
145 | v1 = v0;
146 | v0 = tmp;
147 | }
148 |
149 | var vmid = edges.getValue([v0, v1]);
150 |
151 | if (undefined == vmid) {
152 | var vmid = vertices.length;
153 | edges.setValue([v0, v1], vmid);
154 |
155 | var v = [0, 0, 0, 1];
156 | glmat.vec3.add(v, vertices[v0], vertices[v1])
157 | glmat.vec3.normalize(v, v);
158 | vertices.push(v);
159 | }
160 |
161 | return vmid;
162 | }
163 |
164 | function Subdivide(
165 | triangles: number[],
166 | vertices: number[][]
167 | ): number[] {
168 | var edges = new collections.Dictionary<[number, number], number>();
169 | var result = [];
170 |
171 | for (var ti = 0; ti < triangles.length; ti += 3) {
172 | var vmid = [0, 0, 0];
173 | for (var edgei = 0; edgei < 3; ++edgei) {
174 | vmid[edgei] = VertexForEdge(edges, vertices, triangles[ti + edgei], triangles[ti + (edgei + 1) % 3]);
175 | }
176 |
177 | result.push(triangles[ti]);
178 | result.push(vmid[0]);
179 | result.push(vmid[2]);
180 |
181 | result.push(triangles[ti + 1]);
182 | result.push(vmid[1]);
183 | result.push(vmid[0]);
184 |
185 | result.push(triangles[ti + 2]);
186 | result.push(vmid[2]);
187 | result.push(vmid[1]);
188 |
189 | result.push(vmid[0]);
190 | result.push(vmid[1]);
191 | result.push(vmid[2]);
192 | }
193 |
194 | return result;
195 | }
196 |
197 | function GenerateGeometry(structure: ShapeInstance[], progress: GeoGenProgressFunc): [Float32Array, Float32Array] {
198 |
199 | var [position, color] = AllocBuffers(structure, progress);
200 |
201 | for (var si = 0, pi = 0, ci = 0; si < structure.length; ++si) {
202 |
203 | progress(si, false);
204 |
205 | var rgb = tinycolor(structure[si].colorspace).toRgb();
206 | switch (structure[si].shape) {
207 | case "box":
208 | [pi, ci] = GenerateShapeGeometry(cubetriangles, cubevertices, cubetransformed, structure[si].geospace, rgb, position, color, pi, ci);
209 | break;
210 | case "sphere":
211 | [pi, ci] = GenerateShapeGeometry(spheretriangles, spherevertices, sphereretransformed, structure[si].geospace, rgb, position, color, pi, ci);
212 | break
213 | };
214 | }
215 |
216 | progress(structure.length, true);
217 |
218 | return [position, color];
219 | }
220 |
221 | onmessage = function (e) {
222 |
223 | var worker = this;
224 |
225 | var synth = new Synthesizer(e.data, (() => {
226 | var nshapesLast = 0;
227 | return (shapes: ShapeInstance[], done: boolean) => {
228 | if (shapes.length - nshapesLast >= 100 || done) {
229 | (this).postMessage({ type: 'progress', nshapes: shapes.length, nprocessed: 0 });
230 | nshapesLast = shapes.length;
231 | }
232 | };
233 | })()
234 | );
235 |
236 | console.log('Synthesizing !');
237 | var tstamp = new Date().getTime();
238 | var structure = synth.synthesize();
239 | console.log('Synthesized in ' + (new Date().getTime() - tstamp) + 'ms');
240 |
241 | console.log('Detailing geometry !');
242 | tstamp = new Date().getTime();
243 | var [position, color] = GenerateGeometry(structure, (() => {
244 | var nshapesLast = 0;
245 | return (nshapes: number, done: boolean) => {
246 | if (nshapes - nshapesLast >= 100 || done) {
247 | (this).postMessage({ type: 'progress', nshapes: structure.length, nprocessed: nshapes });
248 | nshapesLast = nshapes;
249 | }
250 | };
251 | })()
252 | );
253 |
254 | console.log('Detailed in ' + (new Date().getTime() - tstamp) + 'ms');
255 |
256 | console.log('Posting structure !');
257 | tstamp = new Date().getTime();
258 | (worker).postMessage({
259 | type: 'done',
260 | background: synth.background,
261 | position: position.buffer,
262 | color: color.buffer
263 | }, [position.buffer, color.buffer]);
264 | console.log('Posted in ' + (new Date().getTime() - tstamp) + 'ms');
265 | }
266 |
267 |
--------------------------------------------------------------------------------
/mega-structure.ts:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016, Sebastien Sydney Robert Bigot
2 | // All rights reserved.
3 | //
4 | // Redistribution and use in source and binary forms, with or without
5 | // modification, are permitted provided that the following conditions are met:
6 | //
7 | // 1. Redistributions of source code must retain the above copyright notice, this
8 | // list of conditions and the following disclaimer.
9 | // 2. Redistributions in binary form must reproduce the above copyright notice,
10 | // this list of conditions and the following disclaimer in the documentation
11 | // and/or other materials provided with the distribution.
12 | //
13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 | //
24 | // The views and conclusions contained in the software and documentation are those
25 | // of the authors and should not be interpreted as representing official policies,
26 | // either expressed or implied, of the FreeBSD Project.
27 | import * as THREE from 'three';
28 | import * as CodeMirror from 'codemirror';
29 | import * as EisenScripts from './examples-generated';
30 | import { ShapeInstance } from './structure';
31 | import { Progress } from './progress';
32 |
33 | var renderer: THREE.WebGLRenderer;
34 |
35 | function toggleFullScreen() {
36 | var doc = document;
37 | if (!doc.mozFullScreenElement && !doc.webkitFullscreenElement) {
38 | var canvas = renderer.domElement;
39 | if (canvas.mozRequestFullScreen) {
40 | canvas.mozRequestFullScreen();
41 | } else {
42 | canvas.webkitRequestFullscreen((Element).ALLOW_KEYBOARD_INPUT);
43 | }
44 | } else {
45 | if (doc.mozCancelFullScreen) {
46 | doc.mozCancelFullScreen();
47 | } else {
48 | doc.webkitExitFullscreen();
49 | }
50 | }
51 | }
52 |
53 | window.addEventListener('load', () => {
54 |
55 | document.addEventListener("keydown", e => {
56 | if (e.keyCode == 122) {
57 | e.preventDefault();
58 | toggleFullScreen();
59 | }
60 | }, false);
61 |
62 | renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
63 | renderer.autoClear = false;
64 | renderer.setPixelRatio(window.devicePixelRatio);
65 | renderer.domElement.addEventListener("dblclick", e => {
66 | toggleFullScreen();
67 | }, false);
68 |
69 | var camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);
70 | camera.position.z = 1.5;
71 |
72 | var view = document.getElementById("structure-view");
73 | view.appendChild(renderer.domElement);
74 |
75 | var scene = new THREE.Scene();
76 | scene.add(camera);
77 |
78 | var progress = new Progress();
79 | var olaycam = new THREE.OrthographicCamera(-0.5 * camera.aspect, 0.5 * camera.aspect, -0.5, 0.5, 0.1, 10000);
80 | olaycam.position.z = 100;
81 | var olayscene = new THREE.Scene();
82 | olayscene.add(olaycam);
83 | olayscene.add(progress);
84 |
85 | var material =
86 | new THREE.MeshPhongMaterial({
87 | vertexColors: THREE.FaceColors,
88 | side: THREE.DoubleSide,
89 | flatShading: true
90 | });
91 |
92 | var mesh = new THREE.Mesh(
93 | new THREE.BoxGeometry(0, 0, 0),
94 | material
95 | );
96 |
97 | scene.add(mesh);
98 |
99 | var ambientLight = new THREE.AmbientLight(0x000000);
100 | scene.add(ambientLight);
101 |
102 | var lights = [];
103 | lights[0] = new THREE.PointLight(0xffffff, 1, 0);
104 | lights[1] = new THREE.PointLight(0xffffff, 1, 0);
105 | lights[2] = new THREE.PointLight(0xffffff, 1, 0);
106 |
107 | lights[0].position.set(0, 200, 0);
108 | lights[1].position.set(100, 200, 100);
109 | lights[2].position.set(-100, -200, -100);
110 |
111 | scene.add(lights[0]);
112 | scene.add(lights[1]);
113 | scene.add(lights[2]);
114 |
115 | var OrbitControls = require('three-orbit-controls')(THREE)
116 | var controls = new OrbitControls(camera, renderer.domElement);
117 | controls.enableKeys = false;
118 | controls.target.set(0, 0, 0);
119 |
120 | var turntable = true;
121 |
122 | var lastTime = new Date().getTime();
123 | function animate() {
124 |
125 | var timeNow = new Date().getTime();
126 |
127 | if (turntable) {
128 | var dt = (timeNow - lastTime) / (60 * 1000);
129 | var dtheta = 2 * Math.PI * 1 * dt
130 | mesh.rotation.x += dtheta;
131 | mesh.rotation.y += dtheta;
132 | }
133 |
134 | renderer.clear(true, true, true);
135 | renderer.render(scene, camera);
136 |
137 | if (progress.visible) {
138 | var s = renderer.getSize();
139 | progress.animate(timeNow);
140 | renderer.clear(false, true, true);
141 | renderer.render(olayscene, olaycam);
142 | }
143 |
144 | requestAnimationFrame(animate);
145 |
146 | lastTime = timeNow;
147 | }
148 |
149 | function doResize() {
150 | var doc = document;
151 | if (!doc.mozFullScreenElement && !doc.webkitFullscreenElement) {
152 | var w = view.offsetWidth, h = window.innerHeight - document.getElementById("header").offsetHeight;
153 | w *= 0.95;
154 | h *= 0.95;
155 | renderer.setSize(w, h);
156 | $(".CodeMirror").height(h + "px");
157 | } else {
158 | renderer.setSize(window.innerWidth, window.innerHeight);
159 | }
160 |
161 | var s = renderer.getSize();
162 | camera.aspect = s.width / s.height;
163 | camera.updateProjectionMatrix();
164 |
165 | olaycam.left = -0.5 * camera.aspect;
166 | olaycam.right = 0.5 * camera.aspect;
167 | olaycam.updateProjectionMatrix();
168 |
169 | progress.setPixelSize(s.height);
170 | };
171 | window.addEventListener('resize', doResize);
172 |
173 | require("./codemirror-eisen-script-mode.js");
174 | require('./node_modules/codemirror/addon/edit/matchbrackets.js');
175 |
176 | var opts = {
177 | lineNumbers: true,
178 | matchBrackets: true,
179 | mode: 'eisen-script',
180 | theme: 'twilight'
181 | };
182 | var codeMirror = CodeMirror(document.getElementById("structure-code"), opts);
183 |
184 | var tstamp = 0;
185 |
186 | function synthetize() {
187 | console.log('Synth request !');
188 | progress.init();
189 | tstamp = new Date().getTime();
190 | myWorker.postMessage(codeMirror.getValue());
191 | }
192 |
193 | function resetViewport() {
194 | var bbox = mesh.geometry.boundingBox;
195 | var diag = new THREE.Vector3();
196 | diag.subVectors(bbox.max, bbox.min);
197 |
198 | camera.position.x = 0;
199 | camera.position.y = 0;
200 | camera.position.z = Math.max(diag.x, diag.y) / Math.tan(0.5 * camera.fov * Math.PI / 180);
201 |
202 | camera.rotation.x = 0;
203 | camera.rotation.y = 0;
204 | camera.rotation.z = 0;
205 |
206 | controls.target.set(0, 0, 0);
207 | controls.update();
208 |
209 | mesh.rotation.x = 0;
210 | mesh.rotation.y = 0;
211 | }
212 |
213 | function toggleTurntable() {
214 | turntable = !turntable;
215 | }
216 |
217 | var exampleSelector = document.getElementById("examples");
218 |
219 | Object.keys(EisenScripts).forEach(e => {
220 | var opt = document.createElement('option');
221 | opt.value = opt.innerHTML = e;
222 | exampleSelector.appendChild(opt);
223 | });
224 |
225 | function exampleChanged() {
226 | codeMirror.setValue(EisenScripts[exampleSelector.value]);
227 | }
228 |
229 | exampleSelector.onchange = exampleChanged;
230 |
231 | document.getElementById("synthBtn").onclick = synthetize;
232 | document.getElementById("resetViewportBtn").onclick = resetViewport;
233 | document.getElementById("toggleTurntableBtn").onclick = toggleTurntable;
234 |
235 | var myWorker = new Worker("synthesizer-webworker.js");
236 | myWorker.onmessage = function (e) {
237 | var msg = e.data;
238 | switch (msg.type) {
239 | case 'progress':
240 | progress.update(msg);
241 | break;
242 | case 'done':
243 | renderer.setClearColor(new THREE.Color(msg.background));
244 | var geometry = new THREE.BufferGeometry();
245 | geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(msg.position), 3));
246 | geometry.addAttribute('color', new THREE.BufferAttribute(new Float32Array(msg.color), 3));
247 | geometry.center();
248 | mesh.geometry = geometry;
249 | resetViewport();
250 | console.log('Synth request processed in ' + (new Date().getTime() - tstamp) + 'ms');
251 | break;
252 | }
253 | }
254 |
255 | exampleSelector.value = 'frameinframe';
256 | exampleChanged();
257 | synthetize();
258 | requestAnimationFrame(animate);
259 | doResize();
260 | });
261 |
--------------------------------------------------------------------------------
/codemirror-eisen-script-mode.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016, Sebastien Sydney Robert Bigot
2 | // All rights reserved.
3 | //
4 | // Redistribution and use in source and binary forms, with or without
5 | // modification, are permitted provided that the following conditions are met:
6 | //
7 | // 1. Redistributions of source code must retain the above copyright notice, this
8 | // list of conditions and the following disclaimer.
9 | // 2. Redistributions in binary form must reproduce the above copyright notice,
10 | // this list of conditions and the following disclaimer in the documentation
11 | // and/or other materials provided with the distribution.
12 | //
13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 | //
24 | // The views and conclusions contained in the software and documentation are those
25 | // of the authors and should not be interpreted as representing official policies,
26 | // either expressed or implied, of the FreeBSD Project.
27 |
28 | (function (mod) {
29 | if (typeof exports == "object" && typeof module == "object") // CommonJS
30 | mod(require("./node_modules/codemirror/lib/codemirror"));
31 | else if (typeof define == "function" && define.amd) // AMD
32 | define(["./node_modules/codemirror/lib/codemirror"], mod);
33 | else // Plain browser env
34 | mod(CodeMirror);
35 | })(function (CodeMirror) {
36 | "use strict";
37 |
38 | CodeMirror.defineMode("eisen-script", function (config, parserConfig) {
39 | var indentUnit = config.indentUnit,
40 | statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
41 | dontAlignCalls = parserConfig.dontAlignCalls,
42 |
43 | indentStatements = parserConfig.indentStatements !== false,
44 |
45 | defKeywords = words("rule"),
46 |
47 | keywords = words("rule set md maxdepth w weight maxobjects minsize maxsize seed background h hue sat b brightness a alpha color blend initial x y z rx ry rz s fx fy fz m raytracer"),
48 |
49 | builtin = words("box grid sphere line point triangle mesh cylinder tube white black red green blue grey"),
50 |
51 | isPunctuationChar = /[\[\]{}\(\),;\:\.]/,
52 |
53 | numberStart = /[\d\.]/,
54 | number = /^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i,
55 |
56 | colorStart = /[\#]/,
57 | color = /^#[\da-f]{3}(?:[\da-f]{3})?/i,
58 |
59 | isOperatorChar = /[+\-/*]/,
60 |
61 | namespaceSeparator = "::";
62 |
63 | var curPunc, isDefKeyword;
64 |
65 | function Context(indented, column, type, align, prev) {
66 | this.indented = indented;
67 | this.column = column;
68 | this.type = type;
69 | this.align = align;
70 | this.prev = prev;
71 | }
72 |
73 |
74 | function isStatement(type) {
75 | return type == "statement";
76 | }
77 |
78 | function pushContext(state, col, type) {
79 | var indent = state.indented;
80 | if (state.context && isStatement(state.context.type) && !isStatement(type))
81 | indent = state.context.indented;
82 | return state.context = new Context(indent, col, type, null, state.context);
83 | }
84 |
85 | function popContext(state) {
86 | var t = state.context.type;
87 | if (t == ")" || t == "]" || t == "}")
88 | state.indented = state.context.indented;
89 | return state.context = state.context.prev;
90 | }
91 |
92 | function typeBefore(stream, state) {
93 | if (state.prevToken == "variable" || state.prevToken == "variable-3") return true;
94 | if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, stream.start))) return true;
95 | }
96 |
97 | function isTopScope(context) {
98 | for (; ;) {
99 | if (!context || context.type == "top") return true;
100 | if (context.type == "}") return false;
101 | context = context.prev;
102 | }
103 | }
104 |
105 |
106 |
107 | function words(str) {
108 | var obj = {}, words = str.split(" ");
109 | for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
110 | return obj;
111 | }
112 |
113 | function contains(words, word) {
114 | if (typeof words === "function") {
115 | return words(word);
116 | } else {
117 | return words.propertyIsEnumerable(word);
118 | }
119 | }
120 |
121 | function tokenComment(stream, state) {
122 | var maybeEnd = false, ch;
123 | while (ch = stream.next()) {
124 | if (ch == "/" && maybeEnd) {
125 | state.tokenize = null;
126 | break;
127 | }
128 | maybeEnd = (ch == "*");
129 | }
130 | return "comment";
131 | }
132 |
133 | function tokenBase(stream, state) {
134 | var ch = stream.next();
135 |
136 | if (isPunctuationChar.test(ch)) {
137 | curPunc = ch;
138 | return null;
139 | }
140 |
141 | if (colorStart.test(ch)) {
142 | stream.backUp(1)
143 | if (stream.match("#define")) {
144 | isDefKeyword = true;
145 | return "keyword";
146 | }
147 | if (stream.match(color)) return "number"
148 | stream.next()
149 | }
150 |
151 | if (numberStart.test(ch)) {
152 | stream.backUp(1)
153 | if (stream.match(number)) return "number"
154 | stream.next()
155 | }
156 | if (ch == "/") {
157 | if (stream.eat("*")) {
158 | state.tokenize = tokenComment;
159 | return tokenComment(stream, state);
160 | }
161 | if (stream.eat("/")) {
162 | stream.skipToEnd();
163 | return "comment";
164 | }
165 | }
166 |
167 | if (isOperatorChar.test(ch)) {
168 | stream.eatWhile(isOperatorChar);
169 | return "operator";
170 | }
171 |
172 | stream.eatWhile(/[\w\$_\xa1-\uffff]/);
173 | // set raytracer::
174 | //if (namespaceSeparator) while (stream.match(namespaceSeparator))
175 | // stream.eatWhile(/[\w\$_\xa1-\uffff]/);
176 |
177 | var cur = stream.current();
178 |
179 | if (contains(keywords, cur)) {
180 | if (contains(defKeywords, cur)) isDefKeyword = true;
181 | return "keyword";
182 | }
183 |
184 | if (contains(builtin, cur)) {
185 | return "builtin";
186 | }
187 |
188 | return "variable";
189 | }
190 |
191 |
192 |
193 | return {
194 | startState: function (basecolumn) {
195 | return {
196 | tokenize: null,
197 | context: new Context((basecolumn || 0) - indentUnit, 0, "top", false),
198 | indented: 0,
199 | startOfLine: true,
200 | prevToken: null
201 | };
202 | },
203 |
204 | token: function (stream, state) {
205 | var ctx = state.context;
206 | if (stream.sol()) {
207 | if (ctx.align == null) ctx.align = false;
208 | state.indented = stream.indentation();
209 | state.startOfLine = true;
210 | }
211 | if (stream.eatSpace()) return null;
212 | curPunc = isDefKeyword = null;
213 | var style = (state.tokenize || tokenBase)(stream, state);
214 | if (style == "comment" || style == "meta") return style;
215 | if (ctx.align == null) ctx.align = true;
216 |
217 | if (curPunc == "{") pushContext(state, stream.column(), "}");
218 | else if (curPunc == "[") pushContext(state, stream.column(), "]");
219 | else if (curPunc == "(") pushContext(state, stream.column(), ")");
220 | else if (curPunc == "}") {
221 | while (isStatement(ctx.type)) ctx = popContext(state);
222 | if (ctx.type == "}") ctx = popContext(state);
223 | while (isStatement(ctx.type)) ctx = popContext(state);
224 | }
225 | else if (curPunc == ctx.type) popContext(state);
226 | else if (indentStatements &&
227 | (((ctx.type == "}" || ctx.type == "top") && curPunc != ";") ||
228 | (isStatement(ctx.type) && curPunc == "newstatement"))) {
229 | var type = "statement";
230 | pushContext(state, stream.column(), type);
231 | }
232 |
233 | if (style == "variable" &&
234 | ((state.prevToken == "def" ||
235 | (parserConfig.typeFirstDefinitions && typeBefore(stream, state) &&
236 | isTopScope(state.context) && stream.match(/^\s*\(/, false)))))
237 | style = "def";
238 |
239 | if (style == "def" && parserConfig.styleDefs === false) style = "variable";
240 |
241 | state.startOfLine = false;
242 | state.prevToken = isDefKeyword ? "def" : style || curPunc;
243 | return style;
244 | },
245 |
246 | indent: function (state, textAfter) {
247 | if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
248 | var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
249 | if (isStatement(ctx.type) && firstChar == "}") ctx = ctx.prev;
250 |
251 | var closing = firstChar == ctx.type;
252 |
253 | if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) {
254 | while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev
255 | return ctx.indented
256 | }
257 | if (isStatement(ctx.type))
258 | return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
259 | if (ctx.align && (!dontAlignCalls || ctx.type != ")"))
260 | return ctx.column + (closing ? 0 : 1);
261 | if (ctx.type == ")" && !closing)
262 | return ctx.indented + statementIndentUnit;
263 |
264 | return ctx.indented + (closing ? 0 : indentUnit);
265 | },
266 |
267 | electricInput: /^\s*[{}]$/,
268 | blockCommentStart: "/*",
269 | blockCommentEnd: "*/",
270 | lineComment: "//",
271 | fold: "brace"
272 | };
273 | });
274 |
275 | });
276 |
--------------------------------------------------------------------------------
/eisen-script.peg:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2016, Sebastien Sydney Robert Bigot
2 | // All rights reserved.
3 | //
4 | // Redistribution and use in source and binary forms, with or without
5 | // modification, are permitted provided that the following conditions are met:
6 | //
7 | // 1. Redistributions of source code must retain the above copyright notice, this
8 | // list of conditions and the following disclaimer.
9 | // 2. Redistributions in binary form must reproduce the above copyright notice,
10 | // this list of conditions and the following disclaimer in the documentation
11 | // and/or other materials provided with the distribution.
12 | //
13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 | //
24 | // The views and conclusions contained in the software and documentation are those
25 | // of the authors and should not be interpreted as representing official policies,
26 | // either expressed or implied, of the FreeBSD Project.
27 | {
28 | function extractOptional(optional, index) {
29 | return optional ? optional[index] : null;
30 | }
31 |
32 | function optionalList(value) {
33 | return value !== null ? value : [];
34 | }
35 |
36 | function buildList(head, tail, index) {
37 | return [head].concat(extractList(tail, index));
38 | }
39 |
40 | function extractList(list, index) {
41 | var result = new Array(list.length), i;
42 |
43 | for (i = 0; i < list.length; i++) {
44 | result[i] = list[i][index];
45 | }
46 |
47 | return result;
48 | }
49 |
50 | var clauseid = 0;
51 | }
52 |
53 | Start = StatementSeq?
54 |
55 | StatementSeq
56 | = _? head:Statement tail:(_ Statement)* _? {
57 | return buildList(head, tail, 1);
58 | }
59 |
60 | Statement "statement"
61 | = SetStatement / RuleInvoc / RuleDef
62 |
63 | RuleDef "definition"
64 | = RuleToken _ rule:RuleName attributes:(_ RuleAttributes)* _? "{" _? production:RuleInvocSeq? _? "}" {
65 |
66 | var weight = 1;
67 | var failover = null;
68 | var maxdepth = -1;
69 |
70 | var attributes = extractList(attributes, 1);
71 | for (var i = 0; i < attributes.length; ++i) {
72 | var attribute = attributes[i];
73 | switch (attribute.type) {
74 | case "md":
75 | maxdepth = attribute.maxdepth;
76 | failover = attribute.failover;
77 | break;
78 | case "weight":
79 | weight = attribute.weight;
80 | break;
81 | }
82 | }
83 |
84 | return {type: "def", rule: rule, weight: weight, failover: failover, maxdepth: maxdepth, production: optionalList(production), id: clauseid++}
85 | }
86 |
87 | RuleInvocSeq "production"
88 | = head:RuleInvoc tail:(_ RuleInvoc)* {
89 | return buildList(head, tail, 1);
90 | }
91 |
92 | RuleAttributes "attributes"
93 | = MaxDepthAttribute / WeightAttribute
94 |
95 | MaxDepthAttribute "maxdepth"
96 | = MaxDepthToken _ maxdepth:UnsignedIntegerLitteral failover:(_? Failover)? {
97 | return {type: "md", maxdepth: maxdepth, failover: extractOptional(failover, 1)};
98 | }
99 |
100 | Failover "failover"
101 | = ">" _? rule:RuleName {
102 | return rule;
103 | }
104 |
105 | WeightAttribute "weight modifier"
106 | = WeightToken _ weight:Float {
107 | return {type: "weight" , weight: weight};
108 | }
109 |
110 | RuleName "name"
111 | = !Reserved identifier:Identifier {
112 | return identifier
113 | }
114 |
115 | RuleInvoc "invocation"
116 | = transformations:Transformation* _? next:Next {
117 | return {type: "invoc", transformations: transformations, next: next}
118 | }
119 |
120 | Next = name:RuleName {
121 | return {type: "call", name: name}
122 | }
123 | / name:Shape {
124 | return {type: "shape", name: name}
125 | }
126 |
127 | Transformation "transformation"
128 | = _? multiplier:Multiplier? "{" _? sequence:ElementaryTransformationSeq? _? "}" {
129 | return {multiplier: multiplier ? multiplier : 1, sequence: optionalList(sequence)};
130 | }
131 |
132 | Multiplier "multiplier"
133 | = multiplier:UnsignedIntegerLitteral _? "*" _? {
134 | return multiplier;
135 | }
136 |
137 | ElementaryTransformationSeq
138 | = head:ElementaryTransformation tail:(_ ElementaryTransformation)* {
139 | return buildList(head, tail, 1);
140 | }
141 |
142 | ElementaryTransformation
143 | = Tx / Ty / Tz / Rx / Ry / Rz / Scale / Fx / Fy / Fz / Matrix / Hue / Sat / Bright / Alpha / SetColor / Blend
144 |
145 | // 3D-space transforms
146 |
147 | Tx "translate x"
148 | = TxToken _ dx:ArithExpression {
149 | return {type: "trans", t: [dx, 0, 0]}
150 | }
151 |
152 | Ty "translate y"
153 | = TyToken _ dy:ArithExpression {
154 | return {type: "trans", t: [0, dy, 0]}
155 | }
156 |
157 | Tz "translate z"
158 | = TzToken _ dz:ArithExpression {
159 | return {type: "trans", t: [0, 0, dz]}
160 | }
161 |
162 | Rx "rotate x"
163 | = RxToken _ theta:ArithExpression {
164 | return {type: "rot", axis: 0, theta: theta}
165 | }
166 |
167 | Ry "rotate y"
168 | = RyToken _ theta:ArithExpression {
169 | return {type: "rot", axis: 1, theta: theta}
170 | }
171 |
172 | Rz "rotate z"
173 | = RzToken _ theta:ArithExpression {
174 | return {type: "rot", axis: 2, theta: theta}
175 | }
176 |
177 | Scale "scale"
178 | = ScaleToken _ x:ArithExpression yz:(_ ArithExpression _ ArithExpression)? {
179 | return {type: "scale", s: yz ? [x, yz[1], yz[3]] : [x, x, x]};
180 | }
181 |
182 | Fx "flip x"
183 | = FxToken {
184 | return {type: "scale", s: [-1, 1, 1]};
185 | }
186 |
187 | Fy "flip y"
188 | = FyToken {
189 | return {type: "scale", s: [1, -1, 1]};
190 | }
191 |
192 | Fz "flip z"
193 | = FzToken {
194 | return {type: "scale", s: [1, 1, -1]};
195 | }
196 |
197 | Matrix "matrix"
198 | = MatrixToken m:(_ ArithExpression)[9] {
199 | return {type: "matrix", m: extractList(m, 1)};
200 | }
201 |
202 | // // TODO Colorspace transforms
203 | Hue "hue"
204 | = HueToken _ hue:ArithExpression {
205 | return {type: "hue", h: hue};
206 | }
207 |
208 | Sat "saturation"
209 | = SatToken _ sat:ArithExpression {
210 | return {type: "sat", s: sat};
211 | }
212 |
213 | Bright "brightness"
214 | = BrightnessToken _ brightness:ArithExpression {
215 | return {type: "brightness", v: brightness};
216 | }
217 |
218 | Alpha "alpha"
219 | = AlphaToken _ alpha:ArithExpression {
220 | return {type: "alpha", a: alpha};
221 | }
222 |
223 | SetColor "set color"
224 | = ColorToken _ color:Color {
225 | return {type: "color", color: color};
226 | }
227 |
228 | Blend "blend"
229 | = BlendToken _ a:ArithExpression _ b:ArithExpression {
230 | return {blend: [a, b]}
231 | }
232 |
233 | // TODO
234 | // set color random
235 | // set colorpool [scheme]
236 |
237 | Shape "shape"
238 | = "box" / "grid" / "sphere" / "line" / "point" / "triangle" / "mesh" / "cylinder" / "tube"
239 |
240 | SetStatement "set statement"
241 | = SetToken _ what:(Maxdepth / MaxObjects / Minsize / Naxsize / Seed / Background / Raytracer) {
242 | return what
243 | }
244 |
245 | Maxdepth "maxdepth"
246 | = MaxDepthToken _? max:UnsignedIntegerLitteral {
247 | return {type: "maxdepth", max: max};
248 | }
249 |
250 | MaxObjects "maxobjects"
251 | = MaxObjectsToken _? max:UnsignedIntegerLitteral {
252 | return {type: "maxobjects", max: max};
253 | }
254 |
255 | Minsize "minsize"
256 | = MinSizeToken _? min:Float {
257 | return {type: "minsize", min: min};
258 | }
259 |
260 | Naxsize "maxsize"
261 | = MaxSizeToken _? max:Float {
262 | return {type: "maxsize", max: max};
263 | }
264 |
265 | Seed "seed"
266 | = SeedToken _? seed:Integer {
267 | return {type: "seed", seed: seed};
268 | }
269 | / SeedToken _? "initial" // TODO
270 |
271 | Background "background"
272 | = BackgroundToken _? color:Color {
273 | return {type: "background", color: color};
274 | }
275 |
276 | // For structure-synth compat
277 | Raytracer "raytracer"
278 | = RaytracerToken (!LineTerminator SourceCharacter)*
279 |
280 | DecimalDigit
281 | = [0-9]
282 |
283 | NonZeroDigit
284 | = [1-9]
285 |
286 | HexDigit "hex digit"
287 | = d:[0-9a-f]i
288 |
289 | Character "character"
290 | = [a-z]i
291 |
292 | UnsignedIntegerLitteral
293 | = UnsignedInteger {
294 | return parseInt(text());
295 | }
296 |
297 | UnsignedInteger "integer"
298 | = "0" / NonZeroDigit DecimalDigit*
299 |
300 | Integer
301 | = [+-]? UnsignedInteger
302 |
303 | ExponentPart
304 | = ExponentIndicator Integer
305 |
306 | ExponentIndicator
307 | = "e"i
308 |
309 | // Litterals
310 | Float "float"
311 | = (Integer "." DecimalDigit* ExponentPart? / "." DecimalDigit+ ExponentPart? / Integer ExponentPart?) {
312 | return parseFloat(text());
313 | }
314 |
315 |
316 | Color "color"
317 | = "#" HexDigit HexDigit HexDigit (HexDigit HexDigit HexDigit)? {
318 | return text()
319 | }
320 | / ColorName {
321 | return text()
322 | }
323 |
324 | ColorName = "white" / "black" / "red" / "green" / "blue" / "grey"
325 |
326 | // Simple arithmetic
327 | ArithExpression "arithmetic expression"
328 | = head:ArithTerm tail:(_? ("+" / "-") _? ArithTerm)* {
329 | var result = head, i;
330 |
331 | for (i = 0; i < tail.length; i++) {
332 | if (tail[i][1] === "+") { result += tail[i][3]; }
333 | if (tail[i][1] === "-") { result -= tail[i][3]; }
334 | }
335 |
336 | return result;
337 | }
338 |
339 | ArithTerm
340 | = head:ArithFactor tail:(_? ("*" / "/") _? ArithFactor)* {
341 | var result = head, i;
342 |
343 | for (i = 0; i < tail.length; i++) {
344 | if (tail[i][1] === "*") { result *= tail[i][3]; }
345 | if (tail[i][1] === "/") { result /= tail[i][3]; }
346 | }
347 |
348 | return result;
349 | }
350 |
351 | ArithFactor
352 | = "(" _? expr:ArithExpression _? ")" { return expr; }
353 | / Float
354 |
355 | Identifier "identifier"
356 | = head:IdentifierStart tail:IdentifierPart* { return head + tail.join("") }
357 |
358 | IdentifierStart
359 | = "_" / Character
360 |
361 | IdentifierPart
362 | = IdentifierStart / DecimalDigit
363 |
364 | SourceCharacter = .
365 |
366 | WhiteSpace "whitespace" = "\t" / "\v" / "\f" / " "
367 |
368 | LineTerminator = [\n\r]
369 |
370 | LineTerminatorSequence "end of line" = "\n" / "\r\n" / "\r"
371 |
372 | Comment "comment"
373 | = MultiLineComment
374 | / SingleLineComment
375 |
376 | MultiLineComment
377 | = "/*" (!"*/" SourceCharacter)* "*/"
378 |
379 | SingleLineComment
380 | = "//" (!LineTerminator SourceCharacter)*
381 | _
382 | = (WhiteSpace / LineTerminatorSequence / Comment)+
383 |
384 | SetToken = "set" !IdentifierPart
385 | RuleToken = "rule" !IdentifierPart
386 | MaxDepthToken = "md" !IdentifierPart / "maxdepth" !IdentifierPart
387 | WeightToken = "w" !IdentifierPart / "weight" !IdentifierPart
388 | MaxObjectsToken = "maxobjects" !IdentifierPart
389 | MinSizeToken = "minsize" !IdentifierPart
390 | MaxSizeToken = "maxsize" !IdentifierPart
391 | SeedToken = "seed" !IdentifierPart
392 | BackgroundToken = "background" !IdentifierPart
393 | HueToken = "h" !IdentifierPart / "hue" !IdentifierPart
394 | SatToken = "sat" !IdentifierPart
395 | BrightnessToken = "b" !IdentifierPart / "brightness" !IdentifierPart
396 | AlphaToken = "a" !IdentifierPart / "alpha" !IdentifierPart
397 | ColorToken = "color" !IdentifierPart
398 | BlendToken = "blend" !IdentifierPart
399 | InitialToken = "initial" !IdentifierPart
400 | TxToken = "x" !IdentifierPart
401 | TyToken = "y" !IdentifierPart
402 | TzToken = "z" !IdentifierPart
403 | RxToken = "rx" !IdentifierPart
404 | RyToken = "ry" !IdentifierPart
405 | RzToken = "rz" !IdentifierPart
406 | ScaleToken = "s" !IdentifierPart
407 | FxToken = "fx" !IdentifierPart
408 | FyToken = "fy" !IdentifierPart
409 | FzToken = "fz" !IdentifierPart
410 | MatrixToken = "m" !IdentifierPart
411 | RaytracerToken = "raytracer" !IdentifierPart
412 |
413 | Reserved "reserved"
414 | = SetToken
415 | / RuleToken
416 | / MaxDepthToken
417 | / WeightToken
418 | / MaxObjectsToken
419 | / MinSizeToken
420 | / MaxSizeToken
421 | / SeedToken
422 | / BackgroundToken
423 | / HueToken
424 | / SatToken
425 | / BrightnessToken
426 | / AlphaToken
427 | / ColorToken
428 | / BlendToken
429 | / InitialToken
430 | / TxToken
431 | / TyToken
432 | / TzToken
433 | / RxToken
434 | / RyToken
435 | / RzToken
436 | / ScaleToken
437 | / FxToken
438 | / FyToken
439 | / FzToken
440 | / MatrixToken
441 | / RaytracerToken
442 | / Shape !IdentifierPart
443 | / ColorName !IdentifierPart
444 |
--------------------------------------------------------------------------------
/synthesizer.ts:
--------------------------------------------------------------------------------
1 | 1// Copyright (c) 2016, Sebastien Sydney Robert Bigot
2 | // All rights reserved.
3 | //
4 | // Redistribution and use in source and binary forms, with or without
5 | // modification, are permitted provided that the following conditions are met:
6 | //
7 | // 1. Redistributions of source code must retain the above copyright notice, this
8 | // list of conditions and the following disclaimer.
9 | // 2. Redistributions in binary form must reproduce the above copyright notice,
10 | // this list of conditions and the following disclaimer in the documentation
11 | // and/or other materials provided with the distribution.
12 | //
13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 | // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 | // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 | // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 | // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 | // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 | // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 | //
24 | // The views and conclusions contained in the software and documentation are those
25 | // of the authors and should not be interpreted as representing official policies,
26 | // either expressed or implied, of the FreeBSD Project.
27 |
28 | var eisenscript = require('./eisen-script');
29 | var glmat = require('gl-matrix');
30 |
31 | import { ShapeInstance } from './structure';
32 | import * as tinycolor from 'tinycolor2';
33 | import * as collections from 'typescript-collections';
34 | import * as seedrandom from 'seedrandom';
35 |
36 | enum Axis { X, Y, Z };
37 |
38 | interface ASTNode {
39 | type: string;
40 | }
41 |
42 | interface DefStatement extends ASTNode {
43 | rule: string;
44 | weight: number;
45 | maxdepth: number;
46 | failover: string;
47 | production: InvocStatement[];
48 | id: number;
49 | }
50 |
51 | interface InvocStatement extends ASTNode {
52 | transformations: Transformation[];
53 | next: NextNode;
54 | }
55 |
56 | interface SetStatement extends ASTNode {
57 | // TODO
58 | }
59 |
60 | interface NextNode extends ASTNode {
61 | name: string;
62 | }
63 |
64 | interface Transformation {
65 | multiplier: number;
66 | sequence: ASTNode[];
67 | }
68 |
69 | interface TransNode extends ASTNode {
70 | t: number[];
71 | }
72 |
73 | interface RotNode extends ASTNode {
74 | axis: Axis;
75 | theta: number;
76 | }
77 |
78 | interface ScaleNode extends ASTNode {
79 | s: number[];
80 | }
81 |
82 | interface MatrixNode extends ASTNode {
83 | m: number[];
84 | }
85 |
86 | interface ColorNode extends ASTNode {
87 | color: string;
88 | }
89 |
90 | interface HueNode extends ASTNode {
91 | h: number;
92 | }
93 |
94 | interface SatNode extends ASTNode {
95 | s: number;
96 | }
97 |
98 | interface BrightnessNode extends ASTNode {
99 | v: number;
100 | }
101 |
102 | interface AlphaNode extends ASTNode {
103 | a: number;
104 | }
105 |
106 | interface MaxNode extends ASTNode {
107 | max: number;
108 | }
109 |
110 | interface MinNode extends ASTNode {
111 | min: number;
112 | }
113 |
114 | interface SeedNode extends ASTNode {
115 | seed: number;
116 | }
117 |
118 |
119 | interface SynthFrame {
120 | rule: string;
121 | globalDepth: number;
122 | clauseDepthMap: collections.Dictionary;
123 | geospace: Float32Array;
124 | colorspace: tinycolor.ColorFormats.HSVA;
125 | }
126 |
127 | function clamp(value: number, min: number, max: number): number {
128 | return Math.min(Math.max(value, min), max);
129 | };
130 |
131 | function normalizeAngle(angle: number, lower: number) {
132 | var upper = lower + 360;
133 | while (angle < lower) {
134 | angle += 360;
135 | }
136 | while (angle > upper) {
137 | angle -= 360;
138 | }
139 | return angle;
140 | }
141 |
142 | interface ProgressFunc {
143 | (nhapes: ShapeInstance[], done: boolean): void;
144 | }
145 |
146 | export class Synthesizer {
147 |
148 | public constructor(script: string, progress: ProgressFunc) {
149 | this.ast = eisenscript.parse(script);
150 | this.index = Synthesizer.indexRules(this.ast);
151 |
152 | this.background = tinycolor("black").toHexString();
153 |
154 | // Let them crash their browser if they're keen.
155 | this.maxObjects = Infinity;
156 | this.maxDepth = Infinity;
157 | this.maxSize = Infinity;
158 | this.minSize = 0;
159 |
160 | this.prng = seedrandom();
161 | this.progress = progress;
162 | }
163 |
164 | private static indexRules(ast: ASTNode[]): collections.Dictionary {
165 | var index = new collections.Dictionary();
166 | for (var si = 0; si < ast.length; ++si) {
167 | if (ast[si].type == "def") {
168 | var def = ast[si];
169 | var wclauses = index.getValue(def.rule);
170 | if (!wclauses) {
171 | wclauses = [0, []];
172 | index.setValue(def.rule, wclauses);
173 | }
174 | wclauses[0] += def.weight;
175 | wclauses[1].push(def);
176 | }
177 | }
178 | index.forEach(function (rule: string, wclauses: [number, DefStatement[]]): void {
179 | wclauses[1].sort(function (left: DefStatement, right: DefStatement): number {
180 | return left.weight - right.weight;
181 | });
182 | });
183 | return index;
184 | }
185 |
186 | private pickClause(rule: string): DefStatement {
187 | var wclauses = this.index.getValue(rule);
188 | var guess = wclauses[0] * this.prng();
189 | for (var ci = 0; ci < wclauses[1].length; ++ci) {
190 | var clause = wclauses[1][ci];
191 | guess -= clause.weight;
192 | if (guess < 0) {
193 | return clause;
194 | }
195 | }
196 | }
197 |
198 | public synthesize(): ShapeInstance[] {
199 | var shapes = new Array();
200 |
201 | for (var si = 0; si < this.ast.length; ++si) {
202 | switch (this.ast[si].type) {
203 | case "invoc":
204 | this.synthesizeOne(this.ast[si], shapes);
205 | break;
206 | case "maxobjects":
207 | this.maxObjects = (this.ast[si]).max;
208 | break;
209 | case "maxdepth":
210 | this.maxDepth = (this.ast[si]).max;
211 | break;
212 | case "background":
213 | this.background = tinycolor((this.ast[si]).color).toHexString();
214 | break;
215 | case "minsize":
216 | this.minSize = (this.ast[si]).min;
217 | break;
218 | case "masize":
219 | this.maxSize = (this.ast[si]).max;
220 | break;
221 | case "seed":
222 | this.prng = seedrandom((this.ast[si]).seed.toString());
223 | break;
224 | }
225 | }
226 |
227 | this.progress(shapes, true);
228 |
229 | return shapes;
230 | }
231 |
232 | private synthesizeOne(prod: InvocStatement, shapes: ShapeInstance[]): void {
233 |
234 | var queue = new collections.Queue();
235 |
236 | this.synthProduction(prod, 0, new collections.Dictionary(), glmat.mat4.create(), tinycolor("RED").toHsv(), queue, shapes);
237 |
238 | while (!queue.isEmpty()) {
239 |
240 | var { rule, globalDepth, clauseDepthMap, geospace, colorspace } = queue.dequeue();
241 |
242 | if (globalDepth >= this.maxDepth) {
243 | continue;
244 | }
245 | ++globalDepth;
246 |
247 | var clause = this.pickClause(rule);
248 |
249 | var clauseDepthMapCopy = new collections.Dictionary();
250 | clauseDepthMap.forEach(function (key: number, value: number) {
251 | clauseDepthMapCopy.setValue(key, value);
252 | });
253 |
254 | if (clause.maxdepth >= 0) {
255 |
256 | var thisClauseDepth = clauseDepthMapCopy.getValue(clause.id);
257 |
258 | if (undefined == thisClauseDepth) {
259 | thisClauseDepth = 0;
260 | }
261 |
262 | if (thisClauseDepth >= clause.maxdepth) {
263 | if (clause.failover) {
264 | queue.enqueue({
265 | rule: clause.failover,
266 | globalDepth: globalDepth + 1,
267 | clauseDepthMap: clauseDepthMapCopy,
268 | geospace, colorspace
269 | });
270 | }
271 | continue;
272 | } else {
273 | clauseDepthMapCopy.setValue(clause.id, thisClauseDepth + 1);
274 | }
275 | }
276 |
277 | for (var pi = 0; pi < clause.production.length; ++pi) {
278 | this.synthProduction(clause.production[pi], globalDepth, clauseDepthMapCopy, geospace, colorspace, queue, shapes);
279 |
280 | if (shapes.length >= this.maxObjects) {
281 | return;
282 | }
283 |
284 | }
285 | }
286 | }
287 |
288 | private synthProduction(prod: InvocStatement,
289 | globalDepth: number,
290 | clauseDepthMap: collections.Dictionary,
291 | geospace: Float32Array,
292 | colorspace: tinycolor.ColorFormats.HSVA,
293 | queue: collections.Queue,
294 | shapes: ShapeInstance[]): void {
295 |
296 | var [childGeospaces, childColorspaces] = this.transform(prod.transformations, geospace, colorspace);
297 |
298 | console.assert(childGeospaces.length == childColorspaces.length);
299 |
300 | switch (prod.next.type) {
301 | case "shape":
302 | for (var mi = 0; mi < childGeospaces.length; ++mi) {
303 | var diag = [1, 1, 1, 0];
304 | glmat.vec4.transformMat4(diag, diag, childGeospaces[mi]);
305 | var size = glmat.vec4.length(diag);
306 | if (this.minSize <= size && size <= this.maxSize) {
307 | shapes.push({ shape: prod.next.name, geospace: childGeospaces[mi], colorspace: childColorspaces[mi] });
308 | this.progress(shapes, false);
309 | if (shapes.length >= this.maxObjects) {
310 | console.log("max objects reached");
311 | return;
312 | }
313 | }
314 | }
315 | break;
316 | case "call":
317 | for (var mi = 0; mi < childGeospaces.length; ++mi) {
318 | var diag = [1, 1, 1, 0];
319 | glmat.vec4.transformMat4(diag, diag, childGeospaces[mi]);
320 | var size = glmat.vec4.length(diag);
321 | if (this.minSize <= size && size <= this.maxSize) {
322 | queue.enqueue({ rule: prod.next.name, globalDepth, clauseDepthMap, geospace: childGeospaces[mi], colorspace: childColorspaces[mi] });
323 | }
324 | }
325 | break;
326 | }
327 | }
328 |
329 | private transform(transforms: Transformation[], geospace: Float32Array, colorspace: tinycolor.ColorFormats.HSVA): [Float32Array[], tinycolor.ColorFormats.HSVA[]] {
330 |
331 | var childGeospaces = new Array();
332 | var childColorspaces = new Array();
333 |
334 | var stack = new collections.Stack<[number, Float32Array, tinycolor.ColorFormats.HSVA]>();
335 | stack.push([0, geospace, colorspace]);
336 | while (!stack.isEmpty()) {
337 | var [ti, childGeospace, childColorSpace] = stack.pop();
338 | if (ti < transforms.length) {
339 | var trans = transforms[ti];
340 | for (var repeat = 0; repeat < trans.multiplier; ++repeat) {
341 | var [childGeospace, childColorSpace] = this.transformOne(transforms[ti].sequence, childGeospace, childColorSpace);
342 | stack.push([ti + 1, childGeospace, childColorSpace]);
343 | }
344 | } else {
345 | childGeospaces.push(childGeospace);
346 | childColorspaces.push(childColorSpace);
347 | }
348 | };
349 |
350 | return [childGeospaces, childColorspaces];
351 | }
352 |
353 | private transformOne(sequence: ASTNode[], geospace: Float32Array, colorspace: tinycolor.ColorFormats.HSVA): [Float32Array, tinycolor.ColorFormats.HSVA] {
354 |
355 | var childGeospace = new Float32Array(geospace);
356 | var childColorspace = { h: colorspace.h, s: colorspace.s, v: colorspace.v, a: colorspace.a };
357 |
358 | for (var si = 0; si < sequence.length; ++si) {
359 | switch (sequence[si].type) {
360 | case "trans":
361 | var trans = sequence[si];
362 | glmat.mat4.translate(childGeospace, childGeospace, trans.t);
363 | break;
364 | case "rot":
365 | var rot = sequence[si];
366 | var thetaRad = rot.theta * Math.PI / 180
367 | switch (rot.axis) {
368 | case Axis.X:
369 | glmat.mat4.rotateX(childGeospace, childGeospace, thetaRad);
370 | break;
371 | case Axis.Y:
372 | glmat.mat4.rotateY(childGeospace, childGeospace, thetaRad);
373 | break;
374 | case Axis.Z:
375 | glmat.mat4.rotateZ(childGeospace, childGeospace, thetaRad);
376 | break;
377 | }
378 | break;
379 | case "scale":
380 | var scale = sequence[si];
381 | glmat.mat4.scale(childGeospace, childGeospace, scale.s);
382 | break;
383 | case "matrix":
384 | var matrix = sequence[si];
385 |
386 | var m = [matrix.m[0], matrix.m[1], matrix.m[2], 0,
387 | matrix.m[3], matrix.m[4], matrix.m[5], 0,
388 | matrix.m[6], matrix.m[7], matrix.m[8], 0,
389 | 0, 0, 0, 1];
390 |
391 | glmat.mat4.multiply(childGeospace, childGeospace, m);
392 | break;
393 | case "color":
394 | var color = sequence[si];
395 | childColorspace = tinycolor(color.color).toHsv();
396 | break;
397 | case "sat":
398 | var sat = sequence[si];
399 | childColorspace.s = clamp(childColorspace.s * sat.s, 0, 1);
400 | break;
401 | case "hue":
402 | var hue = sequence[si];
403 | childColorspace.h = normalizeAngle(childColorspace.h + hue.h, 0);
404 | break;
405 | case "brightness":
406 | var brightness = sequence[si];
407 | childColorspace.v = clamp(childColorspace.v * brightness.v, 0, 1);
408 | break;
409 | case "alpha":
410 | var alpha = sequence[si];
411 | childColorspace.a = clamp(childColorspace.a * alpha.a, 0, 1);
412 | break;
413 | }
414 | }
415 |
416 | return [childGeospace, childColorspace];
417 | }
418 |
419 | private ast: ASTNode[];
420 | private maxObjects: number;
421 | private maxDepth: number;
422 | private maxSize: number;
423 | private minSize: number;
424 | private index: collections.Dictionary;
425 | private prng: seedrandom.prng;
426 | private progress: ProgressFunc;
427 | public background: string;
428 |
429 | }
430 |
--------------------------------------------------------------------------------